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
网友评论