Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)

编程入门 行业动态 更新时间:2024-10-18 18:27:19

Vue响应式数据的实现原理(手写<a href=https://www.elefans.com/category/jswz/34/1621512.html style=副作用函数的存储和执行过程)"/>

Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)

1.命令式和声明式框架

命令式框架关注过程

声明式框架关注结果(底层对命令式的DOM获取和修改进行了封装)

2.vue2 Object.defineProperty()双向绑定的实现

<body><div id="app"><input type="text" /><h1></h1><button>按钮</button> </div>
</body>
<script>//   vue2实现双绑const input = document.getElementsByTagName('input')[0]const h1 = document.getElementsByTagName('h1')[0]const btn = document.getElementsByTagName('button')[0]let data = { text: '' }// input框输入数据,h1数据和text一致;实现点击按钮时,h1 标签数据和input框数据同时更改Object.defineProperty(data, 'text', {get() {return data['text'];},set(value) {// 获取到值后将h1后的内容设置为texth1.innerText = value;input.value = value;return true;}});input.oninput = function (e) {data.text = e.target.value;}btn.onclick = function () {data.text = "你好"}
</script>

3.同样页面Vu3实现 new Proxy()

        // vue3实现双绑const input = document.getElementsByTagName('input')[0]const h1 = document.getElementsByTagName('h1')[0]const btn = document.getElementsByTagName('button')[0]let data = { text: '' }let obj = new Proxy(data, {get(target, property) {return target[property]},set(target, property, value) {h1.innerText = value;input.value = value;return true;}})input.oninput = function (e) {obj.text = e.target.value;}btn.onclick = function () {obj.text = "你好"}

4.响应式数据的基本实现

响应式数据的关键是拦截对象属性的设置和读取操作

const data = { text: '' }
function effect () {document.body.innerText = data.text
}

5. vue2与vue3响应式数据实现区别

  • vue2的实现:当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用Object.defineProperty() 把这些 property 全部转为 getter/setter。
  • vue3的实现:当我们从一个组件的 data 函数中返回一个普通的 JavaScript 对象时,Vue 会将该对象包裹在一个带有 get 和 set 处理程序的Proxy 中

6.vue3 proxy的简单实现响应式数据拦截

// 初始数据
const data = { text: '' }
// 存储副作用函数的桶
const bucket = new Set()
// 对数据进行代理
const obj = new Proxy(data, {get(target, key) {bucket.add(effect)return target[key]},set(target, key, newVal) {target[key] = newValbucket.forEach(fn => fn())return true}
})function effect () {document.body.innerText = obj.text
}effect()setTimeout(() => {obj.text = '你好'
}, 1000)

7.简单实现中出现的问题

思考一下 这一段代码的问题。

  • 1. 副作用函数的名称被写死
  • 2. 没有建立副作用函数和目标字段之前明确的关系

副作用函数的名称被写死——解决:

名称写死问题:真实情况下副作用函数不可能只有一个,那如果有多个函数时,每个函数执行都会调用一个副作用函数。比如,设置obj.a = 2,同样会调用set方法中的bucket.forEach(fn => fn())方法。

通用一个副作用作用函数,将执行DOM修改的函数以闭包形式(回调函数)传入副作用函数,这样每次返回的都不是同一个函数

let activeEffectfunction effect(fn) {activeEffect = fnfn()
}effect(() => {document.body.innerText = obj.text
})const obj = new Proxy(data, {get(target, key) {if (activeEffect) {bucket.add(activeEffect)}return target[key]},set(target, key, newVal) {target[key] = newValbucket.forEach(fn => fn())return true}
})

没有建立副作用函数和目标字段之前明确的关系——解决:

以上代码对每个属性都绑定了同一个副作用函数,实际上真实需要的时,修改text时,调用他自己的函数,修改a时又调用a对应的函数

解决:

  1. 使用Map键值数据结构(分两层)进行存储副作用函数,将每个数据对象对应一个map的键;
  2. 一个数据对象下不同属性又存储到一个Map数据下,这个map的值则存储的副作用函数(Set形式存)
  3. 使用时通过获取对象的map(数据对象)下的对应属性key(每个对象的key)的值的set数据(针对每个属性操作的副作用函数)并进行遍历执行即可

const obj = new Proxy(data, {get(target, key) {console.log(activeEffect, 'activeEffect')// 没有副作用函数,容错处理 直接returnif (!activeEffect) return target[key]// 判断桶中是否已经存在key与target的关联关系let depsMap = bucket.get(target)// 创建一个新的Map结构与 target 关联if (!depsMap) {bucket.set(target, (depsMap = new Map()))}// 判断当前Map数据中是否存在key与effect的关联关系let deps = depsMap.get(key)// 不存在的话 则新建一个Set 与 key关联if (!deps) {depsMap.set(key, (deps = new Set()))}// 最后将当前激活的副作用函数添加到bucket中deps.add(activeEffect)return target[key]},set (target, key, newVal) {target[key] = newVal// 获取bucket下对应的数据const depsMap = bucket.get(target)if (!depsMap) return// 根据key 获取副作用的执行函数const effects = depsMap.get(key)// 执行副作用函数effects && effects.forEach(fn => fn())}
})effect(() => {document.body.innerText = obj.text
})
effect(() => {document.title = obj.a
})

8.完全实现——我的测试

 const data = { text: '这是obj.title', a:'vue响应式数据的实现原理' }// 存储副作用函数的桶const bucket = new Map()// 存储副作用函数的变量let activeEffect;// 对数据进行代理const obj = new Proxy(data, {get(target, key) {// 设置副作用函数到map数据的桶中// 判断不存在activeEffect直接返回if (!activeEffect) return target[key];// 存在activeEffect时将副作用函数设置到桶中let targetMap = bucket.get(target); //target对象存在才能判断key是否存在(targetMap既是bucket的值targetMap = new Map()又是keyMap的键的定义)if (!targetMap) {bucket.set(target, (targetMap = new Map()));}let keyMap = targetMap.get(key); // keyMap既是targetMap的值又是if (!keyMap) {targetMap.set(key, (keyMap = new Set()))}keyMap.add(activeEffect); //副作用函数最终是存在Set结构里面的return target[key]},set(target, key, newVal) {target[key] = newVal;//获取桶里面的对象Map的key的map集合的值,即所有的副作用函数进行循环执行let targetMap = bucket.get(target);if (!targetMap) return;let effects = targetMap.get(key);effects.forEach(fn => fn())return true}})function effect(fn) {// 将函数赋值给activeEffect,数据劫持到activeEffect有值时,会将其设置到bucket存储副作用的桶中,当拦截到数据更改时再获取对应函数并执行activeEffect = fn;fn();}// 副作用函数执行一次effect(() => {document.body.innerText = obj.text})effect(() => {document.title = obj.a})

更多推荐

Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)

本文发布于:2023-12-03 08:30:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1653238.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:副作用   函数   原理   过程   数据

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!