开始看Vue的源码,参考版本:v2.5.17-beta.0
目录结构
Vue.js的源码在项目的src目录下,目录结构如下
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 平台相关的代码
├── server # 服务端渲染
├── sfc # 单文件组件
├── shared # 共享代码
- compiler: Vue编译相关的代码,用来将模版编译成render函数。如果传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 内部的 HTML 作为模板,编译的过程会发生在客户端,这时需要使用带编译器的版本。如果使用webpack、vue-loader这些构建工具,编译就会在构建时完成。
- core: Vue的核心代码,之后要重点分析的地方
- platforms: 平台相关的代码,Vue.js可以运行在web上,也可以配合weex运行在原生移动应用上,plaforms目录中的web和weex两个子目录分别包含对应平台下特定的代码
- server: 服务端渲染相关的逻辑
- sfc: 解析.vue文件的代码
- shared: 主要包含一些工具方法
Vue.js的源码构建
Vue.js 的源码构建使用了Rollup。
Rollup是一个Javascript模块打包器,可以将小块代码编译成大块复杂的代码
通过package.json
中的scripts可以看到, 执行npm run build
的时候实际会执行scripts/build.js
这个脚本,构建不同的版本会传入不同的参数
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
构建过程
在scripts/build.js
中,首先:
let builds = require('./config').getAllBuilds()
// filter builds via command line arg
if (process.argv[2]) {
const filters = process.argv[2].split(',')
builds = builds.filter(b => {
return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
})
} else {
// filter out weex builds by default
builds = builds.filter(b => {
return b.output.file.indexOf('weex') === -1
})
}
build(builds)
这段代码读取scripts/config.js
中的配置,然后根据命令行参数过滤配置,然后调用build
函数
再看scripts/config.js
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format: 'cjs',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.js'),
format: 'cjs',
alias: { he: './entity-decoder' },
banner
},
// Runtime only (ES Modules). Used by bundlers that support ES Modules,
// e.g. Rollup & Webpack 2
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
},
// Runtime+compiler CommonJS build (ES Modules)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
...
}
主要定义了很多构建的配置,包括带compiler和不带compiler, dev和prod版本,weex和server版本。其中entry
表示入口文件的位置;dest
表示构建后生成的文件地址;format
表示构建的格式,即不同的模块规范,cjs
表示CommonJS,‘es’表示ES Module,umd
表示UMD规范,
resovle
函数将参数转换为真实的路径
build
函数,对每一项配置调用buildEntry
:
function build (builds) {
let built = 0
const total = builds.length
const next = () => {
buildEntry(builds[built]).then(() => {
built++
if (built < total) {
next()
}
}).catch(logError)
}
next()
}
buildEntry()
函数:
function buildEntry (config) {
const output = config.output
const { file, banner } = output
const isProd = /min\.js$/.test(file)
return rollup.rollup(config)
.then(bundle => bundle.generate(output))
.then(({ code }) => {
if (isProd) {
var minified = (banner ? banner + '\n' : '') + uglify.minify(code, {
output: {
ascii_only: true
},
compress: {
pure_funcs: ['makeMap']
}
}).code
return write(file, minified, true)
} else {
return write(file, code)
}
})
}
buildEntry()
函数调用rollup.rollup
, 返回一个Promise,得到一个bundle
对象,调用bundle.generate
得到code和sourcemap, 然后调用write
写入到文件系统,如果是production版本,先用uglify压缩代码,加上文件头部的注释信息banner
入口
各个版本的入口文件:
src
├─platforms
├─ web
├─ entry-compiler.js
├─ entry-runtime-with-compilers.js
├─ entry-runtime.js
├─ entry-server-basic-renderer.js
├─ entry-server-renderer.js