深入浅出Vue.js读后总结-最佳实践(1)

学习了一些原理之后,我们便可以更好的去使用vue框架,也能让我们更好的了解到为什么有些使用会更好。下面我就总结一下这本书中最佳实践这一章大致内容。

1.为列表使用设置key

设置key用于vue的diff算法中,vue在更新子节点时,会从旧虚拟节点列表中查找与新虚拟节点列表相同的节点进行更新,就会判断这个key是否相等,如果在查找过程中设置了key,查找速度会快很多,所以推荐在使用v-for是尽可能的设置key,而且key要是那种能标识当前item的,所以不推荐把key设置为index。

2.在v-if/v-if-else/v-else中使用key

如果一组v-if/v-if-else/v-else的元素类型相同,最好设置key(比如两个div元素)

为什么呢?因为在状态变化时,生成的虚拟节点既可能时v-if对应的节点,也可能时v-else对应的节点,所以vue为了更高效的更新DOM,它在相同类型的元素之间切换时会修补旧的元素,而不是将旧的元素移除,然后在同一位置添加一个新元素,所以如果两个本不相同的元素被识别为相同的,那就出bug,所以加key是为了方便vue去区别这两个元素不相同,加了key之后,这样vue就认为是两个不同的节点,于是就会将旧元素移除然后添加一个新元素,从而避免意料之外的副作用

3.同一个路由参数变化,组件的生命周期不变

例如,路由如下:

const routes = [
    {
        path:'detail/:id',
        name:'detail',
        componnet:Detail
    }
]
复制代码

当路由从/detail/1 切换到 /detail/2时,组件时不会发生任何变化的。

因为vue-router会识别出两个路由使用的是同一个组件从而进行复用,所以不会重新创建组件,从而组件的生命周期钩子自然也不会被触发。(重新销毁再创建一个组件存在很大程度上的性能浪费,所以复用组件才是正确的选择,但是也就会导致如上情况)

所以怎么解决这个问题呢:

3.1 使用路由导航守卫beforeRouteUpdate

该守卫在当前路由改变且组件被复用时调用,可以在组件内定义导航守卫来解决这个问题。

所以只要把每次路由切换时需要执行的逻辑放到beforeRouteUpdate守卫中执行即可,比如在beforeRouteUpdate守卫中获取数据,更新状态并且重新渲染,这是这本书作者最推荐的一种方式。

    //写在组件中和data同级的地方
    beforeRouteUpdate(to,from,next){
        console.log(to,from,next)
    },
复制代码

3.2 用watch观察$route对象的变化

这种方式同样可以解决上述问题,但是需要watch,这会带来追踪依赖的内存开销

watch: {
    $route (to, from) {
       //对路由变化作出响应
    },
  },
复制代码

3.3 为router-view组件添加属性key

这种方法非常暴力且有效。本质就是利用虚拟DOM在渲染时通过key来比较两个节点是否相同,如果给router-view设置key,可以使得每次切换路由时的key不一样,这样虚拟DOM就会销毁组件重新渲染,即使时相同的组件,url变了,key就变了,vue也会销毁再重新创建。

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

缺点也很明显,就是组件会被销毁再重新创建,非常浪费性能,优点就是简单粗暴。其原理就是类似本文第2点(给v-if设置key,让vue区分不同,然后销毁旧的重新渲染新的)

4.为所有路由统一添加query

如果需要在所有路由上都加上参数,如juejin.cn/editor/draf…juejin.cn/editor/draf… 中的referer=hao360cn。理想状态是全局统一配置一个query,所有路由中都会携带,但是vue-router并没有提供相应API来解决这种情况。

下面两种解决方法:

4.1 使用全局守卫beforeEach

使用全局守卫beforeEach中的next方法中断当前当前导航,然后切换到新导航,在切换过程中添加query进去,代码如下:

//要放入全局的query
const query = {referer:'hao360cn'}
router.beforeEach((to,from,next)=>{
    //如果query存在referer就直接next,如果不存在中断当前导航,然后就添加上query切换到新导航
    to.query.referer ? next() : next({ ...to, query: { ...to.query, ...query }})
})
复制代码

优点是可以全局统一配置公共的query,并且切换时不需要特殊处理。缺点是每次切换路由时beforeEach会执行两次。

4.2函数劫持

本书作者通过阅读vue-router的源码,找到了目前唯一可以设置query参数并且路由不会切换两次的解决方案。

通过拦截router.history.transitionTo方法,在vue-router内部在切换路由之前将参数添加到query中。 代码如下:

const query = {referer:'hao360cn'}
//用transitionTo变量缓存 vue-router内部的transitionTo方法
const transitionTo = router.history.transitionTo
//重写vue-router内部的transitionTo方法的参数,在loaction中加入query
router.history.transitionTo = function (location, onComplete, onAbort){
    location = typeof loaction === 'object'
    ? { ...location, query:{...loaction.query, ...query }}
    : {path: location,query}
    //执行缓存的transitionTo方法,将修改后的参数传进去
    transitionTo.call(router.history, location, onComplete, onAbort)
}
复制代码

优点是不会切换两次路由,缺点时修改了vue-router内部方法实现目的,操作有危险性。

猜你喜欢

转载自juejin.im/post/7053669342486986760