写点什么

vue 的几个提效技巧

作者:yyds2026
  • 2022 年 10 月 10 日
    浙江
  • 本文字数:4674 字

    阅读完需:约 15 分钟

1.动态组件 <component :is='组件名'></component>

结合 v-for 循环使用

  • 使用环境

  • 如图,这是一个 v-for 渲染的列表(只是目前这个版块才刚开始做,目前只有一个),圆圈内的就是一个组件,也就是要 v-for 动态组件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CgstVJ6d-1665390342414)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/018edf494b164a3b806e550acf4879d8~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)]

  • 实际使用


一开始就是基本的组件引入了


import ColorIn from '@/components/Magic/ColorIn.vue'import LineIn from "@/components/Magic/LineIn.vue";import LineIn from "@/components/Magic/Header.vue";import LineIn from "@/components/Magic/Footer.vue";
export default{ components:{ ColorIn, LineIn, Header, Footer }}
复制代码


接下来就是动态 v-for 动态组件的使用,componentList:['ColorIn','LineIn','Header','Footer']使用下面的代码即可将代码依次循环


<component v-for="(item,index) in componentList" :key="index" :is="item"></component>
复制代码


编译以后的效果就是


<ColorIn></ColorIn><LineIn></LineIn><Header></Header><Footer></Footer>
复制代码

2.watch 进阶使用

立即执行

  • 使用环境


例如场景为页面一进来就调用拉取列表数据getList(),然后监听路由的$route.query.id然后触发列表数据的更新


  • 实际使用


为了让它一开始就执行,我们需要在created()生命周期中执行一次拉取数据的方法


watch:{    '$route.query.id':{        handle(){            this.getList();        },    }},created(){    this.getList();},
复制代码


但是使用immediate即可立即执行,改写以后的代码如下


watch:{    '$route.query.id':{        handle(){          this.getList();        },        immediate:true    }},
复制代码

深度监听

  • 使用环境


在监听对象的时候,对象的内部属性发生变化 watch 无法监听到,这种时候就需要使用深度监听


  • 实际使用


只需要设置deep:true即可开启深度监听


