上篇主要提及开发vue.js项目中的最佳实践,最佳实践通常可以规避错误,同时大幅提高应用的性能,下篇主要提及了开发vue.js项目中的风格规范,良好的风格规范可以改善可读性和开发体验
指令
我们从vue指令开始,逐步理解开发vue.js中的最佳实践并且知道它们为什么好,好在哪里
key
key这个特殊属性主要用在vue中进行虚拟DOM比对算法中,在对比新旧虚拟节点时分辨哪些节点被移动,新增或者删除
v-for
vue官方一直十分推荐在v-for中设置key属性,因为vue的默认更新策略是就地更新,即当依赖的数组发生改变时,哪怕只是移动了其中某一个元素的顺序,vue在虚拟DOM中也不会相应的移动对应节点,而是删除后重新生成对应顺序的节点
key的作用就是在更新子节点时,通过key来查找对应节点,那么查找的顺序就会快上很多,而且也不会发生上述删除,新增节点的情况,vue会尽可能的复用现有节点
<div v-for="item in items" :key="item.id">
<div>{{item.value}}</div>
</div>
v-if/v-else-if/v-else
如果一组v-if和v-else的元素类型相同,那么在状态发生变化的时候生成的虚拟节点可能即是v-if上的,也有可能是v-else上的, 在默认情况下,vue会尽可能的复用已有元素,所以当两个本不相同的元素被识别为相同,则可能会出现意料之外的问题
通过添加key的形式让vue在对比虚拟节点时将其认为是两个不同的节点,从而避免意料之外的问题
<div v-if="isShow" key="show-true">
{{show}}
</div>
<div v-else key="show-false">
{{show}}
</div>
v-show or v-if
对于频繁切换显示状态的元素,使用v-show可以保证虚拟DOM的稳定,避免频繁的新增和删除元素,特别是对于那些内部包含大量dom元素的节点,这一点极其重要
v-if and v-for
我们应该避免将v-if和v-for一起使用,当vue处理指令时,v-for会比v-if有着更高的优先级,所以我们哪怕只是只渲染列表中的一部分元素,也得在重渲染的时候遍历整个列表
我们通常会有两种解决方法
-
如果是本就不该渲染的列表,我们可以通过将v-if放入其列表更上一层的容器中
<div v-if="show"> <div v-for="item in items"> {{item}} </div> </div>
-
如果我们需要过滤列表中的部分元素,我们可以通过计算属性来获得一个过滤后的列表
代码省略
通过这些操作,我们遍历列表时的效率将会更高效,解耦了渲染层的逻辑,可维护性,可扩展性也会更强
非实时绑定的表单
当我们使用v-model来绑定一个表单项时,当用户改变表单项的状态时,数据会随着改变,vue也会发生重渲染,当用户输入十分频繁时这会带来一些性能的开销
我们可以通过使用 lazy 或不使用 v-model 的方式解决该问题,但要注意,这样可能会导致在某一个时间段内数据和表单项的值是不一致的
但注意,这个优化并不是一定的,需要根据具体业务的不同来进行取舍
<input v-model.lazy="value"/>
<!-- or -->
<input @change="change"/>
路由切换组件不变
这个问题我们在开发vue项目时会经常遇到,即当页面切换到同一个路由但参数不同的地址时,组件的生命周期钩子不会重新触发
这是因为vue-router会识别出两个路由为同一个组件从而进行复用,并不会重新创建组件,重新销毁并创建一个组件是十分消耗性能的
我们可以有以下几个方法来解决这个问题:
-
beforeRouteUpdate
vue-router提供了导航守卫相关的api,这些api会在路由改变时调用,所以我们可以将每次切换路由时重新执行的逻辑放在beforeRouteUpdate中 -
watch
我们也可以通过监听route对象发生的变化来做出对应的操作,但如果监听整个route对象会带来额外的内存开销,还会导致相关逻辑的耦合,更加推荐的做法是只观察自己组件需要的query,以减少不必要的逻辑
不好的写法watch: { '$route'(to, from){ //其他请求 //其他请求 } }
推荐的写法
watch: { '$route。query.id'(to, from){ //其他请求 }, '$route。query.page'(to, from){ //其他请求 } }
延时加载
在最开始访问页面时,如果页面的组件太多,会导致页面白屏很长时间,一个可行的办法就是延迟装载组件,让组件按照指定的先后顺序依次一个一个染出来
延迟装载是一个思路,本质上就是利用 requestAnimationFrame 事件分批渲染内容,它的具体实现多种多样
let frameCount = 0;
export const refreshFrame = (maxCount) => {
requestAnimationFrame(() => {
frameCount++;
if (frameCount < maxCount) {
refreshFrame(maxCount);
}
});
}
export const defer = (showCount) => {
return showCount >= frameCount
}
我们可以首先调用refreshFrame之后通过调用defer方法来达到延时加载的目的