切换主题
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是否超出指定个数,如果超出直接将第一个组件销毁。