前言
本人毕业到现在工作一年,一直在开发React项目,但在毕业之前的实习基本上都在用vue开发,前一段时间工作比较轻松,又因为好久没有写vue项目,所以想再去熟悉一下vue, 但是痴情的我,居然想看vue的源码,于是我就去慕课网上买了一门源码讲解的课.前后看了两遍,可能是我太笨了,没有多久就忘记的差不多了,所以我决定再去看第三遍,并要做好记录.下面我就把我的记录和大家分享一下。
学前须知
如果想调试vue的源码,可以先用 vue create my-app 创建一个vue的项目,然后找到node_modules文件夹下的vue文件夹下的dist文件夹下的vue.esm.js或者vue.runtime.esm.js里进行调试
new Vue()后都干了些什么
我们看到上面的代码截图中用到了new Vue(),所以说源码里一定定义了一个名字叫 Vue() 构造函数,接下来我们就来看一下源码里是怎么定义这个函数的 我们看到这个 Vue() 函数就10行左右的代码,是不是很奇怪,我们先保持这个疑问, 向下继续看下面的几行代码是干什么的initMixin(Vue); // vue.prototype上添加 _init()方法
renderMixin(Vue); //vue.prototype上添加 _render()方法
lifecycleMixin(Vue); //vue.prototype上添加 _update()方法
我们这一章主要讲这3行代码衍生出来的代码,让我们一起先进入initMixin(Vue)是干什么的。
initMixin(Vue)
function initMixin (Vue) {
Vue.prototype._init = function (options) {
var vm = this; 创建vm,
...
// 合并options 到 vm.$options, 这里可以自己调试看一下vm.$options返回了什么
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
...
initLifecycle(vm); //初始生命周期
initEvents(vm); //初始化事件
initRender(vm); //初始render函数
callHook(vm, 'beforeCreate'); //执行 beforeCreate生命周期钩子
...
initState(vm); //初始化data,props,methods computed,watch
...
callHook(vm, 'created'); //执行 created 生命周期钩子
if (vm.$options.el) {
vm.$mount(vm.$options.el); //这里也是重点,下面需要用到
}
}
从代码中我们来看一下initMixin() 都干了些什么.
- 第一步创建一个vm, (这个vm非常重要,一定要打个debugger调试以下,看看vm输出了什么)
- 第二步合并 vm.$options (这里可以自己调试看一下vm.$options返回了什么)
- 第三步 开始初始化 生命周期, 事件, render函数等等,
- 第四步 执行 vm.options.el);
这里我们先了解 initState(vm)是干什么的,都执行力什么函数
initState(vm)
function initState (vm) {
...
var opts = vm.$options;
if (opts.props) { //如果你的.vue文件里定义了props,那么就会执行 initProps()
initProps(vm, opts.props);
}
if (opts.methods) { //如果你的.vue文件里定义了methods,那么就会执行 initMethods()
initMethods(vm, opts.methods);
}
if (opts.data) {//如果你的.vue文件里定义了data,那么就会执行 initData()
initData(vm);
}
if (opts.computed) { //如果你的.vue文件里定义了computed,那么就会执行 initComputed()
initComputed(vm, opts.computed);
}
if (opts.watch && opts.watch !== nativeWatch) {//如果你的.watch,那么就会执行 initWatch()
initWatch(vm, opts.watch);
}
}
我们到initState里的代码我们一定有些熟悉的感觉。 这里的代码再配上注释应该非常容易看懂. 由于代码很多,我就挑选了 initData(vm) 来详细讲解一下
function initData (vm) {
var data = vm.$options.data; //获取.vue文件里定义的data
data = vm._data = typeof data === 'function'
? data.call(vm, vm) //如果data定义的是函数就把data的this指向vm
: data || {};
...
var keys = Object.keys(data);
var props = vm.$options.props;
var methods = vm.$options.methods;
//遍历,data,props,methods,看是否定义相同的key,没有相同的name就执行,如果就控制台就会给出提示
...
var i = keys.length;
while (i--) {
var key = keys[i];
proxy(vm, "_data", key);
}
//把data里的数据添加发布,订阅Object。defineProperty()
observe(data, true /* asRootData */); 最后添加响应式,后面章节会详细讲解
}
function proxy (target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
};
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}
从代码中我们来看一下initData() 都干了些什么.
- 第一步 获取data数据判断data是函数还是对象(这是个面试题,经常被问到)
- 第二步 获取data的key, props的key, methods的key, 看看是否有相同的 (如下图这样),如果没有相同的key,就遍历data里定义的数据,用Object.defineProperty()添加发布订阅, 如果不熟悉可以先去了解一下Object.defineProperty(), 这里用了_data, 在项目开发中用 this._data.msg 也可以获取到数据
结束语
new vue() 就讲到这里, 下一次我们在继续向下了解,如果有人不清楚,可以在下方评论,可以向我获取视频