轻笑Chuckle
外观

学习笔记

Rollup

一个小巧强大的ESM打包器

6 分钟 2,224 字 次浏览 条评论

初识

Rollup是一个小巧强大的ESM打包器,Vite在生产环境下的打包基于Rollup

Rollup并不与webpack竞争,初衷是提供一个充分利用ESM各项特性的高效打包器,并不支持如HMR等特性,不能像Webpack那样几乎可以完成开发过程中,前端工程化的绝大多数工作。

所以Rollup偏向应用于JS库,webpack偏向应用于前端工程、UI库。

安装:

# 全局安装
npm i -g rollup
# 本地安装
npm i rollup -D

npx rollup不加任何参数,默认输出help信息

命令行参数:

-c, --config <filename> 使用此配置文件(如果使用参数但未指定值,则默认为 rollup.config.js)
-d, --dir <dirname> 用于块的目录(如果不存在,则打印到 stdout)
-e, --external <ids> 排除模块 ID 的逗号分隔列表
-f, --format <format> 输出类型(amd、cjs、es、iife、umd、system)
-g, --globals <pairs> `moduleID:Global` 对的逗号分隔列表
-h, --help 显示此帮助消息
-i, --input <filename> 输入(替代 <entry file>)
-m, --sourcemap 生成源映射(`-m inline` 为内联映射)
-n, --name <name> UMD 导出的名称
-o, --file <output> 单个输出文件(如果不存在,则打印到 stdout)
-p, --plugin <plugin> 使用指定的插件(可重复)
-v, --version 显示版本号
-w, --watch 监视产物文件并在更改时重新构建
--amd.autoId 基于块名称生成 AMD ID
--amd.basePath <prefix> 要预先添加到自动生成的 AMD ID 的路径
--amd.define <name> 在 `define` 位置使用的函数
--amd.forceJsExtensionForImports 在 AMD 导入中使用 `.js` 扩展名
--amd.id <id> AMD 模块的 ID(默认为匿名)
--assetFileNames <pattern> 发布资源的名称模式
--banner <text> 在产物顶部插入的代码(位于包装器之外)
--chunkFileNames <pattern> 发布次要块的名称模式
--compact 缩小包装器代码
--context <variable> 指定顶级 `this` 值
--no-dynamicImportInCjs 将外部动态 CommonJS 导入编写为 require
--entryFileNames <pattern> 发布入口块的名称模式
--environment <values> 传递给配置文件的设置(请参阅示例)
--no-esModule 不添加 __esModule 属性
--exports <mode> 指定导出模式(auto、default、named、none)
--extend 扩展由 --name 定义的全局变量
--no-externalImportAssertions 在 "es" 输出中省略导入断言
--no-externalLiveBindings 不生成支持实时绑定的代码
--failAfterWarnings 如果生成的构建产生警告,则退出并显示错误
--filterLogs <filter> 过滤日志信息
--footer <text> 在产物底部插入的代码(位于包装器之外)
--no-freeze 不冻结命名空间对象
--generatedCode <preset> 使用哪些代码特性(es5/es2015)
--generatedCode.arrowFunctions 在生成的代码中使用箭头函数
--generatedCode.constBindings 在生成的代码中使用 "const"
--generatedCode.objectShorthand 在生成的代码中使用简写属性
--no-generatedCode.reservedNamesAsProps 始终引用保留名称作为 props
--generatedCode.symbols 在生成的代码中使用符号
--no-hoistTransitiveImports 不将中转导入提升到入口块中
--no-indent 不缩进结果
--inlineDynamicImports 使用动态导入时创建单次打包
--no-interop 不包括交互操作块
--intro <text> 在产物顶部插入的代码(位于包装器内部)
--logLevel <level> 要显示哪种类型的日志
--no-makeAbsoluteExternalsRelative 不规范化外部导入
--maxParallelFileOps <value> 并行读取的文件数
--minifyInternalExports 强制或禁用内部导出的缩小
--noConflict 为 UMD 全局生成 noConflict 方法
--outro <text> 在产物底部插入的代码(位于包装器内部)
--perf 显示性能计时
--no-preserveEntrySignatures 避免入口点的门面块
--preserveModules 保留模块结构
--preserveModulesRoot 将保留的模块放置在根路径下的此路径下
--preserveSymlinks 解析文件时不要跟随符号链接
--no-sanitizeFileName 不要替换文件名中的无效字符
--shimMissingExports 为丢失的导出创建卡扣变量
--silent 不打印警告
--sourcemapBaseUrl <url> 使用给定的基本 URL 发出绝对源映射 URL
--sourcemapExcludeSources 在源映射中不包括源代码
--sourcemapFile <file> 指定源映射的包位置
--sourcemapFileNames <pattern> 编译后 sourcemap 的命名模式
--stdin=ext 指定用于标准输入的文件扩展名
--no-stdin 不要从 stdin 读取 "-"
--no-strict 不在生成的模块中发出 `"use strict";`
--strictDeprecations 抛出有关不推荐使用的功能的错误
--no-systemNullSetters 不要将空的 SystemJS setter 替换为 `null`
--no-treeshake 禁用除屑优化
--no-treeshake.annotations 忽略纯调用注释
--treeshake.correctVarValueBeforeDeclaration 在声明之前将变量取消优化
--treeshake.manualPureFunctions <names> 手动将函数声明为纯函数
--no-treeshake.moduleSideEffects 假设模块没有副作用
--no-treeshake.propertyReadSideEffects 忽略属性访问副作用
--no-treeshake.tryCatchDeoptimization 不要关闭 try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects 假设未知的全局变量不会抛出异常
--validate 验证输出
--waitForBundleInput 等待打包输入文件
--watch.buildDelay <number> 节流观察重建
--no-watch.clearScreen 重建时不要清除屏幕
--watch.exclude <files> 排除要观察的文件
--watch.include <files> 限制观察到指定文件
--watch.onBundleEnd <cmd> 在 "BUNDLE_END" 事件上运行的 Shell 命令
--watch.onBundleStart <cmd> 在 "BUNDLE_START" 事件上运行的 Shell 命令
--watch.onEnd <cmd> 在 "END" 事件上运行的 Shell 命令
--watch.onError <cmd> 在 "ERROR" 事件上运行的 Shell 命令
--watch.onStart <cmd> 在 "START" 事件上运行的 Shell 命令
--watch.skipWrite 在监视时不要将文件写入磁盘

