🤡官:深度讲讲vue3响应式的原理

前言

  • 常网IT源码上线啦!
  • 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。

代码 = 人生。

这就像调试一段极其复杂的、包含了无数变量和未知函数的代码。你需要不断地运行、测试、观察结果、找出bug、修改代码、再次运行......

考完软考,去公园逛逛,感觉很像食人花。还有牙齿。

一、前言

在 JavaScript 中,我们可以通过两种技术劫持 property 访问的方式。

getter / setters 和Proxy。

而在Vue3,我们Proxy给reactive创建响应式对象,使用getter / setters用于ref劫持基本类型。

reactive 的核心代码:

java 复制代码
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      trigger(target, key)
    }
  })
}

ref 的核心代码:

java 复制代码
function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      value = newValue
      trigger(refObject, 'value')
    }
  }
  return refObject
}

如代码所示:

  • get = track: Vue 3 的响应式系统在设置属性时通过 track 跟踪并收集依赖

  • set = trigger:在设置属性时通过 trigger 触发更新,来实现响应性。

直入正文。

🤡官接着往下追问:你说get的时候调用了track,set的时候调用了trigger,那他们内部具体做了什么?

二、深挖track和trigger

track

track() 函数其核心职责是建立​​数据属性​ ​与​​副作用函数(effect)​​之间的动态关联。当响应式对象的属性被访问时,系统通过该函数记录下"哪些 effect 依赖于此属性"的关键信息。

如果有effect,就去查找该属性的订阅者集合(Set,从3.4之后就改为Map)。

java 复制代码
let activeEffect

function track(target, key) {
  if (activeEffect) {
    // 查找相应属性订阅的 effect 集合,如果没有则新建
    const effects = getSubscribersForProperty(target, key) 
    effects.add(activeEffect)
  }
}

activeEffect 是一个全局变量,用于存储当前正在运行的副作用函数。在响应式系统里,副作用函数是指那些会产生副作用(如更新 DOM、发送网络请求等)的函数。在副作用函数运行前,会把这个函数赋值给 activeEffect,方便后续追踪依赖。

函数功能:

track 函数的作用是追踪对象 target 的属性 key 与当前副作用函数 activeEffect 之间的依赖关系。

参数:

  • target:要追踪的对象。

  • key:要追踪的对象属性名。

执行逻辑:

  • if (activeEffect):检查 activeEffect 是否存在。若存在,说明有副作用函数正在运行,需要进行依赖追踪;若不存在,则不做任何操作。

  • const effects = getSubscribersForProperty(target,key):调用 getSubersForProperty 函数,该函数的作用是查找对象 target 的属性 key 所订阅的副作用函数集合。若该集合不存在,就会新建一个。

  • effects.add(activeEffect):将当前的副作用函数 activeEffect 添加到属性 key 对应的副作用函数集合中,以此建立依赖关系。

讲原理是会枯燥一点的,但耐心看下去,多看几遍,知识就是你的了,Get~

trigger

trigger() 用来执行 effect。

trigger() 内部会find寻找 track() 时保存的该属性的所有订阅者的 effect,从而执行它们。

java 复制代码
function trigger(target, key) {
  const effects = getSubscribersForProperty(target, key)
  effects.forEach((effect) => effect())
}

getSubscribersForProperty 函数实现

java 复制代码
// 模拟 getSubscribersForProperty 函数
const targetMap = new WeakMap();
function getSubscribersForProperty(target, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  let effects = depsMap.get(key);
  if (!effects) {
    effects = new Set();
    depsMap.set(key, effects);
  }
  return effects;
}

// 模拟副作用函数
function effect() {
  console.log('副作用函数');
}

// 设置 activeEffect
activeEffect = effect;

const target = { name: 'Dignity' };
// 追踪依赖
track(target, 'name');

// 查看依赖关系
const effects = getSubscribersForProperty(target, 'name');
console.log(effects.has(effect)); // 输出: true

理解一下他们之间的关系:

track :get的时候,跟踪、收集依赖

-- 操作effect

-- 这些依赖会存在一个map,在map如果找到,就知道,此时的effect和这一次的依赖有关系;

-- 当然,如果没找到,就创建一个

trigger():set的时候,先去找track,因为里面有保存该属性的所有订阅者的effect,执行effect

effect:更新视图

三、通俗易懂说

还是把重点讲解一下track和trigger的作用。

activeEffect 就像是一个小本子,专门用来记录当前正在干活的那个"工人"(副作用函数)。在干活之前,我们会把这个"工人"的名字写在小本子上,这样后面就能知道是谁在干活啦。

track 函数就像是一个"牵线人",它的工作是把对象的某个属性和正在干活的"工人"联系起来。

  • 当有"工人"(副作用函数)正在干活的时候(也就是 activeEffect 有记录),"牵线人"就开始工作了。
  • "牵线人"会去看看这个对象的这个属性有没有一个"朋友群"(订阅的副作用函数集合),如果没有就建一个。
  • 然后把正在干活的"工人"拉进这个"朋友群"里,这样这个属性和这个"工人"就建立了联系,以后这个属性有变化,就知道要通知这个"工人"来干活啦。

我们模拟了 getSubscribersForProperty 函数,它就像是一个"管理员",负责管理每个对象属性的"朋友群"。

我们定义了一个"工人"(副作用函数 effect),然后把他的名字写在小本子(activeEffect)上。

接着"牵线人"(track 函数)把对象 targetname 属性和这个"工人"联系起来。

最后我们查看这个属性的"朋友群",发现这个"工人"已经在里面了,说明联系建立成功啦。

至此撒花~

后记

我们在实际项目中或多或少遇到一些奇奇怪怪的问题。

自己也会对一些写法的思考,为什么不行🤔,又为什么行了?

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

以往推荐

vue3对组件通信做了哪些升级?

别在傻傻分不清any void never unknown的场景啦

Vue性能优化:从加载提速到运行时优化

vue2和Vue3和React的diff算法展开说说:从原理到优化策略

玩转Vue插槽:从基础到高级应用场景(内含为何Vue 2 不支持多根节点)

前端哪有什么设计模式

小小导出,我大前端足矣!

前端仔,快把dist部署到Nginx上

多图详解,一次性啃懂原型链(上万字)

Vue-Cli3搭建组件库

Vue实现动态路由(和面试官吹项目亮点)

VuePress搭建项目组件文档

原文链接

juejin.cn/post/751454...

相关推荐
啃火龙果的兔子3 分钟前
安全有效的 C 盘清理方法
前端·css
海天胜景6 分钟前
vue3 数据过滤方法
前端·javascript·vue.js
天生我材必有用_吴用11 分钟前
深入理解JavaScript设计模式之策略模式
前端
海上彼尚13 分钟前
Vue3 PC端 UI组件库我更推荐Naive UI
前端·vue.js·ui
述雾学java14 分钟前
Vue 生命周期详解(重点:mounted)
前端·javascript·vue.js
洛千陨20 分钟前
Vue实现悬浮图片弹出大图预览弹窗,弹窗顶部与图片顶部平齐
前端·vue.js
咚咚咚ddd21 分钟前
微前端第四篇:qiankun老项目渐进式升级方案(jQuery + React)
前端·前端工程化
螃蟹82724 分钟前
作用域下的方法如何调用?
前端
独立开阀者_FwtCoder27 分钟前
TypeScript 杀疯了,开发 AI 应用新趋势!
前端·javascript·github
汪子熙32 分钟前
QRCode.js:一款轻量级、跨浏览器的 JavaScript 二维码生成库
前端·javascript·面试