本篇文章分享如何和用 rollup 来构建一个组件库,以及使用vite搭建一个测试环境
rollup 在构建组件库上,相较于 webpack 有什么优点呢
rollup 基于 esm 模块作为输入模式,在构建组件库时具有更快的构建速度、更简洁的配置,更好的代码质量和更低的内存占用等优点。这些优点让 rollup 成为构建组件库的首选
下面我们来看看如何使用 rollup 构建一个组件库
准备两个组件
首先准备两个组件,Button 和 Input
Button
jsx
import { useState } from "react"
const Button = (props: { content: string }) => {
const [count, setCount] = useState(0);
return <div><button onClick={() => setCount(count + 1)}>count: {count}</button>
<div>{props.content}</div>
</div>
}
export default Button;
Input
jsx
const Input = () => {
return <div>
<span>input anything: </span>
<input></input>
</div>
}
export default Input;
代码都很简单,都是非常基础的代码
然后在 src 根目录下的 index 文件,将这些文件都导入并导出
jsx
import Button from "./Button";
import Input from "./Input"
// 导出组件文件中的非default导出
export * from './Button';
export * from './Input';
// 导出组件本身
export {
Button,
Input
}
组件准备好了,下面就开始打包了
配置 rollup
先在项目的根目录中创建一个 rollup 配置文件:rollup.config.js
jsx
/**@type {import('rollup').RollupOptions} */
module.exports = {
input: "./src/index.tsx",
output: {
dir: "./dist",
format: "esm",
sourcemap: true,
},
plugins: [
//...
],
external: ["react", "react-dom","react/jsx-runtime"],
};
小技巧:如果希望在 js 文件中,还可以获得像 ts 的编译器提示,可以使用 jsDoc,像上面的
/**@type */
配置中,需要指出构建的入口./src/index.tsx
,以及构建产物的目录./dist
,格式,是否生成 sourcemap 等信息。
rollup 不仅可以打包成 esm 格式,还可以打包成 esm 格式,umd 格式,不过对于组件库而言,其应用场景是在 node 环境中的其他项目中引入,所以一般是打包成 esm。不过为了多环境的支持性,会构建 esm 和 umd 两种类型的产物
plugins 中就是放插件,添加额外功能的地方了。external
表示哪些包不放进构建产物中,若它的值是["react", "react-dom"]
,那就表示 react
和 react-dom
, 以及 react 运行时依赖react/jsx-runtime
,均不打包进构建产物中
添加插件
rollup 作为一个构建工具,只提供一些非常基础的能力,像 commonjs 模块解析,json 解析,ts 解析,css 解析等,都不支持。好在 rollup 提供了方便简单的插件机制,并且有非常多的生命周期 hook,使得开发人员为其添加额外的功能颇为方便
1
先添加@rollup/plugin-commonjs, 赋予 rollup 加载第三方依赖的功能,添加@rollup/plugin-commonjs, 赋予rollup将cjc模块转成esm模块的能力
powershell
npm i @rollup/plugin-commonjs @rollup/plugin-commonjs -D
2
添加@rollup/plugin-typescript, 赋予rolllup解析typescript的能力,还要添加相关的依赖: typescript
powershell
npm i typescript @rollup/plugin-commonjs -D
因为需要用 typescript 生成类型文件,所以还需要配置 ts 的配置文件:tsconfig.json
json
{
"compilerOptions": {
"rootDir": "./src",
"baseUrl": "./",
"jsx": "react-jsx",
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "./dist/types",
"lib": [
"es2020"
]
}
}
这里有几个和构建有关的,我需要重点提一下:
- declaration 表示生成类型文件,emitDeclarationOnly 表示只生成类型文件。因为 rollup 是借助 babel 做编译,所以只需要 ts 提供类型文件的输出即可
- outDir 的值是
./dist/types
表示 tsc 的编译产物放到当前目录下的 dist 文件夹下的 types 目录 - rootDIr,默认是 src,即包含所有的 ts 文件。也可以手动指定。
那rootDIr是用来干什么的呢?rootDir 是和 outDir 结合起来用的。如果 rootDir 的值是./src
,那么构建之后的产物就是这样的:
即 src 下面的文件结构直接铺在 types 文件夹下面
那如果 rootDir 的值是./
,就会变成这样:
即 types 文件夹下面还嵌套着一层 src。这就是 rootDir 的作用
使用了@rollup/plugin-typescript,在 rollup build 的时候,就会使用 tsc 对文件进行编译,所以当我仅执行了 rollup -c
后,types 会自动产生
3
添加@rollup/plugin-babel, 赋予 rollup 可以解析 jsx,降级 js 语法的能力。还要添加相关的依赖:@babel/core, @babel/preset-react。
因为是react 组件库,所以还需要安装 react , react-dom,@types/react, @types/react-dom
powershell
npm i react react-dom @types/react @types/react-dom -D
npm i @babel/core @babel/preset-react -D
npm i @rollup/plugin-babel -D
如果需要使用 babel 对 js 语法做降级处理,就需要安装@babel/preset-env, @babel/plugin-transform-runtime
4
添加rollup-plugin-postcss, postcss, 赋予 rollup 解析 css,scss,sass 的能力
powershell
npm i rollup-plugin-postcss postcss -D
完善 rollup 配置
需要的功能都差不多了,完善下 rollup 配置文件吧
javascript
const resolve = require("@rollup/plugin-node-resolve");
const postcss = require("rollup-plugin-postcss");
const typescript = require("@rollup/plugin-typescript");
const commonjs = require("@rollup/plugin-commonjs");
const { babel } = require("@rollup/plugin-babel");
/**@type {import('rollup').RollupOptions} */
module.exports = {
input: "./src/index.tsx",
output: {
dir: "./dist",
format: "esm",
sourcemap: true,
},
plugins: [
resolve(),
commonjs(),
postcss(),
typescript(),
babel({
presets: ["@babel/preset-react"],
exclude: /nodex_module/,
}),
],
external: ["react", "react-dom"],
};
配置文件做好了,下面测试一下,看看 rollup 构建是什么样子
先添加一个 npm script
javascript
// package.json
{
"scripts": {
"build": "rollup -c",
}
}
-c
的意思是,执行 rollup 时,使用 rollup 配置文件中的配置
执行npm run build
构建好了,花费 1.1s。 因为组件简单,就很快
这是构建产物的结构:
有 types 类型文件,还有 index.js 和对应的 index.js.map
再看看 index.js 里面长什么样子:
两个组件都被置于 index 文件中,并且格式是 esm,符合预期
不 bundle 构建
不过传统的 esm 都是不 bundle 的,也就是不打包成单个文件的,再改改 rollup.config.js:
javascript
{
output: {
dir: "./dist",
format: "esm",
sourcemap: true,
//rollup只编译,不打包
preserveModules: true,
},
}
再次执行npm run build
文件结构变成了这个样子:
index.js 的内容:
好了,构建文件做好了,该怎么测试它的可用性呢?
使用 vite 搭建测试环境
测试的思路是使用 vite 新建一个 react demo 项目,然后用 npm link 的方式来测试上面的构建产物
shell
mkdir use-rollup
cd use-rolllup
npm init -y
npm i vite react react-dom @types/react @types/react-dom @vitejs/plugin-react typescript
先创建一个测试项目的文件夹,然后在其中安装必要的依赖
配置 vite.config.js 文件
javascript
import viteReact from "@vitejs/plugin-react";
/**@type {import('vite').UserConfig} */
export default {
plugins: [viteReact()],
server: {
port: 8080,
open: "/",
},
};
vite 自身支持丰富的功能,所以这里仅需要 react 插件即可。还另外配置了本地服务器
配置 tsconfig.json
json
{
"compilerOptions": {
"jsx": "react-jsx",
}
}
因为需要用到 react,所以这里配置不用引入 react 的 ts 配置,其他 ts 配置用默认的就好
编写 index.tsx
jsx
import ReactDOM from 'react-dom/client'
import { Button } from 'rollup-build'
const App = () => {
return <div>
App
</div>
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
准备一个 index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=, initial-scale=1.0" />
<title>use-rollup</title>
<script type="module" src="./src/index.tsx" defer></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
将 index.html 作为 vite 启动的入口文件
好了,到这里所有的东西都准备好了,这是现在的文件结构:
启动项目
先添加一个 npm script:
json
"scripts": {
"start": "vite"
},
执行npm run start
:
启动成功,修改几个字符看看
页面也刷新了。
项目没问题,下面开始测试构建产物
测试构建产物
首先要做到的是修改组件库项目的 package 的 main 和 types 文件指向:
json
{
"main": "./dist/index.js",
"types": "./dist/types",
}
mian 和 types 都指向 dist 文件夹,也就是构建产物的文件夹
然后在组件库项目根目录终端执行: npm link
再在 demo 目录执行npm link rollup-build
link 成功之后,就可以在 index.tsx 中引入对应的组件
保存,刷新页面:
测试成功,没有问题
优化测试的体验
在使用 npm link 测试后,发现了个问题,Button 的 content 的属性名不对,应该是 contents,少了个 s,这时候就要修改组件库的代码了
修改组件库代码之后,还需要 build,构建之后才能在 demo 项目里看见变化。
那这个体验就很糟糕了,有没有什么办法可以像 vite 本地开发服务器一样,一旦文件保存,就可以在测试端看见变化呢?
当然是有办法的
rollup 支持 watch 模式,一旦文件变动,就会自动地单独重新构建这个文件
给组件库添加一个新的 npm script:
json
{
"build:watch": "rollup -c -w"
}
-w
的意思就是开启 watch
下面来试试:
修改 Button 的属性
javascript
const Button = (props: { contents: string }) => {
const [count, setCount] = useState(0);
return <div><button onClick={() => setCount(count + 1)}>count: {count}</button>
<div>{props.contents}</div>
</div>
}
ctrl + s 保存后,主要看看这两个文件有没有变化:
这是构建之后的 Button/index.js
这是 types/Button/index.d.ts
均已发生变化!!
很不错,看看 demo 项目中的 src/index.tsx:
ts 已经提示报错了
总结:
这篇文章分享了如何用 rollup 来构建一个组件库。介绍了 rollup 配置的基本用法以及相关的插件。
为了测试构建产物,还用 vite 来搭建一个 demo 环境,使用 npm link 来测试构建产物的可用性和正确性。
最后为了测试的体验,借助了 rollup 的 watch 模式,可以在监听到文件变化后,自动重新构建。
构建组件库有了,测试教程也有了,但是测试环境是依托于其他消费端项目的。组件库本身却没有一个开发环境,这个留待下篇文章来分享吧
这篇文章看完,大家有什么收获吗,欢迎在评论区留言哦