案例工程:

./src/index.js
import { log, err } from "./mod02";
import info from "./mod01";
log(info.name)
// ./src/mod01.js
export default {
name: 'chuckle',
}
// ./src/mod02.js
export const log = msg => {
console.log("=======Info=======")
console.log(msg)
console.log("==================")
}
export const err = msg => {
console.log("=======Error=======")
console.log(msg)
console.log("==================")
}

使用命令打包:

npx rollup ./src/index.js
npx rollup ./src/index.js --format iife --file ./dist/bundle.js

打包结果:

Rollup的打包结果非常简洁,且默认开启Tree-shaking,它仅仅是将各个模块按依赖顺序拼接,没有Webpack那样存在大量的引导代码和模块函数,

(function () {
'use strict';
const log = msg => {
console.log("=======Info=======");
console.log(msg);
console.log("==================");
};
var info = {
name: 'chuckle',
};
log(info.name);
})();

配置文件

rollup.config.mjs是rollup的默认配置文件,导出一个配置对象

使用配置文件:npx rollup --config <配置文件名>,指定配置文件名以区分生产和打包环境

"scripts": {
"build": "rollup --config"
},

智能提示:由于 Rollup 随附了 TypeScript 类型定义,因此你可以使用 JSDoc 类型提示来利用你的 IDE 的智能感知功能

/**
* @type {import('rollup').RollupOptions}
*/
const config = { /* 你的配置 */ };
export default config;
// or
import { defineConfig } from 'rollup';
export default defineConfig({ /* 你的配置 */ });

基本配置

所有配置

export default {
input: './src/index.js', // 打包入口
output: {
file: './dist/bundle.js', // 打包出口
format: 'iife' // 格式
},
// 插件
plugins: [],
};

output.format配置指定bundle输出格式:

  1. amd – 异步模块加载,适用于 RequireJS 等模块加载器
  2. cjs – CommonJS,适用于 Node 环境和其他打包工具(别名:commonjs)
  3. es/esm – 将 bundle 保留为 ES 模块文件,适用于其他打包工具,以及支持 <script type="module"> 标签的浏览器。
  4. iife – 自执行函数,适用于 <script> 标签。
  5. umd – 通用模块定义规范,同时支持 amd,cjs 和 iife
  6. system – SystemJS 模块加载器的原生格式(别名:systemjs)

插件

插件是Rollup唯一的功能扩展途径。使用插件,插件列表

