vue2.0数据响应式原理
对象
Obect.defineproperty 定义对象的属性mjm
defineproperty 其实不是核心的为一个对象做数据双向绑定,而是去给对象做属性标签,设置一系列操作权限,只不过属性里的get和set实现了响应式
var ob = { a: 1, b: 2 } //1-对象 2-属性 3-对于属性的一系列配置 Object.defineProperty(ob, 'a' , { //a对象则是ob的绝对私有属性,,默认都是true writable: false, enumerable: false, configurable: false }) ob.a = 3 console.log(Object.getOwnPropertyDescriptor(ob, 'a')) console.log(ob.a) //1
var ob = { a: 1, b: 2 } //1-对象 2-属性 3-对于属性的一系列配置 /** * vue双向数据绑定 * 给ob的a属性设置get/set方法,则获取ob的a时,会触发get方法,设置ob的a时,会触发set方法 */ Object.defineProperty(ob, 'a' , { //a对象则是ob的绝对私有属性,,默认都是true get: function(){ console.log('a- get') }, set: function(){ console.log('a- set') } }) ob.a = 3 console.log(ob.a)
//正常用法,,,使用中转,不优雅 var ob = { a: 1, b: 2 } //1-对象 2-属性 3-对于属性的一系列配置 /** * vue双向数据绑定 * 给ob的a属性设置get/set方法,则获取ob的a时,会触发get方法,设置ob的a时,会触发set方法 */ var _value = ob.a //_value 作为一个中转 Object.defineProperty(ob, 'a' , { //a对象则是ob的绝对私有属性,,默认都是true get: function(){ console.log('a- get') return _value; }, set: function(newValue){ console.log('a- set') _value = newValue; } }) ob.a = 3 console.log(ob.a) //get方法必须return ,否则返回undefined
defineProperty 定义的get和set是对象的属性,那么数组怎么办?
做了个装饰者模式
/** * 概述 Object.create() 方法创建一个拥有指定原型和若干个指定属性的对象。 * 被创建的对象继承另一个对象的原型,在创建新对象时可以指定一些属性。 * 语法 Object.create(proto, [ propertiesObject ]) */ //数组 -- 做了个装饰者模式 var arraypro = Array.prototype; var arrob = Object.create(arraypro) var arr = ['push', 'pop', 'shift']; //枚举这三个,vue中还有其他 arr.forEach((method, index)=>{ arrob[method] = function(){ var ret = arraypro[method].apply(this,arguments); dep.notify(); } })
vue3.0数据响应式原理 - Proxy
Proxy对象用于定义基本操作的自定义行为 ,用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
和defineProperty类似,功能几乎一样,但是用法不同
为什么要是用Procy? 1、defineProperty只能监听某个属性,不能对全对象监听,所以可以省去 for in 提升效率
2、会生成新的对象,之后的操作都是针对新对象,不会污染原对象
3、可以监听数组,不用再去单独对数组做特异性操作
改造的observer: vue.prototype.observer = function(obj){ //注册get/set监听 var self = this; this.$data = new Proxy(this.$data, { get: function(target, key, receiver){ return target[key] }, set: function(target, key, value, receiver){ // return Reflect.set(target, key, value); // return target[key] = value target[key] = value; self.render(); } }) }
Proxy 用途 -- 校验类型 -- 真正的私有变量
/** * 类型验证 * 创建一个对象,该对象是个人,他有age和name两个属性 * name必须是中文,age必须是数,而且大于18岁 */ var validator = { //设计模式 -- 策略模式 name: function(value){ let reg = /^[u4E00-u9FA5]+$/; if(typeof value === 'string' && reg.test(value)){ return true } return false }, age: function(value){ if(typeof value === 'number' && value > 18){ return true; } return false; } } function Person(age, name){ this.age = age; this.name = name; let self = this; return new Proxy(this, { get: function(target, key){ // return target[key] if(validator[key](self[key])){ //自己改的,原文为上面注释的情况 return target[key] }else{ throw new Error(key + ' is not right') } }, set: function(target, key, value){ if(validator[key](value)){ return Reflect.set(target, key, value) }else{ throw new Error(key + ' is not right') } } }) } var xiaohong = new Person('daad', 2) console.log(xiaohong.name) //Uncaught Error: name is not right xiaohong.name = 'qwe' //test4.js:32 Uncaught Error: name is not right xiaohong.age = 1 //test4.js:32 Uncaught Error: name is not right
Diff算法和virtua doml -- 虚拟dom,他只在概念里面存在,在vue里是个ast语法树
<template> <div id="123"> <p><span></span></p> <p>11111</p> <p>22222</p> </div> </template> 解析成虚拟DOM -- diff diff <div> props: { id: '123' } children: [ diff <p> props: {} children: [ diff <span> props: {} children: {} ] text: 123 ] 转换成对象的形式 var virtual = { dom: 'div' prop: { id: '123' } children: [ { dom: 'p' children: [ { dom: 'span' children: [] } ] }, { dom: 'p' children: [] }, { dom: 'p' children: [] } ] }
patchVode(oldVnode, vnode){ //先拿到真实dom var el = vnode.el = oldVnode.el; //分别拿出 老节点 的子元素和 新节点 的子元素 let i, oldCh = oldVnode.children, ch = cnode.children; //如果老节点和新节点相同,不做任何事情 if(oldVnode === vnode) return false //分情况操作 - 只有文字节点,,删除/增加/变动 了 子元素 if(oldVnode.text != null && vnode.text!=null && oldVnode.text!=vnode.text){ //只有文字节点 api.setTextContent(el, vnode.text) }else{ updateEle() if(oldCh && ch && oldCh!=ch){ //所有老新节点的子元素都存在,那么肯定是发生了变动 updateChildren() }else if(ch){ //只有新节点有子元素,那么就创建新的子元素 createEl() }else if(oldCh){ //只有老节点有子元素,那么删除子元素 api.removeChildren(el) } } }
类型验证
* 创建一个对象,该对象是个人,他有age和name两个属性
* name必须是中文,age必须是数,而且大于18岁
*/
var validator = { //设计模式 -- 策略模式
name: function(value){
let reg = /^[u4E00-u9FA5]+$/;
if(typeof value === 'string' && reg.test(value)){
return true
}
return false
},
age: function(value){
if(typeof value === 'number' && value > 18){
return true;
}
return false;
}
}
function Person(age, name){
this.age = age;
this.name = name;
let self = this;
return new Proxy(this, {
get: function(target, key){
// return target[key]
if(validator[key](self[key])){ //自己改的,原文为上面注释的情况
return target[key]
}else{
throw new Error(key + ' is not right')
}
},
set: function(target, key, value){
if(validator[key](value)){
return Reflect.set(target, key, value)
}else{
throw new Error(key + ' is not right')
}
}
})
}
var xiaohong = new Person('daad', 2)
console.log(xiaohong.name) //Uncaught Error: name is not right
xiaohong.name = 'qwe' //test4.js:32 Uncaught Error: name is not right
xiaohong.age = 1 //test4.js:32 Uncaught Error: name is not right