前端:Vue学习 - 智慧商城项目

前端:Vue学习 - 智慧商城项目

    • 1. vue组件库 => vant-ui
    • 2. postcss插件 => vw 适配
    • 3. 路由配置
    • 4. 登录页面静态布局
      • 4.1 封装axios实例访问验证码接口
      • 4.2 vant 组件 => 轻提示
      • 4.3 短信验证倒计时
      • 4.4 登录功能
      • 4.5 响应拦截器 => 统一处理错误
      • 4.6 登录权证信息存储
      • 4.7 storage存储模块 => vuex持久化处理
      • 4.8 添加请求loading效果
    • 5. 页面访问拦截
    • 6. 首页布局
    • 7. 搜索历史管理
    • 8. 商品详情页
    • 9. 加入购物车

1. vue组件库 => vant-ui

安装命令为:

npm i vant@latest-v2

vant官网链接为:vant,默认打开的是vant最新版本4,这里使用vant2,链接为:vant2
有两种导入方式,全部导入和按需导入,全部导入直接在main.js加入下述代码即可。

import Vue from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';Vue.use(Vant);

此时在vue文件中就可以添加相应的vant组件代码了,但是性能低。
按需导入首先需要安装babel-plugin-import插件,插件下载命令如下:

//安装插件
npm i babel-plugin-import -D

然后在babel.config.js文件中添加下述代码:

module.exports = {plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',style: true}, 'vant']]
};

之后只需要在main.js中引入vant中对应组件,并进行注册即可。

import { Button } from 'vant';Vue.use(Button)

随着项目逐渐增大,可以把对应的按需导入组件代码写到专门的js文件中,然后在main.js中引入即可。

2. postcss插件 => vw 适配

postcss插件相关内容链接为:postcss
postcss插件安装命令为:

npm install postcss-px-to-viewport@1.1.1 -D

然后在根项目下新建postcss.config.js文件,填入下述配置

