rollup.js

rollup.js是一个JavaScript模块打包器(Bundler),可将小块代码编译成大块复杂的代码。

  • 专注于ES6模块打包

模块化

rollup.js对代码模块化使用了ES6新的标准化格式,而非之前特殊的解决方案,比如CommonJS和AMD。

模块化 描述 加载
IIFE 自执行函数 通过<script>标签加载
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模块系统中的静态结构特性,比如importexport。静态结构的import类似变量引用,无需执行代码,编译时即可确定它是否引用到。若没有引用则不会将该段代码打包进来。

工作原理

rollup.js可将自己编写的JavaScript代码与第三方模块打包在一起形成一个文件,该文件可以是一个库(Library)或一个应用(App),打包过程中可应用各类插件实现特定功能。

Rollup打包原理

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.jsonmain字段去查找入口文件。

从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用于指定模块解析策略,拥有两种可选策略nodeclassic
  • 模块解析是指编译器在查找导入模块内容时所遵循的流程

编译器会尝试定位导入模块的文件,编译器会遵循两种策略之一: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声明文件。
  • declarationallowJs选项不能同时设置为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)中开启allowJsresolveJsonModule两个选项。

$ 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.jsts-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 <filename> 要导报的文件
-o --file <output> 输出的文件,若无则直接输出到控制台。
-f --format <format> 输出的文件类型(amd、cjs、esm、life、umd)
-e --external <ids> 将模块ID的逗号分割列表排除
-g --globals <pairs> module ID:Global键值对形式
-n --name <name> 生成UMD模块的名字
-h --help 输出帮助信息
-m --sourcemap 生成SourceMap信息
--amd.id - AMD模块的ID,默认为匿名函数。
--amd.define - 使用Function来代替define
-w --watch 监视文件打包与重新打包时的变化

自定义脚本命令

  • NPM允许在package.jsonscripts字段中自定义脚本命令
  • 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"}})
    ]
}

配置入口文件打包后输出文件,打包时可指定生成包的格式,打包后可通过<script>标签引入,也可通过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模块文件,现代浏览器可通过<script type="module">标签引入。
iife 自执行函数,适用于<script>标签。
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设置keywordrollup-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函数
        })
    ]
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容