- https://www.rollupjs.com/
rollup.js是一个JavaScript模块打包器(Bundler),可将小块代码编译成大块复杂的代码。
- 专注于ES6模块打包
模块化
rollup.js对代码模块化使用了ES6新的标准化格式,而非之前特殊的解决方案,比如CommonJS和AMD。
模块化 | 描述 | 加载 |
---|---|---|
IIFE | 自执行函数 | 通过 标签加载 |
AMD | 浏览器(Browser)的模块规范 | 通过RequireJS 加载 |
CommonJS | Node服务端模块规范 | 通过Webpack 加载 |
UMD | 兼容IIFE、AMD、CJS三种模块规范 | - |
ESM | ES2015 Module规范 | 可用Webpack、Rollup加载 |
与Webpack偏向于应用打包定位不同,rollup.js更专注于JavaScript类库打包。Webpack对于代码分割和静态资源导入有着先天优势,并支持热模块替换(HRM)。rollup.js不支持代码拆分(Code Splitting)和运行时态加载(Dynamic Import)特性。
开发应用时可优先选择Webpack,rollup.js对于代码的Tree-shaking和ES6模块有着算法优势上的支持,若项目只需要打包一个简单的bundle包,并基于ES6模块开发则优先考虑使用rollup.js。其实Webpack2.x开始支持Tree-Shaking,并在使用babel-loader的情况下可支持ES6 Module的打包。
特性优势
- Tree Shaking:自动移除未使用的代码,输出更小的文件。
- Scope Hoisting:所有模块构建在一个函数内,执行效率更高。
- Config:配置文件支持通过ESM模块格式书写
- 一次输出多种格式
- 文档精简
Tree-shaking
Tree-shaking指的是移除JavaScript上下文中未引用代码,它依赖于ES2015模块系统中的静态结构特性,比如import
和export
。静态结构的import
类似变量引用,无需执行代码,编译时即可确定它是否引用到。若没有引用则不会将该段代码打包进来。
工作原理
rollup.js可将自己编写的JavaScript代码与第三方模块打包在一起形成一个文件,该文件可以是一个库(Library)或一个应用(App),打包过程中可应用各类插件实现特定功能。
rollup.js默认采用ES模块标准,可通过rollup-plugin-commonjs
插件是指支持CommonJS标准。
环境检测
rollup.js依赖于Node.js
$ node -v
v16.0.0
$ npm -v
7.10.0
全局安装rollup.js
$ npm view rollup
$ npm i -g rollup
环境搭建
创建项目
$ mkdir tsr && cd tsr
初始化Node.js项目,生成package.json
项目依赖包配置文件。
$ npm init -y
项目目录结构
文件 | 描述 |
---|---|
src | 源文件目录 |
dist | 编译后的文件目录 |
package.json | Node.js项目依赖包配置文件 |
tsconfig.json | TypeScript配置文件,设置TypeScript编译选项。 |
package.json
Node.js中的模块(Module)是一个库或框架,同时也是一个Node.js项目。Node.js项目遵循模块化的架构,因此创建并初始化一个Node.js项目实际上也创建了一个模块,此模块的描述文件即package.json
,又称为项目的依赖包管理文件。
修改包配置文件package.json
$ vim package.json
{
"name": "tsr",
"version": "1.0.0",
"main": "lib/index.js",
"module": "lib/index.esm.js",
"browser": "lib/index.umd.js",
"license": "MIT",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
},
"devDependencies": {
"@types/node": "^15.0.2",
"rollup": "^2.47.0",
"rollup-plugin-cleandir": "^1.0.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.30.0",
"typescript": "^4.2.4"
}
}
属性 | 描述 |
---|---|
name | 包名 |
version | 包的版本号 |
description | 包的描述信息 |
homepage | 包的官网URL |
author | 包的作者 |
contributors | 包的贡献者 |
dependencies | 生产环境依赖包列表,安装在node_modules目录下。 |
devDependencies | 开发环境依赖包列表,安装在node_modules目录下。 |
repository | 包代码的仓库信息,包括type和URL。 |
bin | 指定内部命令对应的可执行文件的位置 |
入口文件
$ vim package.json
{
"main": "lib/index.js",
"module": "lib/index.esm.js",
"browser": "lib/index.umd.js",
"typings": "types/index.d.ts",
}
属性 | 描述 | 限制 |
---|---|---|
main | CommonJS入口文件 | browser环境和node环境均可使用 |
module | 定义NPM包ESM规范的入口文件 | browser环境和node环境均可使用 |
browser | 定义NPM包在browser环境下的入口文件 | 仅browser环境可用 |
typings | TypeScript入口文件 | IDE环境可用 |
"main": "lib/index.js"
main
包的主入口文件地址,定义引用依赖的文件地址,不同环境下引入包时会加载main
字段中指定的文件,默认是模块根目录下的index.js
文件。
"module": "lib/index.esm.js"
Rollup最早提出了pkg.module
的概念,早期NPM包都是基于CommonJS规范,当require("package")
时会根据package.json
中main
字段去查找入口文件。
从ES2015开始,JavaScript拥有了ES Module,相较于之前的模块化方案更为优雅,ESM也是官方标准的JS规范。CommonJS模块化是一种特殊的传统格式,在ESM提出前作为暂时的解决方案。由于CommonJS规范的包都是以main
字段表示入口文件,ESM如果也使用main
字段会对使用者造成困扰,因此Rollup使用了另一个字段module
。
"typings": "types/index.d.ts",
-
typings
字段是为了方便IDE识别、编辑、智能提示JavaScript语法的工具。 -
typings
入口文件中的代码只是为编辑器智能提示而服务,真正执行程序并不会使用。
TypeScript
项目安装TypeScript
$ npm i -D typescript
$ npm ls typescript
tsr@1.0.0 F:\TS\project\tsr
└── typescript@4.2.4
$ tsc --version
Version 4.2.4
tsconfig.json
初始化生成TypeScript编译器配置文件,当某目录下存在tsconfig.json
文件则认为该目录为TypeScript项目的根目录。
$ tsc --init
message TS6071: Successfully created a tsconfig.json file.
tsconfig.json
配置文件主要分为两部分:指定待编译文件和定义编译选项
$ vim tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom","esnext"],
"module": "esnext",
"moduleResolution": "node",
"declaration": false,
"declarationMap": false,
"sourceMap": true,
"outDir": "./lib/",
"removeComments": true,
"strict": true,
"noImplicitAny": false,
"baseUrl": "./",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"resolveJsonModule": true
},
"include": ["src"]
}
编译选项compilerOptions
"target": "esnext"
-
target
用于指定编译后的JavaScript目标版本 -
esnext
是一个JavaScript库,可将ES6草案规范语法转换为当前JavaScript语法。
"lib": ["dom","esnext"]
lib
选项表示编译过程中需引入的库文件,指定要包含在编译中的库文件。
库文件 | 描述 |
---|---|
dom | DOM运行环境 |
esnext | ES6转换为ES5的环境 |
若lib
没有指定默认注入的库的列表则默认注入的库为
编译目标(target) | 注入库(lib) |
---|---|
ES5 | DOM,ES5,ScriptHost |
ES6 | DOM,ES6,DOM.Iterable,ScriptHost |
"module": "esnext"
-
module
选项用于指定模块化规范,即生成哪种模块系统代码。
模块系统 | 描述 |
---|---|
None | - |
CommonJS | - |
AMD | - |
System | - |
UMD | - |
ES6 | - |
ES2015 | - |
"moduleResolution": "node"
-
moduleResolution
用于指定模块解析策略,拥有两种可选策略node
和classic
。 - 模块解析是指编译器在查找导入模块内容时所遵循的流程
编译器会尝试定位导入模块的文件,编译器会遵循两种策略之一:Classic和Node,这些策略会告知编译器到哪里去查找目标模块。
import Entry from "./components/Entry"
import { DefaultHeaders } from "../constants/http"
import * as $ from "jQuery"
import { Component } from "@angular/core"
解析策略 | 描述 |
---|---|
classic | TS之前默认的解析策略,为向后兼容而保留。 |
node | 运行时模仿Node.js模块解析机制 |
"declaration": true
-
declaration
选项用于指定是否在编译时生成相应地*.d.ts
声明文件,若为true
则表示编译每个.ts
文件会自动生成一个.js
文件和一个*.d.ts
声明文件。 -
declaration
和allowJs
选项不能同时设置为true
。
"declarationMap": false
-
declarationMap
选项用于指定.ts
文件编译时是否为其声明文件.d.ts
生成.d.ts.map
文件
"sourceMap": false
-
sourceMap
选项用于指定编译.ts
文件时是否生成.map
文件
@types/node
在Node.js中搭建TypeScript开发环境后,才能使用TypeScript开发Node.js项目。
TypeScript中无法直接使用Node.js内置模块和第三方模块,无法直接在TypeScript文件中导入模块。根据TypeScript自身机制,需要*.d.ts
的声明文件,来说明模块对外公开的方法和属性类型及内容。
TypeScript2.x以上获取类型声明文件只需使用NPM安装@types/node
插件即可实现
tsconfig.json
配置中通过lib
指定TypeScript环境为["dom", "esnext]
表示采用Node.js环境,此时不存在Node.js环境,需安装@types/node
插件来支持。
$ npm i -D @types/node
安装类型声明后即可直接在.ts
文件中导入Node.js内置模块
$ vim rollup.config.ts
import * as path from "path";
如何在TypeScript中导入本地JSON文件呢?需在tsconfig.json
配置文件的编译器选项(compilerOptions)中开启allowJs
和resolveJsonModule
两个选项。
$ vim tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"resolveJsonModule": true
}
}
编译选项 | 默认值 | 描述 |
---|---|---|
allowJs | false | 是否能在TypeScript文件中引入JavaScript库 |
checkJs | false | 是否检查JavaScript代码合法性 |
resolveJsonModule | false | 是否可以在TypeScript中导入JSON Module |
ts-node
普通运行TypeScript需先通过tsc
命令将.ts
文件编译为.js
文件后才能使用node
命令运行node xxx.js
,ts-node
包装了node
可直接运行TypeScript代码,使用ts-node
仅需ts-node xxx.ts
即可直接运行TypeScript文件。
$ npm i -g ts-node
例如:使用ts-node
命令执行TypeScript文件
$ ts-node index.ts
Rollup
项目安装rollup打包工具
$ npm i -D rollup
$ npm ls rollup
tsr@1.0.0 F:\TS\project\tsr
└── rollup@2.47.0
$ rollup -v
rollup v2.47.0
$ rollup --help
rollup version 2.47.0
命令参数 | 完整参数 | 描述 |
---|---|---|
-i | --input |
要导报的文件 |
-o | --file | 输出的文件,若无则直接输出到控制台。 |
-f | --format |
输出的文件类型(amd、cjs、esm、life、umd) |
-e | --external |
将模块ID的逗号分割列表排除 |
-g | --globals |
以module ID:Global 键值对形式 |
-n | --name |
生成UMD模块的名字 |
-h | --help | 输出帮助信息 |
-m | --sourcemap | 生成SourceMap信息 |
--amd.id | - | AMD模块的ID,默认为匿名函数。 |
--amd.define | - | 使用Function来代替define |
-w | --watch | 监视文件打包与重新打包时的变化 |
自定义脚本命令
- NPM允许在
package.json
的scripts
字段中自定义脚本命令 -
scripts
字段是一个对象,每个属性对应一条脚本。 - 自定义脚本命令使用
npm run
执行脚本
$ vim package.json
{
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
}
}
rollup.config.js
Rollup的配置文件是可选的,位于根目录下的rollup.config.js
,是一个ES6模块对外暴露一个对象。
$ vim rollup.config.js
import * as path from "path";
import {cleandir} from "rollup-plugin-cleandir";
import typescript from "rollup-plugin-typescript2";
import commonjs from "rollup-plugin-commonjs";
import nodeResolve from "rollup-plugin-node-resolve";
import * as pkg from "./package.json"
import * as tsconfig from "./tsconfig.json"
const outDir = path.join(__dirname, tsconfig.compilerOptions.outDir)
//配置规则
export default {
//入口文件
input:path.join(__dirname, "src/main.ts"),
//输出文件
output:[
//输出CommonJS规范的代码
{format:"cjs", name:pkg.name, file:path.join(outDir, "index.js")},
//输出ESM规范的代码
{format:"esm", name:pkg.name, file:path.join(outDir, "index.esm.js")}
],
//配置插件
plugins:[
//自动读取tsconfig.json
typescript(),
//自动清除文件夹
cleandir(outDir),
//配置Rollup支持CommonJS规范用以识别CommonJS规范的依赖
commonjs(),
//解析node_modules中CommonJS规范的第三方模块
nodeResolve({customResolveOptions:{moduleDirectory:"node_modules"}})
]
}
配置入口文件打包后输出文件,打包时可指定生成包的格式,打包后可通过标签引入,也可通过
import
等方式引入作为JavaScript库使用。
Rollup配置选项
核心 | 默认值 | 描述 | 命令 |
---|---|---|---|
input | "/src/main.ts" | 设置包的入口点 | -i/--input |
output | [] | 设置待写入的输出文件列表,可用于生成sourcemap。 | - |
输出文件选项output
输出选项 | 默认值 | 描述 | 命令 |
---|---|---|---|
format | cjs | 生成包的格式 | -f/--output.format |
name | "" | 生成包的名称 | -n/--name |
file | "" | 待写入的文件,也可用生成sourcemap | -o/--output.file |
sourceMap | false | 是否生成SourceMap文件 | - |
{format:"cjs", name:pkg.name, file:dist("index.js"), sourceMap:false}
生成包的格式format
格式 | 描述 |
---|---|
amd | 异步模块定义,用于类似RequireJS这样的模块加载器。 |
cjs | CommonJS,适用于Node.js和Browserify/Webpack打包工具。 |
ems | 将软件包保存为ES模块文件,现代浏览器可通过 标签引入。 |
iife | 自执行函数,适用于 标签。 |
umd | 通用模块定义,以AMD、CommonJS、IIFE为一体。 |
system | SystemJS加载器格式 |
例如:为不同模块化规范输出不同的打包文件
$ vim rollup.config.js
import * as path from "path";
import * as pkg from "./package.json"
const dist = filename => filename!=null ? path.join(path.join(__dirname, "dist"), filename) : path.join(__dirname, "dist")
//配置规则
export default {
//输出文件
output:[
//输出CommonJS规范的代码
{format:"cjs", name:pkg.name, file:dist("index.js"), sourceMap:false},
//输出ESM规范的代码
{format:"es", name:pkg.name, file:dist("index.esm.js"), sourceMap:false}
]
}
使用Rollup的配置文件
$ rollup -c
$ rollup --config
$ rollup --config rollup.config.js
插件
Rollup的插件提供了统一的标准接口,通过约定大于配置定义公共配置,注入当前构造结果相关的属性和方法,供开发者实现增删改查操作。
一个Rollup插件是一个导出了一个函数的包,函数返回了一个对象,对象拥有遵循特定规范的属性和钩子。
- 插件名称必须以
rollup-plugin-
开头 - 在
package.json
设置keyword
为rollup-plugin
- 插件应被测试,推荐采用mocha和ava。
- 尽可能使用异步方法
搜索插件:https://github.com/rollup/awesome
插件 | 描述 |
---|---|
rollup | 核心包 |
typescript2 | 让Rollup识别TypeScript |
buble | 类似babel工具但比babel更轻 |
commonjs | 将CommonJS转换为ES6模块 |
json | 将JSON文件转换为ES6模块 |
node-resolve | 让Rollup能够识别node_modules中的包,引入第三方库默认是无法识别的。 |
terser | 代码压缩,代码最小化打包。 |
filesize | 显示打包出来的文件大小 |
sourcemaps | 生成sourcemaps文件 |
cleandir | 文件夹清除 |
rollup-plugin-typescript2
$ npm i -D rollup-plugin-typescript2
rollup-plugin-commonjs
NPM中大多数包是以CommonJS模块的形式出现的,使用前需将CommonJS模块转换为ES2015模块供Rollup处理。
rollup-plugin-commonjs应该用在其它插件转换自定义模块之前,以防止其他插件改变破坏CommonJS的检查。
$ npm i -D rollup-plugin-commonjs
rollup-plugin-node-resolve
rollup-plugin-node-resolve插件用于告知Rollup如何查找外部模块
$ npm i -D rollup-plugin-node-resolve
rollup-plugin-buble
buble
插件作用是在`rollup.js打包过程中进行代码编译,将ES6+代码编译称为ES2015标准。
Node.js项目中安装buble插件
$ npm i -D rollup-plugin-buble
$ npm ls buble
tsr@1.0.0 F:\TS\project\tsr
└─┬ rollup-plugin-buble@0.19.8
└── buble@0.19.8
Rollup配置插件
$ vim rollup.config.js
import buble from "rollup-plugin-buble"
//配置规则
export default {
//配置插件
plugins:[
//将ES6+代码编译成ES2015
buble()
]
}
rollup-plugin-alias
alias
插件提供为模块起别名的功能
项目中安装alias
插件
$ npm i -D rollup-plugin-alias
为项目添加插件配置
$ vim rollup.config.js
import alias from "rollup-plugin-alias"
//获取绝对路径
const pathResolve = p => path.resolve(__dirname, p)
//配置规则
export default {
//配置插件
plugins:[
//为模块起别名
alias({"@":pathResolve("src")})
]
}
提供pathResolve
函数用于生成绝对路径,引入alias
插件需传入一个对象作为参数,对象的key
是模块中使用的别名,对象的value
是别名对应的真实离苦精。
rollup-plugin-flow-no-whitespace
flow
插件用于在rollup.js打包过程中清除flow
类型检查部分的代码
rollup-plugin-replace
replace
插件的作用是在Rollup打包时动态地替换代码中的内容
$ npm i -D rollup-plugin-replace
rollup-plugin-terser
terser
插件用于在Rollup打包过程中实现代码压缩代码实现最小化打包,支持ES模块。
$ npm i -D rollup-plugin-terser
配置
$ vim rollup.config.js
import {terser} from "rollup-plugin-terser";
//配置规则
export default {
//配置插件
plugins:[
//代码压缩
terser({
output:{ascii_only:true},//仅输出ASCII字符
compress:{pure_funcs:["console.log"]}//去除console.log函数
})
]
}