Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
2013年7月28日,尤雨溪第一次在 GItHub 上为 Vue.js 提交代码;2015年10月26日,Vue.js 1.0.0版本发布;2016年10月1日,Vue.js 2.0发布。
最早的 Vue.js 只做视图层,没有路由, 没有状态管理,也没有官方的构建工具,只有一个库,放到网页里就可以直接用了。
后来,Vue.js 慢慢开始加入了一些官方的辅助工具,比如路由(Router)、状态管理方案(Vuex)和构建工具(Vue-cli)等。此时,Vue.js 的定位是:The Progressive Framework。翻译成中文,就是渐进式框架。
Vue.js2.0 引入了很多特性,比如虚拟 DOM,支持 JSX 和 TypeScript,支持流式服务端渲染,提供了跨平台的能力等。Vue.js 在国内的用户有阿里巴巴、百度、腾讯、新浪、网易、滴滴出行、360、美团等等。
Vue 已是一名前端工程师必备的技能,现在就让我们开始深入学习 Vue.js 内部的核心技术原理吧!
为列表渲染设置属性 key
在我们使用 v-for 遍历列表时,添加属性 key 可以获得性能上的提升。key 主要使用在新旧虚拟 DOM 节点进行对比的时候,当然属性 key 最好使用元素的唯一属性,比如 id,而不是列表的索引值。示例如下:
<div v-for="item in list" :key="item.id">
// 内容
</div>
在 v-if、v-if-else、v-else 中使用 key
当我们使用 v-if、v-if-else、v-else 作用在元素类型相同的元素节点上时,最好使用 key 属性,可以避免判断重新渲染出现意想不到的副作用。
由于当状态发生变化的时候,生成的虚拟节点即有可能是 v-if 上的虚拟节点,也可能是 v-else 上的。默认情况下,当两个相同类型的元素直接切换的时候,Vue 会修补已存在的元素,而不是将旧的元素移除,然后再添加新元素。
这时候,如果本来不相同的元素被识别为相同了,那么可能会出现意想不到的场景。
这时候我们通过在元素上添加 key 属性,可以明确的告诉 Vue.js,这两个相同类型的元素并不一样,此时就会移除旧元素,添加新元素了。示例如下:
<div v-if :key="key001">
// 内容
</div>
<div v-else :key="key002">
// 内容
</div>
路由地址切换,但是组件不变化
const routes = {
path:'/hello/:id',
name:'hello',
component:Hello
}
如上代码所示,在 /hello/1 切换到 /hello/2 的时候,Hello 组件是不会变化的,Hello 组件的生命周期钩子也不会重新触发。
这是因为 Vue.js 会识别出两个路由使用的是同一个组件,从而进行复用,不会创建新的组件,所以生命周期钩子也不会被触发。对于这种情况,这里提供三种方法来解决。
方法一:观察 $route 对象的变化
通过 watch 观察路由对象的变化,从而做出响应。示例如下:
watch:{
$route(to,from){
// 做出响应
}
}
当然,想上面这样可以监听到页面的变化,但是直接对 $route 进行监听不太好,这样会造成一定程度的性能浪费。当我们页面间跳转时,如果只改变的是 id,那么就只监听 id 即可,比如:
watch:{
$route.params.id(){下一个错别字
// 做出响应
}
}
方法二:为 router-view 添加属性 key
这个方法就是直接在 router-view 标签上添加属性 key,并将属性值和页面做唯一绑定。这样每当页面切换的时候,Vue 都会通过key来判断是不是同一个页面,这样就会让 Vue 认为 router-view 组件是一个新节点,从而销毁组件,创建新的组件。即使是相同组件,url 变了,key 也就变了,组件就会被重新创建。示例如下:
<router-view :key="$route.fullPath"></router-view>
方法三:路由导航守卫 beforeRouteUpdate
beforeRouteUpdate 导航守卫是在组件改变且组件被复用的时候后被调用的,所以可以在路由导航中解决这个问题。
我们只需要把切换页面后想要执行的逻辑放到beforeRouteUpdate 导航守卫钩子函数中执行就可以了。
避免 v-if 和 v-for 一起使用
Vue 官方强烈建议不要把 v-if 和 v-for 同时用在同一个元素上。
通常有两种状态,第一种是 v-if 绑定的值是和遍历的列表项有关的,那么此时就将列表项替换为一个计算属性,让它返回过滤后的列表。比如:
// 修改前
<div v-for="item in list" v-if="item.isShow">
// 内容
</div>
// 修改后
<div v-for="item in isShowList">
// 内容
</div>
computed:{
isShowList(){
return this.list.filter((item)=>{
return item.isShow
})
}
}
以为 v-for 比 v-if 具有更高的优先级,即使我们想渲染列表中符合要求的一小部分元素,也需要把整个列表都遍历完毕。我们通过计算属性可以提前过滤掉一部分元素。
第二种是 v-if 绑定的值和遍历的列表项无关。此时我们就需要将 v-if 提取到外层容器盒子上。比如:
// 修改前
<div v-for="item in list" v-if="isShowList">
// 内容
</div>
// 修改后
<div v-if="isShowList">
<div v-for="item in isShowList">
// 内容
</div>
</div>
这样我们不会再检查每一个列表项的 isShowList 属性多次,只需要检查它一次,并且不会在 isShowList 为 false 时运行 v-for 指令。
组件样式设置 scope 作用域
CSS 样式都是全局的,任何一个组件的样式都对整个页面有效。
如果在开发过程中,你A页面的样式是正常的,当跳转到 B 页面后,再次回到 A 页面时,此时 A页面样式出现异常,那么很可能是由于 B 页面的全局样式导致的。
在 Vue 中,我们通过 scope 特性来设置组件的样式作用域。示例如下:
// 修改前
<template>
<div class="main">
hello
</div>
</template>
<style>
.main{
background:#f00
}
<style>
// 修改后
<template>
<div class="main">
hello
</div>
</template>
<style scoped>
.main{
background:#f00
}
<style>
也可以使用 CSS的module,在 react 中使用较多。示例如下:
// 修改前
<template>
<div class="main outer">
hello
</div>
</template>
<style>
.main{
background:#f00
}
<style>
// 修改后
<template>
<div :class="[$style.main,$style.outer]">
hello
</div>
</template>
<style module>
.main{
background:#f00
}
.outer{
background:#00f
}
<style>
避免隐藏的父子组件通信
优先通过props和事件进行父子组件通信,而不是使用 this.$parent 或者改变 prop。遵循 “prop 向下传递,事件向上传递” 原则,这样代码更简洁,更易于理解。
Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩: