首页 > 其他分享 >响应系统的设计与实现

响应系统的设计与实现

时间:2023-01-31 10:27:18浏览次数:41  
标签:const key 系统 return 响应 effectFn deps 设计 target

// 存储副作用函数的桶
const bucket = new WeakMap()

// 原始数据

export const listenInit = (data) => {
  return new Proxy(data, {
    // 拦截读取操作
    get(target, key) {
      track(target, key)
      // 返回属性值
      return target[key]
    },
    // 拦截设置操作
    set(target, key, newVal) {
      // 设置属性值
      target[key] = newVal

      trigger(target, key)
      return Reflect.set(...arguments)
    },
  })
}

// 在 get 拦截函数内调用 track 函数追踪变化
function track(target, key) {
  // 没有 activeEffect,直接 return
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  deps.add(activeEffect)
  activeEffect.deps.push(deps)
}

// 在 set 拦截函数内调用 trigger 函数触发变化
function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)

  const effectsToRun = new Set()
  effects &&
    effects.forEach((effectFn) => {
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn)
      }
    })

  effectsToRun.forEach((effectFn) => {
    // 如果一个副作用函数存在调度器,调用调度器,然后将副作用函数作为参数传递
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      //否则直接执行副作用函数
      effectFn()
    }
  })
}

// 用一个全局变量存储被注册的副作用函数
let activeEffect
let effectStack = []

// effect 函数用于注册副作用函数
export const jobEffect = (fn, options = {}) => {
  const effectFn = () => {
    cleanup(effectFn)
    // 当 effectFn 执行时,将其设置为当前激活的副作用函数
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()

    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }

  // 将options挂载到effectFn上
  effectFn.options = options

  // activeEffect.deps 用来存储所有与该副作用函数相关联的依赖集合
  effectFn.deps = []
  // 执行副作用函数
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}

function cleanup(effectFn) {
  // 遍历 effectFn.deps 数组
  for (let i = 0; i < effectFn.deps.length; i++) {
    // deps 是依赖集合
    const deps = effectFn.deps[i]
    // 将 effectFn 从依赖集合中移除
    deps.delete(effectFn)
  }
  // 最后需要重置 effectFn.deps 数组
  effectFn.deps.length = 0
}

// 定义一个任务队列
const jobQueue = new Set()
// 使用 Promise.resolve() 创建一个 promise 实例,我们用它将一个任务添加到微任务队列
const p = Promise.resolve()

// 一个标志代表是否正在刷新队列
let isFlushing = false
function flushJob() {
  console.log('flushing!!')
  // 如果队列正在刷新,则什么都不做
  if (isFlushing) return
  console.log('flusingDone!!!')
  // 设置为 true,代表正在刷新
  isFlushing = true
  // 在微任务队列中刷新 jobQueue 队列
  p.then(() => {
    jobQueue.forEach((job) => job())
  }).finally(() => {
    // 结束后重置 isFlushing
    isFlushing = false
  })
}

export const scheduler = (fn) => {
  // 每次调度时,将副作用函数添加到 jobQueue 队列中
  jobQueue.add(fn)
  // 调用 flushJob 刷新队列
  flushJob()
}

export const jobWatch = (source, cb, options = {}) => {
  let getter
  if (typeof source === 'function') {
    getter = source
  } else {
    getter = () => traverse(source)
  }
  // 定义旧值与新值
  let oldValue, newValue

  // 提取 scheduler 调度函数为一个独立的 job 函数
  const job = () => {
    newValue = effectFn()
    cb(newValue, oldValue)
    oldValue = newValue
  }

  // 使用 effect 注册副作用函数时,开启 lazy 选项,并把返回值存储到 effectFn 中以便后续手动调用
  const effectFn = jobEffect(() => getter(), {
    lazy: true,
    scheduler: job,
  })

  if (options.immediate) {
    // 当 immediate 为 true 时立即执行 job,从而触发回调执行
    job()
  } else {
    oldValue = effectFn()
  }
}

