学习了一些原理之后,我们便可以更好的去使用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内部方法实现目的,操作有危险性。