当前位置: 首页 > news >正文

vue项目的实用性总结

1、mockjs 基本使用 ★

  1. 安装:npm i mockjs

  2. src/mock/index.js内容如下:

    import Mock from 'mockjs'
    //制订拦截规则
    Mock.mock('http://www.0313.com','get','你好啊')
    
  3. 记得在main.js中引入一下,让其参与整个项目的运行。

  4. 只要发出去的是get类型的ajax请求,地址只要是http://www.0313.com就能拦截了。

  5. 备注:使用mockjs后,浏览器的网络选项卡中一定看不到被拦截的请求。

  6. Test组件中发送一个请求测试一下。

    async handleTest(){const result = await axios.get('http://www.0313.com')console.log(result)
    }
    

2、处理 vuex 假报错 ★

  1. 问题描述:从undefined上读取属性会报错,代码演示如下:

    //例如vuex中数据格式如下:
    const state = {a:[],x:{}
    }
    
    <!--下写法均会报错-->
    <h1>{{a[0].id}}</h1>
    <h1>{{x.y.z}}</h1>
    
  2. 如何解决?

    • 第一种方法:在最初就设计好数据层次 —— 不推荐。

      const state = {a:[{}],x:{y:{}}
      }
      
    • 第二种方法:使用数据时加判断。

      <h1 v-if="a[0]">{{a[0].id}}</h1>
      <h1 v-if="x.y">{{x.y.z}}</h1>
      
    • 第三种方法:使用问号 —— 最推荐。

      <h1>{{a[0]?.id}}</h1>
      <h1>{{x.y?.z}}</h1>
      
    • 第四种方法:若模板中恰巧使用了v-for遍历,那么问题自动消失。

      <h1 v-for="s in carouselList" :key="s.id">{{s.imgUrl}}</h1>
      
    • 第五种方法:使用&&短路

      <h1>{{a[0] && a[0].id}}</h1>
      <h1>{{x.y && x.y.z}}</h1>
      

