# 商城vue项目 **Repository Path**: weimomolin/shop ## Basic Information - **Project Name**: 商城vue项目 - **Description**: 使用Vue制作的家居商城 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-10-23 - **Last Updated**: 2023-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: Vue, API, 服务器 ## README # Vue项目技术汇总 #### 介绍 使用Vue制作的项目合集 **MVVM模式** M (Model,模型层 ) V (View,视图层) VM(ViewModel,V与M连接的桥梁,也可以看作为控制器) 1、 M:模型层,主要负责业务数据相关; 2、 V:视图层,顾名思义,负责视图相关,细分下来就是html+css层; 3、 VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点; MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改, 反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦 ------ #### 软件架构 ##### **1、nvm (node管理)** 安装node很方便,只需要一条命令 可以轻松切换node版本 可以多版本node并存 ``` nvm install 8.11.1 #下载8.11.1 nvm use 8.9.0 #使用8.9.0 nvm alias default 6.10.0 #设置默认版本 ``` ##### **2、Vite(构建工具)** [Vite零基础快速入门](https://yejiwei.com/post/193) [Vite官网](https://vitejs.cn/) 配置vite.config.js文件 ``` import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], server:{ port:3000 // 配置访问接口 http://localhost:3000/ } }) ``` 利用Vite构建工具来创建项目 ``` # npm 6.x npm init vite@latest --template vue # npm 7+,需要加上额外的双短横线 npm init vite@latest -- --template vue cd npm install npm run dev ``` ##### **3、脚手架** ``` // 安装脚手架 `npm install -g @vue/cli` // 更新脚手架 `npm update -g @vue/cli` ``` ------ #### Vue说明 ##### **1. Vue组件的API风格(Vue2/Vue3)** ``` 选项式API (Vue2) 使用选项式API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted 选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例 代码结构 组合式API (Vue3) 组合式API是使用导入的方式来描述组件逻辑,通常会与 ``` ##### **2. 生命周期(Vue2/Vue3)**
Vue2 Vue3 说明
创建实例前/后
beforeCreate setup 创建实例前
vue实例的挂载元素el和数据对象data都为undefined,还未初始化,此时拿不到数据
场景:可以加loading……事件 ,加载实列时触发
created setup 创建实例后
实例已经创建,仍然不能获取DOM节点(有data,没有el)此时data 和 methods已经可以使用 但是页面还没有渲染出来
场景:一些异步请求的调用 ,loading……事件结束等
载入数据前/后
beforeMount onBeforeMount 载入前
vue挂载的根节点已经创建(有data,有el)实例尚未挂载完成 ,此时页面上还看不到真实数据
mounted onMounted 载入后
数据和DOM已被渲染
场景:在这个钩子函数里面可以使用一些第三方的插件
更新 前/后
beforeUpdate onBeforeUpdate 更新前
数据更新时调用 发生在虚拟DOM打补丁之前。 页面上数据还是旧的
updated onUpdated 更新后
更新结束后执行 页面上数据已经替换成最新的
场景:做一些数据统一更新处理的相应函数
销毁 前/后
beforeDestroy onBeforeUnmount 销毁前
在实例销毁之前调用 ,在这一步,实例仍然完全可用
destroyed onUnmounted 销毁后
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
缓存
activated onActivated 组件激活时 / 停用时
这两个钩子需要配合配合<keep-alive><keep-alive/>来使用
keep-alive的作用会缓存不活动的组件实例,而不是销毁它们。当组件在<keep-alive>内被切换,activated和deactivated这两个生命周期钩子函数将会被对应执行 场景:做一些footer底部菜单、tab的切换激活
deactivated onDeactivated
错误处理机制
errorCaptured onErrorCaptured 当这个钩子检测到组件中发生错误时就被调用。
通过err, vm, info这3个参数输出:
err 错误对象
vm 发生错误的组件实例
info 包含错误来源信息的字符串,比如错误所在的生命周期钩子
此钩子可以返回 false 以阻止该错误继续向上传播
场景:能快速找到报错的组件位置,还能解决满屏红等视觉冲击
Vue3新增(仅在开发模式下可用的调试钩子)
----- onErrorCaptured 每次渲染后重新收集响应式依赖,在onMounted前触发,页面更新后也会触发
----- onRenderTriggered 每次触发页面重新渲染时的自动执行,在onBeforeUpdate之前触发
##### **3. setup** Vue3 中新增的钩子,它是我们在编写组件时的一个入口API 同时也是 Vue3 中新增的一个生命周期函数,会在 beforeCreate 之前调用 因为此时组件的 data 和 methods 还没有初始化,因此在 setup 中是不能使用 this 的 所以 Vue 为了避免我们错误的使用,它直接将 setup 函数中的 this 修改成了undefined 并且,我们只能同步使用setup函数,不能用async将其设为异步 setup 函数接收两个参数 props 和 context, 语法为:setup(props,context){} props props 里面包含父组件传递给子组件的所有数据。在子组件中使用 props 进行接收 props 是响应式的, 当传入新的 props 时,会及时被更新 由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式 context context 里面包含 attrs, slots, emit 等数据方法 attrs:获取组件上的属性 slots:获取 slot 插槽的节点 emit :emit 方法(子组件向父组件传递数据) ``` 选项式API (Vue2) 组合式API (Vue3) ``` ------ #### 响应式数据 ##### 1、Vue3中响应式 1. ref - 简单类型的响应式数据 2. reactive - 复杂类型的响应式数据 3. toRef - 从复杂类型中抽离单个 响应式属性出来 4. toRefs - 从复杂类型中抽离多个 响应式属性出来 5. readonly - 设置属性只读 ``` ``` ##### 2、Vue2中set方法设置响应式数据 在Vue2中,修改某一些数据,视图是不能及时重新渲染的 原因就是 Vue2 中的数据响应式是利用 object.definedProperty()实现的,它是无法深层监听数据的变化的 而Vue3,利用的是ES6的proxy,对数据响应式进行一个数据的代理 如果想要让一个对象变为响应式数据,可以使用reactive或ref 因此Vue3也就把 set方法 废了 ``` Vue2中使用前需要再data中现将需要使用的数据定义出来 data:{ user:{ name:"sally", age:31, gender:"male", salary:"secret" }, list:[1,2,3,4] } 对象使用: (1)全局方法:Vue.set(user, 'address', 'beijing')(对象名,键名,键值) (2)实例方法:vm.$set(user, 'address', 'beijing') 数组使用: (1)全局方法:Vue.set(list, 2, 10)(数组名,下标,新的数据) (2)实例方法:vm.$set(list, 3, 8) ``` ------ #### 组件通信与传参 > 可以归为两大类: 全局通信(父传子 父:main.js) 局部通信(组件之间) 父传子(props属性) 子传父(event事件) **由于Vue2和Vue3编写方式不同,每种通信又有两种写法** props和context 1、props(父传子) props 是组件的自定义属性 父组件通过 props 向子组件传递要展示的数据 2、context(子传父) 是一个 Javascript 对象,它公开了三个组件属性 context 里面包含 attrs, slots, emit 等数据方法 attrs:获取组件上的属性(非响应式对象) slots:获取 slot 插槽的节点 emit :emit 方法(子组件向父组件传递数据) ##### 父 传 子(props) 选项式API Vue2写法 > 父组件 ``` ``` > 子组件 把动态的数据项声明为 props 自定义属性 自定义属性可以在当前组件的模板结构中被直接使用 ``` ``` ##### 父 传 子(props) 组合式API Vue3写法 > 父组件 ``` ``` > 子组件 ``` ``` ##### 子 传 父(event) 选项式API Vue2写法 > defineProps() 定义组件属性API defineEmits() 定义组件执行器API defineExpose() 暴露当前组件的数据和方法 useSlots() 获取插槽对象 useAttrs() 获取自定义属性对象 > 父组件 ``` ``` > 子组件 ``` ``` ##### 子 传 父(event) 组合式API Vue3写法 > 父组件 ``` ``` > 子组件 ``` ``` ##### provide/inject (全局属性和方法) > 父传子 provide - 可以向所有子组件提供数据以及提供修改数据的方法 inject - 子组件用inject来接收数据和方法 可以用来设置Vue全局属性和方法 > 父组件 ``` ``` > 子组件 ``` ``` ###### 可以用于挂载全局属性或者方法 src/main.js > src/main.js ``` const test = () => { return '全局方法' } const app = createApp(App) .mount('#app') //全局依赖注入 app.provide('$test', test) ``` > 组件中使用 ``` ``` ---- #### 插槽 > 父组件 ``` ``` > 子组件 son4.vue ``` ``` ---- #### 作用域插槽 > 父组件 ``` ``` > 子组件 son5.vue ``` ``` ---- #### 路由构建 **Vue-Router** [官网](url:https://router.vuejs.org/zh/) **安装路由** `npm install vue-router@4 -S` ##### 逻辑结构 > src/router.js ``` //引入路由对象 import { createRouter, createWebHashHistory} from "vue-router" //引入组件 import home from './components/home.vue' import about from './components/about.vue' // 创建路由 export default createRouter({ history: createWebHashHistory(), //hash路由模式 linkExactActiveClass: 'active', //点击路由跳转的底部样式标量 //路由列表 routes: [ { //默认首页 path: '/', name: 'home', component: home }, { path: '/about', name: 'about', component: about }, ] }) ``` ---- ##### 入口挂载 > src/main.js ``` import router from './router' createApp(App) .use(router) .mount('#app') ``` ---- ##### 页面结构 > src/App.vue ``` ``` ---- #### 动态传参 **逻辑结构** > src/router.js ``` //引入路由对象 import { createRouter, createWebHashHistory} from "vue-router" //引入组件 import list from './components/list.vue' import info from './components/info.vue' // 创建路由 export default createRouter({ history: createWebHashHistory(), //hash路由模式 linkExactActiveClass: 'active', //点击路由跳转的底部样式标量 //路由列表 routes: [ { path: '/list', name: 'list', component: list }, { path: '/info/:id', name: 'info', component: info }, ] }) ``` ##### 路由跳转 > src/App.vue ``` ``` ---- ##### 组件界面 > components/list.vue ``` ``` > components/info.vue ``` ``` #### 路由封装嵌套 由于项目前端中可能会分成好几大不同模块,每个模块下又有许多界面,为了方便管理,可以将路由按照模块进行划分 ``` // 项目路由结构 localhost:3000 一级路由 二级路由 / -> 首页 /business -> 用户相关 /login -> 用户登录界面 /register -> 用户注册界面 ``` > 由此可以将 /routers 文件夹下的index.js中的路由信息,通过变量获取来配置 **实现逻辑思路(以shop项目为例)** > 1、先划分出主要模块,在routers路由文件夹下创建一级路由js文件,目录如下: ![输入图片说明](readme%E8%AF%B4%E6%98%8E%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6/cbd9afb068802955e5e43141f975e44.png) 2、以business.js模块为例,其文件内部与普通路由文件没有太多区别,只需要注意组件单独定义部分,文件如下: ![输入图片说明](readme%E8%AF%B4%E6%98%8E%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6/ed19aa4eadea1921257ffa43bedc062.png) 3、将模块划分配置完成后后便可修改入口index.js文件 4、将路由对象 createRouter 中的 routes 配置项的配置值使用 RouterList 自定义变量控制( **存放路由列表** ) 5、引入当前目录下的所有js文件并使用 modfile 自定义变量存放( /routers文件夹下的js文件存放的都是**路由文件** ) 6、modfile 为数组结构,其中key值为一级路由地址 value值为二级路由地址,具体数据格式如下: ![输入图片说明](readme%E8%AF%B4%E6%98%8E%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6/d1ad7979e763e72848883d48ab3b4e8.png) 7、通过使用Object.value()方法抽离出value,生成新的数组,再利用map方法、同异步操作,将value添加到RouterList路由集合变量中使用 ``` //index.js //引入路由对象 import { createRouter, createWebHashHistory } from 'vue-router' //引入当前目录下面所有的JS文件 const modfile = import.meta.globEager('./*.js') //路由集合 const RouterList = [] // console.log(Object.keys(modfile)) //提取对象中所有的key 放到一个数组中 // console.log(Object.values(modfile)) //提取对象中所有的value 放到一个数组中 Object.values(modfile).map(async mod => { if(mod.default) await RouterList.push(mod.default) }) //创建路由对象 export default createRouter({ history: createWebHashHistory(), //设置路由模式 linkExactActiveClass: 'active', //当前所匹配的链接 routes: RouterList, //路由列表 }) ``` ---- #### 自动导入组件与路径别名@配置 > 抽离公共自定义组件及第三方插件(自动引入组件) **安装自动导入组件的插件** > [*vue3+vite插件配置系列3-unplugin-vue-components(官方文档翻译*)](https://blog.csdn.net/qq_42611074/article/details/123036252) npm install unplugin-vue-components -S > [*unplugin-auto-import的使用*](https://blog.csdn.net/perfect2011/article/details/128539058) npm install unplugin-auto-import -S 安装插件后,需要在vite.config.js中进行相关配置 ``` import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' //引入读取路径的一个nodejs模块 import path from 'path' // 引入自动导入组件插件 import Components from 'unplugin-vue-components/vite' // 自动导入vue,项目插件库 import AutoImport from 'unplugin-auto-import/vite' // https://vitejs.dev/config/ export default defineConfig({ // 别名配置 resolve: { alias: { //设置路径的别名 // __dirname 当前所在的目录 /shop // path.resolve 拼接路径 /shop/src "@": path.resolve(__dirname, "src"), 'vue': 'vue/dist/vue.esm-bundler.js' // 定义vue的别名,如果使用其他的插件,可能会用到别名 } }, // 访问端口设置 server: { port: 3000 }, plugins: [ vue(), //设置自动加载规则 Components({ // 指定组件位置,默认是src/components dirs: ['src/components'], extensions: ['vue'], }), //自动导入库 AutoImport({ imports: ['vue', 'vue-router'] }) ] }) ``` ---- #### API接口封装(FastAdmin框架) > 在后台fastAdmin中,新建一个shop模块、business控制器文件夹、business控制器文件 **在控制器文件中编写接口信息以及处理数据** > 路由文件:routers/business.js 页面组件:components/business/register.vue > 接口地址:www.fast.com/shop/business/register 控制器文件地址:application/shop/Controller/Business.php -> function register > api接口文件模板:application/api/controller/Demo.php ``` // Business.php (API接口文件控制器) BusinessModel = model('Business.Business'); } // 此处注解需要参考命令行>一键生成API文档 /** * 用户注册 * * @ApiTitle (用户注册) * @ApiSummary (用户注册) * @ApiMethod (POST) * @ApiParams (name="mobile", type="string", required=true, description="手机号") * @ApiParams (name="password", type="string", required=true, description="密码") * @ApiReturnParams (name="code", type="integer", required=true, sample="0") * @ApiReturnParams (name="msg", type="string", required=true, sample="返回成功") * @ApiReturnParams (name="data", type="object", sample="{'user_id':'int','user_name':'string','profile':{'email':'string','age':'integer'}}", description="扩展数据返回") * @ApiReturn ({ 'code':'1', 'msg':'返回成功' }) */ // 具体的接口方法(reigster注册接口) public function reigster() { if($this->request->isAjax()) { $mobile = $this->request->param('mobile', '', 'trim'); $password = $this->request->param('password', '', 'trim'); var_dump($mobile, $password); } } } ``` #### 命令行 有许多命令行操作,参考文档: [知音必答_命令行](https://www.quickask.net/docs/book/php/frame/fastadmin/08.html) [fastadmin_命令大全](https://www.kancloud.cn/skyskai/fastadmin_doc/485461) **一键生成API文档** > FastAdmin中的一键生成API文档可以在命令行或后台一键生成我们API接口的接口测试文档,可以直接在线模拟接口请求,查看参数示例和返回示例 **常用命令** ``` //一键生成API文档 php think api --force=true //指定https://www.example.com为API接口请求域名,默认为空 php think api -u https://www.example.com --force=true //输出自定义文件为myapi.html,默认为api.html php think api -o myapi.html --force=true //修改API模板为mytemplate.html,默认为index.html php think api -e mytemplate.html --force=true //修改标题为FastAdmin,作者为作者 php think api -t FastAdmin -a Karson --force=true //查看API接口命令行帮助 php think api -h ``` **参数介绍** ``` -u, --url[=URL] 默认API请求URL地址 [default: ""] -m, --module[=MODULE] 模块名(admin/index/api) [default: "api"] -o, --output[=OUTPUT] 输出文件 [default: "api.html"] -e, --template[=TEMPLATE] 模板文件 [default: "index.html"] -f, --force[=FORCE] 覆盖模式 [default: false] -t, --title[=TITLE] 文档标题 [default: "FastAdmin"] -a, --author[=AUTHOR] 文档作者 [default: "FastAdmin"] -c, --class[=CLASS] 扩展类 (multiple values allowed) -l, --language[=LANGUAGE] 语言 [default: "zh-cn"] ``` **注释规则** > 在我们的控制器中通常分为两部分注释,一是控制器头部的注释,二是控制器方法的注释