Vue项目笔记
mall-project(Vue2)
1.开发笔记
1.项目准备
项目目录
- public文件夹下放置静态资源(图片),打包时原封不动打包到dist文件夹。
- src下的assets文件夹也防止静态资源(组件共用的),打包时assets文件夹下的静态资源会被当做一个模块打包到JS文件中。
小白开发流程
- 书写静态页面
- 拆分组件
- 获取服务器的数据动态展示
- 完成相应的动态业务逻辑
开发经验
-
声明式路由常用于直接跳转,编程式路由常用于跳转前有业务逻辑
-
vue2对应vue-router3
-
路由path不区分大小写,统一转换为小写。
-
路由重定向,‘*’ 号代表所有未定义的路径都将重定向到目标路径
-
设置路由占位符后只能通过params传参,通过query传参无效。但可以同时传params(在前)和query(在后)参数。
-
指定params参数可传可不传,占位符后面加个问号 ? 。如果传递的是空串, 路径会出问题,或上undefined可解决。
-
路由组件可以通过props传递数据,也能通过props传递params和query参数。
-
编程式路由重复跳转报错NavigationDuplicated。声明式路由没有此问题。原因:Promise没有捕获错误,解决方法包装push和replace方法或者调用push和replace时传入回调函数。
// 在router/index.js 中写入 // 保留原来方法 const originPush = VueRouter.prototype.push; const originReplace = VueRouter.prototype.replace; VueRouter.prototype.push = function(location) { // 由于originPush的this指向了window, 所以调用call方法修改this指向为VueRouter实例对象 // 当前函数的this指向函数的调用者,即VueRouter实例对象 // 在vue-router中,push和replace都是的回调为Promise, 所以可以catch捕获 originPush.call(this, location).catch(err => err); } VueRouter.prototype.replace = function(location) { originReplace.call(this, location).catch(err => err); }
-
项目小时可以在生命周期函数中发送请求。大型项目接口统一管理在api文件夹下。
-
webpack默认对外暴露的文件有:图片,JSON文件。
-
置成undefined不会将该属性发送给服务器,但是值为空串的属性会发送给服务器。
2.footer
- Home 和 Search 页面有底部,login 和 register页面没有底部。因此通过路由中的信息控制是否显示底部,具体地为每个路由设置一个元信息控制是否显示底部。
3.home
- 由于用户行为过快,导致浏览器反应不过来,如果回调函数中有较多业务,则可能出现卡顿。利用函数的节流解决。
- 防抖:连续快速触发,只会执行一次。前面所有的触发都被取消,最后一次执行在规定时间之后触发。
- 节流:规定时间间隔内不会重复触发回调。将频繁的操作变为少量的操作。底层都是闭包+延时器。
- 三级联动点击跳转,采用事件委派(非委派绑定太多回调函数)+ 编程式导航(声明式导航加载太多a标签,卡顿)。
- 如何确定点击的是a标签?为a标签添加自定义属性。
- 如果区分标签级别。不同级别的标签添加不同的自定义属性。
- ListContainer组件中完成轮播图部分时,在mounted 钩子函数中直接 new swiper实例是无效的,因为dispatch存在异步请求,v-for需要的数据还未响应回来,DOM结构不完整。 new swiper实例的前提是DOM结构已经存在。可以使用在延时器异步地中new swiper实例。
- Floor组件在mounted 钩子函数中直接 new swiper实例是可以的,因为Floor组件的mounted 钩子函数中没有异步请求,数据是父组件通过props传过来的。Floor执行mounted时,都已经通过父组件中的数据将Floor组件渲染出来了,说明子组件DOM已经存在了。
- 可以将轮播图封装为一个全局组件,但是new swiper的操作时机在ListContainer组件和Floor组件中是不同的。为了封装成一个组件,可以将Floor组件的new swiper的操作也放在watch中,但是监听的数据是从父组件传递过来的,并且没有发生变化,所以需要加上immediate:true 属性让监听数据的事件一上来就执行一次。
- Floor组件的数据返回的是一个数组,所以根据此情况,在Home组件中获取Floor中的数据,然后v-for遍历数据生成Floor实例对象。
4. 三级联动
- 如果在TypeNav挂载完毕发送数据请求,由于从home跳转到search时,加载的是新的TypeNav实例,会重复执行mounted中的代码,即重复发送数据请求。所以可以将TypeNav的数据请求放在App的mounted钩子函数中,只执行一次数据请求。注意:不能放在入口文件main.js 中,因为组件实例的身上才有this.$store属性。
5.search
- Search需要根据路由传递过来的query和params参数去请求数据,在mounted钩子函数中发送数据请求,但是请求参数要在这之前准备好,所以放在beforeMount钩子函数中(放在mounted中请求发送之前也可以)。
- 怎么根据用户的不同选择或搜索关键词实时展示对应的内容?由于用户选择选项或是点击搜索时,路由会发生变化,所以可以监听$route,在监听器的回调函数中请求对应的数据。还有一个思路是路由守卫beforeRouteUpdate。总的来说,变化最终会体现在路由中,根据这一点做文章。
- 面包屑的处理。删除面包屑时也要更改路由query参数,做法是自己跳转到自己,query参数清空。注意:路由的参数改变时不会引起路由的切换,但是由于$route发生改变,监听器监听到后发送请求。
- 面包屑中的关键字清除后,还需要清除Header组件中搜索框的关键字。由于是兄弟关系,可以使用全局事件总线。使用vuex很麻烦,因为一些变量存在Header组件当中。
- 添加属性面包屑时,需要判断是否已经有了该属性面包屑。可以用数组的indexOf方法。
- 分页器,禁止点击时可以采用绑定disabled 属性。
6.detail
- 切换至详情页滚动条回到顶部,可以使用vue router的配置项scrollBehavior。
- 放大镜组件Zoom用到了仓库中的数据,但是在Zoom渲染时可能仓库中的数据还没有请求回来,会报错 cannot read property of undefined, 所以利用计算属性处理 || [] 或者 || {}
7. 购物车
- 点击加入购物车后,需要先发送请求,将产品发送至服务器(用户标识也要传给服务器,让服务器直到返回给谁),并在购买成功页面展示产品。(为什么不直接路由传参呢?因为服务器要记录,必须要走服务器)
- 点击加入购物车 => 发送请求 => 如果成功,带参路由跳转 => 如果失败,提示用户。 但是成功和失败的结果在dispatch中,dispatch中拿不到路由,而组件中拿不到请求的结果。解决方法:1.直接在仓库中存储变量。2.在组件中接收dispatch的返回值,由于是async异步调用,所以返回值是Promise对象。
- 详情页的产品信息传递给添加成功页面。Count可以用query参数传递。产品信息用本地存储传递sessionStorage。
- 挂载时发送请求获取不到数据,因为服务器不知道你是谁,不知道返回什么数据。在请求头中(请求拦截器中设置请求头)带唯一身份token,token需要持久化,同一个用户每次请求的token应该不变,所以使用本地存储。
- 删除选中全部产品的操作。
- 没有一次删除全部选中产品的接口,所以要依次删除。但是不在组件中依次调用删除的方法。而是派发dispatch,在dispatch中用Promise.all 接收所有请求的结果,一次性返回给组件,避免页面的频繁渲染。
- 全选按钮勾选以及取消勾选操作
- 类似于删除全部选中产品操作。派发dispatch,在dispatch中发送请求更新每一个产品的勾选状态,用Promise.all 一次性返回所以请求结果。
8.登录注册
- 获取验证码
- 绑定单击事件,向仓库派发dispatch。
- 在actions中发送异步请求,如果请求成功,将返回的验证码存至仓库。失败则返回错误信息。
- 如果派发的dispatch成功将仓库中的验证码同步到组件内的变量,展现在视图上。失败则弹出错误信息。
- 点击注册
- 邦定单击事件,向仓库派发dispatch,并携带用户填的信息作为参数。
- 在actions中发送异步请求,注意带请求参数。
- 如果派发的dispatch成功,跳转到登录页面,失败则弹出错误信息。
- 点击登录
- 邦定单击事件并阻止默认事件,向仓库派发dispatch,并携带用户填的信息作为参数。
- 在actions中发送异步请求,注意带请求参数。如果请求成功,将返回的token存至仓库和本地存储。失败则返回错误信息。
- 如果派发的dispatch成功,跳转到主页,失败则弹出错误信息。
- vuex的状态是非持久化的,刷新即消失,所以要对token进行持久化存储,使用本地存储实现。
- 登录的一般流程为:携带用户填的信息发送请求 => 返回token => 携带token请求用户信息在各个页面上
- 登录后跳转至主页
- 登录成功跳转至主页,需要显示用户名和退出的视图。
- 在主页挂载时派发action。由于登录成功后多个页面都需要展示用户名信息,所以需要在每一个组件中派发action。如果直接放在App组件中,则首次加载App组件都需要重新登录后刷新。
- 在actions中 异步请求,携带token请求用户信息。这里将token设置在请求头当中,发送至服务器。
- 再根据信息是否为空控制显示对应的视图
- 退出登录
- 绑定单击事件,派发action,发送请求清除服务器存储的数据(token等),清除仓库数据,清除本地存储数据。
- 跳转至首页。
- 各个页面展示用户名信息
- 在全局前置守卫beforeEach中处理,导航触发时,守卫异步解析执行。注意此时vuex中的状态已经刷新了。
- 如果本地存储中有token,并且仓库中没有用户用户数据,派发action。此时如果失败,说明token失效,则派发退出登录的action,清除token记录,跳转至登录页面。
- 在action中请求用户数据,存至仓库。
- 如果已登录则至/login页面的路由定向到首页。
9. 交易、支付、个人中心页面
- 获取用户地址以及商品信息
- 挂载时派发action+仓库三连环
- 点击地址更换默认地址,排他,Array.find 返回第一个符合条件的元素
- 提交订单
- 假设没有vuex,直接在组件里发送请求,为了避免多次引入API接口,可以直接类似于$bus 直接将API 挂到Vue的原型对象上。
- 发送提交订单请求,请求成功会收到订单编号,携带编号跳转至支付页面。
- 在支付页面挂载时携带订单编号发送请求获取支付信息。
- 立即支付
- 按钮绑定事件,单击弹出遮罩层以及二维码。
- 二维码利用qrcode插件生成
- 个人中心
- 包含二级路由,所以要对一级路由重定向到默认的二级路由,否则进到一级路由不显示内容。
- 表格数据展示,面对由行合并的问题,利用index===0 判断。
10. 细节处理
- 未登录状态下去 ‘/myorder’、‘/pay’、 ’/trade’ 等页面会重定向到 /login 登录页面,但是登录后应该跳到想去的页面。解决方法:在全局前置守卫中将想去的路径通过query传递给/login页面,用户登录成功后跳转到想去的页面。如果没有参数默认跳到主页。
- 路由独享守卫判断只能在特定的页面才能跳转到某些页面
- 图片懒加载 vue-lazyload。这里要安装1版本的,3版本的找不到createVNode,vue3中才有。
- 表单验证 vee-validate。 name 属性和 errors.first 的参数对应。和v-model绑定的变量没关系。
- 路由懒加载。
- IP地址怎么和dist文件夹建立联系,以及怎么和项目中请求数据的服务器建立联系。利用nginx反向代理,需要配置一些选项。
2.开发过程中的个人理解
1.Vue插件
1.路由插件
-
比如 Vue.use(VueRouter); 当Vue用了这个插件过后,每个组件实例对象就会添加上路由相关的属性($route、$router 等,只是添加了属性,属性内容此时还不知道)。
-
Vue.use(VueRouter) 怎么将路由相关属性添加到组件实例对象身上的?因为在一个项目中vue也是一个包,所有的组件包括router用的都是同一个vue包,然后在创建vue对象之前,Vue.use(VurRouter) 就为Vue原型对象上添加了路由相关的属性,而在配置项中配置 router后,就为每个实例对象添加了各自路由属性的值。
-
通过params和query传递的数据会被转换为非响应式数据。
2.防抖节流
- 防抖的原理是维护一个延时器,在规定时间内触发相同事件会通过闭包清除之前的延时器并重新开启延时器,如果规定时间内没有再次触发相同事件则执行回调函数。实现多次事件触发只执行最后一个。
// 返回的是一个函数,直接作为监听器的回调
debounce(callback, timeout) {
let timer; // 本次延时器
return function(){
clearTimeout(timer); // 清除上一次延时器
timer = setTimeout(callback, timeout);
}
}
// 解决this指向版本
debounce(callback, timeout) {
let timer; // 本次延时器
return function(){
const context = this; // 用于更改callback中this指向,原本指向window,闭包函数this指向节点
const args = arguments; // 将event事件对象传递给callback
clearTimeout(timer); // 清除上一次延时器
timer = setTimeout(callback.apply(context, args), timeout);
}
}
- 节流的原理也是维护一个延时器,触发相同事件时判断前一个延时器是否执行完毕,执行完毕则开启新的延时器,否则什么都不做。可以通过时间戳和延时器实现。
function throttle(callback, timeout) {
let timer; // 本次延时器
return function(){
if(!timer){ // 如果上一次延时器已经执行完毕
timer = setTimeout(function(){
callback();
timer=null; // 延时器执行完毕
}, timeout)
}
}
}
- 通过CSS也能实现节流。需要用到 pointer-events属性,animation动画,:active
- watch + nextTick 访问 根据数据动态加载的结构时使用。通常在修改数据后立即执行 访问等操作 是行不通的,因为DOM还没更新完,所以在在nextTick的回调中进行操作。
后台管理系统(Vue3)
组件功能
1. TradeMark品牌管理组件
- 在EUI的表格中插入图片或其他自定义内容
- 用作用域插槽,官方文档有API
- 请求数据
- 挂载时默认请求第一页数据,页数或每页条数发生变化重新发送请求。
- 添加品牌
- 点击添加按钮,弹出对话框。
- 收集用户输入对话框中的内容,注意图片要上传后收集url,上传的action要带代理前缀。
- 点击确定按钮发送添加请求
- 添加成功弹出提示框,用Element的$message实现。
- 修改品牌
- 点击添加按钮,弹出对话框。要将更改的产品信息展示在对话框中。
- 所以注意给对象赋值时使用浅拷贝,可以用对象的结构赋值。
- 删除品牌
- 利用EUI实现确认删除对话框
- 发送请求,根据id删除产品。
- 请求产品列表。
2. Attr 属性管理
- 三级联动数据数据展示
- 每一级都对应一个API接口,一级接口不需要传参,二级接口需要一级分类的Id作为参数,三级接口需要二级分类的Id作为参数
- 一级分类数据挂载时请求。一级分类选择框值变化时请求二级分类数据,二级分类选择框值变化时请求三级分类数据
- 注意EUI表单组件 label属性为展示的文本内容,value属性为值,v-model收集value属性的值。
- 二次选择时,应该清除二级三类或三级分类的值。
- 属性数据展示
- 当确定三级分类Id时发送请求获取属性数据。即三级选择框发生change事件时。
- 由于三级联动是单独的组件,所以要将一二三级分类的Id传给父组件Attr
- 父组件的自定义事件被触发获取到一二三级id并请求属性数据。
- 添加属性
- 类似于添加品牌和修改品牌
- 注意修改品牌时需要先把品牌信息展示到表格中,所以需要用到浅拷贝,但是对象中套对象不能直接用对象的解构赋值,需要深层拷贝。用lodash的deepClone实现。
- 属性值查看和修改模式切换
- 为了避免每个属性值的模式相互影响,为每个对象添加一个flag标志控制v-if。更新属性值时需要利用$set添加flag属性才是响应式的。
- v-if 切换为输入框后并没有获得焦点,要想为其设置focus方法, 需要在nextTick钩子中调用focus方法,因为DOM的更新是在下个更新周期。
- 注意添加的属性值不能为空串,也不能为已有值。
- 删除属性值
- 删除数组中的item
- 请求在点击保存按钮时再发送
- 保存
- 先整理参数,属性名不应该出现空串,flag属性不需要传递。
- 发送保存请求
- 重新请求数据
3. Spu组件
-
修改Spu
- 请求数据,注意这里请求数据不能放在mounted中,因为子组件早就挂载了,只是隐藏了。点击修改按钮时发送请求。
- 这里的需求是:点击父组件中的按钮时,触发子组件中的方法发送请求。在父组件中通过$ref获取子组件并调用方法,或者使用$children
4. Sku组件
- 深度选择器
- >>>
- 父组件中scoped内的样式传递给子组件
- scoped原理:自定义属性,然后属性选择器。
- 子组件的跟标签会继承父组件的自定义属性。
5. Home组件
-
canvas
- 浏览器认为其为一张图片
- 具有默认宽高 300 x 150
- 子节点和文字内容不显示
- 不能通过样式设置宽高
菜单权限管理:
- 服务器根据登录的不同角色返回不同的菜单信息、角色信息、按钮权限信息。
-
在仓库中利用返回的菜单信息与异步路由进行对比,决定需要展示的异步路由。
- 合并需要展示的异步路由、常量路由、任意路由。
常量路由:用户都能看见的,如登录、404、首页等
异步路由:根据用户角色动态展示的路由。
任意路由:404
按钮权限管理:
- 自定义指令
- 判断按钮对应什么操作
- 判断是否布尔值是否为真,为真则显示该按钮
Bug笔记
- npm run dev 跑不起来。
- node版本过高,降低为16版本。参考 https://blog.csdn.net/zjjxxh/article/details/127173968