当前位置: 首页 > news >正文

图解Vue3 响应式,手动实现核心原理

响应式 = 同一份引用 + 依赖跟踪 + 变更通知

GET读取
SET修改
Proxy总控
响应式数据
Track收集依赖
Trigger通知更新
Dep管理依赖
Effect改变值
视图更新
Ref基本类型
Reactive引用类型
toRef/toRefs同步引用属性

同一份引用

既然要做到同份引用,就要把 非对象 也变成对象

ref = reactive({value:initVal})

function ref (initValue) {return reactive({value: initValue})
}let num = ref(5)//num就会成为一个响应式的数据
console.log(num.value) // 5

ref VS reactive

特性reactive 【推荐 引用类型,避免冗长.value】ref【推荐 基本数据类型】
数据类型❌只支持引用数据类型✅支持基本数据类型+引用数据类型
属性访问✅在 script 和 template中直接访问❌template中自动解包,可直接访问,script中要.value
属性解构解构时会丢失响应性,需使用toRef/toRefs解构时会丢失响应性,需使用toRef/toRefs
ref、reactive VS shallowRef、shallowReactive【仅在根层次】

toRef/toRefs解构保持响应

响应式对象的属性
批量解构响应式对象
toRef
原数据引用
toRefs
多个原数据引用
同步变化
const state = reactive({foo: 1,bar: 2
})const {foo, bar} = toRefs(state)
const fooRef = toRef(state, 'foo')fooRef.value++
console.log(state.foo,foo) // 2

收集依赖 + 变更通知

手动

ref+effec实现computed

effect():更改数据后执行,更新依赖该数据的数据(依赖)

    function computed(fn) {const result = ref()effect(() => result.value = fn()) // 执行computed传入函数return result}let num1 = ref(5)let num2 = ref(8)let sum1 = computed(() => num1.value * num2.value)let sum2 = computed(() => sum1.value * 10)//初始值console.log(sum1.value) // 40console.log(sum2.value) // 400//响应式顺序://num1->改变了effect中依赖num1->执行effect传入的函数//->改变了响应式的result->sum1在初始化时已经被赋予了响应式的result,所以sum1改变num1.value = 10console.log(sum1.value) // 80console.log(sum2.value) // 800num2.value = 16console.log(sum1.value) // 160console.log(sum2.value) // 1600

effect():类似vue2中的watch

    let name = '林三心', age = 22, money = 20let myself = '', ohtherMyself = ''const effect1 = () => myself = `${name}今年${age}岁,存款${money}`const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}`effect1() // 先执行一次effect2() // 先执行一次console.log(myself) // 林三心今年22岁,存款20元console.log(ohtherMyself) // 22岁的林三心居然有20元money = 300effect1() // 再执行一次effect2() // 再执行一次console.log(myself) // 林三心今年22岁,存款300元console.log(ohtherMyself) // 22岁的林三心居然有300元

track收集依赖的effect放进dep(set去重)

更新时触发trigger函数通知dep里所有effect执行

let name = '林三心', age = 22, money = 20let myself = '', ohtherMyself = ''const effect1 = () => myself = `${name}今年${age}岁,存款${money}`const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}`const dep = new Set()function track () {dep.add(effect1)dep.add(effect2)}function trigger() {dep.forEach(effect => effect())}track() //收集依赖effect1() // 先执行一次effect2() // 先执行一次console.log(myself) // 林三心今年22岁,存款20元console.log(ohtherMyself) // 22岁的林三心居然有20元money = 300trigger() // 通知变量myself和otherMyself进行更新console.log(myself) // 林三心今年22岁,存款300元console.log(ohtherMyself) // 22岁的林三心居然有300元

当依赖的为object类型:WeakMap存obj,Map存obj.props

在这里插入图片描述

    const targetMap = new WeakMap()function track(target, key) {let depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, depsMap = new Map())}let dep = depsMap.get(key)if (!dep) {depsMap.set(key, dep = new Set())}...}function trigger(target, key) {let depsMap = targetMap.get(target)if (depsMap) {const dep = depsMap.get(key)if (dep) {dep.forEach(effect => effect())}}}

proxy

new Proxy(target, handler)

target

包装target (对象/数组/函数甚/proxy对象)

handler

被代理对象上的自定义行为(定义一组处理函数(例如get、set)的对象)

target:被代理者
prop:被代理者的属性
receiver:代理者 ,Proxy 或者继承 Proxy 的对象

    const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {get(target, key, receiver) {return target[key]},set(target, key, value, receiver) {target[key] = value}})console.log(proxyPerson.name) // 林三心proxyPerson.name = 'sunshine_lin'console.log(proxyPerson.name) // sunshine_lin

