简介
vue
函数式组件大部分人在开发过程中用到的不多,就连官网文档地位搁置的也比拟费解,然而在咱们对我的项目做性能优化时,却是一个不错的抉择。本文将对函数式组件初始化过程做一个系统性的论述,通过本文,你将理解到以下内容:
- 什么是函数式组件
- 函数式组件与一般组件间的差别
vue
类似性能优化点
什么是函数式组件
函数式组件即 无状态组件 ,没有data
、computed
、watch
,也没有生命周期办法,组件中也没有this
上下文,只有 props
传参。在开发中,有很多组件仅仅只用到了 props
和插槽,这部分组件就能够提炼为函数式组件。借用官网demo
,最简略的函数式组件如下:
Vue.component('my-component', {
functional: true,
// Props 是可选的
props: {// ...},
// 为了补救短少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {// ...}
})
函数式组件与一般组件间的差别
组件实例化过程大抵分为四步,状态初始化 –> 模板编译 –> 生成VNode
–> 转换为实在DOM
。接下来比照一般组件与函数式组件罕用配置项,比拟下差别。
性能点名称 | 一般组件 | 函数式组件 | 形容 |
---|---|---|---|
vm | Y | N | 组件作用域 |
hooks | Y | N | 生命周期钩子 |
data | Y | N | 数据对象申明 |
computed | Y | N | 计算属性 |
watch | Y | N | 侦听器 |
props | Y | Y | 属性 |
children | Y | Y | VNode 子节点的数组 |
slots | Y | Y | 一个函数,返回了蕴含所有插槽的对象 |
scopedSlots | Y | Y | 作用域插槽的对象 |
injections | Y | Y | 依赖注入 |
listeners | Y | Y | 事件监听 |
parent | Y | Y | 对父组件的援用 |
从上表中能够看出,一般组件与函数式组件最大的差异在于函数式组件 没有独立作用域,没有响应式数据申明。没有独立作用域,会有以下长处:
-
没有组件实例化 (
new vnode.componentOptions.Ctor(options)
),函数式组件获取VNode
仅仅是一般函数调用- 无公共属性、办法拷贝
- 无生命周期钩子调用
-
函数式组件间接挂载到父组件中,缩短首次渲染、
diff
更新门路- 函数式组件在父组件生成
VNode
时,函数式组件render
办法会被调用,生成VNode
挂载到父组件children
中,patch
阶段可间接转换成真是DOM
, 一般组件则在createElm
时,走组件初始化流程。 diff
更新时,函数式组件调用render
,间接创立一般VNode
,而一般组件创立的VNode
的是蕴含组件作用域的,diff
操作时,还有额定调用updateChildComponent
更新属性、自定义事件等,调用链路会比拟长。
- 函数式组件在父组件生成
vue 性能优化点
函数式组件带来的性能晋升次要体现在 缩短渲染门路 与缩小组件嵌套层级,前者与浏览器重绘回流有殊途同归之处,后者能够升高工夫复杂度。
无论何种性能优化,能从代码层面做优化的,无疑是代价最小的,尽管有时成果不是很显著,然而千里之行; 始于足下。在 vue
中,有不少与上述类似的点,能够晋升代码执行效率。
正当申明 data
中数据,确保 data
中申明数据都是必须的
很多时候有一些数据没必要申明在 data
中,比方须要组件内共享,但不须要响应式解决的数据。data
中的数据,对象都会对其深度优先用 Object.defineProperty
申明,数组也会拦挡基本操作办法。不必要的申明会造成无意义的数据劫持。
正当应用 computed
与watch
computed
与 watch
最大的区别在于 computed
是惰性加载的。惰性加载次要体现在两个方面:
- 依赖状态产生扭转时,不会立刻触发,只是扭转以后
Watcher
实例的dirty
属性值为true
- 当对计算属性值取操作时,当且仅当
watcher.dirty === true
时,才会触发计算
以上两点个性,可能防止一些不必要的代码执行,具体代码如下所示:
// src\core\instance\state.js
function createComputedGetter (key) {return function computedGetter () {
// 获取实例上的 computed 属性的 watcher 实例
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
// 当且仅当 computed 依赖属性发生变化 && 对计算属性进行取操作,才会调用 Watcher 的 update 办法,将 dirty 置为 true
if (watcher.dirty) {
// 调用 get 办法,获取到 computed 的值
watcher.evaluate()}
if (Dep.target) {watcher.depend()
}
return watcher.value
}
}
}
// src\core\observer\watcher.js
update () {
// computed watcher lazy === true
if (this.lazy) {
// 标记懒执行是否可执行状态,false 不执行计算属性计算
this.dirty = true
} else if (this.sync) { // 同步执行
this.run()} else {
// 将以后 watcher 放入到 watcher 队列
queueWatcher(this)
}
}
v-for 绑定 key 值
v-for
循环定义 key
值目标是便于精准找到 diff
比对节点,防止一些无意义的比对。
一般 diff: 从头尾开始,新旧节点头尾别离比拟,游标向两头聚拢,当且仅当一个节点遍历完结后,diff
流程完结
带有 key 值 diff: 依据 key
值保护一个 hash
表,每次循环精准定位到更新指标节点,当且仅当一个节点遍历完结后,diff
流程完结
思考
在 vue
中,很多性能优化点都是缩短代码执行门路,尤其在存在大量计算逻辑中,性能的晋升会有肉眼可见的成果。理论开发中,也有不少场景能够用到此类优化办法,举个最简略的例子,关键词高亮匹配。实现这个操作,须要以下几步:
- 获取匹配关键词,将关键词进行格式化(对正则表达式中有意义的字符串进行本义)
- 动静生成匹配的正则表达式
- 依据正则表达式进行
replace
操作
有些时候,第一,二步咱们能够省略,间接执行第三步即可,因为输出关键字可能存在雷同的,因而咱们能够将字符串与正则表达式缓存在 Map
中,下次匹配时,如果存在缓存,间接从缓存中拿即可。
vue
模板编译用到的就是这个个性,每次会把编译的模板字符串作为 key
值,render
办法作为value
,缓存起来,如果遇到一样的模板,能够省去编译流程,带来肯定的性能晋升。
小结
养成良好的编码习惯,对于集体能力,也是一个不错的晋升。