# 优选购
**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)
- 手写分页器