时间:2021-05-28
分享前啰嗦
我之前介绍过vue1.0如何实现observer和watcher。本想继续写下去,可是vue2.0横空出世..所以直接看vue2.0吧。这篇文章在公司分享过,终于写出来了。我们采用用最精简的代码,还原vue2.0响应式架构实现。
以前写的那篇 vue 源码分析之如何实现 observer 和 watcher可以作为本次分享的参考。
不过不看也没关系,但是最好了解下Object.defineProperty
本文分享什么
理解vue2.0的响应式架构,就是下面这张图
顺带介绍他比react快的其中一个原因
本分实现什么
对应的虚拟dom会从
<div><span>before</span></div> 变为 <div><span>after</span></div>
好,开始吧!!!
第一步,讲data 下面所有属性变为observable
来来来先看代码吧
class Vue { constructor(options) { this.$options = options this._data = options.data observer(options.data, this._update) this._update() } _update(){ this.$options.render() } } function observer(value, cb){ Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb)) } function defineReactive(obj, key, val, cb) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: ()=>{}, set:newVal=> { cb() } }) } var demo = new Vue({ el: '#demo', data: { text: 123, }, render(){ console.log("我要render了") } }) setTimeout(function(){ demo._data.text = 444 }, 3000)为了好演示我们只考虑最简单的情况,如果看了vue 源码分析之如何实现observer和watcher可能就会很好理解,不过没关系,我们三言两语再说说,这段代码要实现的功能就是将
var demo = new Vue({ el: '#demo', data: { text: 123, }, render(){ console.log("我要render了") } })中data 里面所有的属性置于 observer,然后data里面的属性,比如 text 以改变,就引起_update()函数调用进而重新渲染,是怎样做到的呢,我们知道其实就是赋值的时候就要改变对吧,当我给data下面的text 赋值的时候 set 函数就会触发,这个时候 调用 _update 就ok了,但是
setTimeout(function(){ demo._data.text = 444 }, 3000)demo._data.text没有demo.text用着爽,没关系,我们加一个代理
_proxy(key) { const self = this Object.defineProperty(self, key, { configurable: true, enumerable: true, get: function proxyGetter () { return self._data[key] }, set: function proxySetter (val) { self._data[key] = val } }) }然后在Vue的constructor加上下面这句
Object.keys(options.data).forEach(key => this._proxy(key))
第一步先说到这里,我们会发现一个问题,data中任何一个属性的值改变,都会引起
_update的触发进而重新渲染,属性这显然不够精准啊
第二步,详细阐述第一步为什么不够精准
比如考虑下面代码
new Vue({ template: ` <div> <section> <span>name:</span> {{name}} </section> <section> <span>age:</span> {{age}} </section> <div>`, data: { name: 'js', age: 24, height: 180 } }) setTimeout(function(){ demo.height = 181 }, 3000)template里面只用到了data上的两个属性name和age,但是当我改变height的时候,用第一步的代码,会不会触发重新渲染?会!,但其实不需要触发重新渲染,这就是问题所在!!
第三步,上述问题怎么解决
简单说说虚拟 DOM
首先,template最后都是编译成render函数的(具体怎么做,就不展开说了,以后我会说的),然后render 函数执行完就会得到一个虚拟DOM,为了好理解我们写写最简单的虚拟DOM
我们运行一下,他会输出
{ tag: 'div', data: {}, children:[ { tag: 'span', data: {}, children: [ { children: undefined, data: undefined, tag: undefined, text: '' // 正常情况为 字符串 before,因为我们为了演示就不写代理的代码,所以这里为空 } ] } ] }这就是 虚拟最简单虚拟DOM,tag是html 标签名,data 是包含诸如class和style这些标签上的属性,childen就是子节点,关于虚拟DOM就不展开说了。
回到开始的问题,也就是说,我得知道,render 函数里面依赖了vue实例里面哪些变量(只考虑render 就可以,因为template 也会是帮你编译成render)。叙述有点拗口,还是看代码吧
var demo = new Vue({ el: '#demo', data: { text: "before", name: "123", age: 23 }, render(){ return this.__h__('div', {}, [ this.__h__('span', {}, [this.__toString__(this.text)]) ]) } })就像这段代码,render 函数里其实只依赖text,并没有依赖name和age,所以,我们只要text改变的时候,我们自动触发render 函数 让它生成一个虚拟DOM就ok了(剩下的就是这个虚拟DOM和上个虚拟DOM做比对,然后操作真实DOM,只能以后再说了),那么我们正式考虑一下怎么做
第三步,'touch' 拿到依赖
回到最上面那张图,我们知道data上的属性设置defineReactive后,修改data 上的值会触发set。
那么我们取data上值是会触发get了。
对,我们可以在上面做做手脚,我们先执行一下render,我们看看data上哪些属性触发了get,我们岂不是就可以知道 render 会依赖data 上哪些变量了。
然后我么把这些变量做些手脚,每次这些变量变的时候,我们就触发render。
上面这些步骤简单用四个子概括就是 计算依赖。
(其实不仅是render,任何一个变量的改别,是因为别的变量改变引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)
第一步:
我们写一个依赖收集的类,每一个data 上的对象都有可能被render函数依赖,所以每个属性在defineReactive时候就初始化它,简单来说就是这个样子的。
然后,当执行render 函数去'touch'依赖的时候,依赖到的变量get就会被执行,然后我们就可以把这个 render 函数加到 subs 里面去了。
当我们,set 的时候 我们就执行 notify 将所有的subs数组里的函数执行,其中就包含render 的执行。
至此就完成了整个图,好我们将所有的代码展示出来
我们看一下运行结果
好我们解释一下 Dep.target 因为我们得区分是,普通的get,还是在查找依赖的时候的get,所有我们在查找依赖时候,我们将
function watch(vm, exp, cb){ Dep.target = cb return exp() }Dep.target 赋值,相当于 flag 一下,然后 get 的时候
get: () => { if (Dep.target) { dep.add(Dep.target) } return val },判断一下,就好了。到现在为止,我们再看那张图是不是就清楚很多了?
总结
我非常喜欢,vue2.0 以上代码为了好展示,都采用最简单的方式呈现。
不过整个代码执行过程,甚至是命名方式都和vue2.0一样。
对比react,vue2.0 自动帮你监测依赖,自动帮你重新渲染,而react 要实现性能最大化,要做大量工作,比如我以前分享的:
react如何性能达到最大化(前传),暨react为啥非得使用immutable.js
react 实现pure render的时候,bind(this)隐患。
而vue2.0 天然帮你做到了最优,而且对于像万年不变的 如标签上静态的class属性,vue2.0 在重新渲染后做diff 的时候是不比较的,vue2.0比达到性能最大化的react 还要快的一个原因。
然后源码在此,喜欢的记得给个star 哦
后续,我会简单聊聊,vue2.0的diff。
本文已被整理到了《Vue.js前端组件学习教程》,欢迎大家学习阅读。
关于vue.js组件的教程,请大家点击专题vue.js组件学习教程进行学习。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
今天我们来说一说vue1.0和vue2.0的主要变化有哪些一.在每个组件模板,不在支持片段代码VUE1.0是: 我是组件我是加粗标签VUE2.0:必须有根元素,
本文实例讲述了Vue2.0实现组件之间数据交互和通信操作。分享给大家供大家参考,具体如下:Vue2.0组件之间数据交互和通信。Vue2.0废弃了dispatch
一、vue2.0移除了$index和$key虽然说现在很多文章说他们的代码是vue2.0版本的,但是有一些仔细一看,发现并不全是2.0版本,有些语法还是1.0的
vue官网关于此解释说明如下:vue2.0里面的深入响应式原理的异步更新队列官网说明如下:只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的
关于vue2.0的事件发射和接收,大家都知道$dispatch和$broadcast在vue2.0已经被弃用了,取而代之的是更加方便快捷的方式,使用事件中心,组