切换主题
Vue
reactivity
响应性
computed
是如何实现缓存功能的?
通过
ComputedRefImpl
实例化计算属性时,实例上有_dirty
属性(默认为true
)、_value
属性:
- 当初次触发实例的
getter
时(只要触发getter
就会有依赖收集
),若_dirty
为true
,则将_dirty
置为false
,并将回调函数的执行结果赋值给_value
,最后返回_value
;- 当依赖的响应式数据无改变,再次触发
getter
时,由于此时_dirty
值为false
,将直接返回_value
,到这里便实现了缓存功能;- 当依赖的响应式数据改变,首先触发响应式数据的
setter
行为,setter
行为会触发trigger
,而trigger
里包含调度器scheduler
(调度器就是实例化ReactiveEffect
时的第二个参数), 当调度器存在时,会优先执行调度器,此时触发依赖
,重新执行回调函数。
scheduler
的作用:控制函数的执行顺序和执行规则。
JavaScript
class ComputedRefImpl {
constructor(getter, _setter) {
this._dirty = true // 缓存标志
this._value = null
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) { // scheduler
this._dirty = true
triggerRefValue(this) // 触发依赖,重新执行回调函数
}
})
}
get value() {
const self = this
trackRefValue(self) // 依赖收集
if (self._dirty) { // 缓存判断
self._dirty = false
self._value = self.effect.run() // 首次调用,执行回调函数
}
return self._value // 返回缓存
}
set value(newValue) {
this._setter(newValue)
}
}
runtime
运行时
何为运行时?
通过
h
函数生成VNode
,再由render
函数将VNode
生成真实DOM
渲染(mount
方法和patch
方法)到页面中。
有状态组件生命周期钩子中访问响应式数据的实现原理?
在
registerLifecycleHook
注册生命周期hook
与callHook
时,通过bind
函数改变this
指向,即:hook?.bind(instance.data)
,instance
为组件实例。
响应式数据改变时,组件是如何响应性更新的?
在组件更新前,组件实例上的标志位
instance.isMounted
为真,组件更新时,执行componentUpdateFn
,由于标志位为真,将执行patch()
函数。
compiler
编译时
compile
的作用及过程?
null
将
template
编译为render
函数,过程如下:
- 解析: 通过
parse
函数将模板template
解析成抽象语法树AST
;
- 生成
tokens
:使用有限自动状态机,将模板转换成tokens
:tokens
扫描:通过递归下降解析算法,自上而下扫依次描token
进栈并做处理(当前栈顶token
即父元素),扫描到结束标签token
就弹栈;- 生成
AST
:弹栈时拼接token
生成AST
。- 转化: 通过
transform
函数将AST
转换成带有codegenNode
的JavaScript AST
;
codegenNode
用于生成render
函数。- 生成: 通过
generate
方法根据JavaScript AST
生成render
函数。
模板中的响应式数据是如何编译处理的?
AST
:AST
的type
值将改变,并做对应的标记;JavaScript AST
:codegenNode
根据NodeTypes
中属性做对应处理;- 再通过
render
函数渲染处理。
模板中的v-xx
指令如何编译处理的?
每个指令的处理都会对应一个
transformXX
函数,该函数会生成一个AST
中的对应属性节点
,用于generate
时的解析,具体如下:
AST
:通过对应的transform
函数生成属性节点,增加props
属性,type
值为7
;JavaScript AST
:增加branches
属性,type
值为10
,codegenNode
根据NodeTypes
中属性做对应处理;- 再通过
render
函数渲染处理。
keep-alive
实现组件状态缓存的原理?
keep-alive
的缓存是通过LRU
缓存实现的。keep-alive
其实是一个函数式组件,没有template
标签,在render
中通过获取组件的name
和include
、exclude
进行匹配:
- 匹配不成功,则不需要进行缓存,直接返回该组件的
vnode
;- 匹配成功就进行缓存,获取组件的
key
在cache
中进行查找:
- 若存在,则将它原来位置上的
key
给移除,同时将这个组件的key
放到数组最后面;- 若不存在,就对组件进行缓存,将当前组件
key
添加到尾部,然后再判断当前缓存的max
是否超出指定个数,如果超出直接将第一个组件销毁。