【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的文件,打包后会多出很多无关的代码。