3、vue-awesome-swiper 的使用 ★

  1. Swiper是专门做轮播图的库,在:原生项目、Vue项目、React项目中,均可使用。

  2. Vue项目中,可以使用vue-awesome-swiper来更方便的实现轮播图。

  3. 安装:npm i vue-awesome-swiper@4.1.1

  4. Test组件中实现一个简单的轮播

    <template><swiper class="swiper" :options="swiperOption"><swiper-slide class="item">Slide 1</swiper-slide><swiper-slide class="item">Slide 2</swiper-slide><swiper-slide class="item">Slide 3</swiper-slide><swiper-slide class="item">Slide 4</swiper-slide><div class="swiper-pagination" slot="pagination"></div><div class="swiper-button-prev" slot="button-prev"></div><div class="swiper-button-next" slot="button-next"></div></swiper>
    </template><script>import {Swiper, SwiperSlide} from 'vue-awesome-swiper'import 'swiper/css/swiper.css'export default {name: 'Test',components: {Swiper,SwiperSlide},data() {return {//轮播图配置对象swiperOption: {slidesPerView: 1, //同时展示几屏spaceBetween: 30, //每屏间隔loop: true, //是否循环轮播speed: 1000, //切换速度//自动轮播autoplay: {delay: 2000,//轮播间隔disableOnInteraction: false //鼠标点击后,是否禁止自动轮播},//分页器配置(小圆点)pagination: {el: '.swiper-pagination', //分页器元素clickable: true //小圆点是否可以点击},//导航按钮(左箭头、右箭头)navigation: {nextEl: '.swiper-button-next',prevEl: '.swiper-button-prev'}}}}}
    </script><style lang="less" scoped>.item {line-height: 200px;text-align: center;font-size: 40px;background-image: linear-gradient(45deg,red,yellow,green);height: 200px;}
    </style>
    

4、整合搜索参数 ★

  1. 目标:无论是点击分类,还是点击搜索Search组件都需要接收路由参数。

  2. 使用watch监听$route

    watch:{	$route:{		//立即监视,目的是为了让第一次搜索的时候,可以拿到参数。immediate:true,//此处的value是谁? —— 是$route的新值(监视的是谁,handler得到的就是谁的新值)handler(value){	console.log(value.query)		}	}
    }
    
  3. 具体思路:使用watch监听$route,只要当前路由信息变化,就存储搜索参数。

    watch:{$route:{//追加立即监视,为了初次挂载拿到参数immediate:true,//注意此处的query是谁?—— 路由传递过来的query参数   handler({query}){//重置之前的一级id、二级id、三级id,随后将最新的参数合并进去Object.assign(this.searchParams,{category1Id:'', //重置一级分类idcategory2Id:'', //重置二级分类idcategory3Id:'', //重置三级分类id},query)}}
    }
    

5、对分页器的理解★

  1. 为什么要用?—— 大量数据不能一次全部展示。
  2. 分页器想工作,需要哪些数据?
    • 总数(total)
    • **页大小(pageSize)**通俗理解:每页展示几条。
    • **页码(pageNo)**通俗理解:看第几页。
    • **连续页数(continues)**一般为奇数,例如:357 等,为什么?—— 好看,通常是5
    • 总页数(totalPage) = total/pageSize(注意要向上取整)

备注:红色是必须传项,绿色的不用传,可以根据红色算出来。

6、分页器_ 连续页_特殊情况一 ★

  • 连续页数continues > totalPage

  • 具体编码:

      export default {      name: "Pagination",   props:['total','pageSize','pageNo','continues'],computed:{// 计算总页数totalPage(){return Math.ceil(this.total / this.pageSize)},// 计算连续页的起始页、结束页startEnd(){// 获取外部传入的各种参数const {continues,pageNo,totalPage} = thislet start = 0 //起始页初始值let end = 0 //结束页初始值// 判断一下目前的情况是不是一个“大变态”//(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)if(continues > totalPage){start = 1end = totalPage}else{// 标准情况start = pageNo - (continues - 1)/2end = pageNo + (continues - 1)/2}return {start,end}}}};
    

7. 分页器_ 连续页_特殊情况二★

  • 算连续页start的时候,往左推,推多了,出现 start < 1 了。

  • 处理方式如下:

    export default {      name: "Pagination",   props:['total','pageSize','pageNo','continues'],computed:{// 计算总页数totalPage(){return Math.ceil(this.total / this.pageSize)},// 计算连续页的起始页、结束页startEnd(){// 获取外部传入的各种参数const {continues,pageNo,totalPage} = thislet start = 0 //起始页初始值let end = 0 //结束页初始值// 判断一下目前的情况是不是一个“大变态”//(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)if(continues > totalPage){start = 1end = totalPage}else{// 标准情况(正常左推,右推)start = pageNo - (continues - 1)/2end = pageNo + (continues - 1)/2// 判断左边是否“冒了”(start是否小于1)if(start < 1){ start = 1end = continues}}return {start,end}}}};
    

8. 分页器_ 连续页_特殊情况三★

  • 往右推,算结束页码的时候,推多了,出现end > totalPage 了。

    >

  • 处理方式如下:

    export default {      name: "Pagination",   props:['total','pageSize','pageNo','continues'],computed:{// 计算总页数totalPage(){return Math.ceil(this.total / this.pageSize)},// 计算连续页的起始页、结束页startEnd(){// 获取外部传入的各种参数const {continues,pageNo,totalPage} = thislet start = 0 //起始页初始值let end = 0 //结束页初始值// 判断一下目前的情况是不是一个“大变态”//(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)if(continues > totalPage){start = 1end = totalPage}else{// 标准情况(正常左推,右推)start = pageNo - (continues - 1)/2end = pageNo + (continues - 1)/2// 判断左边是否“冒了”(start是否小于1)if(start < 1){ start = 1end = continues}// 判断右边是否“冒了”(end是否大于totalPage)if(end > totalPage){ start = totalPage - continues + 1end = totalPage}}return {start,end}}}};
    

9. 分页器_整体显示

  • 分页器整体的显示,逻辑如下:

    • 当前已经是第一页了,那么【上一页】就不能点了。
    • 当前已经是最后一页了,那么【下一页】就不能点了。
    • 两个数中间,有其他数,再出现三个点。
    	<div class="pagination"><button :disabled="pageNo === 1">上一页</button><button v-if="startEnd.start !== 1">1</button><span v-show="startEnd.start > 2">···</span> <!-- 连续页区域 --><button v-for="(number,index) in (startEnd.end - startEnd.start + 1)" :key="index" :class="{active: (index + startEnd.start) === pageNo }">{{index + startEnd.start}}</button><span v-show="startEnd.end < totalPage - 1">···</span><button v-show="startEnd.end !== totalPage">{{totalPage}}</button><button :disabled="pageNo === totalPage">下一页</button><span>共 {{total}} 条</span> </div>
    

10.分页器_整体交互

  • 第一步:对接真实数据,把:totalpageNopageSize,传给Pagination组件。

    注意1:total是服务器返回给我们的,要去searchResInfo中拿。

    注意2:pageNopageSize是我们传给服务器的,要去searchParams中拿。

    注意3:continues是连续页数,是我们自己指定的。

    <!-- 分页器 -->
    <Pagination :total="searchResInfo.total" :pageNo="searchParams.pageNo" :pageSize="searchParams.pageSize" :continues="5"
    />
    
  • 第二步:传递页码,Pagination组件将用户点击的页码,传给Search组件,Search组件接收参数,随后重新请求数据。

    1. Saecrh组件中:

      <template>	<!-- 分页器 -->    <Pagination         :total="searchResInfo.total" :pageSize="searchParams.pageSize" :pageNo="searchParams.pageNo" :continues="5":changePageNo="changePageNo" />
      </template><script>	//......    methods:{        //用于接收分页器传递过来的页码(pageNo)       changePageNo(n){            //更新参数            this.searchParams.pageNo = n}    }    //......
      </script>
      
    2. Pagination组件中:

      <template>	<div class="pagination"><!-- 此处给所有能点击的东西绑定上click事件,事件的回调为sendPageNo --><button @click="changePageNo(????)">????</button></div>
      </<template><script>   export default {        name: "Pagination",        props:['total','pageNo','pageSize','continues','changePageNo'],        }; 
      </script>
      

11、 详情组件 _ 放大镜★

  • 思路:鼠标动,遮罩动,大图区域跟着动。

  • 具体代码:

    详情组件,给放大镜组件传递图片url

    <Zoom :imgurl="info.skuInfo.skuDefaultImg"/>
    

    放大镜组件:

    <template><div class="spec-preview"><img :src="imgurl" /><div class="event" @mousemove="move"></div><div class="big"><img :src="imgurl" ref="bigImg"/></div><div class="mask" ref="mask"></div></div>
    </template><script>export default {name: "Zoom",props:['imgurl'],methods: {move(event){// 获取鼠标位置let mouseX = event.offsetXlet mouseY = event.offsetY// 获取遮罩DOM元素let {mask,bigImg} = this.$refs// 计算出遮罩位置let maskX = mouseX - mask.offsetWidth/2let maskY = mouseY - mask.offsetHeight/2// 限制遮罩不要超出父容器if(maskX < 0){maskX = 0}else if(maskX > mask.offsetWidth){maskX = mask.offsetWidth}if(maskY < 0){maskY = 0}else if(maskY > mask.offsetHeight){maskY = mask.offsetHeight}// 给遮罩应用位置mask.style.left = maskX + 'px'mask.style.top = maskY + 'px'// 给大图应用位置bigImg.style.left = -2*maskX + 'px'bigImg.style.top = -2*maskY + 'px'}},}
    </script>
    

    用第三方库实现:vue-photo-zoom-pro

12、限制购买数量_思路★

购买商品时,输入的最大购买数量,有两种选择:200 最大库存量,大部分电商的输入限制是200

  • 必须是1200的正整数。
  • 若输入的是1-200之间的小数,则取其整数部分。
  • 若输入小于1,则重置为1
  • 若输入大于200,则重置为200
  • 若输入的是其他值,则重置为1

13、购物车_理解临时标识★

明确几个点:

  • 每个用户都有自己的购物车,不可能大家共用一个。

  • 如何区分不同的用户?(服务器如何知道你是谁?)

    办法一:登录系统 (最正式的做法)

    办法二:使用临时标识(其实就是:随机生成的一个UUID,不能真正下单,只是为了临时支撑购物车)。

  • 为什么有的网站,不登录也有购物车?

    尽可能引导用户消费,或者说增强用户体验。

  • 为什么有的网站,必须登录才有购物车,甚至必须登录才能搜索?

    ①用户量大(不愁用户)②网站业务逻辑复杂,登录以后有些逻辑好处理。

14、 购物车_实现临时标识★

  1. 使用uuidjs生成,安装uuid:npm i uuid

  2. 为确保生成的userTempId不丢失,需要存到localStorage中。

  3. /src/utils/auth.js中创建一个getUserTempId的方法,用于提供userTempId

    整体思路为:

    • 如果localStorage中有,就返回。
    • 没有就生成一个,随后存到localStorage中,再返回。
    /* getUserTempId的功能是:方法会从localStorage中读取用户临时标识,1.如果有就返回,随后就用。2.如果没有第一件事:生成一个新的临时标识,第二件事:存入localStorage  ——  为了以后使用第三件事:返回 ——  为了这次使用*/
    import {v4} from 'uuid';export function getUserTempId(){// 尝试从localStorage中读取临时标识const userTempId = localStorage.getItem('userTempId')// 判断标识是否存在if(userTempId){// 返回之前存入的标识return userTempId}else {// 生成一个新的临时标识const newUserTempId = v4()// 存入localStoragelocalStorage.setItem('userTempId',newUserTempId)// 返回return newUserTempId}
    }
    
  4. 编辑/src/api/myAxios.js,在axios请求拦截器中,让所有请求头,都携带userTempId

    import {getUserTempId} from '@/utils/auth'//请求拦截器
    myAxios.interceptors.request.use((config)=>{//进度条走起nprogress.start()//向请求头中添加userTempIdconfig.headers.userTempId = getUserTempId()//返回本次请求的配置信息return config
    })
    

15.购物车勾选_prevent一个小优化★

当我们勾选一个商品时,先阻止掉默认行为,即:不让checkbox立刻勾选上。因为这样checkbox的勾选才是完全由服务器决定,操作很简单:加上一个prevent修饰符。

<input type="checkbox" name="chk_list" :checked="cartInfo.isChecked" @click.prevent="checkOne(cartInfo)"
>

16.函数防抖、函数节流 ★

16.1 函数防抖

  1. 标准概念:事件触发n秒后再执行逻辑,若这n秒内事件又被触发,则重新计时n秒,之前的逻辑不执行。

  2. 通俗理解:要做的事,总是改来改去,那么就等你下发指令后n秒,我再做,免得你再改。

  3. 目的:防止复杂逻辑频繁触发(例如:复杂的运算、网络请求等)。

  4. 生活中的例子:调节空调温度时,按下【+】或【-】,等1秒空调才有反应。

    最简洁的记法:就要最后那一下。

  5. 最具代表性的应用场景:实时搜索,但要注意:像百度这种专业做搜索的网站,为了用户的丝滑体验,没有做防抖,但像boss直聘这种主营业务不是搜索,但有搜索功能的网站,都会做防抖。

  6. 例子如下:

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8" /><title>1.函数防抖</title><script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script></head><body><input type="text" placeholder="请输入关键词" id="input"><script type="text/javascript" >const input = document.getElementById('input')// 靠程序员自己去用定时器实现防抖/* let timerinput.addEventListener('keyup',(e)=>{if(timer){clearTimeout(timer)}timer = setTimeout(()=>{console.log('发请求了',e.target.value)},300)}) */// 靠lodash去实现防抖input.addEventListener('keyup',_.debounce((e)=>{console.log('发请求了',e.target.value)},300))</script></body>
    </html>
    

16.2 函数节流

  1. 标准概念:在n秒内,无论触发事件多少次,逻辑只执行一次。

  2. 通俗理解:你催的再急,也没用,我的速度是有极限的。

  3. 目的:在一定的时间内,防止复杂逻辑频繁触发(例如:复杂的运算、网络请求等)。

  4. 生活中的例子:

    • 就算火车要开了,我3秒内也只能吃一口面。
    • 玩游戏时候的技能冷却时间。

    通俗理解:别催,催也没有用,我就是这个速度。

  5. 最具代表性的应用场景:秒杀按钮。

  6. 关于lodash节流的配置

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8" /><title>2.函数节流</title><script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script></head><body><button id="btn">立即购买</button><script type="text/javascript" >const btn = document.getElementById('btn')let canExecute = true //用于标识是否可以执行逻辑/* 靠程序员,用定时器去实现节流,不推荐 *//* btn.onclick = ()=>{if(canExecute){canExecute = falseconsole.log('发起网络请求~~')setTimeout(()=>canExecute = true,2000)}} */btn.onclick = _.throttle(()=>{console.log('发起网络请求~~')},2000,{trailing:false})</script></body>
    </html>
    

17、element-ui_按需引入(推荐)★

  1. 第一步:安装,命令为 npm i element-ui

  2. 第二步:安装一个包,npm install babel-plugin-component -D

  3. 第三步:修改babel.config.js内容如下:

    module.exports = {presets: ['@vue/cli-plugin-babel/preset'],plugins: [["component",{"libraryName": "element-ui","styleLibraryName": "theme-chalk"}]]
    }
    
  4. 第三步:去main.js中引入并注册,你想用的组件 或 使用插件

    import {Button} from 'element-ui';// 第一种方式:全局注册按钮组件
    // Vue.component(Button.name, Button);// 第二种方式:使用Button插件
    Vue.use(Button)
    
  5. 第四步:随便在任何一个组件里,去使用上一步注册的组件即可

18、. element-ui_提示类组件★

  • main.js中引入所有提示相关组件,并放在Vue原型上。

    import {Message} from 'element-ui';Vue.prototype.$message = MessageBox;
    
  • 所有用到提示的地方,直接复制官网上的代码即可,例如:

    this.$message.info('一些消息');
    this.$message.success('一些成功消息');
    this.$message.warning('一些警告消息');
    this.$message.error('一些错误消息');this.$message({type:'success',message:'恭喜支付成功!',showClose:true,duration:1000,
    })
    

19、对 token 的理解 ★

  1. 只有登录成功,服务器才会为该用户生成一个token

  2. token是用户的真正标识,根据token,可以去服务器那查询到该用户的所有信息。

  3. 浏览器端需要保存一份token,哪里保存? —— localStorage中。

    为什么? —— 为了做自动登录(即:刷新页面、重开浏览器,依然是登录的状态)

  4. 我们的服务器在登录成功后,会返回:用户昵称、用户真实姓名、token等等,但我们只存token

    为什么? —— 根据token可以找服务器获取到更多的用户信息。

    大多数情况,登录成功后,服务器不会给任何多余的信息,只给一个token

  5. 若服务器不允许多端登录,则服务器要在用户登录后,把之前的token销毁,再生成一个新的token

    这个得看具体需求,目前我们的尚品汇服务器允许多端登录。

  6. token不是永久有效的,通常都有一个有效期,短的2小时,长的30天,具体时间看项目需求。

  7. 服务器那边什么情况需要销毁token?—— 以下几个场景:

    • 到了token的过期时间,服务器销毁了token
    • 若不允许多端登录,用户在其他终端登录了系统,服务器销毁了之前的token
    • 若允许多端登录,但达到了上限,服务器会销毁最开始的token
    • 用户触发退出登录,服务器会销毁该用户的token

20、token 持久化存储+携带 ★

  1. 明确:登录成功后,token要存入localStorage中。

  2. 编写src/utils/auth.js追加三个方法,如下:

    //用于将token存入localStorage
    export function saveToken(token){localStorage.setItem('token',token)
    }//用于从localStorage中读取token
    export function readToken(){return localStorage.getItem('token')
    }//从localStoragre删除用户token
    export function deleteToken(){  localStorage.removeItem('token')
    }
    
  3. Login组件中,登录成功后,将token存入localStorage

    methods: {async login() {// 获取登录需要的参数const { phone, password } = this;// 请求登录let { code, data, message } = await reqLogin({ phone, password });if (code === 200) {// 登录成功给个提示this.$message.success("登录成功!");//token存入本地saveToken(data.token)// 跳转到主页this.$router.push("/home");} else {// 登录失败提示原因this.$message.warning(message);}},
    },
    
  4. 修改src/api/ajax.js每次请求,都要携带token

    import {getUserTempId,readToken} from '@/utils/auth'//请求拦截器
    request.interceptors.request.use((config)=>{/*****/// 携带tokenconfig.headers.token = readToken()/*****/return config
    })
    

21、 获取用户信息存入 vuex ★

  1. 明确:根据token获取到的用户信息,存在vuex中,方便其他组件使用。

  2. 编写接口请求函数,根据token获取用户信息。

    // 此函数专门用于获取用户信息
    export const reqUserInfo = () => request.get('/api/user/passport/auth/getUserInfo')
    
  3. 操作文件:src/store/modules/user.js,编写vuex代码,存储用户信息。

    import {reqUserInfo} from '@/api'
    import {Message} from 'element-ui'const actions = {async getUserInfo({commit}){//发送请求获取用户信息const {code,message,data} = await reqUserInfo()//判断是否获取成功if(code === 200){commit('SAVE_USER_INFO',data)}else{Message.error(message)}}
    }const mutations = {// 保存用户数据SAVE_USER_INFO(state,info){state.info = info}
    }const state = {info:{} //初始化用户信息
    }export default {namespaced:true,actions,mutations,state
    }
    
  4. Login组件中,登录成功后,dispatch一个getUserInfo获取用户信息。

    async login(){// 获取用户的输入const {phone,password} = this// 请求登录const result = await reqLogin({phone,password})// 判断登录是否成功if(result.code === 200){// 第一步:提示信息this.$message.success('登录成功')// 第二步:存储tokensaveToken(result.data.token)// 第三步:获取用户信息this.$store.dispatch('user/getUserInfo')// 第四步跳转到主页this.$router.push('/home')}else{// 若登录失败,提示原因this.$message.error(result.message)}
    }
    

22. 用户信息如何不丢失 ★

问题描述:刷新会导致vuex中的用户信息丢失。

如何解决?

  • 第一种方式:登录成功后dispatch一下,在App组件中mounted的时候再dispatch一下

    import { readToken } from './utils/auth'export default {name:'App',components:{Header,Footer},mounted() {if(readToken()){this.$store.dispatch('user/getUserInfo')}},
    }
    
  • 第二种方式:靠导航守卫去维护用户信息。

    token存入localStorage,根据token获取的用户信息存入vuex,每次刷新页面时,靠导航守卫去维护用户信息。

23. 导航守卫 ★

  1. 概念:路由跳转时,在特定的时刻,执行的一些函数。

  2. 作用:可以在路由跳转时,追加特殊逻辑(路由鉴权、获取用户信息等)。

  3. 技巧:守卫越早使用越好。

  4. 分类:

    • 全局守卫(很常用)====> 宏福科技园门口的大爷
    • 路由独享守卫 =========> 宏福科技园A座楼的保安
    • 组件内守卫 ===========> 宏福科技园A座楼3027室的保安
  5. 我们的项目为什么使用导航守卫?

    • 每次路由跳转、刷新页面时,都要获取用户信息。
    • 对某些敏感路由做出访问限制。
  6. 严重注意:导航守卫一定一定要有出口!—— 什么是出口?next()就是。

  7. 导航守卫小案例:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../pages/Home'
    import Hot from '../pages/Hot'
    import Like from '../pages/Like'
    import Near from '../pages/Near'Vue.use(VueRouter)//暴露路由器
    const router =  new VueRouter({mode:'history',routes:[{path:'/home',component:Home},{path:'/hot',component:Hot},{path:'/like',component:Like},{path:'/near',component:Near},]
    })//需求:只有尊贵的vip用户,才能访问: /like   /near// 用于标识是否为vip
    let isVIP = false
    // 设计一个鉴权名单
    let authPath = ['/like','/near']// beforeEach所指定的函数就是全局守卫
    // 全局守卫何时调用?—— 1.整个应用初始化的时候调用(一上来)2.每次路由跳转的时候
    router.beforeEach((to,from,next)=>{// 判断是否为尊贵的VIPif(isVIP){console.log('你是尊贵的VIP,你随便去哪,我直接放行')next()}else{console.log('你不是尊贵的VIP,我需要进一步看看,你要去哪?')if(authPath.includes(to.path)){console.log('想得美,还看VIP专属路由,放行到/home')next('/home')}else{console.log('还好,你看的是那些随便看的,直接放行')next()}}
    })export default router
    

24. dispatch 的返回值★

  1. this.$store.dispatch('xxx')的返回值是一个Promise实例。

  2. 返回的这个Promise实例,成功还是失败,要看xxx函数的返回值,规则同then方法。

  3. 可以去Test组件中做一个测试

  • src\store\index.js

    import Vue from 'vue'
    import Vuex,{Store} from 'vuex'
    import user from './modules/user'
    import home from './modules/home'Vue.use(Vuex)// actions中的方法用于响应组件中的动作 —— 服务员
    const actions = {jia({state,commit},value){if(state.a < 10){commit('JIA',value)// return ''}else{// alert('不能再加了,因为不能超过10')return Promise.reject('不能大于10')}}
    }// mutations中的方法用于修改状态 —— 厨师
    const mutations = {JIA(state,value){state.a += value}
    }// state用于指定初始的数据 —— 原材料
    const state = {a:1
    }export default new Store({actions,mutations,state,modules:{user,home}
    })
    
  • src\pages\Test\index.vue

    async test() {//第一种写法/* const x = this.$store.dispatch("jia");x.then(() => {console.log("加成了")},() => {console.log("没加成")}) *///第二种写法try {let x = await this.$store.dispatch("jia", 1);console.log(x);} catch (error) {console.log(error);}
    },
    

25. 维护用户信息 _思路 ★

26. 维护用户信息 _实现

  1. 去:src\store\user.js里,找到:getUserInfo,让getUserInfo可以反馈自己的工作成果,同时修改一下获取信息失败的提示,换成一个比较好的提示 —— 【身份过期,请重新登录!】
const actions = {// 根据token获取用户信息(token已经在请求头中携带了)async getUserInfo({commit}){/**********/if(code === 200){//如果用户信息获取成功,则不写return,相当于return了undefined,//那么dispatch('getUserInfo')得到的值就是一个成功的Promise实例/**********/}else{/**********///如果用户信息获取失败,则return一个失败的Promise实例 //那么dispatch('getUserInfo')得到的值就是一个失败的Promise实例Message.warning('身份过期,请重新登录!')return Promise.reject() }}
}
  1. 守卫中的逻辑:
router.beforeEach(async(to,from,next)=>{// 从localStorage中读取tokenconst token = readToken()// 判断token是否存在if(token){console.log('你登录过,我要继续从vuex中读取用户信息')// 从vuex中读取用户信息const {info} = store.state.user// 判断用户信息是否为空if(info.id){console.log('要token有token,要信息有信息,直接放行')// 放行next()}else{console.log('虽然有token,但没信息,拿着token去要信息')try {// 获取用户信息await store.dispatch('user/getUserInfo')console.log('拿着你的token获取到了用户信息,漂亮!放行!')next()} catch (error) {console.log('拿着你的token没有获取到用户信息,你那个破token失效了,我帮你删了,放行到登录')// 删除已经不靠谱的token// deleteToken()next('/login')}}}else{console.log('你没有登录,所以有些敏感路由,你不能去了,但现在没有啥路由是敏感的,所以暂时饶你一命,想去哪就去哪')next()}
})
  1. 备注:逻辑写完了如何去测试?
  • localStorage中删除token,不登录,随便点一点,所有路由都能看才正常。
  • 去登录,然后到各种地方去刷新,看看vuex里有没有用户信息,一直有才正常。
  • localStorage中,故意把token改错,刷新,打回登录才正常。

27、支付路由_弹窗 ★

使用element-ui实现付款弹窗

<a class="btn" @click="pay">立即支付</a><script>pay(){// 弹窗中的html内容const htmlStr = '<img style="width:200px" src="http://49.232.112.44/images/hot.jpg">'// 弹窗的具体配置const options = {dangerouslyUseHTMLString: true, //用于支持html字符串的解析center:true,showCancelButton:true,cancelButtonText:'支付遇到问题',confirmButtonText	:'已完成支付',showClose:false,title:'微信扫码支付'}// 弹窗弹起来!this.$alert(htmlStr,{// 弹窗的配置...options,// 点击弹窗中的确定或关闭按钮的回调callback(type){if(type === 'confirm'){console.log('你点了确定按钮')}else{console.log('你点了关闭按钮')}}});}
</script>

28. qrcode 的使用★

<template><div><button @click="test">点我测试一下qrcode</button><br><img :src="url" alt=""></div>
</template><script>import QRCode from 'qrcode'export default {name:'Test',data() {return {url:'http://49.232.112.44/images/hot.jpg'}},methods: {test(){// 将“泰裤辣”转为二维码const x = QRCode.toDataURL('泰裤辣,草莓,蓝莓、蔓越莓、今天你想我了没')x.then(value => {console.log('转换二维码成功',value)this.url = value},reason => {console.log('转换二维码失败',reason)})}},}
</script><style lang="less" scoped></style>

29. 支付路由_生成二维码

  • 安装qrcode:npm i qrcode

  • 代码如下:

    <script>import {reqPayInfo} from "@/api";import QRCode from "qrcode";export default {methods: {async pay(){try {// 调用QRCode将codeUrl转为二维码const url = await QRCode.toDataURL(this.payInfo.codeUrl)// 弹窗中的html内容const htmlStr = `<img style="width:200px" src="${url}">`// 弹窗的具体配置const options = {dangerouslyUseHTMLString: true, //用于支持html字符串的解析center:true, //居中布局showCancelButton:true, //显示取消按钮cancelButtonText:'支付遇到问题',confirmButtonText	:'已完成支付',showClose:false, //隐藏右上角的xtitle:'微信扫码支付'}// 弹窗弹起来!this.$alert(htmlStr,{// 弹窗的配置...options,// 点击弹窗中的确定或关闭按钮的回调callback(type){if(type === 'confirm'){console.log('你点了确定按钮')}else{console.log('你点了关闭按钮')}}});} catch (error) {// 若转换二维码失败,提示一下this.$message.warning('支付二维码显示失败,请联系客服!')}}}
    };
    </script>
    

30、图片懒加载★

  1. 安装vue-lazyloadnpm i vue-lazyload@1

    注意:vue-lazyload这包,默认版本是3的内测版,所以请安装时,加上版本限制

  2. 提前准备好一个过渡图片:/assets/images/loading.gif

  3. main.js

    import VueLazyload from 'vue-lazyload'
    import picture from '@/assets/images/loading.gif'
    //....
    Vue.use(VueLazyload,{loading:picture
    })
    
  4. 使用图片的地方改为

    <img v-lazy="goods.defaultImg" />
    

31. 路由懒加载 ★

  1. 什么是懒加载:其实就是延迟加载,即当需要用到的时候再去加载。

  2. Vue中路由的懒加载:

    //原来的引入方式
    /* import Home from '@/pages/Home'import Login from '@/pages/Login'import Register from '@/pages/Register'import Search from '@/pages/Search'import Detail from '@/pages/Detail'import AddCartSuccess from '@/pages/AddCartSuccess'import Cart from '@/pages/Cart'import Trade from '@/pages/Trade'import Pay from '@/pages/Pay'import PaySuccess from '@/pages/PaySuccess' 
    *///懒加载的引入方式
    export default [{path:'/home',component:() => import('@/pages/Home'),},{path:'/login',component:() => import('@/pages/Login'),meta:{isHideFooter:true}},{path:'/register',component:() => import('@/pages/Register'),meta:{isHideFooter:true}},{path:'/search',component:() => import('@/pages/Search'),},{path:'/test',component:() => import('@/pages/Test'),},{name:'detail',path:'/detail/:id',component:() => import('@/pages/Detail'),},{path:'/addcart_success',component:() => import('@/pages/AddCartSuccess'),},{path:'/shopcart',component:() => import('@/pages/ShopCart'),},{path:'/trade',component:() => import('@/pages/Trade'),},{path:'/pay',component:() => import('@/pages/Pay'),},{path:'/paysuccess',component:() => import('@/pages/PaySuccess'),},{path:'/',redirect:'/home',}
    ]
    

32、vee-validate_基本使用★

  • 第一步:安装 npm i vee-validate@3

    Vue2中,我们要使用vee-validate2版本、或3版本,vee-validate4版本是给Vue3用的

  • 第二步:在main.js中引入:ValidationProviderextend,并注册ValidationProvider

    import {ValidationProvider,extend} from 'vee-validate';Vue.component('ValidationProvider',ValidationProvider)
    
  • 第三步:使用ValidationProvider包裹表单元素,并指定具体规则。

    <ValidationProvider rules="shouji" v-slot="{errors}"><input type="text" placeholder="请输入你的手机号" v-model="phone"><span class="error-msg">{{errors[0]}}</span>
    </ValidationProvider>
    
  • 第四步:在main.js中,创建一个手机的验1证规则

    const phoneReg = /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/extend('shouji',{validate:(value)=>phoneReg.test(value),message:'手机号格式不合法'
    })
    

33. vee-validate_进阶使用★

分析:很多的输入项需要多种规则,例如手机号的规则是:必填项、格式要合法。

  • 第一步:在main.js中,引入内置的验必填项规则

    import {required} from 'vee-validate/dist/rules'
    //extend('required',required)
    
  • 第二步:在内置规则基础上,自定义一下必填项规则

    extend('bixu',{...required,message:'{_field_}必须填写',
    })
    
  • 第三步:组件中使用规则

    <ValidationProvider name="手机" rules="shouji|bixu" v-slot="{errors}"><input type="text" placeholder="请输入你的手机号" v-model="phone"><span class="error-msg">{{errors[0]}}</span>
    </ValidationProvider>
    

随后完成:验证码、密码、重复密码、用户协议的校验,且要注意重复密码需要传参

  • 补充其他规则:
const codeReg = /^\d{6}$/
const passwordReg = /^\w{6,21}$/extend('code',{validate:(value)=> codeReg.test(value),message:'验证码必须为6位数字',
})extend('pwd',{validate:(value)=> passwordReg.test(value),message:'密码必须为:数字、字母、下划线',
})extend('agree',{validate: value => value,message:'请同意协议',
})extend('repwd',{validate:(value,[params])=> {console.log(params)return value === params},message:'确认密码必须与密码一致'
})
  • 组件中使用
<ValidationProvider name="验证码" rules="code|bixu" v-slot="{errors}"><input type="text" placeholder="请输入验证码" v-model="code"><button class="getcode" @click="getCode">获取验证码</button><span class="error-msg">{{errors[0]}}</span>
</ValidationProvider><ValidationProvider name="密码" rules="pwd|bixu" v-slot="{errors}">  <input type="password" placeholder="请输入你的登录密码" v-model="password"><span class="error-msg">{{errors[0]}}</span>
</ValidationProvider><ValidationProvider name="确认密码" :rules="`repwd:${password}|bixu`" v-slot="{errors}">  <input type="password" placeholder="请输入确认密码" v-model="rePassword"><span class="error-msg">{{errors[0]}}</span>
</ValidationProvider><ValidationProvider name="用户协议" rules="agree" v-slot="{errors}"> <input name="m1" type="checkbox" v-model="agree"><span>同意协议并注册《尚品汇用户协议》</span><span class="error-msg">{{errors[0]}}</span>
</ValidationProvider>

34. 统一校验表单★

  • 操作Register组件

    <ValidationObserver ref="register"><!-- 所有输入项 -->
    </ValidationObserver><script>async register(){const result = await this.$refs.registerForm.validate()if(result){// 获取用户输入的东西const {phone,password,code} = this// 发请求去注册const {code:c1,message,data} = await reqRegister({phone,password,code})if(c1 === 200){this.$message.success(`注册成功`)this.$router.push('/login')}else{this.$message.warning(`注册失败:${message}`)}}else{this.$message.warning('请检查输入项的合法性')}}
    </script>
    

34. 完善路由鉴权★

1. 基本鉴权

  1. 明确:有些路由只有登录了,才能看。—— 使用全局守卫去做。

  2. 他们分别是:/trade/pay/paysuccess/order

  3. 在全局导航守卫中追加几个规则,具体编码如下:

    // 以下路由必须登录后才能看
    const authPath = ['/trade','/pay','/paysuccess','/order']
    

2. 细化鉴权

  1. 明确:只有在特定路由,才能去特定路由。

  2. 使用【路由独享守卫】细化鉴权规则,配置写在路由规则中(src/router/routes.js)。

    	//只有从【详情】页面才能跳到【添加购物车成功】页面{path:'/addcart_success',component:AddCartSuccess,beforeEnter: (to, from, next) => {if(from.path.slice(0,7) === '/detail') next()else next('/home')}},//只有从【交易】页面才能跳到【支付】页面{path:'/pay',component:Pay,beforeEnter: (to, from, next) => {if(from.path === '/trade') next()else next('/home')}},
    
  3. 使用【组件内守卫】细化鉴权规则,配置写在组件中(.vue文件)

    export default {name: 'PaySuccess',//只有从【支付】页面,才能去【支付成功】页面beforeRouteEnter(to, from, next) {if(from.path === '/pay'){next()}else{next('/home')}}
    }
    

相关文章:

vue项目的实用性总结

1、mockjs 基本使用 ★ 安装&#xff1a;npm i mockjs。 在src/mock/index.js内容如下&#xff1a; import Mock from mockjs //制订拦截规则 Mock.mock(http://www.0313.com,get,你好啊)记得在main.js中引入一下&#xff0c;让其参与整个项目的运行。 只要发出去的是get类型…...

IOC容器

DI&#xff08;依赖注入&#xff09;&#xff1a;DI&#xff08;Dependency Injection&#xff09;是一种实现松耦合和可测试性的软件设计模式。它的核心思想是将依赖关系的创建与管理交给外部容器&#xff0c;使得对象之间只依赖于接口而不直接依赖于具体实现类。通过依赖注入…...

若依框架浅浅介绍

由若依官网所给介绍可知 1、文件结构介绍 在ruoyi-admin的pom.xml文件中引入了ruoyi-framework、ruoyi-quartz和ruoyi-generatior模块&#xff0c;在ruoyi-framework的pom.xml文件中引入了ruoyi-system模块。 2、技术栈介绍 前端&#xff1a;Vue、Element UI后端&#xff1a…...

echarts 柱状图-折线图-饼图的基础使用

上图示例图表展示相关配置&#xff1a; var myChart echarts.init(this.$refs.firstMain);myChart.setOption({legend: { // 图例设置top: "15%",type: "scroll",orient: "vertical",//图例列表的布局朝向。left: "right",pageIconCo…...

mac电脑 node 基本操作命令

1. 查看node的版本 node -v2. 查看可安装的node版本 sudo npm view node versions3. 安装指定版本的node sudo n 18.9.04. 安装最新版本node sudo n latest5. 安装最新稳定版 sudo n stable6. 清楚node缓存 sudo npm cache clean -f7. 列举已经安装的node版本 n ls 8. 在…...

Hlang社区项目说明

文章目录 前言Hlang社区技术前端后端 前言 Hello,欢迎来到本专栏&#xff0c;那么这也是第一次做这种类型的专栏&#xff0c;如有不做多多指教。那么在这里我要隆重介绍的就是这个Hlang这个项目。 首先&#xff0c;这里我要说明的是&#xff0c;我们的这个项目其实是分为两个…...

RTC实验

一、RTC简介 RTC(Real Time Clock)即实时时钟&#xff0c;它是一个可以为系统提供精确的时间基准的元器件&#xff0c;RTC一般采用精度较高的晶振作为时钟源&#xff0c;有些RTC为了在主电源掉电时还可以工作&#xff0c;需要外加电池供电BCD码&#xff0c;四位二进制表示一位…...

C#多线程报错:The destination thread no longer exists.

WinForm&#xff0c;C#多线程报错&#xff1a; System.ComponentModel.InvalidAsynchronousStateException: An error occurred invoking the method. The destination thread no longer exists. 研究一番&#xff0c;找到了原因&#xff1a; 有问题的写法&#xff1a; ne…...

使用 Visual Studio GoogleTest编写 C/C++ 单元测试——入门篇

入门教程 Visual Studio 新建 GoogleTest项目&#xff0c;一路选默认参数 pch.h #pragma once#include "gtest/gtest.h"int add(int a, int b);pch.cpp #include "pch.h"int add(int a, int b) {return a b; }test.cpp #include "pch.h"TES…...

Linux下TA_Lib安装失败的问题处理

Linux下TA_Lib安装失败的问题处理 TA_Lib是python的量化指标库&#xff0c;其中包含了很多150多种量化指标 &#xff0c;量化分析中经常使用。 This is a Python wrapper for TA-LIB based on Cython instead of SWIG. From the homepage: TA-Lib is widely used by trading …...

egg.js企业级web框架

egg与express、koa的区别 三者皆为node.js web框架&#xff0c;但&#xff1a; express适合做个人项目&#xff0c;灵活性太高&#xff1b;egg是基于koa封装的企业级框架&#xff0c;奉行约定优于配置&#xff0c;按照一套统一的约定进行应用开发&#xff0c;减少开发学习成本…...

小说网站第二章-关于文章的上传的实现

简述 因为最近比较忙&#xff0c;所以只有时间把以前的东西整理一下。前端方面&#xff0c;我使用了既存md5框架语法来保存数据&#xff0c;原谅我展示没找到好的方法。后端的话&#xff0c;我使用nodemongodb来保存数据。下面我就来简单介绍一下我的东西。 前端的实现 前端的…...

Java面试题01

1、以下不属于oracle的逻辑结构的是&#xff1f;答案&#xff1a;B A.段 B.数据文件 C.表空间 D.区 2、构造函数何时被调用&#xff1f;答案&#xff1a;A A.创建对象时 B.使用对象变量时 C.调用对象方法时 D.类定义时 3、下列排序…...

6.3 社会工程学攻击

数据参考&#xff1a;CISP官方 目录 社会工程学攻击概念社会工程学攻击利用的人性 “弱点”典型社会工程学攻击方式社会工程学攻击防护 一、社会工程学攻击概念 什么是社会工程学攻击 也被称为 "社交工程学" 攻击利用人性弱点 (本能反应、贪婪、易于信任等) 进…...

typeScript 之 Map

工具&#xff1a; PlayGround 源码&#xff1a; GitHub TypeScript Map简介 Map是ES6引入的一种新的数据结构&#xff0c; 它是一只用于存储**键值对(key-value)**的集合。 let map new Map(); let map_1: Map<string, number> new Map(); let map_2: Map<string…...

Apache Doris 入门教程29:文件管理器

文件管理器 Doris 中的一些功能需要使用一些用户自定义的文件。比如用于访问外部数据源的公钥、密钥文件、证书文件等等。文件管理器提供这样一个功能&#xff0c;能够让用户预先上传这些文件并保存在 Doris 系统中&#xff0c;然后可以在其他命令中引用或访问。 名词解释​ …...

【佳佳怪文献分享】MVFusion: 利用语义对齐的多视角 3D 物体检测雷达和相机融合

标题&#xff1a;MVFusion: Multi-View 3D Object Detection with Semantic-aligned Radar and Camera Fusion 作者&#xff1a;Zizhang Wu , Guilian Chen , Yuanzhu Gan , Lei Wang , Jian Pu 来源&#xff1a;2023 IEEE International Conference on Robotics and Automat…...

word 应用 打不开 显示一直是正在启动中

word打开来显示一直正在启动中&#xff0c;其他调用word的应用也打不开&#xff0c;网上查了下以后进程关闭spoolsv.exe,就可以正常打开word了...

Flink-----Yarn应用模式作业提交流程

Yarn应用模式作业提交流程 在Yarn当中又分为Session,PerJob,Application,建议和推荐使用独立集群的,其中就包含PerJob 和Application,但是1.17版本的Flink已将PerJob标记为过时,并且Application可以解决PerJob的一些痛点,减轻客户端的一些压力,所以需要重点了解Yarn应…...

Python学习笔记_基础篇(五)_数据类型之字典

一.基本数据类型 整数&#xff1a;int 字符串&#xff1a;str(注&#xff1a;\t等于一个tab键) 布尔值&#xff1a; bool 列表&#xff1a;list 列表用[] 元祖&#xff1a;tuple 元祖用&#xff08;&#xff09; 字典&#xff1a;dict 注&#xff1a;所有的数据类型都存在想对…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

高考志愿填报管理系统---开发介绍

高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发&#xff0c;采用现代化的Web技术&#xff0c;为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## &#x1f4cb; 系统概述 ### &#x1f3af; 系统定…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...

如何把工业通信协议转换成http websocket

1.现状 工业通信协议多数工作在边缘设备上&#xff0c;比如&#xff1a;PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发&#xff0c;当设备上用的是modbus从站时&#xff0c;采集设备数据需要开发modbus主站&#xff1b;当设备上用的是西门子PN协议时&#xf…...