【rollup】构建发布一个npm包
提示
本篇文章尚处在编写状态中...
# 背景
我们在项目开发的过程中,经常会封装组件与一些函数库方便使用。但每次开发不同项目时都要花费时间重新开发,其实很多组件与工具函数在多数场景都是可复用的。所以,我打算把日常使用频率较高的组件和函数库进行逻辑封装,发布成一个npm
包小工具库等,这样做,一来可以借机完善组件与工具库业务逻辑,二来可以熟悉不同工具对打包构建的流程,最后还可以实战下npm
发包。
虽然有大量现成的类库,类似lodash
、moment
这种,但对于其内部实现我们却很少了解,只是简单的使用。当然我们不可能把所有开源的源码都看一遍,但对于一些优秀的开源类库,还是很有必要学习下的,了解其内部实现逻辑与原理对于自身技术提升也是很有帮助的~
特别是开源项目的工程化配置与各种标准规范,具有很高的学习价值,redux
的包发布方式就是采用的rollup
,我们可以参照其实现方式来实现一个我们自己的npm
包。
这篇文章主要就是记录我在使用rollup
构建、发包的一些心得体会,如有错误,欢迎指正~
# 目标
- 现阶段开源项目基于
ES6
的写法越来越普遍,所以我们发布的包必须支持ESM
标准。 - 因为还有不少项目是基于
nodejs
环境开发,使用的是commonjs
协议,所以也应该做到对CJS
的支持。 - 当然也必须要支持浏览器环境以
CDN
的形式直接引入使用,所以也要做到对UMD
的支持。(该模式支持浏览器和Nodejs)
- 支持以
Typescript
语法开发 - 支持以
ES6
语法开发 - 支持导出
ES module
规范 - 支持导出
commonjs
规范 - 支持导出
umd
规范 - 支持配置兼容版本
- 支持
tree-shaking
- 支持代码压缩
# 介绍
本篇文章不做入门使用介绍,我们主要按业务需求做一些必要说明,以确保对整个逻辑有比较清晰的认识。
# 前言
市面上知名的库,如Vue
、React
、D3
、Redux
等项目都是使用的Rollup
进行构建打包,而目前尤大新出的最火热的Vite
构建工具在打包构建项目时也是使用的Rollup
。
Rollup
从设计之初就是面向ES module
的,它诞生时AMD
、CMD
、UMD
的格式之争还很火热,作者希望充分利用ES module
机制,构建出结构扁平
,性能出众
的类库。
# ES Module
ES module
的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS
和 AMD
模块,都只能在运行时确定这些东西,举例来说:
ES import
只能作为模块顶层的语句出现,不能出现在function
里面或是if
里面。ES import
的模块名只能是字符串常量。- 不管
import
的语句出现的位置在哪里,在模块初始化的时候所有的import
都必须已经导入完成。 import binding
是immutable
的,类似const
。比如说你不能import { a } from './a'
然后给a
赋值个其他什么东西。
这些设计虽然使得灵活性不如CommonJS
的require
,但却保证了 ES modules
的依赖关系是确定的,和运行时的状态无关,从而也就保证了ES modules
是可以进行可靠的静态分析的。
# 概述
Rollup
是一个使用新的ES6标准化格式的 JavaScript
模块打包器,可以将小块代码编译成大块复杂的代码。通过 Tree-shaking
静态分析代码中的 import
,排除任何未实际使用的代码。
# Tree-shaking 是什么
Tree-shaking
, 也被称为 "live code inclusion," 它是清除实际上并没有在给定项目中使用的代码的过程,但是它可以更加高效。
- 在使用
CommonJS
时,必须导入(import
)完整的工具(tool
)或库(library
)对象。
// 使用 CommonJS 导入(import)完整的 utils 对象
var utils = require( 'utils' );
var query = 'Rollup';
// 使用 utils 对象的 ajax 方法
utils.ajax( 'https://api.example.com?search=' + query ).then( handleResponse );
2
3
4
5
- 但是在使用
ES6
模块时,无需导入整个utils
对象,我们可以只导入(import
)我们所需的ajax
函数:
// 使用 ES6 import 语句导入(import) ajax 函数
import { ajax } from 'utils';
var query = 'Rollup';
// 调用 ajax 函数
ajax( 'https://api.example.com?search=' + query ).then( handleResponse );
2
3
4
5
因为 Rollup
只引入最基本最精简代码,所以可以生成轻量、快速,以及低复杂度的 library
和应用程序。因为这种基于显式的 import
和 export
语句的方式,它远比「在编译后的输出代码中,简单地运行自动 minifier
检测未使用的变量」更有效。
# Rollup 对比 Webpack
首先我们要清楚这两种工具的定位是不同的。
rollup
倾向于类库方向,而webpack
则是倾向于复杂SPA
的模块化应用构建。
就目前webpack
的强大生态在构建应用程序开发上市具有很大的优势的,各种工具、loader、plugin都很全面。
# webpack
- 强大的插件生态
- 通过
loader
处理各种各样的资源依赖 HMR
模块热替换- 按需加载、路由拆分、资源缓存
- 提取公共模块
- 支持各类型的模块依赖处理
# rollup
- 编译出来的代码可读性好
- 不会像
webpack
打包后会生成__webpack_require__
等冗余runtime
代码,相对干净和小巧。 - 支持
tree-shaking
- 支持导出
es
模块文件(webpack
不支持导出es
模块)
# 缺点
- 模块过于静态化,HMR很难实现
- 仅面向
ES module
,无法可靠的处理commonjs
以及umd
依赖
# 为什么使用rollup
rollup
不是为了替代webpack
而出现,webpack
有自身丰富的生态圈,但webpack
对于多模块打开之后产生的额外开销有很多,比如各种没用的依赖,使用webpack
打包,你会发现,原来只有几kb
的文件,打包后会多出很多无关的代码。