data(){    return{        queryList:{            count:0,            name:'',        }    }},watch:{    queryList:{         handle(newValue,oldValue){         //do something         },         deep:true    }},
复制代码

计算属性之 setter

  • 实际使用我们一般平常使用的都是getter,但其实它还有个setter,当计算属性的fullName触发更新的时候,就会触发setter回调


data(){    return{        firstName:'',        lastName:'',    }},computed:{    fullName:{        get(){            return `${this.firstName} ${this.lastName}`;        },        set(newValue){            let names=newValue.split(' ');            this.firstName=names[0];            this.lastName=names[1];        }    }},
复制代码

$on('hook:生命周期')来简化 window 监听

  • 实际使用


先来看一下平常的使用方法,参考 vue 实战视频讲解:进入学习


mounted () {    window.addEventListener('resize', this.resizeHandler);},beforeDestroy () {    window.removeEventListener('resize', this.resizeHandler);}
复制代码


改写以后的代码为,相比于上面的写法,这个写法的好处在于可以开启一个事件监听器的同时,就在beforeDestroy生命周期中挂载一个删除事件监听器的事件。比上面的写法会更加安全,更加有助于避免内存泄露并防止事件冲突


mounted () {  window.addEventListener('resize', this.resizeHandler);  this.$on("hook:beforeDestroy", () => {    window.removeEventListener('resize', this.resizeHandler);  })}
复制代码

子组件@hook:生命周期监听子组件的生命周期回调

  • 实际使用


<child @hook:mounted="listenChildMounted" />
复制代码

v-pre

  • 使用环境


不需要编译的 html 代码可以使用 v-pre,可以提高性能


  • 实际使用


<span v-pre>{{message}}</span>    //就算data里面定义了message,渲染完也是{{message}}
复制代码

v-once

  • 使用环境


只需要渲染一次,适用于渲染完以后就不会更新的内容,降低性能开销


  • 实际使用


<span v-once>{{message}}</span>    //message的值会编译后渲染,但是编译以后再次修改message的值不会触发更新
复制代码


  • v-pre 与 v-once 的区别


v-pre 相当于不编译,直接显示,v-once 相当于只编译一次,后面的更新不编译了

Vue.set()

  • 使用环境


① 当你利用索引直接设置一个数组项时


② 当你修改数组的长度时


③ 对象属性的添加或删除时


由于Object.defineprototype()方法限制,数据不响应式更新


  • 实际使用


this.$set(arr,index,item);
复制代码

$forceUpdate()

  • 使用环境


$set() 也有一定的使用限制,当对象没有这个属性的时候,$set() 就会报错,这种时候,直接修改数据,再使用 $forceUpdate() 强制视图刷新即可


  • 实际使用


this.$forceUpdate();
复制代码

keep-alive

  • 使用环境


当这个页面没有数据更新,或者是想保存状态,下次进来还是这样子的时候,例如淘宝查看列表页,点进去查看详情之后,返回列表页依旧到上次浏览到的地方,都可以使用keep-alive


  • 实际使用分为配合路由使用,使用max,include,exclude,以及特殊的生命周期activateddeactivated

$route 路由信息

  • $route.query.id


用来拿取路由传值的信息,比如路由的后缀?id=1,$route.query.id 拿到的值为 1


  • $route.meta.flag


用来拿取路由 meta 中的信息,路由信息里的 meta 是可以自定义属性的,我一般导航栏当前选中的 nav 用来和 $route.meta.flag 进行匹配,来拿到当前页面应该激活哪一个选项卡


  • base 路由


比方说百度的所有路由前缀要加/baidu,那么可以设置路由的 base 为/baidu


export const router = new Router({  base:'/baidu/',}
复制代码


此外,打包的时候,请修改config/index.jsbuild块中的assetsPublicPath为 '/baidu/',不然打包以后是找不到资源文件路径的


module.exports = {    build:{        assetsPublicPath: '/baidu/',    }}
复制代码


  • 全局路由钩子


使用场景一般为用户的登录鉴权


router.beforeEach((to, from, next) => {  //一定要调用next()才能到下一个页面  if (path === '/login') {    next()  }else{    if(token){      next();    }else{      next('/login');    }    }})
复制代码


  • 组件路由钩子中访问 this


组件路由的钩子一开始还未初始化,不能访问到 vue 实例 beforeRouteEnter (to, from, next) {// 这里还无法访问到组件实例,this === undefinednext( vm => {// 通过 vm 访问组件实例})}

$route 路由信息不刷新问题

  • 使用场景


有的时候,你从 /user?id=1 跳转到 /user?id=2 的时候,由于渲染同样的 User 组件,导致路由会复用,此时,页面就会仍然是用户 1 的信息。

解决方案

  • 组件内的路由守卫


beforeRouteUpdate(to, from, next) {    // 在当前路由改变,但是该组件被复用时调用    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。    // 可以访问组件实例 `this`  },
复制代码


  • <router-view> 绑定 key


<router-view :key="$route.fullpath"></router-view>
复制代码


  • 使用 watch 监听路由


watch:{   '$route':{       hander(){           // do something...       },       immediate:true   //如果要首次加载就触发   }}
复制代码

$emit 传参同时拿到父子组件两者入参的值

在实际项目开发中,可能会遇到 $emit 的值和父组件的 index 都要拿的情况,但是按照之前的写法,只能拿到其中一个的值,要么子组件,要么父组件,鱼和熊掌不可兼得


  • 子组件入参


this.$emit('uploadSuccess',res);
复制代码


  • 父组件入参


<Up @uploadSuccess="uploadLogoImage(index,arguments)"  />
复制代码


  • 方法取参


uploadLogoImage(){        console.log(arguments[0]);          //index        console.log(arguments[1][0]);      //res},
复制代码

样式穿透

  • 使用环境


一般在修改插件样式的时候使用的比较多


  • 实际使用


分为两种,一般stylus中使用>>>less中使用/deep/sass没有使用经验,不予说明


>>>.el-dialog .el-dialog__body{  padding 0  text-align center  border-radius 0 0 4px 4px}/deep/.el-dialog .el-dialog__body{  padding 0  text-align center  border-radius 0 0 4px 4px}
复制代码

Object.freeze()

  • 使用环境


我们都知道vue是使用Object.defineProperty对数据进行双向绑定,而对于只做展示使用的长列表,可以使用 Object.freeze()进行冻结,使它无法被修改,从而提高性能


  • 实际使用


getList().then(res=>{    this.list=Object.freeze(res.data.result);})
复制代码


值得注意的是,改变 list 的值不会更新,但改变引用会触发更新

组件通信技巧

  • props

  • $emit

  • $attrs & $listeners

  • provide & inject

  • vuex

  • Observable

  • eventBus

  • $refs

  • slot-scope & v-slot

  • scopedSlots

  • $parent & $children & $root

父子组件参数同时获取

  • 使用环境,有的时候父组件中,要拿取子组件中 $emit 传递的值,并且,要拿到父组件 v-for 当前的 index


子组件入参


this.$emit('uploadSuccess',res);
复制代码


父组件入参


<Up @uploadSuccess="uploadLogoImage(index,arguments)"  />
复制代码


  • 实际使用


uploadLogoImage(){   console.log(arguments[0]);          //index   console.log(arguments[1][0]);      //res},
复制代码

mixins 混入的使用

  • 使用环境


一般获取验证码,收藏,点赞等公用且逻辑一样(有些逻辑是根据页面的不同而不同的不建议使用混入)等场景都可以使用混入


  • 实际使用


这里我直接封装了一个 vue 新开窗口的混入方法,引入了以后,混入中的所有data,methods,以及生命周期都会共享


//openWindow.jsexport default {  methods:{    openUrl(url){      const link= this.$router.resolve({path: url});      window.open(link.href,'_blank');    },  }}
//其他页面使用import openWindow from "../../mixins/openWindow";
export default{ mixins:[openWindow],}
复制代码


  • 注意点(使用的页面统称为组件)


① 混入比组件优先执行


② 当混入中的属性或者方法与组件中的属性或者方法名称相同时,以组件中的值为准(结合上一条规则,因为混入先执行,所以组件会将混入覆盖)


③ 比方说 A 页面和 B 页面都使用了同一个混入,A 页面与 B 页面的状态同样是独立的

qs

  • 使用场景,get传输的时候都是路由拼接方式(?a=1&b=2),而不是json方式

  • 实际使用


//安装依赖npm install qs --save
//页面中或者直接api.js中直接序列化使用import qs from 'qs'qs.stringify(params)
//axios拦截器中直接使用import qs from 'qs'axios.interceptors.request.use( config => { if (config.method === 'get') { config.data = qs.stringify(config.data) })
复制代码

v-for 绑定 key 不建议使用 index

  • 主要原因


有的时候 v-for 列表可能存有删除,交换位置等操作,这种时候 index 的顺序变换会导致同一条数据,在此刻的 index 置换,所以,不建议 v-for 的 key 绑定 index


  • 解决方案


建议使用另外的并且值唯一的变量,例如后台给你的 id,反正只要是唯一,不会重复即可

v-for 不建议配合 v-if

  • 主要原因


v-for 的优先级比 v-if 高,也就是说,假设总计 50 条数据,即使经过 v-if 以后,只剩下 25 条显示,但是 v-for 早就循环了一遍 50 条数据,解决办法就是用一个计算属性先将数据过滤了以后,v-for 循环过滤了之后的数据


  • 解决方案


使用 computed 计算属性,对列表进行过滤,只剩下过滤之后需要的数据

document.body.contentEditable

  • 操作方法


打开控制台,输入document.body.contentEditable=true,然后敲回车,网页可以像 word 一样编辑,很方便对于页面的布局抗压能力做测试

用户头像

yyds2026

关注

还未添加个人签名 2022.09.08 加入

还未添加个人简介

评论

发布
暂无评论
vue的几个提效技巧_Vue_yyds2026_InfoQ写作社区