# Vue主线剧情之build
# scripts/build.js
const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const rollup = require('rollup')
const terser = require('terser')
// 验证或创建dist打包输出目录
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist')
}
// 获取所有的打包项
let builds = require('./config').getAllBuilds()
// 通过命令行构建过滤器
if (process.argv[2]) {
// eg: "npm run build -- web-runtime-cjs,web-server-renderer"
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 {
// 默认情况下过滤掉weex的版本
builds = builds.filter(b => {
return b.output.file.indexOf('weex') === -1
})
}
// 传入过滤后的打包配置项
build(builds)
function build (builds) {
let built = 0
// 打包项的个数
const total = builds.length
// 便于完成这次打包后的下一次打包
const next = () => {
buildEntry(builds[built]).then(() => {
built++
// 是否打包完成
if (built < total) {
next()
}
}).catch(logError)
}
next()
}
// 打包入口(config是单一rollup的打包配置)
function buildEntry (config) {
const output = config.output
const { file, banner } = output
const isProd = /(min|prod)\.js$/.test(file)
return rollup.rollup(config)
.then(bundle => bundle.generate(output))
.then(({ output: [{ code }] }) => {
if (isProd) {
const minified = (banner ? banner + '\n' : '') + terser.minify(code, {
toplevel: true,
output: {
ascii_only: true
},
compress: {
pure_funcs: ['makeMap']
}
}).code
return write(file, minified, true)
} else {
return write(file, code)
}
})
}
// 写入文件
function write (dest, code, zip) {
return new Promise((resolve, reject) => {
function report (extra) {
console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))
resolve()
}
fs.writeFile(dest, code, err => {
if (err) return reject(err)
if (zip) {
zlib.gzip(code, (err, zipped) => {
if (err) return reject(err)
report(' (gzipped: ' + getSize(zipped) + ')')
})
} else {
report()
}
})
})
}
// 换算大小
function getSize (code) {
return (code.length / 1024).toFixed(2) + 'kb'
}
// 打印
function logError (e) {
console.log(e)
}
// ANSI escape code 语法
function blue (str) {
return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
}
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
通过大致的对build的分析,我们看到主要是对 process.argv 和 ./config
这个文件的 getAllBuilds 方法的引用。
# 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)
}
}
// 所有的打包配置
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',
// Vue的版权
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
},
...
}
// 返回name对应的rollup配置项
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
plugins: [
// rollup-plugin-flow-no-whitespace
flow(),
// rollup-plugin-alias
alias(Object.assign({}, aliases, opts.alias))
].concat(opts.plugins || []),
output: {
file: opts.dest,
format: opts.format,
banner: opts.banner,
name: opts.moduleName || 'Vue'
},
onwarn: (msg, warn) => {
if (!/Circular/.test(msg)) {
warn(msg)
}
}
}
// built-in vars
const vars = {
__WEEX__: !!opts.weex,
__WEEX_VERSION__: weexVersion,
__VERSION__: version
}
// feature flags
Object.keys(featureFlags).forEach(key => {
vars[`process.env.${key}`] = featureFlags[key]
})
// build-specific env
if (opts.env) {
vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)
}
config.plugins.push(replace(vars))
if (opts.transpile !== false) {
config.plugins.push(buble())
}
Object.defineProperty(config, '_name', {
enumerable: false,
value: name
})
return config
}
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
用过rollup
的童鞋们,相信你们一眼就抓到了重点,这个config.js
主要的工作就是存储和处理对应参数,从而返回一个rollup的配置。而且仔细阅读config.js
的话还可以了解到Vue通过当前web、server(服务端渲染)、weex、webpack插件等配置来打包。
其中builds的配置项说明:
{
entry: '构建的入口文件',
dest: '构建后的文件地址',
format: '构建使用的规范',
env: '构建环境',
alias: '别名配置',
banner: 'Vue的版权'
}
2
3
4
5
6
7
8
我们注意到里面有一个很有意思的项就是 aliases 这个呢是别名路径。那我们接下来就进入到这个别名的路径中。
# 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')
}
2
3
4
5
6
7
8
9
10
11
12
13
14
可以看到这里很简单,就是返回一个别名和对应的路径的resolve。
# 总结
基本上主线任务的序章就已经结束了,通过package.json的scripts来分析是我们常用的看代码手段。
这部分主要就是通过 npm run build
-> package.json
-> scripts/build.js
-> scripts/config.js
-> scripts/alias.js
-> src目录
这样的流程让我们清晰了打包的每一个过程。
虽然你有这个游戏的大号,知道src目录就是项目的代码目录,可以直接跳过序章看src目录。但是你开局过个序章任务,可以很好的了解这个游戏。
下面我们就进入新手村了,开启新的主线剧情,Vue主线剧情之core/index
# 致谢
感谢大家阅读我的文章,如果对我感兴趣可以点击页面右上角,帮我点个star。
作者:前端小然子
链接: https://xiaoranzife.com/guide/vue/build.js.html
来源:前端小然子的博客
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。