响应式数据与副作用函数

副作用函数就是会产生副作用的函数, 例如:

function effect () {  document.body.innerText = 'hello vue3'}

当函数effect运行后, 会设置body的文本使其扭转, 而其余函数也能够扭转与获取body中的文本, 也就是说effect函数的执行间接或间接影响了其余其余函数的执行后果, 这是effect的副作用. 副作用函数有很多也十分常见比方扭转某一个全局变量的值.

响应式的根本思维

const obj = { text: 'hello world' }function effect () {  document.body.innerText = obj.text}obj.text = 'hello no world'

如上代码, 副作用函数effect执行时会触发obj.text的读取[[GET]]操作, 而对obj.text赋值时会触发设置[[SET]]操作, 因而读取操作时, 能够把副作用函数effect存储到某一数据结构中, 当设置时能够把副作用函数effect取出在运行, 这就实现了最根本的响应式如下图:

对于监听读取与设置操作, 能够应用代理对象Prosy实现:

  // 存储副作用函数  const bucket = new Set()    // 原始数据  const data = {    text: 'hello world'  }  // 解决原函数数据  const obj = new Proxy(data, {    // 拦挡读取操作    get (target, key) {      // 存储副作用函数effect      bucket.add(effect)      // 返回属性值      return target[key]    },      // 拦挡设置操作    set (target, key, newVal) {      console.log(newVal);      // 设置属性值      target[key] = newVal      // 把副作用函数取出并执行      bucket.forEach(fn => fn())      // 返回 true 代表操作胜利      return true    }  })

目前尽管能够实现, 然而还有许多缺点, 比方这里间接通过名字获取副作用函数, 这种硬编码的形式不灵便, 并且没有在副作用函数与被操作的指标对象的属性没有建设明确的关系, 此时若读取obj中的属性, 都会把副作用函数存储, 若设置某一个属性都会把副作用函数取出并执行, 因而能够应用某一全局变量贮存副作用属性:

// 用于贮存被注册的副作用的函数let activeEffect = undefinedfunction effect (fn) {  activeEffect = fn  fn()}

并应用WeakMap, Map, Set别离贮存:


之所以应用WeakMap作为贮存副作用函数的容器, 是因为当WeakMapkey是弱援用, 所援用的对象不存在时, 会主动被垃圾回收机制回收避免内存溢出:

// 贮存副作用函数的桶const bucket = new WeakMap()// 用于贮存被注册的副作用的函数let activeEffect = undefinedfunction effect (fn) {  activeEffect = fn  fn()}const data = {  text: 'hello world'}const obj = new Proxy(data, {  // 拦挡读取操作  get (target, key) {    // 没有 activeEffect, 间接 return    if (!activeEffect) return target[key]    // 依据 target 从'桶'中回去 depsMap, 它也是一个 Map 类型: key ---> effects    let depsMap = bucket.get(target)    // 如果 depsMap 不存在, 则新建一个 Map 并与 target 关联    if (!depsMap) bucket.set(target, (depsMap = new Map()))    // 再依据 key 从depsMap 中去的 deps, 它是一个 Set 类型    // 外面存贮所有与以后 key 相干的副作用函数: effects    let deps = depsMap.get(key)    // 如果 deps 不存在, 同样新建一个 Set 并与 key 关联0    if (!deps) depsMap.set(key, (deps = new Set()))    // 最初将以后激活的副作用函数增加到'桶'里    deps.add(activeEffect)    // 返回属性值    return target[key]  },  // 拦挡设置操作  set (target, key, newVal) {    // 设置属性值    target[key] = newVal    // 依据 target 从'桶'中获得 depsMap, 它是 key --> effects    const depsMap = bucket.get(target)    if (!depsMap) return    // 依据 key 获得所有的副作用函数 effects    const effects = depsMap.get(key)    // 执行副作用函数    effects && effects.forEach(fn => fn())  }  })

#### 读取操作流程

设置操作流程