vue3.0和2.0数据绑定实现区别

vue3去年就已经发布了阿尔法版本,今年尤大表示只剩下些2.0的迁移工作,不过毕竟不想react和ng大树地下好乘凉,尤大单枪匹马的3.0究竟啥时候能到来谁也说不准,但是这并不耽误提前了解和学习,退一万步将哪怕胎死腹中,仍然可以嫖到思想。
github关注动态

简单聊下说烂了的2.0绑定方式,就是使用definedProperty中的get\set方法来完成数据劫持,之后在通过diff算法对比新老dom差异修改vdom,重新render完成渲染;
首先了解下definedProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法
Object.defineProperty(obj, prop, descriptor)

释义
obj 要定义属性的对象。
prop 要定义或修改的属性的名称或 Symbol 。
descriptor 要定义或修改的属性描述符。

举个例子

	let obj = {};
	Object.defineProperty(obj, 'name', {
	  	value: 'dahuang',
	  	writable: false
	});
	console.log(obj.name);
	// 'dahuang'

主要看下descriptor中的属性

属性 描述 默认值
configurable 描述符能否被改变 false
enumerable 能否被枚举 false
value 该属性对应的值。 undefined。
writable 能否被改写 false
get 当访问该属性时调用 undefined
set 当属性值被修改时调用。该方法接受一个被赋予的新值 undefined

重点就是这个get和set,对何时触发再举个例子

	let obj = {a:1}
	Object.defineProperty(obj,'a',{
		get:function(){
		},
		set:function(){
		}
	})
	obj.a 			//触发get函数
	obj.a = 2		//触发set函数

了解了这些就可以创建一个简易版的响应式

	function vue(){}//不想起名字就还叫vue了
	vue.prototype.observe = function(obj){//完成数据绑定的方法
		var val;
		var that = this;
		for(let key in obj){//遍历所有属性,依次设置监听
			val = obj[key];
			if(typeof val ==='object'){
				this.observe(val)	//如果是对象的话进行递归
			}else{
				Object.defineProperty(this.$data,key,{
					get:function(){
						//vue中还会进行依赖收集,确定变量需要在哪部分修改,属于优化操作,这里先不考虑,有兴趣可以看源码dep对象
						//return this.$data[key]每次那到的都是上一次的值,所以外部保存变量
						return val;
					},
					set:function(newVal){
						val = newVal;
						that.render();
					}
				})
			}
		}
	}
	vue.prototype.render = function(){}//完成渲染的方法	

这里写的很糙,省去了很多东西,只是举个例子说下defineProperty,实际的vue中还做了很多别的,有兴趣可以看看源码,1040+行,或者想了解具体实现的推荐一篇博文
原理剖析

3.0由defineProperty改为了proxy;defineProperty是侵入原对象,改变了原有的一些东西,诸如枚举,读写之类的属性操作,上边列举属性表也可以看的出来,默认值都是false,换句话说就是默认的对象都是稳定的对象,而修改后就需要耗费额外的性能去做更多的处理;
proxy是一个代理,还是老规矩,先看属性

Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

语法
const p = new Proxy(target, handler);

描述
target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

handler对象方法

名称 描述
handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible() Object.isExtensible 方法的捕捉器。
handler.preventExtensions() Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.defineProperty() Object.defineProperty 方法的捕捉器。
handler.has() in 操作符的捕捉器。
handler.get() 属性读取操作的捕捉器。
handler.set() 属性设置操作的捕捉器。
handler.deleteProperty() delete 操作符的捕捉器。
handler.ownKeys() Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
handler.apply() 函数调用操作的捕捉器。
handler.construct() new 操作符的捕捉器。

重点还是看get\set,举个例子

	let a = {b:1,c:2}
	let proxya = new Proxy(a,{
		get:function(){},
		set:function(){}
	})

乍看上去好像没什么区别,不过这里不对a直接进行操作,而是返回一个代理对象proxya;从代码上来看,也比defineProperty省去了for循环遍历取值的操作,代码更加简洁
把上边observe中的defineProperty替换成proxy对比看下

	vue.prototype.observe = function(obj){
		let that = this;
		this.$data = new Proxy(this.$data,{
			get:function(tag,key){
				return tag[key]
			},
			set:function(tag,key,newVal){
				tag[key] = newVal;
				that.render();
			}
		})
	}

高下立判;

另外就是对vdom的对比进行了升级;
2.0的diff算法简单来讲会将所有的dom节点依次展开,并获取属性和子节点;一段div.wrap>p.txt1+p.txt2

	<div class='wrap'>
		<p class='txt1'>asdp>
		<p class='txt2'>{{asd}}p>
	div>

展开大致就是

	Diff
	div
		att from div
		children from div
		innerHTML from div
			p
				att from p
				children from p
				innerHTML from p
			p
				att from p
				children from p 
				innerHTML from p

想表达的意思vdom会展开templent下所有节点,就是说如果txt1中并没有需要改变的数据,虚拟dom树在进行diff比对的时候仍然会展开并对比这个元素,无疑造成了性能的浪费;
3.0中又新增了一个block tree;对于目标模板,以vue指令进行切分,分成静态堆和动态堆,静态堆仅记录位置,而对比时只对比动态堆;
想了解block tree,可以参看vue3 bolck tree

你可能感兴趣的:(vue,javascript,vue,js,proxy,dom)