[TOC]
1. 概念
Rollup是一个JavaScript模块打包工具,可以将小块代码编译成大块复杂的代码。开发者可以使用ES2015模块和TypeScript,最终打包成一个独立的可运行在浏览器或者Node.js环境的文件。
2. tree-shaking
Rollup能让打包文件体积很小,因为内部使用了tree-shaking机制。
2.1 概念
tree-shaking是能够在模块的层面上做到打包后的代码只包含被引用并被执行的模块,而不被引用或者不被执行的模块被删除掉,以达到减包的效果。
2.2 处理范围
- 只能处理模块级别,消除引用但没有使用的模块,不能处理函数级别的冗余;
- 只能处理JavaScript相关冗余代码,不能处理CSS冗余代码。
2.3 严格模式下不能处理函数级别的冗余的原因
类的新方法可以通过Object.assign()
添加到prototype
对象中,如:
class Person {
public name:String
constructor(name:String) {
this.name = name
}
}
Object.assign(Person.prototype,{
getName() {
console.log(this.name);
},
setName(name:String) {
this.name = name
}
})
console.log(Person.prototype);
在严格模式下,类内部的方法是不可枚举的,因为babel会将类内部的方法编译成Object.defineProperty()。因为识别不到类内部的方法是否存在,也就不能知道究竟有没有用到过类内部的方法,所以tree-shaking不能识别类内部的方法是否未被使用而删除掉。
2.4 module特征
- 只能作为模块顶层的语句出现
- import的模块名只能是字符串常量
- 模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析。
注意:在es6后才开始关注tree-shaking,是因为es6之前的模块化,是通过require引入一个模块,只有运行后才能知道引用了模块里的哪些方法。
import {button,tab} from 'element-ui'
require('elememt-ui')
2.5 例子
singleton/index.ts
var getSingle = function(fn:Function) {
var result:any
return function () {
return result || (result = fn.apply(this,arguments))
}
}
var getName = function(name:String) {
console.log(this.name);
}
export {
getSingle,
getName
}
main.ts
import {getSingle} from './singleton/index'
import {createDiv} from './singleton/demo'
var createSingleDiv = getSingle(createDiv)
createSingleDiv()
编译后的文件
main.min.js
var getSingle = function getSingle(fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
};
};
var createDiv = function createDiv() {
var div = document.createElement('div');
div.innerHTML = '这个是单例模式创建的div';
document.body.appendChild(div);
return div;
};
var createSingleDiv = getSingle(createDiv);
createSingleDiv();
如图所示,未使用的getName方法在打包后的文件中消失了。
2.6 非严格模式下可以处理函数级别的冗余
在非严格模式下,类内部的方法编译后是直接加在原型链上的,变得可枚举,可以被识别并筛选
但是,不建议使用非严格模式。
3 Rollup工程搭建
3.1 工程目录
|--src //ts相关代码文件夹
|--styles//样式相关文件夹
|--src //测试用例相关文件夹
|--.babelrc //babel配置文件
|--jest.config.js //jest配置文件
|--package.json //配置文件
|--rollup.config.js //rollup配置文件
|--tsconfig.json //TypeScript配置文件
|--tslint.json //tslint配置文件
3.2 安装rollup并且创建配置文件
3.2.1 安装rollup
yarn add rollup --save-dev
3.2.2 创建rollup.config.js
export default {
entry: 'src/main.ts',//入口文件
dest: 'build/js/main.min.js',//输出文件
format: 'iife',//输出格式:立即执行函数表达式
sourceMap: 'inline' //代码映射,方便调试
}
3.2.3 配置babel
3.2.3.1 安装插件
yarn add rollup-plugin-babel babel-preset-latest --save-dev
3.2.3.2 作用
用于在旧版浏览器或者环境中将es2015+转换为向后兼容的JavaScript版本。
3.2.3.3 babel过程
1.编译:解析代码生成相同的代码
2.转换:所有语法的转换工作都是由插件完成的
3.生成:生成向后兼容的es5
3.2.3.4 .babelrc
文件
{
"presets": [
["latest", {
"es2015": {
"modules": false
}
}]
],
"plugins": [
"external-helpers"
]
}
3.2.3.5 更新rollup.config.js
文件
import babel from 'rollup-plugin-babel'
export default {
entry: 'src/main.ts',
dest: 'build/js/main.min.js',
format: 'iife',
sourceMap: 'inline',
plugins: [
babel({
exclude: 'node_modules/**',
plugins: ['external-helpers']
})
]
}
3.2.4 配置TypeScript
3.2.4.1 安装TypeScript
yarn add typescript ts-loader rollup-plugin-typescript2 --save-dev
yarn add awesome-typescript-loader --save
3.2.4.2 tsconfig.json
文件
{
"compilerOptions": {
"sourceMap": true, //代码映射
"noImplicitAny": true, //强类型检查
"module": "es2015", //组织代码方式
"target": "es2015", //编译目标平台
"allowJs": true, //允许使用js
"allowSyntheticDefaultImports": true //允许从没有设置默认导出的模块中默认导入。这并不影响代码的显示,仅为了类型检查。
},
"include": [
"./src/**/*"
]
}
3.2.4.3 更新rollup.config.js
文件
import rollupTypescript from 'rollup-plugin-typescript2'
...
plugins: [
...
rollupTypescript(),
]
...
3.2.5 其他插件
3.2.5.1 rollup-plugin-postcss
rollup中集成PostCSS
3.2.5.2 rollup-plugin-node-resolve
解析node_modules中的模块
3.2.5.3 rollup-plugin-commonjs
转换COMMONJS(通过module.export暴露模块接口,通过require引入模块,特点是同步执行)到ES6 Module(export/import)
3.2.5.4 rollup-plugin-serve
提供静态服务器能力
3.2.5.5 rollup-plugin-replace
可在源码中通过process.env.NODE_ENV用于构建区分Development与Production环境
3.2.5.6 rollup-plugin-uglify
压缩bundle文件
3.2.5.7 rollup-watch
实时监听rollup代码更改,当代码发生变化时,会自动编译文件更新内容。
在配置文件中不需要配置该插件,只需要:
"scripts": {
"build": "rollup -c",
"dev": "rollup -c --watch",
"pro": "SET NODE_ENV=production",
"run": "npm run pro && npm run build",
"test": "jest"
},
3.2.6 完整的rollup.config.js
// Rollup plugins
import babel from 'rollup-plugin-babel'
import resolve from 'rollup-plugin-node-resolve' //解析node_modules中的模块
import commonjs from 'rollup-plugin-commonjs'//转换COMMONJS(通过module.export暴露模块接口,通过require引入模块,特点是同步执行)到ES6 Module(export/import)
import serve from "rollup-plugin-serve"//提供静态服务器能力
import replace from 'rollup-plugin-replace'//可在源码中通过process.env.NODE_ENV用于构建区分Development与Production环境
import uglify from 'rollup-plugin-uglify'//压缩bundle文件
import postcss from 'rollup-plugin-postcss'
// PostCSS plugins
import simplevars from 'postcss-simple-vars'
import nested from 'postcss-nested'
import cssnext from 'postcss-cssnext'
import cssnano from 'cssnano'
import rollupTypescript from 'rollup-plugin-typescript2'
export default {
entry: 'src/main.ts',
dest: 'build/js/main.min.js',
format: 'iife',
sourceMap: 'inline',
plugins: [
postcss({
plugins: [
simplevars(),
nested(),
cssnext({
warnForDuplicates: false,
}),
cssnano(),
],
extensions: ['.css','.scss'],
}),
rollupTypescript(),
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
babel({
exclude: 'node_modules/**',
plugins: ['external-helpers']
}),
replace({
exclude: 'node_modules/**',
ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
}),
(process.env.NODE_ENV === 'production' && uglify()),
serve({
// Launch in browser (default: false)
open: true,
// Show server address in console (default: true)
verbose: false,
// Folder to serve files from
contentBase: '',
// Multiple folders to serve from
contentBase: ['build'],
// Set to true to return index.html instead of 404
historyApiFallback: true,
// Options used in setting up server
host: 'localhost',
port: 10001
})
],
}
参考文献
- 如何使用Rollup打包JavaScript
- rollupjs
- TypeScript
- 你的Tree-Shaking并没什么卵用