赞
踩
Vue 最独特的特性之一,是其非侵入性(不用调用Vue的api来实现数据更新)的响应式系统。
响应式:即数据改变,对应的视图也会改变。
响应式原理
:采用数据劫持结合发布-订阅者模式的方式,通过Object.defineProperty()来劫持data里面各个属性的setter和getter,在数据变动的时候,触发set方法,检测到数据发生变化,会发布消息给订阅者,触发相应的监听回调,生成新的虚拟DOM树,视图更新。
Object.defineProperty()
可以直接在一个对象上定义一个新的属性,或者修改一个对象的现有属性,并返回此对象。还可以设置一些额外隐藏的属性(例如是否可写writeable,是否可以枚举enumerate)。
当页面渲染时(render),触发data里面的数据(getter),watcher(观察者)会把接触过的数据记录为依赖。每个数据都有getter和setter,当你修改这个数据的时候,依赖的setter触发,然后通知到watcher,从而使关联这个数据的组件重新渲染。
实现一个Object.defineProperty()功能。
var obj={} //defineReactive就是响应式 function defineReactive(data,key,val){ //data数据对象,key键(即属性),val值(用来周转,set里面修改值之后,设置val为新的值,get才能获取到新的val) Object.defineProperty(data,key,{ //getter get(){ //里面的get()和set()与外面的defineReactive构成了一个闭包环境(因为要内存函数使用到val,所以要用闭包) console.log('访问对象的'+key+'属性') return val; //要有返回值,才能获取到这个属性的值 }, //setter set(newValue){ console.log('改变对象的'+key+'属性',newValue) if(val==newValue){ return ; //如果传入的参数(属性要改为的值)和原来的值相等,就不做任何处理 } val=newValue //如果传入的参数是一个新的值,就改变这个属性的值 } }) }
defineReactive(obj,'a',10)
console.log(obj.a) //访问对象的属性 /n 10
obj.a=15
obj.a++ //先访问到val,再改变
console.log(obj.a) //改变对象的属性 /n 访问对象的属性 /n
defineReactive(obj,'a',10)
defineReactive(obj,'b',20)
console.log(obj.a)
console.log(obj.b)
obj.b=30
console.log(obj.b)
例:使用Object.defineProperty()实现一个响应式功能.
<div id="app"> <input type="text" id="a"> <span id="b"></span> </div> <script type="text/javascript"> var data = { //模拟vue里面的data属性 a:1, b:2 }; var vm={} //模拟vue实例 function defineReactive(vm,key,val){ //defineReactive就是响应式 //实现数据双向绑定的方法————数据劫持:当访问或者设置vm中的某一个成员时,做一些干预操作。 Object.defineProperty(vm, key, { //参数1:vue实例,参数2:要劫持的属性,参数3:对象(用来获取和设置对象的属性值) //getter get: function() { console.log('get vm '+key+' val:'+ val); document.getElementById('a').value = val; document.getElementById('b').innerHTML = val; return val; }, //setter set: function(newVal) { if(val===newVal){ return ; } val = newVal; console.log('set vm '+key+' val:'+ val); document.getElementById('a').value = val; document.getElementById('b').innerHTML = val; } }); } document.addEventListener('keyup', function(e) {//触发事件的时机,从而执行相应的操作 vm.a=e.target.value }); Object.keys(data).forEach(k=>{ //data里面有多个属性时,遍历data的各个属性 defineReactive(vm,k,data[k]) }) </script>
Vue2存在的问题:
响应式原理
:Vue3通过ES6的代理对象Proxy进行响应式,代理data对象里面所有的属性及数组,访问属性时触发get(),改变属性值时触发set(),然后发布消息给订阅者,重新渲染页面。
实现Proxy的功能。
<div id="app"> </div> <script> let data={ msg:'Hello', count:0, arr:[1,2,3,4] } //模拟vue实例 const vm=new Proxy(data,{ //用Proxy,不用循环就可以遍历到data对象的所有属性,数组也可以更改数值,改变数组长度 //执行代理行为的函数 //当访问vm的成员会执行 get(target,key){ //target就相当于是data对象,key是对象的属性 console.log('get key:',key,target[key]) document.querySelector('#app').textContent=target[key] return target[key] }, //当设置vm的成员会执行 set(target,key,newValue){ console.log('set key',key,newValue) if(target[key]===newValue){ return; } target[key]=newValue document.querySelector('#app').textContent=target[key] } }) </script>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。