前段干货系列----vue核心源码解读
vue现在非常火了,好多人都在用。博主为了提高自己的技术水平,也开始了解读vue的源码。过程很疼苦,多亏了从公众号里看到的一个帖子。博主也不藏私发布出来供大家一起欣赏(相关代码在最底部):https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651554000&idx=1&sn=08219fe9
vue现在非常火了,好多人都在用。博主为了提高自己的技术水平,也开始了解读vue的源码。过程很疼苦,多亏了从公众号里看到的一个帖子。博主也不藏私发布出来供大家一起欣赏(相关代码在最底部):
https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651554000&idx=1&sn=08219fe9433fef033b17b98d1072367f&chksm=80255711b752de07e163f621ed848096850130a4484e0c81357917fd6e044a76a3cd5cf17926&scene=0#rd
说起来这解读的也不是vue的源码,而是这位大神的源码。只是跟vue实现绑定的的原理一样。而我这篇文章也是对他里面源码的一些解读,希望能够让更多的人更能理解vue。反正博主看懂了之后,对vue的作者崇拜更深了。
vue原理的实现主要是通过 Object对象的defineProperty属性来完成的。
你心里现在可能要骂爹了,就这么一个东西就实现了双向绑定。这不就是一个Object定义属性的一个方法么,不知道你是啥心情,博主反正就是这种心情。但是呢,首先你不能把vue想的太牛逼了,虽然就是很牛逼。但是当你懂了之后你知道他其实并没有太复杂,这就是所谓的大道至简吧。也只有这样的代码才会让更多的人感觉很美妙不是么。
vue的运行可以通过一张图来展示
这里你不用急着弄懂,等你看了博主一步步的讲解之后这里每一个箭头的含义自然而然的就明白了。
由于正序要很久才会切入界面变化的实现效果,所以博主在这里采用倒序的方式进行讲解。让你从界面上产生的疑惑一步一步的追到根。
根据vue的双向绑定原理,只要input框绑定的值跟div绑定的值是同一个,你修改input框的时候div的值也会发生改变。如果你想用原生js人为的修改div的值找到需要修改的元素,设置一下innerHTML是不是就可以了。你想要修改input的值只需要修改一下value的值是不是就可以了。
document.getElementById("**").value="333"
document.getElementById("**").innerHtml="333"
所以这里定义了一个Watcher对象来完成这件事情相关代码如下:
function Watcher(name, el, vm, exp, attr) {
this.name = name; //指令名称,例如文本节点,该值设为"text"
this.el = el; //指令对应的DOM元素
this.vm = vm; //指令所属myVue实例
this.exp = exp; //指令对应的值,本例如"number"
this.attr = attr; //绑定的属性值,本例为"innerHTML"
this.update();
}
Watcher.prototype.update = function() {
this.el[this.attr] = this.vm.$data[this.exp];
}
只要实例化一个Watcher,然后想改动页面数据的时候调用一下Watcher的update方法是不是就实现了界面数据的更改。
现在界面数据的更改你知道了,现在你肯定想知道如何通知的Watcher的了吧。从图中你可以看到是Observer发送的通知对吧,而Observer又是new MVVM() 衍生过来的,所以在new MVVM()的原型链中肯定有Observer这个方法,上文中提到的defineProperty也是在这里定义的,不懂defineProperty的可以参考下面这篇文章
https://segmentfault.com/a/1190000011294519
defineProperty可以为添加的属性设置set方法,当属性修改了的时候就会执行这个方法,那我们是不是只需要在set方法里面调用一下我们定义的Watcher的update方法就可以实现界面数据修改了呢(相关代码在最底部)。
myVue.prototype._obverse = function(obj) {
.......
Object.defineProperty(_this.$data, key, {
.....
set: function(newVal) {
if (value !== newVal) {
value = newVal;
binding._directives.forEach(function(item) {
//item就是Watcher对象
item.update();
})
}
}
})
}
})
}
这样剩下的事情就只有一个了就是图中的compile解析命令了。解析的就是你在模板中加入的b-click,b-model,b-bind等等命令。这里只实现了这三个命令,其他的命令你可以自由实现,原理一样。这个时候你就疑惑了咋解析啊,说解析你可能比较糊涂,我换个词。我把解析换成判断元素是否有这个属性,你是不是觉得瞬间简单了不少。判断元素的属性只需要用hasAttribute方法是不是就可以了,当你知道他有这个属性的时候,给对应的属性添加一个事件监听是不是就可以了,比如input修改事件:
if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
node.addEventListener('input', (function(key) {
var attrVal = node.getAttribute('v-model');
_this._binding[attrVal]._directives.push(new Watcher(
'input',
node,
_this,
attrVal,
'value'
))
return function() {
_this.$data[attrVal] = nodes[key].value;
}
})(i));
}
上文compile方法中提到的item也是在这里定义的对象,读到这里你是不是整个流程灌输起来了。有一种茅塞顿开的感觉,但是你仔细琢磨就会有一种懂了,但是开口却说不出来,因为很多细节文章中没有提到,这些细节都比较简单,这个思路你明白了。这些细节只是将你脑海中的那张图描绘的更清楚的。
你可能会疑问如何监听的所有的属性,这里我可以告诉你用到的是递归方法。你如果不懂递归也可以看博主的这篇文章
https://blog.csdn.net/m0_37479946/article/details/79973315
代码连接:
https://pan.baidu.com/s/1lYGjz24KFau6MpY7fKYsJw
秘钥可以关注博主公众号吵吵日记回复myVue获取哦。
更多推荐
所有评论(0)