Skip to main content

源码学习 - 构建

构建过程

克隆项目后,来到 package.json 文件中,我们看 script 脚本部分, 有下面这几个 build 命令,执行完 npm run build 命令之后,dist 目录下方会生成很多特殊命名的 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"
}
}

来到 scripts\build.js 里看一下, 这里引用了同级目录下 config.jsgetAllBuilds() 方法

let builds = require("./config").getAllBuilds();

那么,再来到scripts\config.js目录下看一下最后导出的内容

if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET);
} else {
exports.getBuild = genConfig;
exports.getAllBuilds = () => Object.keys(builds).map(genConfig);
}

builds是一个对象,里面存放着各种编译配置项,每一项也是一个对象

  • entry代表入口
  • dest代表目标
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
),
},
};

entrydest都使用了resolve()函数, 那么再看一下这个函数,该函数接收一个参数,截取/前一项,并且去scripts\alias.js中找到对应的映射文件 比如传入参数web/entry-runtime.js,截取到web,在scripts\alias.js中会通过resolve方法当前目录为基础,在上一级目录中,也就是根目录中找到src/platforms/web路径和entry-runtime.js

// 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);
}
};
// scripts\alias.js
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"),
};

构建文件分类

UMDCommonJSES Module
Fullvue.jsvue.common.jsvue.esm.js
Runtime-onlyvue.runtime.jsvue.runtime.common.jsvue.runtime.esm.js
Full(production)vue.min.jsvue.common.prod.js
Runtime-only(production)vue.runtime.min.jsvue.runtime.common.prod.js

vue-build-files1

  • Full:全量包,包含编译器(compiler)运行时(runtime)
  • Compiler: 编译器,负责将模板字符串(类 HTML 语法的模板代码)编译为 JS 的 render 函数
  • Runtime:创建 Vue 实例,渲染函数、patch 虚拟 DOM 等代码
  • UMD:兼容 CommonJS 和 AMD 规范,如通过 CDN 引入的 vue.js 就是 UMD 规范的,包含编译器和运行时
  • CommonJS:如 nodeJS,为了给 browserify 和 webpack1 旧的打包器使用。默认入口文件为 vue.runtime.common.js
  • ES Module: 现代 JS 规范,get webpack2+ 和 rollup 等现代打包器使用,打包器默认使用仅包含运行时的 vue.runtime.esm.js

运行时 runtime + 编译器 compiler VS 仅运行时 (runtime-only)

  • Runtime only

使用 vue-loadervueify时,*.vue文件中的模板在构建时会被编译为 JS 渲染函数,不需要包含编译器的全量包,只需仅包含运行时的包

  • Runtime + Compiler

如果需要动态编译模板,如将字符串模板传递给 template 或 通过提供一个挂载元素的方式编写 HTML 模板则需要编译器

// 需要编译器的版本, 因为最终渲染都是通过 render 函数,如果写 template 属性,则需要编译成 render 函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本
new Vue({
template: "<div>{{ hi }}</div>",
});

// 这种情况不需要
new Vue({
render(h) {
return h("div", this.hi);
},
});

仅运行时的包体积比全量包体积小 30%,全量包需要进行一些配置

webpack

这里的 $ 代表精确匹配, module.rulestest: /\.vue$/$ 是正则的 $,跟 alias$ 不一样

module.exports = {
// ...
resolve: {
alias: {
vue$: "vue/dist/vue.esm.js",
},
},
};

rollup

const alias = require("rollup-plugin-alias");

rollup({
// ...
plugins: [
alias({
vue: "vue/dist/vue.esm.js",
}),
],
});

Browserify

Browserify是一个node.js模块,主要用于改写现有的CommonJS模块,使得浏览器端也可以使用这些模块。在package.json中添加下列代码:

{
// ...
"browser": {
"vue": "vue/dist/vue.common.js"
}
}