我们来看一看 vue 是使用了 Rollup 进行打包项目的。通过 npm 创建一个项目,然后创建 script.js 或者 index.js 文件,来作为可执行的脚步文件,首先我们需要引入所需要库。
今天我们就 vue 打包的源码分析来看一看 rollup 在 vue 中应用。
const fs = require('fs')
const path = require('path')
const rollup = require('rollup')
const terser = require('terser')
fs 和 path 是 nodejs 提供有关文件操作和路径解析的包, rollup 和 terser 是用于构建打包项目库,当然这里 rollup 使我们今天主角。如果不存在 dist 文件夹我们就创建一个。
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist')
}
接下来读取 config 这个配置文件,
let builds = require('./config').getAllBuilds()
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
//通常我们会调用这个
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
这里输出 getAllBuilds 是返回一个方法,方法Object.keys 获取对象的属性集合,
var foo = {name:'koo',age:23}
undefined
Object.keys(foo)
(2) ["name", "age"]
Object.keys(foo).map(function(item){ console.log(foo[item])})
VM493:1 koo
VM493:1 23
用 genConfig 为每个要打包module输出配置
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
plugins: [
flow(),
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
}
这里主要是返回一个 rollup 在构建项目所用的配置文件,简单地解释一下 config 各个属性,
input 输入文件
plugins 插件,vue 开始使用 flow 来作为自己类型系统的,alias 方法
-i, --input 要打包的文件(必须)
-o, --output.file 输出的文件 (如果没有这个参数,则直接输出到控制台)
-f, --output.format [es] 输出的文件类型 (amd, cjs, es, iife, umd)
-e, --external 将模块ID的逗号分隔列表排除
-g, --globals 以module ID:Global
键值对的形式,用逗号分隔开
任何定义在这里模块ID定义添加到外部依赖
-n, --name 生成UMD模块的名字
-m, --sourcemap 生成 sourcemap (-m inline
for inline map)
--amd.id AMD模块的ID,默认是个匿名函数
--amd.define 使用Function来代替define
--no-strict 在生成的包中省略"use strict";
--no-conflict 对于UMD模块来说,给全局变量生成一个无冲突的方法
--intro 在打包好的文件的块的内部(wrapper内部)的最顶部插入一段内容
--outro 在打包好的文件的块的内部(wrapper内部)的最底部插入一段内容
--banner 在打包好的文件的块的外部(wrapper外部)的最顶部插入一段内容
--footer 在打包好的文件的块的外部(wrapper外部)的最底部插入一段内容
--interop 包含公共的模块(这个选项是默认添加的用
Object.defineProperty
将 name 作为 _name 属性写入到 config 对象中并且为不可遍历。
根据 name 获取 opts, 就是 web-runtime-cjs-dev
属性值,opts 对象提供压缩web-runtime-cjs-dev
基本信息。我们可以将每个属性和上面函数进行对应。
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/zi.runtime.common.dev.js'),
format: 'cjs',
env: 'development',
banner
}
}
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)
}
}
alias 文件
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
web: resolve('src/platforms/web'),
}
build 函数接受 builds 作为参数通过回调中再次调用自己来完成异步执行完所有的构建任务。
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()
}
最后看看 buildEntry 方法,我们这里调用 rollup 成功后构建项目代码通过 primise 来获取,然后就可以对处理好的代码进行一些写入附加信息等操作。
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)
}
})
}