Vue源码阅读(一)—— 源码目录及构建
Vue简介
Vue.js官方文档的介绍是这样的:
Vue.js是什么
Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页应用提供驱动。
源码目录
Vue.js的源码都在./src
目录下,使用tree
命令查看目录结构
1 | ./src |
可以看到,源码主要包含六个部分:
compiler
该目录包含Vue.js编译相关的代码,包括把模板解析成AST语法树,AST语法树优化,代码生成等功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22./src/compiler
├── codeframe.js
├── codegen # 把AST转换成Render函数
│ ├── events.js
│ └── index.js
├── create-compiler.js
├── directives
│ ├── bind.js
│ ├── index.js
│ ├── model.js
│ └── on.js
├── error-detector.js
├── helpers.js
├── index.js
├── optimizer.js
├── parser # 把模板解析成AST
│ ├── entity-decoder.js
│ ├── filter-parser.js
│ ├── html-parser.js
│ ├── index.js
│ └── text-parser.js
└── to-function.jscore
该目录包含了Vue.js的核心代码,包括内置组件、全局API封装,Vue实例化、观察者、虚拟DOM、工具函数等等
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47./src/core
├── components # 组件相关,主要是Keep-Alive
│ ├── index.js
│ └── keep-alive.js
├── config.js
├── global-api # Vue全局API
│ ├── assets.js
│ ├── extend.js
│ ├── index.js
│ ├── mixin.js
│ └── use.js
├── index.js
├── instance # 实例化相关,生命周期、事件等
│ ├── events.js
│ ├── index.js
│ ├── init.js
│ ├── inject.js
│ ├── lifecycle.js
│ ├── proxy.js
│ ├── render-helpers
│ ├── render.js
│ └── state.js
├── observer # 观察者,响应式相关
│ ├── array.js
│ ├── dep.js
│ ├── index.js
│ ├── scheduler.js
│ ├── traverse.js
│ └── watcher.js
├── util # 工具方法
│ ├── debug.js
│ ├── env.js
│ ├── error.js
│ ├── index.js
│ ├── lang.js
│ ├── next-tick.js
│ ├── options.js
│ ├── perf.js
│ └── props.js
└── vdom # 虚拟dom
├── create-component.js
├── create-element.js
├── create-functional-component.js
├── helpers
├── modules
├── patch.js
└── vnode.jsplatforms
Vue.js是一个跨平台的MVVM框架,它可以跑在web上,也可以配合weex跑在native客户端上
platforms是Vue.js的入口,2个目录分别代表2个主要入口,分别打包成运行在web上和weex上的Vue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18./src/platforms
├── web
│ ├── compiler
│ ├── entry-compiler.js
│ ├── entry-runtime-with-compiler.js
│ ├── entry-runtime.js
│ ├── entry-server-basic-renderer.js
│ ├── entry-server-renderer.js
│ ├── runtime
│ ├── server
│ └── util
└── weex
├── compiler
├── entry-compiler.js
├── entry-framework.js
├── entry-runtime-factory.js
├── runtime
└── utilserver
Vue.js 2.0支持了服务端渲染,所有服务端渲染相关的逻辑都在这个目录下, 这部分代码是跑在服务端的Node.js
服务端渲染主要的工作是把组件渲染为服务器端的HTML字符串,将它们直接发送到浏览器,最后将静态标记”混合”为客户端上完全交互的应用程序
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
27./src/server
├── bundle-renderer
│ ├── create-bundle-renderer.js
│ ├── create-bundle-runner.js
│ └── source-map-support.js
├── create-basic-renderer.js
├── create-renderer.js
├── optimizing-compiler
│ ├── codegen.js
│ ├── index.js
│ ├── modules.js
│ ├── optimizer.js
│ └── runtime-helpers.js
├── render-context.js
├── render-stream.js
├── render.js
├── template-renderer
│ ├── create-async-file-mapper.js
│ ├── index.js
│ ├── parse-template.js
│ └── template-stream.js
├── util.js
├── webpack-plugin
│ ├── client.js
│ ├── server.js
│ └── util.js
└── write.jssfc
把*.vue文件内容解析成一个JavaScript的对象
1
2./src/sfc
└── parser.jsshared
Vue.js会定义一些工具方法,这里定义的工具方法都是会被浏览器端的Vue.js和服务端的Vue.js所共享的
1
2
3./src/shared
├── constants.js
└── util.js
源码构建
Vue源码是基于Rollup
构建的,相关配置都在./scripts
目录下
1 | ./scripts |
构建脚本
通常会在./package.json
中配置scripts
作为npm的执行脚本
1 | { |
当在命令行中执行npm run build
时,实际上是在执行node scripts/build.js
构建过程
打开./scripts/build.js
,其中主要的部分是以下这段代码:
1 | let builds = require('./config').getAllBuilds() |
这段代码的逻辑是:先从./scripts/config.js
中通过getAllBuilds()
读取参数,在通过命令行参数对参数进行过滤,最后build(builds)
在./scripts/config.js
中,配置遵循Rollup的构建规则
其中entry
属性表示构建的入口JS文件地址,dest
属性表示构建后的JS文件地址,format
属性表示构建的格式,cjs
表示构建出来的文件遵循CommonJS
规范,es
表示构建出来的文件遵循ES Module
规范,umd
表示构建出来的文件遵循UMD
规范
1 | const builds = { |
各个配置可以通过resolve
函数找到真实路径,resolve
定义在./scripts/config.js
中
1 | const aliases = require('./alias') |
resolve
函数传入参数p
,并通过/
分割成数组,取数组第一个元素设置为base
,然后调用path.resolve
,传入aliases
中键名为base
的项以及p
中base
之后的部分
在./scripts/alias.js
中
1 | const resolve = p => path.resolve(__dirname, '../', p) |
以./scripts/config.js
中的entry: resolve('web/entry-runtime.js')
来看
参数
p
在分割后生成的base
为web
,在./scripts/alias.js
中web
对应的真实路径是path.resolve(__dirname, '../', 'src/platforms/web)
通过
path.resolve(aliases[base], p.slice(base.length + 1))
,最终对应的就是./src/platforms/web
目录下的entry-runtime.js