rollup简介
首先,rollup.js是一个JavaScript 模块打包器。
能够将咱们本人编写的js代码与第三方模块打包在一起,也能够将小块代码编译成大块简单的代码,例如 library 或应用程序。
rollup间接反对tree shaking只有ES模块才反对,在打包构建时,会对编译的代码进行动态剖析,打包后果只蕴含应用到的代码,这样能够大幅精简代码量。
和咱们相熟的webpack相比,它更专一于js类库打包,而webpack更偏差于利用打包。
Rollup根本利用
从0应用Rollup实现一个根底应用案例, 全程应用npm操作,yarn同理(论团队对立npm和yarn以及对应版本对立的重要性)
初始化操作
npm i rollup -g
mkdir roolup-demo
cd roolup-demo
npm init
mkdir src
vim src/name.js
// i 输出
const name = 'Ada'
export default name
// esc :wq
vim src/main.js
// 输出
import name from './name.js'
export default function() {
console.log(name)
}
预览打包后源码
rollup src/main.js -f es
输入
解释一下命令参数
-f –format的缩写,代表生成代码的格局,amd就是AMD规范,cjs是commonjs,es就是es规范
上面咱们输入一下构建后的文件
rollup src/main.js -f es -o dist/output.js
-o 等同output.file
输入文件:
再来输入一个commonjs格调文件
rollup src/main.js --format cjs --o dist/output-cjs.js
test
$ node
> const fn = require('./dist/index-es.js')
> fn()
Ada
拓展node 不反对ES规范 如何解决
所以默认以上代码应用node执行es版本会报错,咱们能够应用babel-node解决
装置如下
npm i @babel/core @babel/node @babel/cli -g
而后创立babel的配置文件.babelrc
touch .babelrc
// 内容
{
"presets": ["@babel/preset-env"]
}
装置依赖
npm i -D @babel/core @babel/preset-env
而后通过babel编译代码
babel dist/output.js
$ babel-node
> require('./dist/output.js')
{ default: [Function: main] }
> require('./dist/output.js').default()
Ada
这里babel认为default是function name
上面再介绍一下rollup.js配置文件
// 创立配置文件
touch rollup.config.js
内容如下
export default {
input: './src/main.js',
output: [{
file: './dist/index-cjs.js',
format: 'cjs',
banner: '// cjs banner',
footer: '// cjs footer'
}, {
file: './dist/index-es.js',
format: 'es',
banner: '// es test banner',
footer: '// es test footer'
}]
}
rollup的配置文件阐明
rollup的配置文件须要采纳ES模块规范编写
input示意入口文件的门路
output示意输入文件的内容:
- output.file:输入文件的门路(老版本为dest,曾经废除)
- output.format:输入文件的格局
- output.banner:文件头部增加的内容
- output.footer:文件开端增加的内容
有了配置文件就能够间接应用rollup -c进行打包了
最初咱们来实现一个简略地build代码
npm i -D rollup
touch rollup-build.js
touch rollup-input.js
touch rollup-output.js
// 内容
// input
module.exports = {
input: './src/main.js'
}
// output
module.exports = [{
file: './dist/index-cjs.js',
format: 'cjs',
banner: '// hello',
footer: '// bye'
},
{
file: './dist/index-es.js',
format: 'es',
banner: '// hello A',
footer: '// bye A'
},{
file: './dist/index-amd.js',
format: 'amd',
banner: '// hello B',
footer: '// bye B'
},
{
file: './dist/index-umd.js',
format: 'umd',
name: 'umd',
banner: '// hello C',
footer: '// bye C'
}]
// build
// 通过rollup.rollup(input)获取输出
// 通过bundle.write(output)输入文件
const rollup = require('rollup')
const inputOptions = require('./rollup-input')
const outputOptions = require('./rollup-output')
async function rollupBuild(input, output) {
const bundle = await rollup.rollup(input) // 依据input配置进行打包
console.log(`current:${output.file}`)
await bundle.write(output) // 依据output配置输入文件
console.log(`${output.file} success!`)
}
(async function () {
for (let i = 0; i < outputOptions.length; i++) {
await rollupBuild(inputOptions, outputOptions[i])
}
})()
执行测试一下
node rollup-build.js
根底拓展:watch 监听文件变动主动build
touch rollup-watch-build.js
touch rollup-watch.js
module.exports = {
include: 'src/**', // 监听的文件夹
exclude: 'node_modules/**' // 排除监听的文件夹
}
watch build
const rollup = require('rollup')
const inputOptions = require('./rollup-input')
const outputOptions = require('./rollup-output')
const watchOptions = require('./rollup-watch')
const options = {
...inputOptions,
output: outputOptions,
watchOptions
}
const watcher = rollup.watch(options) // 调用rollup的api启动监听
watcher.on('event', event => {
console.log('从新打包...', event.code)
})
这样当咱们批改src目录文件后就会主动从新打包
平时咱们也能够脚本间接写 rollup -wc 命令
rollup插件扫盲 不便浏览vue构建源码
插件 | 形容 |
---|---|
resolve插件 | 集成内部模块 |
commonjs插件 | 反对CommonJS模块 |
babel插件 | 编译ES6语法,兼容低版本浏览器 |
commonjs插件 | 反对CommonJS模块 |
uglify插件 | 代码最小化打包 |
json插件 | 反对json |
插件的应用都很简略,大家能够按需进行尝试
上面咱们进入vue build局部,去剖析一下在vue源码中build做了那些事。
vue build 剖析
间接从源码动手,以下代码外围局部已削减对应备注。
1.首先将下载源码到本地装置依赖
git clone https://github.com/vuejs/vue.git"
cd vuejs
npm i
2.咱们来看一下 package.json 咱们以后须要关怀的局部
"scripts": {
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex"
...
},
"devDependencies": {
"rollup": "^1.0.0",
"rollup-plugin-alias": "^1.3.1",
"rollup-plugin-buble": "^0.19.6",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-flow-no-whitespace": "^1.0.0",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-replace": "^2.0.0",
...
}
由依赖能够看出vue有应用rollup
上面咱们依据 build scripts去看一下构建脚本都做了什么事
3.scripts/build.js
正文曾经退出源码之中 咱们间接来看源码 一个惯例vue源码构建咱们次要看以下三个文件就能够 build.js config.js alias.js
build 执行大抵能够分为以下5局部
以下为build.js 源码请联合正文进行查看
const fs = require('fs') // 文件解决
const path = require('path') // 本地门路解析
const zlib = require('zlib') // gzip压缩
const rollup = require('rollup') // 打包工具
const terser = require('terser') // 代码压缩
// 1、创立dist目录
// 判断dist 是否存在 不存在进行创立
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist')
}
// 2、通过 config 生成rollup配置
let builds = require('./config').getAllBuilds()
// 3、rollup配置文件过滤。
// 依据传入的参数,对rollup配置文件的内容进行过滤,排除不必要的打包我的项目
// filter builds via command line arg
console.log('process.argv', red(process.argv))
if (process.argv[2]) {
const filters = process.argv[2].split(',')
console.log('filters', red(filters))
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
})
}
// 4.rollup打包
build(builds)
function build (builds) {
let built = 0 // 以后打包项序号
const total = builds.length // // 须要打包的总次数
const next = () => {
buildEntry(builds[built]).then(() => {
built++
if (built < total) {
next() // 如果打包序号小于打包总次数,则继续执行next()函数
}
}).catch(logError)
}
next()
}
// 打包执行外围函数
function buildEntry (config) {
// 获取rollup 配置信息
const output = config.output
const { file, banner } = output
const isProd = /(min|prod)\.js$/.test(file) //是否压缩 min结尾 标识
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)
}
})
}
// 5. 文件输入 zlib.gzip压缩
// dest 门路 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 本义码 \x1b[(文字装璜);(颜色代码):
function blue (str) {
return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
}
// 同理咱们实现一个 输出红色文本
function red (str) {
return '\x1B[31m' + str + '\x1B[39m'
}
config.js 选取局部内容
const path = require('path') // 本地门路解析
const buble = require('rollup-plugin-buble') // es6+ 语法转为es5
const alias = require('rollup-plugin-alias') // 替换模块别名
const cjs = require('rollup-plugin-commonjs') // 反对commonjs模块
const replace = require('rollup-plugin-replace') // 替换代码中变量为指定值
const node = require('rollup-plugin-node-resolve') //内部模块集成
const flow = require('rollup-plugin-flow-no-whitespace') // 去除flow动态类型查看
const version = process.env.VERSION || require('../package.json').version // 获取以后版本
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
// weex版本 Weex 是一款轻量级的挪动端跨平台动态性技术解决方案 The Weex podling retired on 2021-05-14
const featureFlags = require('./feature-flags') // 增加2个构建常量
// 打包后 banner
const banner =
'/*!\n' +
` * Vue.js v${version}\n` +
` * (c) 2014-${new Date().getFullYear()} Evan You\n` +
' * Released under the MIT License.\n' +
' */'
// 打包weex-factory应用
const weexFactoryPlugin = {
intro () {
return 'module.exports = function weexFactory (exports, document) {'
},
outro () {
return '}'
}
}
const aliases = require('./alias') // 别名及其对应的绝对路径
const resolve = p => {
const base = p.split('/')[0]
// aliases 匹配 name是否存在
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
// 不存在则合并根门路和传入
return path.resolve(__dirname, '../', p)
}
}
// build 配置
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
},
...
}
// 依据name生成对应环境的rollup配置
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
})
// console.log('vars', vars)
// console.log('config', JSON.stringify(config, null, 2))
return config
}
// 判断环境变量TARGET是否定义 存在即输入
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
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')
}
以上就是本次分享的内容,心愿大家能够一步一步跟着去操作,进行实际。
发表回复