1. 前言
理解响应式有助于理解代码逻辑
Vue3 基于
proxy
Vue2 基于
Object.defineProperty
2. 什么是数据响应式
所谓的数据响应式就是能够使数据的
变化
可以被检测
并对这种变化做出
响应
的机制我们常见的
MVVM
框架中要解决的一个核心问题就是连接数据层
和视图层
,通过
数据驱动
应用,数据变化,视图更新
,需要对数据做
响应式处理
,这样一旦数据发生变化就可以立即做出``更新处理`
响应式
数据变化可侦测, 从而对使用数据的地方进行更新
3.Vue中的应用
通过数据响应式 加上
虚拟DOM
和patch算法
,可以使我们只需要操作数据,完全不用
接触繁琐的DOM操作
,从而大大提升开发效率,降低开发难度
4. 简要对比
4.1 Vue2
基于
Proxy
的数据响应式Vue 2
的响应式系统使用Object.defineProperty
的getter
和setter
。
Vue2的数据响应式会根据数据类型做不同的处理:
对象
就采用Object.defineProperty()
的定义方式来拦截数据,当数据被访问或者发生变化时,我们感知并作出响应;
数组
则通过覆盖数组原型
的方法,扩展它的7个变更方法,使这些方法可以额外的做更新通知,从而做出响应
这种机制很好的解决了数据响应式的问题,但也存在缺点:
1.比如
初始
化的时候递归
遍历会造成性能损失2.
新增或删除
属性时需要用户使用Vue.set/delete
这样的特殊api
才能生效3.对于es6中的
Map,Set
这些数据结构不支持
等问题
4.2 Vue3
Vue 3
将使用 ES2015 Proxy
作为 其观察机制,这将会带来如下变化:
1.组件实例初始化的速度
提高 100%
2.使用 Proxy
节省
以前一半的内存
开销,加快速度,但是存在低浏览器版本的不兼容3.为了继续支持
IE11
,Vue 3 将发布一个支持旧观察者机制
和新 Proxy 版本的构建4.编程体验是一致的,不需要使用特殊的api
5.Vue2实现数据的监听的简要核心代码
5.1 简要代码
if(typeof obj !== "object" || obj == null){ return } const keys = Object.keys(obj) for(let i = 0;i < keys.length;i++){ const key = keys[i] defineReactive(obj,key,obj[key]) } console.log("变了",obj) } function defineReactive(obj,key,val){ observe(obj) Object.defineProperty(obj,key,{ get (){ return val }, set(v){ val = v update() } }) } function update(){ console.log(obj.yzs) } const obj = {} defineReactive(obj,"yzs","名字") obj.yzs = "幸福一家人"
5.2 运行
1.可以作为
nodejs
来运行2.也可以在前端页面引入查看浏览器控制台结果
5.3 分析
1.拦截每个
key
从而可以侦测
数据的变化
2.只能对于
对象
支持比较好 , 数组的话就得单独写3.遍历每个key
成本高
:内存大,速度慢4.新增或删除 属性无法监听 需要使用
特殊的API
5.Vue.set(obj,"yzs","幸福一家人")
6.Vue.delete(obj,"幸福一家人")
7.不支持 Map,Set,Class等数据结构
6. Vue3响应式简要代码
6.1 核心代码
function reactive(obj) { return new Proxy(obj,{ get(target,key){ console.log("get 的key",key); return target[key] }, set(target,key,val){ // notify 通知 console.log("set 的key",key); target[key] = val }, deleteProperty(target,key){ // notify 通知 console.log("delete 的key",key); delete target[key] } }) } const state = reactive({ name:"yzs" }) state.yzs state.yzs = "yzs001" delete state.yzs state.age = 31 state.age
6.2 分析
代理整个对象,从而侦测数据变化
1.
语言
级别的支持
对象数组 都可以监听2.
Proxy
原理就是 在对象外面套一层壳
,这个壳就是Proxy ,属于
懒处理
不访问不进行处理例如:不恰当的列子,Vue2就是全员检测 Vue3就是只针对出门的进行检测
3.es6的
proxy
数据响应式,很好的解决了以上问题4.上面的代码其实只能检测到 单层对象对象里面嵌套的话检测不到,我把代码贴到下边,有兴趣的可以看看
7. 嵌套对象监听
//代理整个对象,从而侦测数据变化 function reactive(obj) { return new Proxy(obj,{ get(target,key){ console.log("get 的key",key); // 依赖手机 track(target,key) return typeof target[key] === "object" ? reactive(target[key]) : target[key] }, set(target,key,val){ // notify 通知 console.log("set 的key",key); trigger(target,key) target[key] = val }, deleteProperty(target,key){ // notify 通知 console.log("delete 的key",key); delete target[key] } }) } // 临时存储副作用函数 const effectStack = [] // 1. 依赖 收集函数 // 包装 fn // 立即执行 fn // 返回 fn function effect(fn) { const e = createReactiveEffect(fn) e() return e } function createReactiveEffect(fn) { const effect = function () { try { effectStack.push(fn) return fn() } catch (error) { } finally{ effectStack.pop() } } return effect } // 保存依赖关系的数据结构 const targetMap = new WeakMap() // 弱引用 不去影响垃圾回收机制 // 2. 依赖收集:建立 target/key 和 fn 之间的映射关系 function track(target,key) { // 1. 获取当前的副作用函数 const effect = effectStack[effectStack.length - 1] if(effect){ // 2. 取出 target/key 对应的map let depMap = targetMap.get(target) if(!depMap){ depMap = new Map() targetMap.set(target,depMap) } // 3. 获取key 对应的 set let deps = depMap.get(key) if(!deps){ deps = new Set() depMap.set(key,deps) } // 4. 存入set deps.add(effect) } } // 3. 触发更新函数: 当某个响应数据发生变化,根据 target key 获取对应的 fn并执行他们 function trigger(target,key) { // 1. 获取 target/key 对应的set 并遍历执行他们 const depMap = targetMap.get(target) if(depMap){ const deps = depMap.get(key) if(deps){ deps.forEach(dep =>dep()); } } } const state = reactive({ name:"yzs", children:{ age:3 } }) // state.children.age // state.children = {num:2} effect(()=>{ console.log("effect-1",state.name); }) effect(()=>{ console.log("effect-2",state.name,state.children.age); }) state.name = "yzs001" state.children.age = 30