大家好,在网上看到过很多vue源码的解析,大多是copy和摘抄的,少有自己的理解,所以下决心花时间整理一遍自己对vue源码的理解。我只是一个普普通通的码农,如果本文有写的不对之处,希望各路大神指正,谢谢!
vue版本:2.5.17
源代码相关链接:https://cdn.bootcdn.net/ajax/libs/vue/2.5.17/vue.js
首先,本人采取的是最简单的debugger调试方法,按照步骤与逻辑分层次解析vue源码,我认为这是最快、最简单、也是最有效的方法。本文使用的vue调试代码如下:
<div id="main">
<h1>count: {{times}}</h1>
<p @click="cons">{{text}}</p>
</div>
<script src="./vue2.5.17/dist/vue.js"></script>
<script>
var timeId = null
var vm = new Vue({
el: '#main',
data: function () {
return {
times: 1,
text: "我的"
};
},
created: function () {
var me = this;
timeId = setInterval(function () {
me.times++;
}, 1000);
},
methods: {
cons() {
console.log(6211)
debugger
this.text = "你的"
}
}
});
这是一个非常简单的例子,但可以很好地帮助我们解读vue的整个运行流程。
vue点击事件篇
事件监听
主要是在原型链上绑定四种事件监听:$on
(点击事件)、$once
(单次点击事件)、$off
(移除事件)、$emit
(抛出或触发自定义的事件)等监听。
dom元素(标签)上的事件
举例:<div @click="test01"></div>
用v-on
指令(该指令经常简写为@
)监听DOM事件,并在触发时运行以下代码
通过processAttrs()解析属性时
Vue利用正则表达式dirRE
、onRE
匹配到该dom
元素及相关事件,之后添加属性。
这里说明一下,因为Vue初始化时,调用initEvents(vm)对事件进行初始化,所以之后我们通过点击的方式去触发事件,原生绑定nativeBind就会触发事件,改变this的指向后,找到对应的事件方法,解析并执行。
initEvents方法在vm上创建一个_events对象,用来存放事件。
接下来就调用initState()方法初始化事件,相关代码段:
opts的methods属性里有我们自定义的test测试方法,然后就会执行initMethods。
请注意:这里最关键的一句代码,initMethods遍历定义的methods,通过调用bind改变函数的this指向,修饰了事件的回调函数,组件上挂载的事件都是在父作用域中的。
nativeBind方法里的bind改变了函数的this指向,这里的this指向ctx的地址值
Bind改变this指向的原理可参考:https://www.cnblogs.com/xljzlw/p/3775162.html
Ast的解析过程可参考:https://segmentfault.com/a/1190000015848917
然后通过add$1方法里的addEventListener将事件绑定到target(目标)标签上
组件的点击事件
<div @click.native="test01"></div>
<child-test>
标签定义了click
事件,若click事件没加修饰符.native
,点击按钮不出发任何事件,这里.native
修饰符的作用就是在原生dom
上绑定事件。
如果不添加.native
的的事件解析过程与上文相同,而添加了.native
之后,v-on
的事件会被放到nativeOn
数组中,解析后的render
如下图所示:
我们再来分析一下事件的初始化,
调用genHandlers
方法时,会先判断该事件是否为native
,如果是,解析的事件字符串就会用'nativeOn{}'
字符串包裹。
html
解析成vnode
之后会调用createComponent
进行处理。
createComponent方法里会.native修饰符的事件放在data.on上面。接下来data.on上的事件(本文中的alert(‘001号被点了!’);)会按普通的html事件继续执行下方代码。
最终通过target$1.addEventListener
添加事件监听。
而标签内没有.native
的修饰符调用的是$on
方法。
updateListeners
又调用了add方法
小结:
对于普通html元素和在组件标签内添加了.native
修饰符的事件,都通过target1.addEventLister()
来挂载事件。而定义在组件上的事件会调用原型上的on
等方法。