Vue2源码阅读-介绍

ObjectKaz Lv4

从拷源码开始

首先,想搞清楚 Vue2 的源码,至少要把源码拷下来吧:

1
git clone --branch v2.6.14 https://github.com/vuejs/vue.git

我们这里选用的是最新版本 2.6.14 的源代码。

目录结构简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
vue
├── dist # 构建后的文件
├── examples # 例子
├── flow # flow 类型声明相关
├── packages
│ ├── vue-server-renderer
│ ├── vue-template-compiler
│ ├── weex-template-compiler
│ └── weex-vue-framework
├── README.md
├── scripts
├── src
│ ├── compiler # 模板编译器
│ ├── core # 核心代码,与平台无关
│ │ ├─observe # 变化侦听
│ │ ├─vdom # 虚拟DOM相关代码
│ │ ├─instance # Vue 实例相关代码
│ │ ├─global-api # 全局API相关代码
│ │ └─components # 内置组件相关代码
│ ├── platforms # 面向不同平台,不同环境的入口文件
│ ├── server # 服务器渲染相关代码
│ ├── sfc # 单文件组件
│ └── shared # 共享工具库
├── test # 测试代码
├── types # 类型声明文件

我们学习 Vue2 源码最重要的是理解它的核心逻辑,所以我们最重要的是关注 src/coresrc/complier 这两个文件夹。

在阅读源码的过程中,我们只需要搞清楚它的核心逻辑和流程,没有必要去纠结于过于细节以及和平台相关的东西。

寻找入口

Vue 的核心入口文件就在 src/core/index.js,如果不想看寻找过程的可以跳过

拿到一个开源项目,首先就是找到它的入口文件。通过 dist 目录我们可以发现,它产出了很多的文件。这意味着 Vue 本身是有很多个入口的。

我们现在可以简要的看看像 scripts 目录和 package.json,通过这两块来寻找它的入口。

先看 package.json,寻找 build 相关的命令:

1
2
3
4
5
{
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex"
}

还是那句话,我们暂时不考虑与平台相关的。很明显,build:ssrbuild:weex 这两个是和平台有关的(一个服务端渲染,一个移动端框架),暂时不管了,我们就看第一个。

从这段代码我们就发现了 script/build.js 这个文件,我们立马打开一看。点击打开这个文件

可能这个文件有点复杂,但是我们看到这个文件里面有一个直接调用的 build 函数:

1
build(builds)

那么 builds 是从哪儿来的呢?往上一看:

1
let builds = require('./config').getAllBuilds()

接下来我们来打开 config.js,迅速找到 getAllBuilds点击跳转

可以看到:

1
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)

这个代码就是对 builds 这个对象进行了一个简单的遍历操作。

所以接下来看 builds点击跳转

一切豁然开朗了,所有的构建目标似乎都在这里了。

我们以第一个目标为例:

1
2
3
4
5
6
7
8
9
{
'web-runtime-cjs-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.dev.js'),
format: 'cjs',
env: 'development',
banner
}
}

entry 就是它的入口位置。

但是很不巧的是,这里的路径似乎都是一些相对路径,不过我们可以使用 vscode 的文件搜索(Ctrl+P)来找到 web/entry-runtime.js:点击跳转

这个文件的代码非常简单,就是直接把 Vue./runtime/index 引入又导出了:

1
2
3
4
5
/* @flow */

import Vue from './runtime/index'

export default Vue

接下来我们找到 ./runtime/index点击跳转

可以看到第一行代码就是:

1
import Vue from 'core/index'

仔细观察文件可以发现其后面的一些工作都是做一些适配、提示等辅助性的工作。最后导出也是这个 Vue

所以,我们接下来打开/src/core/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue

结果这似乎仍然在做一些辅助性的工作,我们接着打开/src/core/instance/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}

this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

终于看到 Vue 的定义了,这就是入口文件。

  • 标题: Vue2源码阅读-介绍
  • 作者: ObjectKaz
  • 创建于: 2021-09-25 12:18:15
  • 更新于: 2022-03-15 13:53:26
  • 链接: https://www.objectkaz.cn/820a20ca5fc9.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
Vue2源码阅读-介绍