文章目录
- 项目核心
-
- 一、项目准备
- 二、脚手架目录作用
- 三、项目的其他配置
- 四、路由的分析
-
- 1、路由组件的搭建
- 2、配置路由
- 3、路由组件和非路由组件区别:
- 4、路由的跳转
- 5、路由传参
- 6、路由传递参数相关面试题
- 7、路由传参方式
- 8、NavigationDuplicated的警告错误
- 五、注册全局组件
- 六、关于axios
-
- 1、axios的二次封装
- 2、通过代理解决跨域问题
- 3、请求接口统一封装
- 4、nprogress进度条插件
- 七、vuex 状态管理库
-
- 1、什么是vuex
- 2、vuex的使用
- 3、vuex的模块化使用
- 八、函数的防抖与节流
-
- 1、函数防抖
- 2、函数节流
- 3、将changeIndex设置了节流
- 九、联动路由跳转
-
- 1、router-link:声明式导航
- 2、编程式导航
- 3、最优方案:编程式导航+事件委派
- 十、模拟数据-mock插件
-
- 1、安装mockjs
- 2、使用步骤
- 十一、vuex数据具体使用方法
- 十二、swiper插件
-
- 1、无法加载轮播图片的问题
- 2、将swiper封装组件
- 十三、getters
- 十四、ES6新增:Object.asign
- 十五、面包屑相关内容
-
- 1、删除 query — 分类名
- 2、删除 params —关键字
-
- 1、关键点:设计组件通
- 2、$bus 使用
- 十六、封装分页器
- 十七、[滚动行为](https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html)
- 十八、浏览器存储功能
- 十九、购物车相关知识
-
- 问题一:cartList为空
- 问题二:如何传uuid_token
- 1、判断底部勾选框是否勾选
- 2、修改购物车数量
- 二十、登录业务
-
- 1、注册
- 2、登录
- 二十一、[导航守卫](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html)
-
- 1、全局守卫
- 2、路由独享守卫
- 3、组件内的守卫
- 二十二、表单验证
- 二十三、路由懒加载
- 二十四、项目打包上线
项目核心
- 书写静态页面(HTML + CSS)
- 拆分组件
- 获取服务器的数据动态展示
- 完成相应的动态业务逻辑
一、项目准备
1、vue-cli 脚手架初始化项目
2、node +webpack+淘宝镜像
二、脚手架目录作用
1、node_modules — 放置项目依赖文件夹
2、public — 一般放置一些共用的静态资源(图片),打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面
3、src — 程序员源代码文件夹
- assets文件夹 — 经常放置一些静态资源(多个组件公用的静态资源),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
- components — 一般放置非路由组件(全局组件)
- api — axios 相关文件
- views | pages — 放置路由组件
- router — 配置路由的文件夹
- App.vue — 唯一的根组件
- main.js — 入口文件【程序最先执行的文件】
- babel.config.js — babel配置文件
- package.json — 看到项目描述、项目依赖、项目运行指令
- package-lock.json — 缓存性文件
- README.md — 项目说明文件
三、项目的其他配置
1、项目运行,浏览器自动打开
<!-- package.json文件夹 -->"scripts": {"serve": "vue-cli-service serve --open","build": "vue-cli-service build","lint": "vue-cli-service lint"},
2、关闭eslint校验工具
- 根目录下创建vue.config.js,进行配置
module.exports = {lintOnSave:false,
}
3、src文件夹配置别名,创建jsconfig.json,用@/代替src/,exclude表示不可以使用该别名的文件
{"compilerOptions": {"baseUrl": "./","paths": {"@/*": ["src/*"]}},"exclude": ["node_modules","dist"]}
四、路由的分析
vue-router
前端路由:kv键值对
Key — 即为URL(地址栏中的路径)
Value — 即为相应的路由组件
1、路由组件的搭建
- components — 一般放置非路由组件(全局组件)
- views | pages — 放置路由组件
2、配置路由
放置在 router 文件夹中
3、路由组件和非路由组件区别:
- 非路由组件放在 components 中,路由组件放在 pages 或 views 中
- 非路由组件使用时以标签的形式使用,路由组件需要在 router 文件中进行注册(使用的是组件的文字)通过路由使用
- 注册完路由,所有的路由和非路由组件身上都会拥有$router $route属性
$router:一般进行编程式导航进行路由跳转【 push | replace】
$route: 一般获取路由信息【query、path、params等】
4、路由的跳转
-
声明式导航 — < router-link > ,(务必要有to属性)
-
编程式导航 —< push | replace >
注:声明式导航能做的编程式都能做,而且还可以处理一些业务
5、路由传参
- params参数:路由需要占位,程序就崩了,属于URL当中一部分
参数对应的路由信息要修改为
path: "/search/:id"
这里的/:id
就是一个params参数的占位符 - query参数:路由不需要占位,写法类似于ajax当中query参数
书写形式:
path: "/search?k=v"
6、路由传递参数相关面试题
-
路由传递参数(对象写法)path是否可以结合params参数一起使用
—- 不可以,程序会崩掉 -
如何指定params参数可传可不传?
— 解决方法:在占位后面加 ? =>path: "/search/:keyword?"
-
params参数可以传递也可以不传递,但是如果传递是空串,如何解决?
— 解决方法:用 undefined =>params:{keyword:''||undefined}})
-
路由组件能不能传递props数据?
— 可以,但是只能传递params参数,具体知识为props属性
7、路由传参方式
- 字符串形式
this.$router.push(“/search/”+this.params传参+“?k=”+this.query传参)
- 模板字符串
this.$router.push(`/search/${this.params传参}?k=${this.query传参}`)
- 对象(常用)
this.$router.push({name:“路由名字”,params:{传参},query:{传参})
8、NavigationDuplicated的警告错误
程式导航路由跳转到当前路由(参数不变), 多次执行会抛出NavigationDuplicated的警告错误?
-
原因:vue-router3.1.0之后, 引入了push()的promise的语法, 如果没有通过参数指定回调函数就返回一个promise来指定成功/失败的回调, 且内部会判断如果要跳转的路径和参数都没有变化, 会抛出一个失败的promise
-
解决方法:
1、是给push 方法,传入相应的成功的回调与失败的回调,可以捕捉当前错误,代码如下
// 这种写法治标不治本,将来在别的组件中push|replace,编程式导航还是会有类似错误
this.$router.push({name:‘Search’,params:{keyword}},()=>{},()=>{})
2、重写push 方法(二次封装),代码如下
//1、先把VueRouter原型对象的push,保存一份
let originPush = VueRouter.prototype.push;
//2、重写push|replace
//第一个参数:告诉原来的push,跳转的目标位置和传递了哪些参数
VueRouter.prototype.push = function (location,resolve,reject){if(resolve && reject){originPush.call(this,location,resolve,reject)}else{originPush.call(this,location,() => {},() => {})}
}
五、注册全局组件
// mian.js 中书写
//将三级联动组件注册为全局组件
import TypeNav from '@/pages/Home/TypeNav';
//第一个参数:全局组件名字,第二个参数:全局组件
Vue.component(TypeNav.name,TypeNav);
//在页面中使用时: 直接通过 <TypeNav/> 使用,不需要引入
六、关于axios
1、axios的二次封装
- 为什么需要进行二次封装axios?
- 请求拦截器:可以在发请求之前,处理一些业务
- 响应拦截器:当服务器数据返回后,可以处理一些事情
//在根目录下创建api文件夹,创建request.js文件
import axios from "axios";
//1、对axios二次封装
const requests = axios.create({//基础路径,requests发出的请求在端口号后面会跟改baseURlbaseURL:'/api',timeout: 5000,
})
//2、配置请求拦截器
requests.interceptors.request.use(config => {//config内主要是对请求头Header配置return config;
})
//3、配置相应拦截器
requests.interceptors.response.use((res) => {//成功的回调函数return res.data;
},(error) => {//失败的回调函数console.log("响应失败"+error)return Promise.reject(new Error('fail'))
})
//4、对外暴露
export default requests;
2、通过代理解决跨域问题
module.exports = {//代理服务器解决跨域proxy: {//会把请求路径中的/api换为后面的代理服务器'/api': {//提供数据的服务器地址// 时间2022/10/23,服务器地址为http://gmall-h5-api.atguigu.cntarget: 'http://gmall-h5-api.atguigu.cn',}},}
3、请求接口统一封装
- 项目过大时,推荐使用。如果项目小,在生命周期函数中发请求即可
- 将每个请求封装为一个函数,并暴露出去,组件只需要调用相应函数即可,这样当我们的接口比较多时,如果需要修改只需要修改该文件即可
//在文件夹api中创建index.js文件,用于封装所有请求
import requests from "@/api/request";
//首页三级分类接口
export const reqCateGoryList = () => {return requests({url: '/product/getBaseCategoryList',method: 'GET'})
}
4、nprogress进度条插件
//安装nprogress进度条插件
cnpm install--save nprogress
相关配置
- start:进度条开始 done 进度条结束
- 进度条的颜色可以修改,修改源码样式.bar类名的
//引入进度条
import nprogress from 'nprogress';
//引入进度条样式
import "nprogress/nprogress.css";
requests.interceptors.request.use(config => {//开启进度条nprogress.start();return config;
})
requests.interceptors.response.use((res) => {//响应成功,关闭进度条nprogress.done()
},(error) => {
})
七、vuex 状态管理库
并不是所有项目都需要vuex,如果项目小,完全不需要使用vuex,当项目很大、组件很多、数据维护费劲时使用
1、什么是vuex
集中式管理项目中的组件公用的数据
- Vuex核心概念:state、actions、mutations、getters、modules
2、vuex的使用
- 首先确保安装了vuex,根目录创建store文件夹,文件夹下创建index.js,内容如下:
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)//对外暴露store的一个实例
export default new Vuex.Store({//state:仓库存储数据的地方,公共状态state:{},//mutations:修改state的唯一手段,只支持同步mutations:{},//actions:处理action,书写自己的业务逻辑,提交mutation,但是不能修改state支持同步异步actions:{},//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便(一般不使用)getters:{},
})
3、vuex的模块化使用
采用vuex模块式管理数据的原因:
- 项目过大、组件过多、接口过多、返回数据多时,如果还用以前的方式存储数据,导致vuex中的state数据格式比较复杂。
1、建立小仓库 =>在store文件下 home.js,
// home 模块的小仓库
//home 相关逻辑在此页面进行书写
const state = {}
const getters = {}
const mutations = {}
const actions = {}
const modules = {}
export default {state,getters,mutations,actions,modules
}
2、在大仓库中引入小仓库 =>在store文件下 index.js,
import Vue from 'vue'
import Vuex from 'vuex'
// 引入小仓库
import home from './home'
Vue.use(Vuex)
export default new Vuex.Store({// 实现vuex 仓库模块式开发存储数据modules: {home}
})
八、函数的防抖与节流
-
出现卡顿现象原因:事件触发非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很可能出现浏览器卡顿
-
此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果
-
安装lodash插件,该插件提供了防抖和节流的函数【闭包 + 延迟器】,我们可以引入js文件,直接调用。向外暴露 _ 函数
ladash官网
1、函数防抖
-
前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发,只会执行最后一次
用户操作很频繁,但是只是执行一次
ladash官网–防抖用法
2、函数节流
-
在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发
用户操作很频繁,但是把频繁的操作变为少量操作【给浏览器充裕的时间解析代码】
ladash官网–节流用法
3、将changeIndex设置了节流
// 全部引入 =>import _ from 'lodash'=>使用时 _.throttle
// 最好按需加载
import throttle from 'lodash/throttle'methods: {//采用键值对形式创建函数,将changeIndex定义为节流函数,该函数触发很频繁时,设置50ms才会执行一次// throttle回调函数别用箭头函数,防止出现this问题changeIndex: throttle(function (index){this.currentIndex = index},50),}
九、联动路由跳转
1、router-link:声明式导航
-
缺点:由于生成的标签过多,出现卡顿现象
-
出现卡顿的原因:
— router-link是一个组件:相当于VueComponent类的实例对象,一瞬间new VueComponent很多实例(1000+),很消耗内存,因此导致卡顿。
2、编程式导航
我们是通过触发点击事件实现路由跳转。同理有多少个a标签就会有多少个触发函数。虽然不会出现卡顿,但是也会影响性能。
3、最优方案:编程式导航+事件委派
事件委派即把子节点的触发事件都委托给父节点
- 事件委派问题:
1、如何确定我们点击的一定是a标签呢?如何保证我们只能通过点击a标签才跳转呢?
- 解决办法: 把子节点当中
a
标签,加上自定义属性:data-categoryName
,其余的子节点没有
2、如何区分一、二、三级的标签
- 解决办法:
-
为三个等级的a标签再添加自定义属性
data-category1Id
、:data-category2Id
、:data-category3Id
来获取三 个等级a标签的商品id,用于路由跳转。 -
我们可以通过在函数中传入event参数,通过
event.target
属性获取当前点击节点,再通过dataset属性获取节点的属性信息
-
十、模拟数据-mock插件
mock官网
-
生成随机数据,拦截 Ajax 请求,返回我们自定义的数据用于测试前端接口
-
前端mock数据不会和你的服务器进行任何通信
1、安装mockjs
cnpm install mockjs
2、使用步骤
-
在项目当中src文件夹中创建mock文件夹
-
设计JSON 数据结构(在mock文件中创建相应JSON文件)
注意:JSON文件需要格式化一下,不能留有空格,否则跑不起来
-
将所需图片放置在public文件夹中
- public文件夹在打包时,会把相应的资源打包dist文件夹中
-
创建mockServe.js,通过mockjs插件实现模拟数据
import Mock from 'mockjs' //webpack默认对外暴露:json、图片 import banner from './banner.json' import floor from './floor.json' //mock数据:第一个参数请求地址、第二个参:请求数据 Mock.mock("/mock/banner",{code:200,data:banner}) Mock.mock("/mock/floor",{code:200,data:floor})
-
mockServe.js文件在入口文件中引入(至少需要执行一次,才能模拟数据)
//在main.js中引入 import ''@/mock/mockServe
-
在API文件夹中创建mockRequest.js
- 专门请求mock接口的axios封装
十一、vuex数据具体使用方法
把公共数据、获取数据方法都统一放在store中,取其数据的固定步骤如下:(轮播)
1、派发action:通过vuex 发起Ajax请求,将数据储存在仓库中
//ListContainer.vue 文件mounted() {this.$store.dispatch("getBannerList")},
2、在store文件中的home.js中,书写相关逻辑,并提交mutations
actions:{//获取首页轮播图数据async getBannerList({commit}){let result = await reqGetBannerList()if(result.code === 200){commit("BANNERLIST",result.data)}}}
3、获取到数据后,在mutations
中修改state
mutations:{BANNERLIST(state,bannerList){state.bannerList = bannerList}},
4、在state中,添加 bannerList
状态
state:{bannerList:[]
}
5、在ListContainer.vue组件在store中获取轮播图数据。
import {mapState} from "vuex";
export default {computed: {...mapState({bannerList: state => state.home.bannerList})},}
十二、swiper插件
swiper官网
swiper使用方法
1、无法加载轮播图片的问题
原因:在mounted中,先异步请求轮播图数据,然后创建swiper实例。由于请求数据是异步的,所以浏览器不会等待该请求执行完再去创建swiper,而是先创建了swiper实例,但是此时我们的轮播图数据还没有获得,就导致了轮播图展示失败。
解决方法:
1、添加延迟器(不是完美的解决方案)
mounted() {setTimeout(()=>{let mySwiper = new Swiper(...)},2000)},
缺点:无法确定用户请求到底需要多长时间,因此没办法确定延迟器时间
2、watch + nextTick
this. $nextTick
:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
<template><!--banner轮播--><div class="swiper-container" ref="mySwiper"><div class="swiper-wrapper"><div class="swiper-slide" v-for="(carouse,index) in bannerList" :key="carouse.id"><img :src="carouse.imgUrl" /></div></div><!-- 如果需要分页器 --><div class="swiper-pagination"></div><!-- 如果需要导航按钮 --><div class="swiper-button-prev" ></div><div class="swiper-button-next"></div></div></div>
</template>
<script>
// 引入Swiper
import Swiper from 'swiper'
// 引入Swiper样式
import 'swiper/css/swiper.css'
import {mapState} from "vuex";
export default {mounted() {// ajax请求轮播图图片this.$store.dispatch("getBannerList")},computed:{...mapState({// 从仓库中获取轮播图数据bannerList: (state) => {return state.home.bannerList}})},watch:{bannerList(newValue,oldValue){// this.$nextTick()使用this.$nextTick(()=>{let mySwiper = new Swiper(this.$refs.mySwiper,{pagination:{el: '.swiper-pagination',clickable: true,},// 如果需要前进后退按钮navigation: {nextEl: '.swiper-button-next',prevEl: '.swiper-button-prev',}})})}}
}
</script>
2、将swiper封装组件
由于 floor.vue 中,数据是由父组件传递过来的,且从来没有发生改变,因此监听不到list。所以我们需要借助immediate
。
immediate
:立即监听,不管数据是否变化,都监听一次
<template><div><div class="swiper-container"ref="cur"><div class="swiper-wrapper"><div class="swiper-slide"v-for="carousel in list":key="carousel.id"><img :src="carousel.imgUrl" /></div></div><!-- 如果需要分页器 --><div class="swiper-pagination"></div><!-- 如果需要导航按钮 --><div class="swiper-button-prev"></div><div class="swiper-button-next"></div></div></div>
</template>
<script>
import Swiper from 'swiper'
export default {name: 'Carousel',props: ['list'],watch: {list: {// 立即监听:不管数据是否变化,都监听一次immediate: true,handler () {this.$nextTick(() => {// eslint-disable-next-line no-unused-varsconst mySwiper = new Swiper(this.$refs.cur, {loop: true,// 如果需要分页器pagination: {el: '.swiper-pagination',clickable: true},// 如果需要前进后退按钮navigation: {nextEl: '.swiper-button-next',prevEl: '.swiper-button-prev'}})})}}}
}
</script>
<style lang="less" scoped>
</style>
十三、getters
- 作用:简化仓库中的数据(相当于计算属性)
mapGetters
里面的写法:数组,因为getters
计算是没有划分模块的(state
:划分home,search)
十四、ES6新增:Object.asign
Object.assign()
方法将所有可枚举的自有属性从一个或多个源对象复制到目标对象,返回修改后的对象。
-
实现对象拷贝,合并对象
// 举例 const obj1 = {a:1,b:2 }; const obj2 = Object.assign({b:3, c:4}, obj1); console.log(object1) // { a: 1, b: 2} console.log(object2) // { b: 2 c: 4, a: 1} // 如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性
十五、面包屑相关内容
1、删除 query — 分类名
-
方法:删除时,将属性值赋值成
undefined
,并且进行路由跳转(自己跳自己)removeCategoryName () {// 赋值成undefined(不消耗宽带)this.searchParams.categoryName = undefinedthis.searchParams.category1Id = undefinedthis.searchParams.category2Id = undefinedthis.searchParams.category3Id = undefinedthis.getData()// 地址栏需要改:进行路由跳转if (this.$route.params) {this.$router.push({ name: 'search', params: this.$route.params })}},
2、删除 params —关键字
- 方法:删除时,将keyword赋值成
undefined
,然后修改路由信息,同时删除兄弟组件Header中输入框内的关键字
1、关键点:设计组件通
- props:父子
- 自定义事件:子父
- vuex:万能
- 插槽:父子
- pubsub-js:完成
- $bus:全局事件总线(本案例运用此方法)
2、$bus 使用
-
在main.js中配置
$bus
new Vue({//全局事件总线$bus配置beforeCreate() {//此处的this就是这个new Vue()对象Vue.prototype.$bus = this}, }).$mount('#app')
-
在Search.vue中触发$bus
-
$emit ('事件名',data)
removeKeyword () {// 参数赋空this.searchParams.keyword = undefinedthis.getData()// 通知兄弟组件清除关键字// data用于传递数据,此页面只是用于通知header组件进行相应操作,故不传datathis.$bus.$emit('clear')// 进行路由跳转this.$router.push({ name: 'search', query: this.$route.query }) }
-
-
在Header.vue 中监听$bus
-
$on('事件名',( )=>{ })
mounted () {// 通过全局总线清除关键字this.$bus.$on('clear', () => {this.keyword = ''})}
-
十六、封装分页器
- 分页器需要的数据(条件)
- pageNo :当前页数
- pageSize :每页展示几条数据
- total :总共返回多少条数据
- continues:连续页码数【5|7】
- 重点:计算出连续页面起始数字和结束数字
// 连续页数的起始页数和结束页数 startNumAndEndNum () {const { continues, pageNo, totalPage } = thislet start = 0let end = 0// 判断总页数是否等于连续页(5),// 不等于if (continues > totalPage) {start = 1end = totalPage} else {// 等于start = pageNo - parseInt(continues / 2)end = pageNo + parseInt(continues / 2)// start= 0/负数if (start < 1) {start = 1end = continues}// end>总页码if (end > totalPage) {start = totalPage - continues + 1end = totalPage}}return { start, end } }
十七、滚动行为
-
当切换到新路由时,滚动条的位置,vue-router就可以做到。
-
vue-route 提供了
scrollBehavior
方法(只在支持history.pushState
的浏览器中可用)scrollBehavior (to, from, savedPosition) {// return 期望滚动到哪个的位置return { y: 0 }}
十八、浏览器存储功能
HTML5中新增,本地存储和会话存储。
-
本地存储–localStorage:持久化的(5M)
-
会话存储–sessionStorage:并非持久–会话结束(关闭浏览器)就消失、
十九、购物车相关知识
问题一:cartList为空
-
发现:发请求时,获取不得购物车里面的数据
=>原因:因为服务器不知道你是谁
=>解决方案:生成uuid(临时游客身份)
-
由于每个用户的uuid不能发生变化,还要持久存储,所以封装游客身份模块uuid—生成一个随机字符串
-
创建utils文件夹=>创建uuid_token.js文件
// 我用 uuid_token 会有红杠,提示没有用驼峰写法,因此加上了下面注释 /* eslint-disable camelcase */import { v4 as uuidv4 } from 'uuid' // 生成一个随机字符串,且每次执行都不能发生变化,还要持久存储 export const getUUID = () => {// 判断本地存储是否由uuidlet uuid_token = localStorage.getItem('UUIDTOKEN')// 本地存储没有uuidif (!uuid_token) {// 生成uuiduuid_token = uuidv4()// 存储本地localStorage.setItem('UUIDTOKEN', uuid_token)}// 当用户有uuid时就不会再生成return uuid_token }
-
问题二:如何传uuid_token
-
购物车的请求函数中没有参数,无法传 uuid_token
=>解决方法:把uuid_token加在请求头中
-
在store中的detail模块中定义uuid_token
const state = {//游客身份uuid_token: getUUID() }
-
在request.js中设置请求头
// 引入store import store from '@/store'; requests.interceptors.request.use(config => {// 判断uuid_token是否为空if (store.state.detail.uuid_token) {// 请求头添加一个字段:userTempId字段和后端统一config.headers.userTempId = store.state.detail.uuid_token}return config; })
-
1、判断底部勾选框是否勾选
=>解决方法:every
//判断底部勾选框是否全部勾选
isAllCheck() {//every遍历某个数组,判断数组中的元素是否满足表达式,全部为满足返回true,否则返回falsereturn this.cartInfoList.every(item => item.isChecked === 1)}
2、修改购物车数量
-
@click @change 使用同一个方法进行修改
=>问题:如何辨别点击哪一个
=>解决方法:通过传参来区分谁是谁
<a @click="handler('minus',-1,cartInfo)">-</a><input @change="handler('change',$event.target.value,cartInfo)"><a @click="handler('add',1,cartInfo)">+</a>
-
handler 函数=> 需要节流
import throttle from 'lodash/throttle'handler: throttle(async function (type, disNum, cart) {// type:区分三个元素// disNum:+ 变化量(1) -变化量(-1) input最终个数(不是变化量)// cart:分辨那个产品// 向服务器发请求switch (type) {case 'add':disNum = 1breakcase 'minus':// 判断产品个数: skuNum>1,传-1,skuNum<=1,传0disNum = cart.skuNum > 1 ? -1 : 0breakcase 'change':// 用户输入非数字||负数=>0disNum = (isNaN(disNum) || disNum < 1) ? 0 : parseInt(disNum) - cart.skuNumbreak}try {await this.$store.dispatch('addOrUpdateShopCart', { skuId: cart.skuId, skuNum: disNum })this.getDate()} catch (error) {} }, 1000)
二十、登录业务
1、注册
=> 通过数据库存储用户信息(名字、密码)
2、登录
=>登录成功时,后台为了区分用户是谁,服务器会下发token【token:令牌=>唯一的标识符===uuid】
=>注意:一般流程=>登录成功服务器下发token,前台持久化存储token【带着token找服务器要用户信息进行展示】
二十一、导航守卫
=>导航:表示路由正在发生改变。进行路由跳转
=>守卫:相当于“紫禁城护卫”
1、全局守卫
=>在项目中,只要发生路由变化,守卫就能监听到
-
前置守卫:在路由跳转之前进行判断
router.beforeEach((to, from, next) => {// to:可以获取你要跳转到的那个路由信息// from:可以获取你从哪里来的那个路由信息// next:放行函数 next()=>直接放行 next(path)=>放行到指定路由 next(false) })
-
后置守卫:路由跳转已经完成在执行。
2、路由独享守卫
=>只在进入路由时触发
beforeEnter: (to, from,next) => {
}
3、组件内的守卫
beforeRouteEnter
//进入beforeRouteUpdate
//更新,指的是参数发生更新时触发beforeRouteLeave
//离开
二十二、表单验证
-
Vue官方提供的一个表单验证的插件:vee-validate
=>不好用,看懂老师操作即可
-
【elementUI】推荐用它!
二十三、路由懒加载
路由懒加载:当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
写法:
// 将 import Home from '@/views/Home'
// 替换成
const Home = () => import('@/views/Home')
const router = createRouter({routes: [{ path: '/home', component: Home }],
})
//上面是官网上的介绍,下面是优化写法
const router = createRouter({routes: [{ path: '/home', component:() => import('@/views/Home') }],
})
二十四、项目打包上线
执行npm run build
map文件:因为代码是经过加密的,如果运行时报错,输出的错误信息无法准确得知时那里的代码报错。有了map就可以向未加密的代码一样,准确的输出是哪一行那一列有错,所以该文件如果项目不需要是可以去掉的。【map文件较大】
//在vue.config.js配置
module.exports ={productionSourceMap: false
}
// 修改需要重新build