Skip to content
本页目录

Vue

reactivity响应性

computed是如何实现缓存功能的?

通过ComputedRefImpl实例化计算属性时,实例上有_dirty属性(默认为true)、_value属性:

  • 当初次触发实例的getter时(只要触发getter就会有依赖收集),若_dirtytrue,则将_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注册生命周期hookcallHook时,通过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转换成带有codegenNodeJavaScript AST
    • codegenNode用于生成render函数。
  • 生成: 通过generate方法根据JavaScript AST生成render函数。

模板中的响应式数据是如何编译处理的?

  • ASTASTtype值将改变,并做对应的标记;
  • JavaScript ASTcodegenNode根据NodeTypes中属性做对应处理;
  • 再通过render函数渲染处理。

模板中的v-xx指令如何编译处理的?

每个指令的处理都会对应一个transformXX函数,该函数会生成一个AST中的对应属性节点,用于generate时的解析,具体如下:

  • AST:通过对应的transform函数生成属性节点,增加props属性,type值为7
  • JavaScript AST:增加branches属性,type值为10codegenNode根据NodeTypes中属性做对应处理;
  • 再通过render函数渲染处理。

keep-alive实现组件状态缓存的原理?

keep-alive的缓存是通过LRU缓存实现的。
keep-alive其实是一个函数式组件,没有template标签,在render中通过获取组件的nameincludeexclude进行匹配:

  • 匹配不成功,则不需要进行缓存,直接返回该组件的vnode
  • 匹配成功就进行缓存,获取组件的keycache中进行查找:
    • 若存在,则将它原来位置上的key给移除,同时将这个组件的key放到数组最后面;
    • 若不存在,就对组件进行缓存,将当前组件key添加到尾部,然后再判断当前缓存的max是否超出指定个数,如果超出直接将第一个组件销毁。