Reflect

Proxy和Reflect的方法都是一一对应的,在Proxy里使用Reflect会提高语义化

  • Proxy的get对应Reflect.get
  • Proxy的set对应Reflect.set

属性访问方法

A.属性访问器(访问):obj.key,obj[key]

B.函数调用:mp.get(key)

Reflect.get(target, propertyKey[, receiver])函数调用方式从对象中读值

receiver

如果target对象中指定了getterreceiver则为getter调用时的this

该方法会拦截目标对象的以下操作:

  • 访问属性:proxy[foo] 和 proxy.bar
  • 访问原型链上的属性:Object.create(proxy)[foo]
    const person = { name: '林三心', age: 22 }const proxyPerson = new Proxy(person, {get(target, key, receiver) {return Reflect.get(receiver, key) // 相当于 receiver[key]},set(target, key, value, receiver) {Reflect.set(receiver, key, value) // 相当于 receiver[key] = value}})console.log(proxyPerson.name)proxyPerson.name = 'sunshine_lin' // 会直接报错,栈内存溢出 Maximum call stack size exceeded

在这里插入图片描述

reactive(被依赖的数据){return proxy}

    function reactive(target) {const handler = {get(target, key, receiver) {track(receiver, key) // 访问时收集依赖return Reflect.get(target, key, receiver)},set(target, key, value, receiver) {Reflect.set(target, key, value, receiver)trigger(receiver, key) // 设值时自动通知更新}}return new Proxy(target, handler)}const person = reactive({ name: '林三心', age: 22 }) // 传入reactiveconst animal = reactive({ type: 'dog', height: 50 }) // 传入reactive

vue3新增全局变量activeEffect存储当前执行的effect

track不必再判断obj和obj.key,直接dep存储当前activeEffect

let activeEffect = null
function effect(fn) {
activeEffect = fn
activeEffect()
activeEffect = null // 执行后立马变成null
}
function track(target, key) {
// 如果此时activeEffect为null则不执行下面
// 这里判断是为了避免例如console.log(person.name)而触发track
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, depsMap = new Map())
}let dep = depsMap.get(key)if (!dep) {depsMap.set(key, dep = new Set())}dep.add(activeEffect) // 把此时的activeEffect添加进去}// 每个effect函数改成这么执行effect(effectNameStr1)effect(effectNameStr2)effect(effectAgeStr1)effect(effectAgeStr2)effect(effectTypeStr1)effect(effectTypeStr2)effect(effectHeightStr1)effect(effectHeightStr2)

参考链接

林三心画了8张图,最通俗易懂的Vue3响应式核心原理解析 - 掘金

http://www.dtcms.com/a/520429.html

相关文章:

  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(三)
  • 【设计模式】外观模式/门面模式(Facaed)
  • 矽塔 SA8206 36V/2.5A 过压/过流保护芯片
  • 莱州做网站网站建设给客户看的ppt
  • Windows - Maven 安装到 IDEA 配置全流程
  • java填充word模版导出word文件支持导出pdf,支持本地下载和网络下载,使用jar包
  • 网络安全:Apache Druid 安全漏洞
  • 宁波公司建站模板wordpress用户调用
  • 70%的RAG性能与分块有关
  • 足球网站开发外贸网站优化推广
  • Uncertainty-Aware Null Space Networks for Data-Consistent Image Reconstruction
  • 孝感网站seodw做网站的导航栏怎么做
  • LeetCode 每日一题 166. 分数到小数
  • 封面论文丨薄膜铌酸锂平台实现强耦合电光调制,《Light Sci. Appl. 》报道机器学习优化新范式
  • 做外贸找产品上哪个网站好flash素材网站有哪些
  • Rust内存安全:所有权与生命周期的精妙设计
  • 2510rs,稳定裸函数
  • 西安住房建设局网站首页企业网站 设计需求
  • LangChain:让大模型具备思考与行动能力的框架
  • MySQL 及 SQL 注入详细说明
  • 医院移动护理系统源码,JAVA移动护理系统源码,医院移动护士站源码
  • 网站建设营销型新型塑料建筑模板图片
  • Linux 有哪些功能相似的命令
  • 外贸推广网站冲压加工瑞安有做网站吗
  • 【开题答辩实录分享】以《租房小程序的设计和实现》为例进行答辩实录分享
  • vscode debug Transformer源码说明
  • 仓颉语言核心特性深度解析:类型系统与内存安全实践
  • IP 地址 (Internet Protocol Address) 详细介绍
  • PHP网站开发常用函数房城乡建设部网站
  • 开源的SSR框架都是怎么实现的?