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描述符能否被改变falseenumerable能否被枚举falsevalue该属性对应的值。undefined。writable能否被改写falseget当访问该属性时调用undefinedset当属性值被修改时调用。该方法接受一个被赋予的新值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'>asd</p> <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