相关文档

Vue技术内幕
github-learn-vue
滴滴-vue技术内幕
7个vue源码解析文章
掘金解读

调试准备

克隆工程

  1. git clone https://github.com/vuejs/vue.git
  2. ### 没装 rollup 执行全局安装
  3. npm i -g rollup
  4. ### 安装依赖
  5. npm install
  6. ### 修改package.json dev 脚本命令 添加sourcemap 以供调试
  7. "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
  8. ### 执行 npm run dev
  9. npm run dev
  10. ### 即可以看到dist 目录下 vue.js 有改动

在examples目录下创建test.html 并引入vue.js
image.png
调试小技巧

  1. ctrl+p 搜索源文件
  2. 进入函数 setp-into-function f11
  3. 函数调用栈

image.png

Vue构造函数的定义

通过断点不难发现其文件路径在http://localhost:8000/src/core/instance/index.js

本文所涉及到的http://localhost:8000 均可替换成vue路径,因为是在vue目录下启动的服务

image.png
下面通过打包方式来定位到Vue函数定义所在处:

  1. 根据打包命令的TARGET值:web-full-dev 定位到
  2. image.png
  3. 根据resolve函数定位到web其实是src/platforms/web的别名
  4. image.png
  5. 因此入口文件在vue\src\platforms\web\entry-runtime-with-compiler.js
  6. 根据上述 文件不难发现

image.png

  1. 顺藤摸瓜 后我 们可以查看最终Vue的路径在vue\src\core\instance\index.js
  2. 整个Vue的引用顺序可以总结如下:

  3. vue\src\core\instance\index.js

  4. vue\src\core\index.js
  5. vue\src\platforms\web\runtime\index.js
  6. vue\src\platforms\web\entry-runtime-with-compiler.js

在这四个文件我们可以发现其实在new Vue之前 已经调用了许多内部方法,并在Vue上添加了静态方法以及在Vue.prototype 上添加了许多属性和方法,我们可以通过引用打包后的Vue文件并在控制台上输出
image.png
除了函数的prototype 方法是函数固有属性,因此Vue一共给Vue.prototype 添加了36个属性和方法,其中有4个不可枚举的属性 ‘$data’, ‘$props’, ‘$isServer’, ‘$ssrContext’

image.png

Vue的静态方法总共有 17个 其中包含2个不可枚举的属性:config 和 FunctionalRenderContext

那这些方法在哪里定义和挂载的呢

首先看vue\src\core\instance\index.js
image.png
调用了五个方法

  1. initMixin(Vue) src/core/instance/init.js

定义Vue.prototype._init

  1. stateMixin(Vue) src/core/instance/state.js

定义Vue.prototype.$data
定义Vue.prototype.$props
定义Vue.prototype.$set
定义Vue.prototype.$delete
定义Vue.prototype.$swatch
image.png

  1. eventsMixin src/core/instance/events.js

定义Vue.prototype.$on
定义Vue.prototype.$once
定义Vue.prototype.$off
定义Vue.prototype.$emit
image.png

  1. lifecycleMixin src/core/instance/lifecycle.js

定义Vue.prototype._update
定义Vue.prototype.$forceUpdate
定义Vue.prototype.$destroy
image.png

  1. renderMixin src/core/instance/render.js

定义Vue.prototype.$nextTick
定义Vue.prototype._render
调用 installRenderHelpers 函数: 添加_o 等 17个方法
image.png
image.png
至此我们在vue\src\core\instance\index.js 已经在原型上定义了 1+5+4+3+19 = 32个方法

接着往下走第二个文件:vue\src\core\index.js
定义了Vue.prototype.$isServer
定义了Vue.prototype.$ssrContext 32+2 = 34 总共36个 还差两个

定义了Vue.version
定义了Vue.FunctionalRenderContext
image.png
调用了initGlobalAPI : src/core/global-api/index.js
定义了全局API Vue.config
定义了全局API Vue.util
定义了全局API Vue.set
定义了全局API Vue.del
定义了全局API Vue.nextTick
定义了全局API Vue.observable
定义了全局API Vue.options // 同时定义了Vue.options.component / Vue.options.directive / Vue.options.filter / Vue.options._base Vue.options.component 挂载了keep-alive transition transition-group
定义了全局API Vue.use // src/core/global-api/use.js
定义了全局API Vue.mixin // src/core/global-api/mixin.js
定义了全局API Vue.extend // src/core/global-api/extend.js
定义了全局API Vue.cid // src/core/global-api/extend.js
// src/core/global-api/assets.js
定义了全局API Vue.component
定义了全局API Vue.directive
定义了全局API Vue.filter

//—-总共定义了16个Vue的静态方法
image.png
接着往下走第三个文件:vue\src\platforms\web\runtime\index.js
定义了Vue.prototype.patch
定义了Vue.prototype.$mount
——- 至此Vue.protytype上的全部36个方法定义完毕—-
image.png
最后一个文件:vue\src\platforms\web\entry-runtime-with-compiler.js
定义了Vue.compile
并重写了Vue.prototype.$mount
image.png

new Vue 做了哪些事情

调用了this._init
image.png
initRender 定义了两个重要函数
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

initState 重要 数据响应式入口
image.png
initData
image.png
observe(data, true / asRootData /)
重要函数observe data 即是我们用户手动传入的data
image.png
Object.isExtensible表示给定对象是否可扩展的一个Boolean 这里可以做优化就是我们无需给他加做响应式处理
接下来最重要的一点就是Observer 类了
ob = new Observer(value)
image.png
看看构造函数做了哪些事情
假设传入的为data 则
假设Observer类的实例是ob
ob.value = data
ob.dep = new Dep()
data.ob=ob
可以下看下Dep类
image.png

然后调用了this.walk(value)

image.png

image.png
对对象的每一个key 值都生成一个Dep类, 并递归如果对象key值依然是对象 那么继续对他做响应式处理

———————- end—————————

来看下vue\src\core\instance\index.js做了哪些事情
image.png
不难发现,new Vue就调用了this._init(options)。但在此之前,已经做了5个初始化的函数。

来看 initMixin(Vue)做了什么

image.png
在这里定义了_init函数,我们继续看此函数内部做了什么事情,也就是我们newVue内部调用的函数

image.png
经历了一系列init函数,这里暂时不讨论,最终判断如果有options里有el元素则会手动调用vm.$mount(vm.$options.el)

首先要看下$mount定义在什么地方
image.png
不难 发现,最后的定义的地方在入口文件,但是有一个引用代码const mount = Vue.prototype.$mount 那这个$mount在哪定义的,在vue\src\platforms\web\runtime\index.js
image.png
让我们继续回到入口文件的$mount方法,因为在_init函数里我们有el元素逻辑走到此处,最终会走到
image.png
而这里的mount函数即是我们的vue\src\platforms\web\runtime\index.js内定义的
image.png