相关文档
Vue技术内幕
github-learn-vue
滴滴-vue技术内幕
7个vue源码解析文章
掘金解读
调试准备
克隆工程
git clone https://github.com/vuejs/vue.git
### 没装 rollup 执行全局安装
npm i -g rollup
### 安装依赖
npm install
### 修改package.json dev 脚本命令 添加sourcemap 以供调试
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
### 执行 npm run dev
npm run dev
### 即可以看到dist 目录下 vue.js 有改动
在examples目录下创建test.html 并引入vue.js
调试小技巧
- ctrl+p 搜索源文件
- 进入函数 setp-into-function f11
- 函数调用栈
Vue构造函数的定义
通过断点不难发现其文件路径在http://localhost:8000/src/core/instance/index.js
本文所涉及到的http://localhost:8000 均可替换成vue路径,因为是在vue目录下启动的服务
下面通过打包方式来定位到Vue函数定义所在处:
- 根据打包命令的TARGET值:web-full-dev 定位到
- 根据resolve函数定位到web其实是src/platforms/web的别名
- 因此入口文件在vue\src\platforms\web\entry-runtime-with-compiler.js
- 根据上述 文件不难发现
- 顺藤摸瓜 后我 们可以查看最终Vue的路径在vue\src\core\instance\index.js
整个Vue的引用顺序可以总结如下:
vue\src\core\instance\index.js
- vue\src\core\index.js
- vue\src\platforms\web\runtime\index.js
- vue\src\platforms\web\entry-runtime-with-compiler.js
在这四个文件我们可以发现其实在new Vue之前 已经调用了许多内部方法,并在Vue上添加了静态方法以及在Vue.prototype 上添加了许多属性和方法,我们可以通过引用打包后的Vue文件并在控制台上输出
除了函数的prototype 方法是函数固有属性,因此Vue一共给Vue.prototype 添加了36个属性和方法,其中有4个不可枚举的属性 ‘$data’, ‘$props’, ‘$isServer’, ‘$ssrContext’
Vue的静态方法总共有 17个 其中包含2个不可枚举的属性:config 和 FunctionalRenderContext
那这些方法在哪里定义和挂载的呢
首先看vue\src\core\instance\index.js
调用了五个方法
- initMixin(Vue) src/core/instance/init.js
定义Vue.prototype._init
- stateMixin(Vue) src/core/instance/state.js
定义Vue.prototype.$data
定义Vue.prototype.$props
定义Vue.prototype.$set
定义Vue.prototype.$delete
定义Vue.prototype.$swatch
- eventsMixin src/core/instance/events.js
定义Vue.prototype.$on
定义Vue.prototype.$once
定义Vue.prototype.$off
定义Vue.prototype.$emit
- lifecycleMixin src/core/instance/lifecycle.js
定义Vue.prototype._update
定义Vue.prototype.$forceUpdate
定义Vue.prototype.$destroy
- renderMixin src/core/instance/render.js
定义Vue.prototype.$nextTick
定义Vue.prototype._render
调用 installRenderHelpers 函数: 添加_o 等 17个方法
至此我们在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
调用了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的静态方法
接着往下走第三个文件:vue\src\platforms\web\runtime\index.js
定义了Vue.prototype.patch
定义了Vue.prototype.$mount
——- 至此Vue.protytype上的全部36个方法定义完毕—-
最后一个文件:vue\src\platforms\web\entry-runtime-with-compiler.js
定义了Vue.compile
并重写了Vue.prototype.$mount
new Vue 做了哪些事情
调用了this._init
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 重要 数据响应式入口
initData
observe(data, true / asRootData /)
重要函数observe data 即是我们用户手动传入的data
Object.isExtensible表示给定对象是否可扩展的一个Boolean 这里可以做优化就是我们无需给他加做响应式处理
接下来最重要的一点就是Observer 类了
ob = new Observer(value)
看看构造函数做了哪些事情
假设传入的为data 则
假设Observer类的实例是ob
ob.value = data
ob.dep = new Dep()
data.ob=ob
可以下看下Dep类
然后调用了this.walk(value)
对对象的每一个key 值都生成一个Dep类, 并递归如果对象key值依然是对象 那么继续对他做响应式处理
———————- end—————————
来看下vue\src\core\instance\index.js做了哪些事情
不难发现,new Vue就调用了this._init(options)。但在此之前,已经做了5个初始化的函数。
来看 initMixin(Vue)做了什么
在这里定义了_init函数,我们继续看此函数内部做了什么事情,也就是我们newVue内部调用的函数
经历了一系列init函数,这里暂时不讨论,最终判断如果有options里有el元素则会手动调用vm.$mount(vm.$options.el)
首先要看下$mount定义在什么地方
不难 发现,最后的定义的地方在入口文件,但是有一个引用代码const mount = Vue.prototype.$mount 那这个$mount在哪定义的,在vue\src\platforms\web\runtime\index.js
让我们继续回到入口文件的$mount方法,因为在_init函数里我们有el元素逻辑走到此处,最终会走到
而这里的mount函数即是我们的vue\src\platforms\web\runtime\index.js内定义的