plugin-json

使用plugin-json从 JSON 文件中导入数据

安装:npm i @rollup/plugin-json -D

export default {
plugins: [
json(),
],
}

读取package.json测试插件

import { log, err } from "./mod02";
import { name, version } from "../package.json"
log(name + " " + version)

打包结果:

package.json中的name和version成功打包进来了

(function () {
'use strict';
const log = msg => {
console.log("=======Info=======");
console.log(msg);
console.log("==================");
};
var name = "01";
var version = "1.0.0";
log(name + " " + version);
})();

加载npm模块

为了保持Rollup的精简,node-resolve不是内置功能,原因

这意味着需要安装node-resolve才能在模块中导入第三方npm模块

安装:npm i @rollup/plugin-node-resolve -D

import nodeResolve from '@rollup/plugin-node-resolve';
plugins: [nodeResolve()]

测试:

import { max } from "lodash-es"
const arr = [1, 2, 3, 4, 5]
console.log(max(arr))

打包结果:

(function () {
'use strict';
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
var freeGlobal$1 = freeGlobal;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal$1 || freeSelf || Function('return this')();
var root$1 = root;
/** Built-in value references. */
var Symbol = root$1.Symbol;
var Symbol$1 = Symbol;
/** Used for built-in method references. */
var objectProto$1 = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto$1.hasOwnProperty;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString$1 = objectProto$1.toString;
/** Built-in value references. */
var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
/**
* A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the raw `toStringTag`.
*/
function getRawTag(value) {
var isOwn = hasOwnProperty.call(value, symToStringTag$1),
tag = value[symToStringTag$1];
try {
value[symToStringTag$1] = undefined;
var unmasked = true;
} catch (e) {}
var result = nativeObjectToString$1.call(value);
if (unmasked) {
if (isOwn) {
value[symToStringTag$1] = tag;
} else {
delete value[symToStringTag$1];
}
}
return result;
}
/** Used for built-in method references. */
var objectProto = Object.prototype;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString = objectProto.toString;
/**
* Converts `value` to a string using `Object.prototype.toString`.
*
* @private
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
*/
function objectToString(value) {
return nativeObjectToString.call(value);
}
/** `Object#toString` result references. */
var nullTag = '[object Null]',
undefinedTag = '[object Undefined]';
/** Built-in value references. */
var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
/**
* The base implementation of `getTag` without fallbacks for buggy environments.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag;
}
return (symToStringTag && symToStringTag in Object(value))
? getRawTag(value)
: objectToString(value);
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return value != null && typeof value == 'object';
}
/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';
/**
* Checks if `value` is classified as a `Symbol` primitive or object.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
* @example
*
* _.isSymbol(Symbol.iterator);
* // => true
*
* _.isSymbol('abc');
* // => false
*/
function isSymbol(value) {
return typeof value == 'symbol' ||
(isObjectLike(value) && baseGetTag(value) == symbolTag);
}
/**
* This method returns the first argument it receives.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Util
* @param {*} value Any value.
* @returns {*} Returns `value`.
* @example
*
* var object = { 'a': 1 };
*
* console.log(_.identity(object) === object);
* // => true
*/
function identity(value) {
return value;
}
/**
* The base implementation of `_.gt` which doesn't coerce arguments.
*
* @private
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @returns {boolean} Returns `true` if `value` is greater than `other`,
* else `false`.
*/
function baseGt(value, other) {
return value > other;
}
/**
* The base implementation of methods like `_.max` and `_.min` which accepts a
* `comparator` to determine the extremum value.
*
* @private
* @param {Array} array The array to iterate over.
* @param {Function} iteratee The iteratee invoked per iteration.
* @param {Function} comparator The comparator used to compare values.
* @returns {*} Returns the extremum value.
*/
function baseExtremum(array, iteratee, comparator) {
var index = -1,
length = array.length;
while (++index < length) {
var value = array[index],
current = iteratee(value);
if (current != null && (computed === undefined
? (current === current && !isSymbol(current))
: comparator(current, computed)
)) {
var computed = current,
result = value;
}
}
return result;
}
/**
* Computes the maximum value of `array`. If `array` is empty or falsey,
* `undefined` is returned.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Math
* @param {Array} array The array to iterate over.
* @returns {*} Returns the maximum value.
* @example
*
* _.max([4, 2, 8, 6]);
* // => 8
*
* _.max([]);
* // => undefined
*/
function max(array) {
return (array && array.length)
? baseExtremum(array, identity, baseGt)
: undefined;
}
// import { log, err } from "./mod02";
// import info from "./mod01";
// import { name, version } from "../package.json"
const arr = [1, 2, 3, 4, 5];
console.log(max(arr));
})();

