尚硅谷–尚品汇项目笔记

文章目录

  • 项目核心
    • 一、项目准备
    • 二、脚手架目录作用
    • 三、项目的其他配置
    • 四、路由的分析
      • 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、使用步骤

  1. 在项目当中src文件夹中创建mock文件夹

  2. 设计JSON 数据结构(在mock文件中创建相应JSON文件)

    注意:JSON文件需要格式化一下,不能留有空格,否则跑不起来

  3. 将所需图片放置在public文件夹中

    • public文件夹在打包时,会把相应的资源打包dist文件夹中
  4. 创建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})
    
  5. mockServe.js文件在入口文件中引入(至少需要执行一次,才能模拟数据)

    //在main.js中引入
    import ''@/mock/mockServe
    
  6. 在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 = ''})}
      

十六、封装分页器

  • 分页器需要的数据(条件)
    1. pageNo :当前页数
    2. pageSize :每页展示几条数据
    3. total :总共返回多少条数据
    4. 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

Published by

风君子

独自遨游何稽首 揭天掀地慰生平