# 优选购 **Repository Path**: heliangcheng/shopping ## Basic Information - **Project Name**: 优选购 - **Description**: Vue制作优选购项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-09-03 - **Last Updated**: 2023-05-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## vue-router 1、vue-router @3.5.4版后 返回的对象是一个 promise 类型 编程式路由点击两次就会飘红 ## 接口统一管理 1、api文件中响应的是 /product/getBaseCategoryList 有跨域问题 本地:http://localhost:8080/#/home 请求的:http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList 所以此次是有跨域问题的 常用解决跨域:jsonp,cros,代理 2、nprogress 进度条使用 start() 进度条开始 done() 进度条结束 3、三级联动完成 获取三级联动首先解决跨域问题 jsonp、cords、代理(利用webpack) 4、三级联动那 gosearch searchBtn params、query url参数合并 5、利用 mock 模拟假数据(轮播图) ## nextTick ```javascript // nextTick: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法 // nextTick解释: 当页面DOM都渲染完毕同时所有的for循环液结束后, 当数据发生改变之后 ``` ## Floor组件 - 可以在自定义组件身上遍历循环 - 利用 porps 传递数据给 floor - home组件中有 三处使用了轮播图 所以将这三处提取出来作为全局组件 ## search模块 ### 第六天 - 监听路由 ```js // 性能优化 // 如果属性值为空('')的话 数据是会把相应的字段带给服务器 // 当把字段改为 undefined 时, 当前字段不会带给服务器 this.searchParams.category1Id = undefined this.searchParams.category2Id = '' this.searchParams.category3Id = '' ``` - 处理面包屑 - 路由跳转【自己跳自己】 - 在 keyword 那时, 利用全局事件总线进行兄弟之前的联系 排序方式 1: 综合, 2: 价格 asc: 升序, desc: 降序 示例: "1:desc" ### 分页器 - pageNo : 当前第几个 - pageSize : 每一页展示多少条数据 - continues :分页连续页码个数 测试时(5个) - total: 页面的总数量 ## 详情页模块 - 路由滚动事件 ```js // 返回顶部 scrollBehavior(to, from, savedPosition) { // 始终滚动到顶部 0 的距离 return { y: 0 } // 返回离顶部 100 的距离 return { y: 100 } }, ``` - 因为在 index.html 里面引入了 swiper 所以在组件里面不需要引入 swiper, 否则会不起效果 ## 购物车 - vuex 里面的都是一些 promise - 调用 /api/cart/addToCart/{ skuId }/{ skuNum } 接口时 调用 vuex 里面的 primise - 当将商品添加的购物车时 此时必须知道用户的身份 不然知道这个商品时谁加的 同时接口也不会返回数据 - 解决方案 - nanoId、uuId、token、Math.Rand() + now时间戳 等一些唯一的东西 - 使用简单解决方案 uuId - 利用响应头添加唯一 uuId_token - Array.prototype.every: 全部为一样时则返回真 - 当三个元素都用到同一事件时 并切还需要知道此事件时哪个元素时 可为其添加 类型 比如(a元素传1,b元素传2) ```js // lodash的 节流和防抖 import _ from "lodash" // 防抖 _.debounce(function(){}) // 节流 _.throttle(function(){}) ``` - 在 shopCart 中修改 购物车数量的时候 必须来一 async await 不然数据就是同步数据 请求数据会很迟钝 - 修改购物车产品数量时 卡在 input 输入框那里 无论怎么改 disNum 最后的结果一直是 0 - 最后的原因是 在服务器请求的数据 input 那用了 v-model 导致 disNum 一边 input 跟着变 ```js if(isNaN(disNum) || disNum < 1){ disNum = 0 }else{ disNum = parseInt(disNum) - parseInt(cart.skuNum) console.log(disNum) } ``` - 删除所有选中的产品 - vuex 调用 vuex 里面的数据方法(新知识) 来以实现 - 最后利用 Promise.all方法实现 ```js // 获取删除所有选中的商品数据 deleteAllCheckedCart({dispatch,getters}){ let promiseAll = [] // vuex 调用 vuex 里面的数据方法 getters.cartInfoList.cartInfoList.forEach(item => { // let promise = ''; // if(item.isChecked === 1){ // promise = dispatch('getDeleteCartById',item.skuId) // }else{ // promise = '' // } let promise = item.isChecked === 1 ? dispatch('getDeleteCartById',item.skuId) : '' // console.log(promise) promiseAll.push(promise) }) // Promise.all([p1,p2,p3...]) 当 里面的值都是成功的时候 则成功 如有一个是失败的 则是失败 return Promise.all(promiseAll) } ``` ## 登录与注册模块 账号: 137 0000 0000 密码: 111111 自己的: 15573987152 密码: aaaaaa - assets 文件夹存放置的是所有组件公用的静态资源 - 在样式中也可以使用 @符号【src别名】 但后面一定要加上 ~ - 利用 token 来实现用户登录管理 - vuex 保存的数据都不持久化的 刷新一下就没了 - /api/user/passport/auth/getUserInfo 此接口没有参数可传 - 将 token 信息 信息放在响应头 - 登录验证(利用 ElementUi) - 里面有点 bug ### 路由守卫 - 利用路由守卫控制 url 跳转 - next() 直接放行 - next('/login') 只能放行至 login 路由(相当于重定向) - next(false) 回退到当前页面 比如(湖南到北京 去北京的路上被拦截了 最后又只能回到湖南) - 整个项目,游客(uuid)与 用户(token), 以token为大 - 游客登录时 如果用户还没有登录 则不能跳转到 [pay|paySuccess|trad|/center] - 路由路面默认是不区分大小写的 所有有时候到indexOf || include 时判断里面是否有时 不太好判断 需要在路由色设置区分大小写 ```js // 前置路由守卫 router.beforeEach(async (to,from,next) => { // to: 跳转的地方 // from: 来自哪个路由 let token = getToken('TOKEN') let name = store.state.user.userName.name // 跳转前 判断是否又 token if(token){ // 重定向 home 路由 if(to.path === '/login'){ next('/home') }else{ // 判断是否有用户名 有的话 则路由随意跳 // 第一次肯定时没有 name 的 因为还没派发 getUserInfo, 所以刚进来会执行 else // console.log(store.state.user.userName) if(name){ // console.log('1111',name) next() }else{ try { // 当没有用户名时 派发 actions 获取用户名 await store.dispatch('user/getUserInfo') next() // console.log('222') } catch (error) { // 只有当token过期的时候 才会执行这条语句 await store.dispatch('user/loginOut') // 当 token 失效时 自动跳转登录页面 (很多网站都是这样 比如执教云 登录进去后 几分钟没操作token失效,用户回到登录界面) router.push('/login') alert('token已过期,请重新登录',error.message) } } } }else{ let toPath = to.path // 未登录时 以下路径是不能去的 if(toPath.includes('/pay') || toPath.includes('trade') || toPath.includes('/center')){ // 利用 query 带入用户登录之前想去的路径(比如用点击我的订单 但是没有登录,当再次登录时 则直接跳到我的订单) next(`/login?redirect=${to.path}`) }else{ next() } } }) ``` - 路由的一些配置 ```js export default new Router({ 2 mode: 'history', //路由模式,取值为history与hash 3 base: '/', //打包路径,默认为/,可以修改 4 routes: [ 5 { 6 path: string, //路径 7 component: Component; //页面组件 8 name: string; // 命名路由-路由名称 9 components: { [name: string]: Component }; // 命名视图组件 10 redirect: string | Location | Function; // 重定向 11 props: boolean | string | Function; // 路由组件传递参数 12 alias: string | Array; // 路由别名 13 children: Array; // 嵌套子路由 14 beforeEnter?: (to: Route, from: Route, next: Function) => void; // 路由单独钩子 15 meta: any; // 自定义标签属性 16 icon: any; // 图标 17 // 2.6.0+ 18 caseSensitive: boolean; // 匹配规则是否大小写敏感?(默认值:false) 19 pathToRegexpOptions: Object; // 编译正则的选项 20 } 21 ] 22 }) ``` - beforeEnter 路由独享守卫 ```js { name:'trade', path:'/trade', component: Trade, meta:{show:true}, // 路由独享守卫 beforeEnter:((to, from, next) => { // 约束只能在购物车点击结算 跳转到结算页面 console.log(to.path,from.path) if(from.path == '/shop'){ next() }else{ next(false) // next(false): 回到 from.path 的位置 } // 解决刷新就空白的问题 (因为一刷新时 from.path 会变 /) if(from.path == '/'){ next() } }) }, ``` - 优化路由(路由懒加载:当此路由没有用) - 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。 ```js // 正常映入路由 import Home from '../pages/Home/Home' // 分割后的路由(懒加载) const Home = () => { return import('../pages/Home/Home') } // 完整写法 routes[ { name:'home', path:'/home', component: () => import('../pages/Home/Home'), meta:{show:true} } ] ``` ### 提交订单 - trade页面中的地址 自己mock的一些死数据 - 从这开始所以的接口都不用 vuex 直接在组件里面捞数据 - 接口全部接收过来 这样就引入一次就可以了,这样引入的是一个对象 ```js // 将所有的接口全部接收过来 这样全局引入一次就可以了, import * as api from './api' // 再在 vue 里面注册(好比全局事件总线一样) beforeCreate(){ // 注册全局事件总线 Vue.prototype.$bus = this, // 将所有的接口注册在 vue 里面 Vue.prototype.$api = api }, ``` ### 支付页面 - 利用 elementUi 设置弹出层 ```js // 引入 ElementUI 全部效果 // import ElementUI from 'element-ui'; // 按需引入 button 样式 import { Button,MessageBox } from 'element-ui'; // 引入 ElementUI 全部样式 import 'element-ui/lib/theme-chalk/index.css'; // 使用 ElementUI 中 button组件 Vue.use(Button); // 将弹出框的效果注册到 Vue 原型上面 Vue.prototype.$msgbox = MessageBox; Vue.prototype.$alert = MessageBox.alert; ``` - 弹出层里的 二维码使用 QRcode 生成二维码 ```js // QRcode 返回的是一个promise // 此时生成的二维码 用手机扫一下会出现 I am a pony!(一般放一个收款码) console.log(await QRCode.toDataURL('I am a pony!')) ``` - 图片懒加载 Vue-Lazyload(加载图片时 还未返回数据时则会呈现 Vue-Lazyload 设置的图片) - vue2 里面安装 1.3.3 版不会报错 ```js // 图片懒加载 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { preLoad: 1.3, error: errorimage, loading: loadimage, // 一般用这个 attempt: 1 }) ``` ### 打包上线 - 项目打包后,代码都是经过压缩的,如果运行时报错,输出的错误信息无法准确的得知哪里的代码报错 有了 map 文件后就可以向未加密时代码一样 准确的输出哪一行报的错 - 同时 map 文件 也挺大的,项目都准备上线了,肯定是没有错才上线的 所以 map 文件一般不需要 - 在 vue.config.js 中配置 productionSourceMap: false(默认为true) - 手写分页器