加载CommonJS模块

Rollup设计之初只处理ESM模块的打包,若需导入CommonJS模块,则需要安装plugin-commonjs插件,文档

安装:npm i @rollup/plugin-commonjs -D

import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
plugins: [
nodeResolve(),
commonjs(),
],

注意,Tree-shaking不对CommonJS模块起作用

压缩代码

plugin-terser是rollup的一种输出插件,用来压缩rollup分析完的代码

安装:npm i @rollup/plugin-terser -D

import terser from '@rollup/plugin-terser';
plugins: [terser()]

技巧:同时输出未压缩和压缩bundle

output: [
{
file: './dist/bundle.js',
format: 'cjs'
},
{
file: './dist/bundle.min.js',
format: 'iife',
plugins: [terser()]
}
],

清空输出目录

rollup-plugin-clear在每次打包前清空输出目录

安装:npm i rollup-plugin-clear -D

plugins: [
clear({
// 需要清空的文件夹
targets: ['dist'],
// 在监视模式下进行汇总重新编译时是否清除目录
watch: true, // default: false
}),
],

生成html

plugin-html生成html

安装:npm i @rollup/plugin-html -D

import html from '@rollup/plugin-html';
plugins: [
html({
fileName: 'index.html',
title: 'Rollup Demo',
attributes: { lang: 'en' },
publicPath: '',
meta: [
{ charset: 'utf8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
{ name: 'keywords', content: 'rollup, demo' },
],
template({ attributes, bundle, files, publicPath, title, meta }) {
return `<!DOCTYPE html>
<html${Object.entries(attributes).reduce((prev, [key, value]) => prev + ` ${key}="${value}"`, '')}>
<head>
${meta.reduce((prev, item) => prev + `<meta${Object.entries(item).reduce((prev, [key, value]) => prev + ` ${key}="${value}"`, '')}>`, '')}
<title>${title}</title>
</head>
<body>
<script src="https://requirejs.org/docs/release/2.3.6/minified/require.js" data-main="${publicPath}index.min.js"></script>
</body>
</html>`;
}
})
],

处理css

plugin-postcss支持css文件的加载、css加前缀、css压缩、对scss/less的支持等等

安装:npm i rollup-plugin-postcss -D

import postcss from 'rollup-plugin-postcss';
plugins: [
postcss({
extract: 'index.css', // 提取css为独立文件
minimize: true, // 最小化
plugins: [], // 支持其它处理css的插件
}),
]

开发服务器

plugin-serve搭建开发服务器

安装:npm i rollup-plugin-serve -D

plugins: [
serve({
port: 3000, // 端口
contentBase: 'dist', // 输出目录
openPage: '/index.html', // 打开的是哪个文件
open: true, // 自动打开浏览器
})
]

自动刷新

plugin-livereload自动刷新浏览器

安装:npm i rollup-plugin-livereload -D

-w启动监视模式"dev": "rollup -c -w -m"

import livereload from "rollup-plugin-livereload";
plugins: [
clear({
targets: ['dist'],
watch: false, // 需要设为false,否则和自动刷新冲突
}),
serve({
port: 3000, // 端口
contentBase: 'dist', // 输出目录
openPage: '/index.html', // 打开的是哪个文件
open: false, // 自动打开浏览器
}),
livereload(),
]

JS兼容

plugin-babel用于源代码转译,对低版本NODE或者浏览器环境进行兼容

安装:

  1. npm i -D @rollup/plugin-babel @babel/core @babel/plugin-external-helpers @babel/plugin-transform-runtime @babel/preset-env @babel/preset-typescript
  2. npm i --save @babel/runtime
import { babel } from '@rollup/plugin-babel';
plugins: [
babel({
exclude: "**/node_modules/**",
babelHelpers: "runtime",
extensions: ['.js', '.ts'],
}),
]
{
"presets": [
[
"@babel/preset-env",
{
"modules": false // Rollup要求Babel配置保持ES6模块语法不变
}
],
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-external-helpers",
[
"@babel/plugin-transform-runtime",
{
"useESModules": false
}
]
]
}

代码拆分

动态加载或多个入口点,Rollup会自动将代码拆分成块,也可以通过output.manualChunks显式配置要拆分的块,文档

动态加载、多入口打包

input: {
index: './src/index.js',
main: './src/main.js',
},
output: {
dir: 'dist',
entryFileNames: '[name].js',
format: 'amd' // 为了实现动态导入,不能是UMD和IIFE,为了适应浏览器环境,使用amd模块格式
// [!] RollupError: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
},
import("./mod01").then(({ default: info }) => {
console.log(info.name);
});

打包结果:

mod01-4ejDwqza.js
Promise.resolve().then(function () { return require('./mod01-4ejDwqza.js'); }).then(({ default: info }) => {
console.log(info.name);
});
var mod01 = {
name: 'chuckle',
};
exports.default = mod01;

tip:多入口打包时,会自动提取公共的模块

页面还需引入amd模块的实现库,如requirejs

通过data-main指定模块的入口文件

<script
src="https://requirejs.org/docs/release/2.3.6/minified/require.js"
data-main="/dist/index.min.js"
></script>

output.manualChunks

output: {
dir: 'dist',
entryFileNames: '[name].js',
format: 'amd',
manualChunks: {
lodash: ['lodash-es'] // 拆分引入的lodash-es模块
}
}

区分环境

使用多配置文件区分开发和生产环境

rollup.config.common.mjs 基础公共配置

import json from '@rollup/plugin-json';
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import clear from 'rollup-plugin-clear';
import typescript from '@rollup/plugin-typescript';
export default {
input: {
index: './src/index.ts',
}, // 打包入口
plugins: [
json(),
nodeResolve(),
commonjs({extensions: ['.js', '.ts']}),
clear({
// 需要清空的文件夹
targets: ['dist'],
// 在监视模式下进行汇总重新编译时是否清除目录
watch: false, // default: false
}),
typescript({
compilerOptions: {
lib: ["es5", "es6", "dom"],
target: "es6"
}
}),
],
};

rollup.config.dev.mjs 开发环境配置

import serve from 'rollup-plugin-serve';
import livereload from "rollup-plugin-livereload";
import html from '@rollup/plugin-html';
import postcss from 'rollup-plugin-postcss';
import common from './rollup.config.common.mjs';
import { htmlDevTemple } from './html-temple.mjs';
export default Object.assign({}, common, {
output: [
{
dir: 'dist',
entryFileNames: '[name].js',
format: 'es',
manualChunks: {
lodash: ['lodash-es']
}
}
],
plugins: [
...common.plugins,
serve({
port: 3000, // 端口
contentBase: 'dist', // 输出目录
openPage: '/index.html', // 打开的是哪个文件
open: false, // 自动打开浏览器
}),
livereload(),
html(htmlDevTemple),
postcss({
extract: 'index.css', // 提取css为独立文件
minimize: false, // 最小化
plugins: [], // 支持其它处理css的插件
}),
],
});

rollup.config.prod.mjs 生产环境配置

import html from '@rollup/plugin-html';
import postcss from 'rollup-plugin-postcss';
import terser from '@rollup/plugin-terser';
import { babel } from '@rollup/plugin-babel';
import common from './rollup.config.common.mjs';
import { htmlProdTemple } from './html-temple.mjs';
export default Object.assign({}, common, {
output: [
{
dir: 'dist',
entryFileNames: '[name].js',
format: 'amd',
},
{
dir: 'dist',
entryFileNames: '[name].min.js',
format: 'amd',
plugins: [terser()],
manualChunks: {
lodash: ['lodash-es']
}
}
],
plugins: [
...common.plugins,
html(htmlProdTemple),
postcss({
extract: 'index.css', // 提取css为独立文件
minimize: true, // 最小化
plugins: [], // 支持其它处理css的插件
}),
babel({
exclude: "**/node_modules/**",
babelHelpers: "runtime",
extensions: ['.js', '.ts'],
}),
],
});

html-temple.mjs HTML模板

const config = {
fileName: 'index.html',
title: 'Rollup Demo',
attributes: { lang: 'en' },
publicPath: '',
meta: [
{ charset: 'utf8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
{ name: 'keywords', content: 'rollup, demo' },
]
}
export const htmlDevTemple = {
...config,
template({ attributes, bundle, files, publicPath, title, meta }) {
const link = `<link rel="stylesheet" href="${publicPath}index.css">`;
const script = `<script type="module" src="${publicPath}index.js"></script>`;
return renderHtml([link], [script], { attributes, bundle, files, publicPath, title, meta });
}
}
export const htmlProdTemple = {
...config,
template({ attributes, bundle, files, publicPath, title, meta }) {
const link = `<link rel="stylesheet" href="${publicPath}index.css">`;
const requirejs = "https://requirejs.org/docs/release/2.3.6/minified/require.js";
const script = `<script src="${requirejs}" data-main="${publicPath}index.min.js"></script>`;
return renderHtml([link], [script], { attributes, bundle, files, publicPath, title, meta });
}
}
function renderHtml(additionalHead, additionalBody, { attributes, bundle, files, publicPath, title, meta }) {
return `<!DOCTYPE html>
<html${renderAttributes(attributes)}>
<head>
${renderMeta(meta)}
<title>${title}</title>
${arrToString(additionalHead)}
</head>
<body>
${arrToString(additionalBody)}
</body>
</html>`;
}
function arrToString(arr) {
return arr.reduce((prev, item) => prev + item, '')
}
function renderAttributes(attributes) {
return Object.entries(attributes).reduce((prev, [key, value]) => prev + ` ${key}="${value}"`, '')
}
function renderMeta(meta) {
return meta.reduce((prev, item) => prev + `<meta${Object.entries(item).reduce((prev, [key, value]) => prev + ` ${key}="${value}"`, '')}>`, '')
}

修改package.json

"scripts": {
"build": "rollup --config rollup.config.prod.mjs",
"dev": "rollup --config rollup.config.dev.mjs -w -m "
},

总结

rollup优点:

  1. 输出结果更加扁平
  2. 自动移除未引用代码
  3. 打包结果依然完全可读

rollup缺点:

  1. 加载非ESM的第三方模块比较复杂
  2. 模块最终都被打包到一个函数中,无法实现HMR
  3. 浏览器环境中,代码拆分功能依赖AMD库

rollup适用于开发框架和库,对于开发应用则不太合适

Webpack大而全,Rollup小而美

配置TS环境

安装依赖:npm i @rollup/plugin-typescript tslib -D

import typescript from '@rollup/plugin-typescript';
input: {
index: './src/index.ts',
},
typescript({
compilerOptions: {
lib: ["es5", "es6", "dom"],
target: "es5"
}
})
{
"compilerOptions": {
"incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
"tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
"diagnostics": true, // 打印诊断信息
"target": "esnext", /* 指定 ECMAScript 目标版本:'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "esnext", /* 输出的代码使用什么方式进行模块化: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [ /* 指定引用的标准库 */
"esnext",
"dom",
"dom.iterable",
], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
"allowJs": true, // 允许编译器编译JS,JSX文件
"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
"outDir": "./dist", // 指定输出目录
"rootDir": "./src", // 指定输出文件目录(用于输出),用于控制输出目录结构
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
"declarationDir": "./dist/typings", // 指定生成声明文件存放目录
// "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
"sourceMap": false, // 生成目标文件的sourceMap文件
// "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
"declarationMap": false, // 为声明文件生成sourceMap
// "typeRoots": [], // 声明文件目录,默认时node_modules/@types
"types": [], // 加载的声明文件包
"removeComments": true, // 删除注释
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"noEmitOnError": true, // 发送错误时不输出任何文件
"noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
"importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
"downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
"strict": true, // 开启所有严格的类型检查
"alwaysStrict": true, // 在代码中注入'use strict'
"noImplicitAny": true, // 不允许隐式的any类型
"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
"strictFunctionTypes": true, // 不允许函数参数双向协变
"strictPropertyInitialization": true, // 类的实例属性必须初始化
"strictBindCallApply": true, // 严格的bind/call/apply检查
"noImplicitThis": true, // 不允许this有隐式的any类型
"noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
"noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
"noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
"noImplicitReturns": true, //每个分支都会有返回值
"esModuleInterop": true, // 允许export=导出,由import from 导入
"allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { // 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
// "jquery": [
// "node_modules/jquery/dist/jquery.min.js"
// ],
"@/*": [
"src/*"
]
},
"rootDirs": [
"src"
], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
"listEmittedFiles": true, // 打印输出文件
"listFiles": true, // 打印编译的文件(包括引用的声明文件)
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"resolveJsonModule": true
},
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
"src/**/*",
],
// 指定一个排除列表(include的反向操作)
// "exclude": [
// "demo.ts"
// ],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
// "files": [
// "demo.ts"
// ]
}

评论

快速前往