本文使用的是vue2.6.9版本,大家可以去全球最大的同性交友网站(github)下载~,链接附上
github.com/vuejs/vue/t…
调试vue项目的方式
- 安装依赖:npm i
- 安装打包工具:vue是用rollup来进行打包的,所以我们直接在控制台安装 npm i rollup -g
- 修改package.json里面的dev脚本(加入了sourcemap):
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
- 执行打包:npm run dev (打包完成后,就可以ctrl + c 关掉了,毕竟我们也不需要修改源码)
- 测试样例:在 examples 文件夹下的内容,都是测试样式,只需要将index.html中引入的vue.min.js 文件修改成我们打包出来的 vue.js 文件,就可以啦
整体启动顺序(主线任务):
如何找到项目入口
根据 package.json 中scripts的dev命令可知,TARGET:web-full-dev
我们全局搜索 web-full-dev,可以发现入口是在 scripts\config.js文件中

scripts\config.js
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'), // 入口
dest: resolve('dist/vue.js'), // 出口
format: 'umd', // 使用umd方式打包
env: 'development', // 开发环境
alias: { he: './entity-decoder' }, // 别名
banner
}
很明显,我们真正的入口就在这里啦:web/entry-runtime-with-compiler.js
web平台入口,挂载$mount
src\platforms\web\entry-runtime-with-compiler.js
我们来看看vue在这里做了什么(只讲解关键内容)
import Vue from './runtime/index'
这个文件中并没有真正的Vue构造函数,可以看到这里在引入Vue。
往下看,可以看到这个文件最大的作用其实是实现了$mount

注:Vue使用的是flow语法,这是一种类似ts的语法
接下来我们观察一下$mount的关键流程

通过上述代码,我们可以得知,在 new Vue() 这个操作时,我们赋予vue模板的配置项优先级为:render -> template -> el
指令和组件的扩展
src\platforms\web\runtime\index.js

我们可以看到,这里又在导Vue了,说明这个文件依旧不是Vue的构造函数
来看看这个文件做了什么事情~

可以发现,这里配置并挂载了一些特定平台的工具,然后扩展了一些平台运行时指令和组件
初始化全局API
src\core\index.js

可以看到第一行,这里又在导Vue,说明这里依旧不是Vue的构造函数。
这个文件很短,主要作用就是对全局API做了初始化,我们可以进去看看做了它哪些工作
全局API初始化
src\core\global-api\index.js

这里挂载了我们常用的一些全局方法:Vue.set、Vue.delete、Vue.nextTick

以及下面还有一些初始化:Vue.use、Vue.mixin、Vue.extend
initAssetRegisters的作用:initGlobalAPI通过initAssetRegisters()进行组件注册
到达Vue真正的构造函数
src\core\instance\index.js

至此我们终于到达了Vue真正的构造函数,这里使用this._init 对Vue进行初始化。
可以看到,这里还做了一些关键的操作:
- initMixin(Vue)
- stateMixin(Vue)
- eventsMixin(Vue)
- lifecycleMixin(Vue)
- renderMixin(Vue)
initMixin:初始化created之前的内容
src\core\instance\init.js
在initMixin函数中,可以看到,它做了非常多特别重要的事情

看到这里的 initState(vm) 在 callHook(vm, 'created') 的前面,也就可以知道为什么最早可以操作data中数据的钩子函数是 "created" 了
initLifecycle
src\core\instance\lifecycle.js
我们进入到 initLifecycle ,来看看这个函数做了哪些工作

实际上它就是在vm实例上挂载了一下 $parent和 $root,然后初始化了一下 $children 和 $refs,下面那些 _ 开头的参数,是不希望用户使用的(Vue内部需要使用)
initEvents
src\core\instance\events.js

拿到 listeners 后,执行 updateComponentListeners(vm, listeners) 方法:
来看看 updateComponentListeners 这个方法

它实际上就是执行了 updateListeners 这个方法
对与 updateListeners 方法以后再进行补充说明
initRender:初始化render函数
src\core\instance\render.js

在 initRender 方法中,为Vue的实例添加了几个属性值,最后定义了 $attrs 和 $listeners的监听方法。
想要了解createElement实现过程的同学,可以从这里进去看看。我这里不再详细叙述了(以后可能会更新)
initInjections:初始化 inject
src\core\instance\inject.js
我们先了解一个概念:provide/inject,这个是Vue在2.2.0版本新增的一个属性,按照Vue官网的说法,它的作用是:
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
该方法在初始化 data/props 之前被调用,主要作用是初始化Vue实例的inject。

我们来看第一行代码:
const result = resolveInject(vm.$options.inject, vm)
首先就是一个对 inject 进行处理的方法 resolveInject,让我们来看下这个方法的实现:

回到initInjections方法。
可以看到从provide中找到了inject的值之后,接下来就是对result的一些处理。可以得知result中的值是响应式的,但是在非生产环境会报出警告(说明Vue是不建议这里使用他的响应式的,想要修改Provide中的值,最好是调用Provide传入的方法)
initState:初始化状态
src\core\instance\state.js

可以看到这里做了很多关键信息的初始化:props、methods、data、computed、watch
其中Vue的响应式原理,Vue的监听方式,都可以知道是怎么处理的。我这里先不详细叙述。(以后补充)
initProvide:初始化provide
src\core\instance\inject.js

可以看到 initProvide 中基本没有什么内容,就是将 $options 里的provide赋值到当前实例上。