Skip to content
On this page

Vue3源码实现解析

点击查看【bilibili】

响应式系统实现

effect - 副作用函数

effect是Vue3响应式系统的核心,用于处理和调度副作用函数的执行。

typescript
const effectStack = []  // 存储effect嵌套调用的栈
let activeEffect = null // 当前激活的effect

function effect(fn, options) {
  const effectFn = () => {
    try {
      fn()
      activeEffect = effectFn
      effectStack.push(activeEffect)
    } finally {
      effectStack.pop()
      activeEffect = effectStack[effectStack.length - 1]
    }
  }
  if (!options.lazy) {
    effectFn()
  }
  if (options.scheduler) {
    effectFn.scheduler = options.scheduler
  }
  return effectFn
}

// 依赖收集
const targetMap = new WeakMap()
function track(target, key) {
  let depMap = targetMap.get(target)
  if (!depMap) {
    targetMap.set(target, (depMap = new Map()))
  }
  let deps = depMap.get(key)
  if (!deps) {
    deps.set(key, activeEffect)
  } else {
    deps.add(activeEffect)
  }
}

// 触发更新
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const deps = depsMap.get(key)
  if (!deps) return
  deps.forEach(effectFn => {
    if (effectFn.scheduler) {
      effectFn.scheduler()
    } else {
      effectFn()
    }
  })
}

reactive - 响应式对象

reactive用于创建响应式对象,通过Proxy代理实现属性的响应式访问。

typescript
function reactive(target) {
  if (!isObject(target)) {
    return
  }
  const proxy = new Proxy(target, {
    get() {},
    set() {}
  })
}

ref - 响应式值

ref用于处理基本类型值的响应式,内部通过类实现get/set拦截。

typescript
function ref(value) {
  if (isRef(value)) {
    return value
  }
  return new RefImpl(value)
}

class RefImpl {
  constructor(value) {
    this._isRef = true
    this._value = convert(value)
  }
  get value() {
    track(this, 'value')
    return this._value
  }
  set value(val) {
    if (hadChanged(val, this._value)) {
      this._value = this.convert(val)
      trigger(this, 'value')
    }
  }
}

function convert() {
  return isObject(value) ? reactive(value) : this._value
}

computed - 计算属性

computed实现了计算属性的懒执行和缓存机制。

typescript
function computed(getterOrOptions) {
  let getter, setter
  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = () => {}
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  return new ComputedImpl(getter, setter)
}

class ComputedImpl {
  constructor(getter, setter) {
    this._setter = setter
    this._value = undefined
    this._dirty = true
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        if (!this._dirty) {
          this._dirty = true
          trigger(this, 'value')
        }
      }
    })
  }
  get value() {
    if (this._dirty) {
      this._value = this.effect()
      this._dirty = false
      track(this, 'value')
    }
    return this._value
  }
  set value(newVal) {
    this._setter(newVal)
  }
}

性能优化

位运算优化

Vue3使用位运算来提高节点类型对比的速度:

节点类型位运算

位运算示例

响应式陷阱与优化

绕过响应式的情况

在Vue3中,由于采用Proxy实现响应式,某些操作可能会绕过响应式系统:

javascript
// 错误示例 - Vue3
const list = [
    {name: '--'}
]

const data = ref(list.map(m => {
     fetch().then(res => {
            m.name = res.name  // 这里修改的是原始list对象,不会触发响应式更新
     })
     return m
    })
)

相比之下,Vue2由于使用Object.defineProperty劫持,这种情况是可以正常工作的:

javascript
// Vue2中可以正常工作
const list = [
    {name: '--'}
]

this.data = list.map(m => {
     fetch().then(res => {
            m.name = res.name
     })
     return m
})

优化方案

在Vue3中,正确的做法是确保操作的是响应式数据:

javascript
const list = [
    {name: '--'}
]

const data = ref(list)

for (const m of data.value) {
    fetch().then(res => {
        m.name = res.name  // 直接操作响应式数据
    })
}

粤ICP备2024285819号