function traverse(value, seen = new Set()) {
  // 如果要读取的数据是原始值,或者已经被读取过了,那么什么都不做
  if (typeof value !== 'object' || value === null || seen.has(value)) return
  // 将数据添加到 seen 中,代表遍历地读取过了,避免循环引用引起的死循环
  seen.add(value)
  // 暂时不考虑数组等其他结构
  // 假设 value 就是一个对象,使用 for...in 读取对象的每一个值,并递归地调用 traverse 进行处理
  for (const k in value) {
    traverse(value[k], seen)
  }

  return value
}


// 调用方法
import { listenInit, jobWatch } from './jobQueue'
let errorMessage = listenInit({ msg: '' })
jobWatch(
  () => errorMessage.msg,
  (newVal, oldVal) => {
    if (newVal != oldVal) {
      message.error(newVal)
    }
  }
)

  《Vue.js设计与实现》,书写的真好,学习到很多,vue3实现数据响应的方式了解了。

  “他的眼睛很小,跟猪的差不多,却闪动着十足的热情。不知是怎的,人们见到这样的人总是心生厌恶” ——《巴黎伦敦落魄记》

  这么回事嘛,要内敛一些呀。

标签:const,key,系统,return,响应,effectFn,deps,设计,target
From: https://www.cnblogs.com/wlxll/p/17078064.html

相关文章

  • 企业用好WMS(仓库管理系统),需要注意的几个要点
    企业用好WMS(仓库管理系统),需要注意的几个要点 企业的仓库部门是非常重要的后勤业务部门,它主要为企业的生产和销售提供支撑,同时也为其它各个业务部门提供物品管理服务......
  • Qt音视频开发13-视频解码线程基类的设计
    一、前言这个解码线程基类的设计,是到目前为止个人觉得自己设计的最好的基类之一,当然也不是一开始就知道这样设计,没有个三五年的摸爬滚打以及社会的毒打,是想不到要这样设计......
  • Linux系统Shell脚本第三章:for、while循环及脚本实操
    目录一、echo命令二、查看当前系统的时间—date命令三、for循环语句四、while循环语句结构(迭代)五、until循环语句结构六、continue和break  一、echo命令ech......
  • 违RESTful规范设计
    违RESTful规范设计3.1文档系统无论是独立的wiki还是整合在网关系统中,文档系统都应该支持全局模糊搜索。文档有3种,全局设计规范、API参考手册、文档系统本身的使用指南。......
  • 24种设计模式--工厂模式(Factory)创建型
    目录1.简单工厂模式simpleFactory概述接口类实现类简单工场类测试类测试结果:参考链接1.简单工厂模式simpleFactory概述工厂模式中,我们在创建对象时不会对客户端暴露创......
  • 一篇文章带你了解设计模式——创建者模式
    一篇文章带你了解设计模式——创建者模式在之前的文章中我们已经学习了设计模式的基本原则和基本分类下面我们来介绍第一种设计模式,创建型模式的主要关注点是怎样创建对......
  • 实战案例!1行Python代码识别车牌号码,轻松写一个停车场管理系统,YYDS
    大家好,这里是程序员晚枫。你家停车场的摄像头,是怎么识别出你的车牌的?今天我们一起来看一下~识别车牌识别车牌的代码很简单,只需要1行代码,如下所示。......
  • 操作系统的运行机制和体系结构
    操作系统的运行机制和体系结构1、运行机制2、操作系统的内核内核是计算机上配置的底层软件,是操作系统最基本、最核心的部分。实现操作系统内核功能的那些程序就是......
  • 操作系统的发展与分类
    操作系统的发展与分类1、手工操作阶段2、批处理阶段单道批处理系统多道批处理系统(操作系统开始出现)3、分时操作系统4、实时操作系统5、其他的几种操作系......
  • 基于JAVA springboot+mybatis智慧生活分享平台设计和实现
    基于JAVAspringboot+mybatis智慧生活分享平台设计和实现文章目录​​基于JAVAspringboot+mybatis智慧生活分享平台设计和实现​​​​主要功能模块设计:​​​​系统前端......