Vue.js源码构建
Vue.js源码是基于Roolup够贱的,他的构建相关配置都在scripts目录下。
构建脚本
通常一个基于NPM托管的项目都会有一个package.json文件,他是对项目的描述文件,他的内容实际上是一个标准的JSON对象。
我们通常会配置scripts字段作为NPM的执行脚本,Vue.js源码构建的脚本如下:
{
"scripts":{
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
}
}
这里总共有三条命令,作用都是构建Vue.js,后面两条是在第一条命令的基础上,添加一些配置参数,当在命令行运行npm run build 的时候,实际上就会执行node scripts/build.js接下来我们来看看他实际是怎么构建的。
构建过程
我们对于构建过程分析是基于源码的,先打开构建的入口JS文件,在scripts/build.js文件:
//读取配置
let builds = require('./config').getAllBuilds()
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 {
builds = builds.filter(b => {
return b.output.file.indexOf('weex') === -1
})
}
build(builds)
这段代码逻辑非常简单,先从配置文件读取配置,在通过命令行参数对构建配置做过滤,这样可以构建出不同用途的Vue.js。接下来我们看一下配置文件,在scripts/config.js中:
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.dev.js'),
format: 'cjs',
env: 'development',
banner
},
'web-runtime-cjs-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.prod.js'),
format: 'cjs',
env: 'production',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.dev.js'),
format: 'cjs',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
'web-full-cjs-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.prod.js'),
format: 'cjs',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// Runtime only ES modules build (for bundlers)
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
},
// Runtime+compiler ES modules build (for bundlers)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.js'),
format: 'es',
transpile: false,
env: 'development',
alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.min.js'),
format: 'es',
transpile: false,
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// runtime-only build (Browser)
'web-runtime-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.js'),
format: 'umd',
env: 'development',
banner
},
// runtime-only production build (Browser)
'web-runtime-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.min.js'),
format: 'umd',
env: 'production',
banner
},
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler production build (Browser)
'web-full-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.min.js'),
format: 'umd',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// Web compiler (CommonJS).
'web-compiler': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
},
// Web compiler (UMD for in-browser use).
'web-compiler-browser': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/browser.js'),
format: 'umd',
env: 'development',
moduleName: 'VueTemplateCompiler',
plugins: [node(), cjs()]
},
// Web server renderer (CommonJS).
'web-server-renderer-dev': {
entry: resolve('web/entry-server-renderer.js'),
dest: resolve('packages/vue-server-renderer/build.dev.js'),
format: 'cjs',
env: 'development',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-prod': {
entry: resolve('web/entry-server-renderer.js'),
dest: resolve('packages/vue-server-renderer/build.prod.js'),
format: 'cjs',
env: 'production',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-basic': {
entry: resolve('web/entry-server-basic-renderer.js'),
dest: resolve('packages/vue-server-renderer/basic.js'),
format: 'umd',
env: 'development',
moduleName: 'renderVueComponentToString',
plugins: [node(), cjs()]
},
'web-server-renderer-webpack-server-plugin': {
entry: resolve('server/webpack-plugin/server.js'),
dest: resolve('packages/vue-server-renderer/server-plugin.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-webpack-client-plugin': {
entry: resolve('server/webpack-plugin/client.js'),
dest: resolve('packages/vue-server-renderer/client-plugin.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
// Weex runtime factory
'weex-factory': {
weex: true,
entry: resolve('weex/entry-runtime-factory.js'),
dest: resolve('packages/weex-vue-framework/factory.js'),
format: 'cjs',
plugins: [weexFactoryPlugin]
},
// Weex runtime framework (CommonJS).
'weex-framework': {
weex: true,
entry: resolve('weex/entry-framework.js'),
dest: resolve('packages/weex-vue-framework/index.js'),
format: 'cjs'
},
// Weex compiler (CommonJS). Used by Weex's Webpack loader.
'weex-compiler': {
weex: true,
entry: resolve('weex/entry-compiler.js'),
dest: resolve('packages/weex-template-compiler/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
}
}
这里列举了一些Vue.js构建的配置,关于还有一些服务端渲染webpack插件以及weex的打包配置就不列举了。
对于单个配置,他是遵循了Rollup的构建规则的。其中entry属性表示构建的入口JS文件地址,dest属性表示构建后的JS文件地址。format属性表示构建的格式,cjs表示构建出来的文件遵循CommonJS规范,es表示构建出来的文件遵循ES Model规范。umd表示构建出来的文件遵循UMD规范
以web-runtime-cjs配置为例,它的entry是resolve('web/entry-runtime.js'),先来看一下resolve函数的定义。
源码目录:scripts/config.js
const aliases = require('./alias')
const resolve = p => {
const base = p.split('/')[0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
这里的resolve函数非常简单,他先把resolve函数传递的参数p通过/做分割,分割成数组,然后取数组第一个元素设置为base。在我们这个例子中,参数p是web/entry-runtime.js,那么base则为web。base并不是实际路径,它的真实借助了别名的配置,我们来看一下别名配置的代码,在scripts/alias中:
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
compiler: resolve('src/compiler'),
core: resolve('src/core'),
shared: resolve('src/shared'),
web: resolve('src/platforms/web'),
weex: resolve('src/platforms/weex'),
server: resolve('src/server'),
sfc: resolve('src/sfc')
}
很显然,这里web对应的真实的路径是path.resolve(__dirname,'..src/platforms/web'),这个路径就找到了Vue.js源码的web目录。然后resolve函数通过path.resolve(aliases[base]),p.slice(base.length + 1)找到了最终路径,他是Vue.js源码web目录下的entry-runtime.js。因此,web-run-cjs配置对应的入口文件就找到了。他经过Rollup的构建打包后,最终会在dist目录下生成vue.runtime.common.js。
Runtime Only Vs Runtime+Cimpiler
通常我们利用vue-cli去初始化我们的Vue.js项目的时候会询问我们用Runtime Only 版本的还是Runtime+Compiler版本。下面我们来对比这两个版本
- Runtime Only
我们通常使用Runtime Only 版本的Vue.js的时候,通常需要借助如webpack的vue-loader工具吧.vue文件编译成JavaScript,因为是在编译阶段做的,所以他只包含运行时的Vue.js代码,因此代码体也更轻量。
- Rumtime +Compiler
我们如果没有对代码做预编译,但又实用了Vue的template属性并传入一个字符串,则需要在客户端编译模板,如下所示:
// 需要编译的版本
new Vue({
template: "<div>{{h1}}</div>"
})
// 这种情况不需要编译
new Vue({
render (h) {
return h('div', this.hi)
}
})
因为在Vue.js2.0中,最终渲染都是通过render函数,如果写了template属性,则需要编译成render函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本。
很显然,这个编译过程对性能会有一定的损耗,所以通常我们更推荐使用Runtime-Only的Vue.js。
总结
通常这一节的分析,我们可以了解到Vue.js的构建打包过程,也知道了不同作用和功能的Vue.js他们最终编译生成的JS文件,尽管在实际开发中我们会使用Runtime Only 版本开发比较多。