module.exports = {plugins:{"postcss-px-to-viewport":{viewportWidth: 375// 标准屏幕宽度}}
}

在这里插入图片描述
上述设置了375,此时 80vw=300px,计算方式为 300/375*100 = 80。

3. 路由配置

凡是单个页面独立展示的,都是一级路由。
一级路由有登录页面、首页、搜索页、搜索列表页、商品详情页、结算支付页、订单管理页;二级路由在首页下有首页、分类、购物车、我的。
路由配置代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入一级路由组件
import Login from "@/views/login/Login.vue"
import Layout from "@/views/layout/Index.vue"
import MyOrder from '@/views/myorder/MyOrder.vue'
import Pay from '@/views/pay/Pay.vue'
import ProDetail from '@/views/prodetail/ProDetail.vue'
import List from '@/views/search/List.vue'
import Index2 from '@/views/search/Index2.vue'
// 引入二级路由组件
import Home from '@/views/layout/Home.vue'
import Cart from '@/views/layout/Cart.vue'
import Category from '@/views/layout/Category.vue'
import User from '@/views/layout/User.vue'Vue.use(VueRouter)const routes = [{path:"/login",component:Login},{path:"/",component:Layout,children:[// 二级路由{path:"/home",component:Home},{path:"/category",component:Category},{path:"/cart",component:Cart},{path:"/user",component:User}],redirect:"/home"// 重定向},{path:"/search",component:Index2},{path:"/searchList",component:List},{path:"/prodetail/:id",component:ProDetail},// 动态路由{path:"/pay",component:Pay},{path:"/myorder",component:MyOrder}
]const router = new VueRouter({routes
})export default router

运行结果:
请添加图片描述

4. 登录页面静态布局

使用vant的NavBar组件

import { NavBar } from 'vant';// 导航栏
Vue.use(NavBar);
<template><div class="login"><van-nav-bar title="登录" left-arrow @click-left="onClickLeft"/><div class="login-b"><p class="login-info">手机号注册</p><p class="login-info-2">未注册的手机号登录后会自动注册</p><input type="text" placeholder="请输入手机号码" class="phone margin-b-20"><div class="img-code margin-b-20"><input type="text" placeholder="请输入图形验证码"><img src="@/static/code.png" alt=""></div><div class="xx-code margin-b-20"><input type="text" placeholder="请输入短信验证码"><button>获取验证码</button></div><button class="login-btn">登录</button></div></div>
</template><script>
export default {name:"Login",methods:{onClickLeft(){this.$router.back();}}
}
</script><style lang="less" scoped>.login-b{padding-top: 40px;padding-left: 30px;padding-right: 30px;.login-info{font-size: 35px;font-weight: 500;}.login-info-2{color: rgb(220, 223, 227);padding: 9px 0;font-size:14px;}input{width: 100%;border: none;border-bottom: 1px solid rgb(220, 223, 227) !important;height: 50px;line-height: 50px;font-size: 16px;}.margin-b-20{margin-bottom: 20px;}.img-code{position: relative;input{width: 220px;}img{width: 75px;position: absolute;bottom: 5px;right: 0;}}.login-btn{color: white;background-color: rgb(233, 189, 32);border: none ;width: 100%;height: 40px;line-height: 40px;border-radius: 20px;margin-top: 14px;}.xx-code{position: relative;input{width: 220px;}button{position: absolute;right: 0;bottom: 5px;border: none;background-color: white;color: rgb(233, 189, 32);}}}
</style>

运行结果:
请添加图片描述

4.1 封装axios实例访问验证码接口

封装axios实例,js代码如下:

import axios from 'axios'const instance = axios.create({baseURL: 'http://localhost:9998/w',timeout: 1000,headers: { 'X-Custom-Header': 'foobar' }
});// 添加请求拦截器
instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data;
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});export default instance
// 导出配置

使用封装好的axios实例访问接口,如下:

import request from '@/utils/request.js'export const getPicCode = function(){return request.get('?str_1=/captcha/image')
}

按需导出

4.2 vant 组件 => 轻提示

使用Toast请提示,注册安装

import { Toast } from 'vant';Vue.use(Toast);
// 轻提示

使用直接this.$toast(‘提示内容’),需要注意的是只能在组件内部使用。另外一种是在任何地方都可以使用。

import {Toast} from "vant"Toast('提示内容')

运行结果:
请添加图片描述

4.3 短信验证倒计时

准备data数据,三个,分别为totalSecond、second、timer;

data(){return{totalSecond:60, // 总秒数second:60, // 当前秒数timer:null}
}

点击发送验证码按钮代码逻辑,

getCode(){// timer判断,防止重复点击if(!this.timer && this.second === this.totalSecond){this.timer = setInterval(()=>{this.second --;if(this.second <= 0){clearInterval(timer);this.timer = null;this.second = totalSecond;}// 倒计时显示为0,关闭定时器},1000);}
}

页面点击按钮页面布局为:

<button @click="getCode">{{second === totalSecond?"获取验证码": second + "秒之后重新发送"}}</button>

运行结果:

请添加图片描述
另外还存在一个问题,就是离开这个页面时,定时器还在运行,需要在destroy函数中关闭当前定时器。

在这里插入图片描述

 // 离开页面,清除定时器destory(){clearInterval(this.timer);}

下述是对输入的手机号和图片验证码进行验证:

validFn(){if(!/^1[3-9]\d{9}$/.test(this.mobile)){this.$toast("请输入正确的手机号!")return false;}if(!/^\w{4}$/.test(this.picCode)){this.$toast("请输入正确的图形验证码!")return false;}// 正则表达式验证手机号码和图形验证码是否输入正确return true;
}

请求短信验证码接口,只是演示效果而已

// 短信验证码
// 短信验证码
export const getMsgCode = function (captchaCode, captchaKey, mobile) {return request.post('?type=code&str_1=/captcha/sendSmsCaptcha',{captchaCode,captchaKey,mobile})
}
async getCode(){if(!this.validFn()){// 点击验证码按钮之后进行判断return }// timer判断,防止重复点击if(!this.timer && this.second === this.totalSecond){await getMsgCode(this.picCode,this.picKey,this.mobile);this.$toast("短信发送成功!");this.timer = setInterval(()=>{this.second --;// console.log("正在倒计时。。。");if(this.second <= 0){clearInterval(this.timer);this.timer = null;this.second = this.totalSecond;}// 倒计时显示为0,关闭定时器},1000);}
}

实现步骤:

  1. 点击按钮,实现倒计时效果
  2. 倒计时之前进行校验(手机号、图片验证码)
  3. 请求短信验证码接口,添加相应提示

4.4 登录功能

登录之前仍需要对手机号、图片验证码和短信验证码进行校验,然后调用相应的请求登录接口的方法,发送请求,请求成功后添加相应的提示并跳转。

export const codeLogin = function(mobile,smsCode){return request.post('/w3?type=codeLogin',{mobile,smsCode})
}
if(!this.validFn()) returnif(!/^\d{6}$/.test(this.msgCode)){this.$toast("请输入正确的短信验证码!")return}const res = await codeLogin(this.mobile,this.msgCode);console.log(res);this.$toast("登录成功!")this.$router.push("/")

请添加图片描述

4.5 响应拦截器 => 统一处理错误

上述请求对应接口只是在考虑请求结果正确的情况下。在封装的axios模块下的响应拦截器添加如下代码:

import {Toast} from 'vant';// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 对响应数据做点什么const res = response.data;if(res.status != 200){Toast(res.message);return Promise.reject(res.message);}return res;
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});

运行结果:
请添加图片描述

4.6 登录权证信息存储

vuex构建user模块存储登录权证。
构建user模块

export default{namespaced:true,state(){return{userInfo:{token:"",userId:""}}},mutations:{},actions:{},getters:{}
}

挂载user到全局上去

import Vue from 'vue'
import Vuex from 'vuex'
import user from "@/store/modules/user"Vue.use(Vuex)export default new Vuex.Store({state: {},getters: {},mutations: {},actions: {},modules: {user}
})

mutations中设置state相应的值

mutations:{setUserInfo(state,obj){state.userInfo = obj;}
},

页面中进行调用

this.$store.commit("user/setUserInfo",{token:res.token,userId:res.userId});

请添加图片描述

4.7 storage存储模块 => vuex持久化处理

封装storage模块,利用本地存储,进行vuex持久化处理。

const INFO_KEY = "lz_info"export const getInfo = ()=>{const defaultObj = {token:'',userId:''};const res = localStorage.getItem(INFO_KEY);return res ? JSON.parse(res) : defaultObj;
}
// 获取个人信息export const setInfo = (obj)=>{localStorage.setItem(INFO_KEY,JSON.stringify(obj));
}
// 设置个人信息
export const removeInfo = ()=>{localStorage.removeItem(INFO_KEY);
}
// 移除个人信息

在user模块使用

import { getInfo, setInfo } from "@/utils/storage"export default{namespaced:true,state(){return{userInfo:getInfo()}},mutations:{setUserInfo(state,obj){state.userInfo = obj;setInfo(obj);}},actions:{},getters:{}
}

运行结果:
即使刷新了页面,token信息也不会丢失。
请添加图片描述

4.8 添加请求loading效果

请求后台时,添加loading效果。实现:在请求拦截器中,每次请求,打开loading;在响应拦截器中,每次响应,关闭loading。

// 添加请求拦截器
instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 开启loading,禁止背景点击Toast.loading({message:"加载中...", // 设置轻提示内容forbidClick:true, // 禁止背景点击duration:0  // 不会自动消失})return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 对响应数据做点什么const res = response.data;if(res.status != 200){Toast(res.message);return Promise.reject(res.message);}else{Toast.clear();// 清除loading效果}return res;
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});

请添加图片描述

5. 页面访问拦截

有的页面只有当用户满足一定条件下才能访问,比如登录成功后才能查看购物车中商品信息。
这里可以考虑使用路由导航守卫,全局前置守卫。所有的路由一旦被匹配到,都会先经过全局前置守卫;只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容。
具体而言,跳转路由后先经过全局前置守卫,在这里边进行判断要跳转的页面的是否存在权限问题,如果没有权限问题,直接放行;否则判断是否有token信息,有的话直接跳到对应页面进行渲染即可;否则,跳转到登录页面。

在路由配置文件添加如下配置:

import store from "@/store/index"const urls = ["/pay","/myorder"];router.beforeEach((to,from,next)=>{// to 到哪个页面去的完整路由对象// from 从哪个页面来的完整路由对象// next() 是否放行if(!urls.includes(to.path)){next();return }const token = store.getters.token;if(token){next();}else{next("/login");}
})export default router

token是全局的配置如下:

import Vue from 'vue'
import Vuex from 'vuex'
import user from "@/store/modules/user"Vue.use(Vuex)export default new Vuex.Store({state: {},getters: {token(state){return state.user.userInfo.token;}},mutations: {},actions: {},modules: {user}
})

运行结果:
请添加图片描述

6. 首页布局

请添加图片描述
首页数据获取模块的封装

import request from '@/utils/request'export const getHomeData = ()=>{return request.get('w4?pageId=0&str_1=/page/detail');
}

调用封装好的模块进行页面渲染

<template><div class="home"><van-nav-bar title="智慧商城" fixed/><!-- 顶部 --><van-search v-model="value" placeholder="请输入搜索关键词" /><!-- 搜索框 --><van-swipe :autoplay="3000" :height="200"><van-swipe-item v-for="(image, index) in images" :key="index"><img v-lazy="image.imgUrl" /></van-swipe-item></van-swipe><!-- 轮播图 --><van-grid square icon-size="40" :column-num="5"><van-grid-item v-for="(item,index) in images2" :key="index" :icon="item.imgUrl" :text="item.text" /></van-grid><div class="middle-sec"><img :src="totalImg" alt=""></div><div class="goods-item"><p class="goods-top">-猜你喜欢-</p><GoodItem v-for="item in proList" :key="item.goods_id" :pro="item"></GoodItem></div></div>
</template><script>
import GoodItem from '@/components/GoodItem.vue'
import {getHomeData} from '@/api/home'export default {name:'Home',components:{GoodItem},data(){return{value:"",images:[],// 轮播图images2:[],// 导航proList:[],totalImg:''}},async created(){const {data:{pageData}} = await getHomeData();console.log(pageData);this.images = pageData.items[1].data;this.images2 = pageData.items[3].data;this.proList = pageData.items[6].data;this.totalImg = pageData.items[4].data[0].imgUrl;}
}
</script><style lang="less" scoped>.van-nav-bar{background-color: red;/deep/ .van-nav-bar__title{color: white;font-size: 16px;}}.van-search{margin-top:12.26667vw;;}.van-swipe{width: 100%;height: 200px;img{height: 200px;}/deep/ .van-swipe__indicator{background-color: red;}}/deep/ .van-icon__image{border-radius: 8px;}.middle-sec{width: 100%;img{width: 100%;}}.goods-item{margin-bottom: 40px;}.goods-top{height: 30px;width: 100%;text-align: center;line-height: 30px;font-size: 16px;}
</style>

7. 搜索历史管理

在搜索页面添加历史记录管理,便捷用户操作。
点击搜索按钮或底下历史记录,都能进行搜索,若之前没有相同搜索关键字,则直接追加到最前面;若之前已有相同搜索关键字,则该原有关键字移除,再追加,这样操作的话新搜索关键字可以在搜索历史记录中提前。
显示效果如下:
请添加图片描述

8. 商品详情页

商品详情页下需要通过该商品id获取对应信息及获取对应的用户评论数据。界面如下:
请添加图片描述

9. 加入购物车

只有登录的用户,才能加入购物车。
只需要判断token是否存在,就可以发送购物车的请求;如果token不存在,那么给个提示,引导用户登录,然后再跳回来这个页面。具体实现视频链接在这:加入购物车

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/389129.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis学习(2)

分页 目的&#xff1a;减少数据的处理量 方式一&#xff1a;使用limit实现分页&#xff0c;核心SQL sql语法&#xff1a;select * from user limit startIndex&#xff0c;pageSize; 步骤&#xff1a; 1、接口 2、Mapper.xml 3、测试 方式二&#xff1a;使用注解开发 1、…

每日一题~EC168 A+B+C+D

A 题意&#xff1a; 字符串 每一个字符的花费是2&#xff0c;如果ai-1 ai ,那么ai 的花费是1. 现在可以插入一个字符&#xff0c;得到最大花费。输出插入字符之后的字符串。 分析&#xff1a;只需要在相同的连续字符中间插入一个不同的字符就可以了。如果没有连续的相同字符&am…

Python酷库之旅-第三方库Pandas(059)

目录 一、用法精讲 226、pandas.Series.pad方法 226-1、语法 226-2、参数 226-3、功能 226-4、返回值 226-5、说明 226-6、用法 226-6-1、数据准备 226-6-2、代码示例 226-6-3、结果输出 227、pandas.Series.replace方法 227-1、语法 227-2、参数 227-3、功能 …

最强开源模型 Llama 3.1 部署推理微调实战大全

目录 引言一、Llama 3.1简介二、Llama 3.1性能评估三、Llama 3.1模型推理实战1、环境准备2、安装依赖3、模型下载4、模型推理 四、Llama 3.1模型微调实战1、数据集准备2、导入依赖包3、读取数据集4、处理数据集5、定义模型6、Lora配置7、配置训练参数8、开始Trainer训练9、合并…

什么是负责任的人工智能

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识AI 进阶&#xff1a;企业项目实战 可直接在橱窗里购买&#xff0c;或者到文末领取优惠后购买&#xff1a; 拥有权利的同时也被赋予了重大的…

Modbus通讯协议

Modbus通讯协议 Modbus协议是一种用于电子控制器之间的通信协议&#xff0c;‌它允许不同类型的设备之间进行通信&#xff0c;‌以便进行数据交换和控制。‌Modbus协议最初为可编程逻辑控制器&#xff08;‌PLC&#xff09;‌通信开发&#xff0c;‌现已广泛应用于工业自动化领…

详细分析nohup后台运行命令

目录 1. 基本知识2. Demo 1. 基本知识 Unix/Linux 命令&#xff0c;用于在后台运行程序&#xff0c;并确保它在用户退出或注销后继续运行 nohup 的主要作用是使程序在终端会话结束后继续运行&#xff0c;这对需要长时间执行的任务特别有用 基本的用法如下&#xff1a; nohu…

3.1 拓扑排序

有向图的存储 邻接矩阵 邻接表 拓扑排序 有向无环图&#xff1a;不存在环的有向图 环&#xff1a; 在有向图中&#xff0c;从一个节点出发&#xff0c;最终回到它自身的路径被称为环 入度&#xff1a; 以节点x为终点的有向边的条数被称为x的入度 出度&#xff1a; 以节…

哈默纳科HarmonicDrive谐波减速机的使用寿命计算

在机械传动系统中&#xff0c;减速机的应用无处不在&#xff0c;而HarmonicDrive哈默纳科谐波减速机以其独特的优势&#xff0c;如轻量、小型、传动效率高、减速范围广、精度高等特点&#xff0c;成为了众多领域的选择。然而&#xff0c;任何机械设备都有其使用寿命&#xff0c…

数据集成是什么意思?方法有哪些?数据集成三种方法介绍

1 数据集成是什么 数据集成(Data Intergration)&#xff0c;也称为数据整合&#xff0c;是通过将分布式环境中的异构数据集成起来&#xff0c;为用户提供统一透明的数据访问方式。该定义中的集成是指从整体层面上维护数据的一致性&#xff0c;并提高对数据的利用和共享&#x…

【Redis 进阶】事务

Redis 的事务和 MySQL 的事务概念上是类似的&#xff0c;都是把一系列操作绑定成一组&#xff0c;让这一组能够批量执行。 一、Redis 的事务和 MySQL 事务的区别 1、MySQL 事务 原子性&#xff1a;把多个操作打包成一个整体。&#xff08;要么全都做&#xff0c;要么都不做&am…

用 Python 编写的井字游戏

一.介绍 在本文中&#xff0c;我将向您展示如何使用 Python 创建一个非常简单的井字游戏。 井字游戏是一种非常简单的双人游戏。因此每次只能有两个玩家玩。该游戏也称为井字游戏或 Xs 和 Os 游戏。一个玩家玩 X&#xff0c;另一个玩家玩 O。在这个游戏中&#xff0c;我们有一…

树组件 el-tree 数据回显

树组件 el-tree 数据回显 树型结构的数据回显问题&#xff1a; 这里我只放了核心代码&#xff0c;主要是如何获取选中的树节点的id集合和如何根据树节点的id集合回显数据 大家根据需要自行更改&#xff01; <el-tree ref"authorityRef" node-key"id" …

SSH访问控制:精确管理你的服务器门户

“ 在数字世界中&#xff0c;服务器的安全性是任何网络管理员的首要任务。特别是对于远程登录协议如SSH&#xff0c;确保只有授权用户可以访问是至关重要的。 今天&#xff0c;记录两种有效的方法来控制用户对特定服务器的访问&#xff1a;通过sshd_config实现黑/白名单机制和利…

【Python】pandas:替换值、添加行/列,删除行/列,更改形状(含数据透视表)

pandas是Python的扩展库&#xff08;第三方库&#xff09;&#xff0c;为Python编程语言提供 高性能、易于使用的数据结构和数据分析工具。 pandas官方文档&#xff1a;User Guide — pandas 2.2.2 documentation (pydata.org) 帮助&#xff1a;可使用help(...)查看函数说明文…

前端面试宝典【HTML篇】【4】

欢迎来到《前端面试宝典》,这里是你通往互联网大厂的专属通道,专为渴望在前端领域大放异彩的你量身定制。通过本专栏的学习,无论是一线大厂还是初创企业的面试,都能自信满满地展现你的实力。 核心特色: 独家实战案例:每一期专栏都将深入剖析真实的前端面试案例,从基础知…

【区块链+绿色低碳】山东邹平:区块链生态环境监管平台 | FISCO BCOS应用案例

山东省滨州市生态环境局邹平分局通过实地考察和调研发现&#xff0c;执法大队在执法工作中存在各排污企业设备系统无 法互通、终端采集数据固证难且可信度低、环境执法电子证据采集规则与司法采信标准不统一等痛点。而区块链 的分布式记账、不易篡改性和智能合约自动执行机制&a…

【源码+文档+调试讲解】学生党务学习系统的设计与实现

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统大学生党务学习平台信息管理难度大&#xff0c;容错率低&…

前端技术回顾系列 14 | 总结 + Vue 3.x 必修课

欢迎关注公众号&#xff1a;CodeFit 创作不易&#xff0c;如果你觉得这篇文章对您有帮助&#xff0c;请不要忘了 点赞、分享 和 关注&#xff0c;为我的 持续创作 提供 动力&#xff01; 1. 回顾系列的初衷和目标 在六月初&#xff0c;我开始编写 「前端技术回顾系列 2024」&a…

C++基础知识:构造函数的分类和调用,有参构造和无参构造,有参构造和无参构造,三种调用方式:括号法,显示法,隐式转换法,以及相关代码演示和注意事项

1.构造函数的分类及调用: 2.两种分类方式: 按参数分为: 有参构造和无参构造 按类型分为:有参构造和无参构造 3.三种调用方式: 括号法 显示法 隐式转换法 2.调用方法代码演示 1.括号法代码演示&#xff1a; #include<iostream>using namespace std;//1.构造函数的分类和…