vue2和vue3响应式原理
一、问题分析?
1.如何实现数据变了,视图也跟着变?核心是:如何知道对象的属性值被修改了? 2.如何实现一个数据变量,多出视图跟着变? 3.如何保证视图改变,数据也跟这改变? 这个问题也是最简单的,我们只需要给表单监听input事件,通过e.tatget.value实时获取表单值,设置给对象中对应的属性
二、Object.defineProperty
使用方法:
var obj = {a:1,b:2}
Object.defineProperty(obj,"a",{
相关配置项.....
})
6个配置项(4个酱油,2个重要)
value:设置属性的值 writable:如果是false,则代表属性值不让修改 configurable:如果是false,后续不能再次通过Object.defineProperty()配置,也不能通过delete删除该属性 enumerable:是否可以枚举, get:拦截获取对象属性的动作 set:拦截设置对象属性值的动作
三、实现一个简单的响应式代码
const obj = {a:1,b:2}
const newObj = {}
function observe(){
Object.keys(obj).forEach(key=>{
Object.definePropertery(newOjb,key,{
get(){
return obj[key]
},
set(val){
obj[key]=val
}
})
})
}
observe(obj)
通过上述的案例明白了问题分析中的第一个问题,要想明白第二个问题,就需要知道什么是观察者模式
四、观察者模式
概念:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有的观察者
function EventCenter(){
this.guanjia = {
}
}
EventCenter.Prototype.$on(EventName,cb){
if(this.guanjia.EventName){
this.guanjia.EventName.push(cb)
}else{
this.guanjia.EventName=[cb]
}
}
EventCenter.Prototype.$emit(EventName){
if(this.guanjia.EventName){
this.guanjia.EventName.forEach(fn=>fn())
}
}
总结:问题分析中的第二个问题迎刃而解
解决了上述全部问题,那么下面实现一个vue(乞丐版)
五、实现一个简易的vue
模仿vue实现一个简单的mvvm,做到数据改变能影响视图,视图变化数据也会改变
案例HTML结构模板如下:
<div id="app">
<h2 v-html="salary"></h2>
<input type="text" v-model="salary">
<p v-html="salary"></p>
<div v-html="bonus"></div>
</div>
JS实现代码分两个部分,一个是观察者一个是mvvm,代码分别如下:
- 观察者从第四点中的代码复用
- 实现一个MVVM如下
function MVVM(options){
const {data,el}=options
const ec = new EventCenter()
Object.keys(data).forEach(key=>{
Object.defineProperty(this,key,{
set(val){
console.log(`有人设置了${key}属性,值为${val}`)
data[key]=val
ec.$emit(key,val)
},
get(){
console.log(`有人读取了${key}属性`)
return data[key]
}
})
})
const rootNode = document.querySelector(el)
Array.from(rootNode.children).forEach(node=>{
if(node.hasAttribute("v-html")){
let key = node.getAttribute("v-html")
node.innerHTML= this[key]
ec.$on(key,()=>{
node.innerHTML= this[key]
})
}else if(node.hasAttribute("v-model")){
let key = node.getAttribute("v-model")
node.value = this[key]
ec.$on(key,()=>{
node.innerHTML= this[key]
})
node.addEventListener("input",(e)=>{
this[key] = e.target.value
})
}
})
}
new MVVM({
el:"#app",
data:{
salary:20000,
bonus:1000
}
})
六、vue3实现响应式
vue3通过ES6新增的Proxy来实现响应式
var obj = {a:1,b:2}
var newObj = new Proxy(obj,{
set(target,prop,value){
target[prop]=value
},
get(target,prop){
return target[prop]
}
})
七、vue2和vue3实现响应式的区别
1.效率更高一些 Proxy可以处理一类动作 Object.defineProperty只能处理某个具体属性名,而且需要通过循环遍历属性 2. 能直接监控数组的操作 vue2.0其实不能直接监控数组的操作
- 数组长度的变化不会直接去更新页面,(数组和对象不一样)
- 通过数组小编修改数组元素,页面也不会更新
vue2.0动态添加属性的时候也不是响应式的
- 因为在数据初始化阶段,vue才会对每一个属性进行Object.defineProperty拦截,后续添加的属性,vue根本拦截不到
八、浅淡vue2为什么不能直接监控数组的操作
为什么尤大大不监控数组的操作? 因为Object.defineProperty要监听到对象的具体属性,数组的属性又是一堆索引,并没有什么语义化,而且可能数
组的长度非常大,就极容易导致性能过低.所以尤大大没用使用Object.defineProperty来监听数组的操作
而是尤大大通过拦截了数组的7个方法(官网有写哪7个),用户只有调用这7个方法时候,vue2才能感知数组的变化,
才能更新视图
拦截的7个数组方法: push,pop,shifr,unshift,reserve,splice,sort
九、自我总结
vue2在初始化的时候,遍历所有的数据属性,然后通过Object.defineproperty进行数据劫持,在渲染模板的时候,在结和观察者模式,对属性的改变进行监听函数,并且加入到一个数组里面,当某一个属性发现改变时候,就会被set拦截,在set函数中发布属性的改变,这样对这个属性用到的地方都会执行页面的更新
|