专栏名称: 奇舞精选
《奇舞精选》是由奇舞团维护的前端技术公众号。除周五外,每天向大家推荐一篇前端相关技术文章,每周五向大家推送汇总周刊内容。
目录
相关文章推荐
物道  ·  同龄人都老了,而你还是绝色 ·  5 小时前  
无时尚中文网  ·  严重缺钱 持续甩卖资产 ... ·  2 天前  
阿尔法工场研究院  ·  特朗普为何看上加沙?知情人:他想在那里开发房地产 ·  2 天前  
物道  ·  加拿大葡萄园:这下麻烦大了 ·  2 天前  
51好读  ›  专栏  ›  奇舞精选

为什么组件库打包用 Rollup 而不是 Webpack?

奇舞精选  · 公众号  ·  · 2024-12-10 18:00

正文

Rolup 是一个打包工具,类似 Webpack。

组件库打包基本都是用 Rollup。

那 Webpack 和 Rollup 有什么区别呢?为什么组件库打包都用 Rollup 呢?

我们来试一下:

mkdir rollup-test
cd rollup-test
npm init -y

我们创建两个模块:

src/index.js

import { add } from './utils';

function main({
    console.log(add(12))
}

export default main;

src/utils.js

function add(a, b{
    return a + b;
}

export {
    add
}

很简单的两个模块,我们分别用 rollup 和 webpack 来打包下:

安装 rollup:

npm install --save-dev rollup

创建 rollup.config.js

/** @type {import("rollup").RollupOptions} */
export default {
    input'src/index.js',
    output: [
        {
            file'dist/esm.js',
            format'esm'
        },
        {
            file'dist/cjs.js',
            format"cjs"
        },
        {
            file'dist/umd.js',
            name'Guang',
            format"umd"
        }
    ]
};

配置入口模块,打包产物的位置、模块规范。

在 webpack 里叫做 entry、output,而在 rollup 里叫做 input、output。

我们指定产物的模块规范有 es module、commonjs、umd 三种。

umd 是挂在全局变量上,还要指定一个全局变量的 name。

上面的 @type 是 jsdoc 的语法,也就是 ts 支持的在 js 里声明类型的方式。

效果就是写配置时会有类型提示:

不引入的话,啥提示都没有:

这里我们用了 export,把 rollup.config.js 改名为 rollup.config.mjs,告诉 node 这个模块是 es module 的。

配置好后,我们打包下:

npx rollup -c rollup.config.mjs

看下产物:

三种模块规范的产物都没问题。

那用 webpack 打包,产物是什么样呢?

我们试一下:

npm install --save-dev webpack-cli webpack

创建 webpack.config.mjs

import path from 'node:path';

/** @type {import("webpack").Configuration} */
export default {
    entry'./src/index.js',
    mode'development',
    devtoolfalse,
    output: {
        path: path.resolve(import.meta.dirname, 'dist2'),
        filename'bundle.js',
    }
};

打包下:

npx webpack-cli -c webpack.config.mjs

可以看到,webpack 的打包产物有 100 行代码:

因为它多了很多 webpack 的运行时代码:

这是 webpack 实现 code spliting 等功能的代码。

也就是这个:

代码里通过 import() 引入模块,打包时会单独拆分出来,然后运行时用到这个模块再异步加载:

但正因为有了这些 runtime 代码,webpack 的产物就不纯粹了。

相比之下,rollup 的产物就非常干净,没任何 runtime 代码:

所以说:

webpack 是为了浏览器打包而生的,而 rollup 一般用于 js 库的打包。

前面说组件库打包一般都用 rollup,我们来看下各大组件库的打包需求。

安装 antd:

npm install --no-save antd

在 node_modules 下可以看到它分了 dist、es、lib 三个目录:

分别看下这三个目录的组件代码:

lib 下的组件是 commonjs 的:

es 下的组件是 es module 的:

dist 下的组件是 umd 的:

然后在 package.json 里分别声明了 commonjs、esm、umd 还有类型的入口:

这样,当你用 require 引入的就是 lib 下的组件,用 import 引入的就是 es 下的组件。

而直接 script 标签引入的就是 unpkg 下的组件。

再来看一下 semi design 的:

npm install --no-save @douyinfe/semi-ui

也是一样:

只不过多了个 css 目录。

所以说, 组件库的打包需求就是组件分别提供 esm、commonjs、umd 三种模块规范的代码,并且还有单独打包出的 css。

那 rollup 如何打包 css 呢?

我们试一下:

创建 src/index.css

.aaa {
    background: blue;
}

创建 src/utils.css

.bbb {
    background: red;
}

然后分别在 index.js 和 utils.js 里引入下:

安装 rollup 处理 css 的插件:






    
npm install --save-dev rollup-plugin-postcss

引入下:

import postcss from 'rollup-plugin-postcss';

/** @type {import("rollup").RollupOptions} */
export default {
    input'src/index.js',
    output: [
        {
            file'dist/esm.js',
            format'esm'
        },
        {
            file'dist/cjs.js',
            format"cjs"
        },
        {
            file'dist/umd.js',
            name'Guang',
            format"umd"
        }
    ],
    plugins: [
        postcss({
            extracttrue,
            extract'index.css'
        }),
    ]
};

然后跑一下:

npx rollup -c rollup.config.mjs

可以看到,产物多了 index.css

而 js 中没有引入 css 了:

被 tree shaking 掉了,rollup 默认开启 tree shaking。

这样我们就可以单独打包组件库的 js 和 css。

删掉 dist,我们试下不抽离是什么样的:

npx rollup -c rollup.config.mjs

可以看到,代码里多了 styleInject 的方法:

用于往 head 里注入 style

一般打包组件库产物,我们都会分离出来。

然后我们再用 webpack 打包试试:

安装用到的 loader:

npm install --save-dev css-loader style-loader

css-loader 是读取 css 内容为 js

style-loader 是往页面 head 下添加 style 标签,填入 css

这俩结合起来和 rollup 那个插件功能一样。

配置 loader:

module: {
    rules: [{
        test/\.css$/i,
        use: ["style-loader""css-loader"],
    }],
}

用 webpack 打包下:

npx webpack-cli -c webpack.config.mjs

可以看到 css 变成 js 模块引入了:

这是 css-loader 做的。

而插入到 style 标签的 injectStylesIntoStyleTag 方法则是 style-loader 做的:

然后再试下分离 css,这用到一个单独的插件:

npm install --save-dev mini-css-extract-plugin

配一下:

import path from 'node:path';
import MiniCssExtractPlugin from "mini-css-extract-plugin";

/** @type {import("webpack").Configuration} */
export default {
    entry'./src/index.js',
    mode'development',
    devtoolfalse,
    output: {
        path: path.resolve(import.meta.dirname, 'dist2'),
        filename'bundle.js',
    },
    module: {
        rules: [{
            test/\.css$/i,
            use: [MiniCssExtractPlugin.loader, "css-loader"],
        }],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename'index.css'
        })
    ]
};

指定抽离的 filename 为 index.css

抽离用的 loader 要紧放在 css-loader 之前。

样式抽离到了 css 中,这时候 style-loader 也就不需要了。

打包下:

npx webpack-cli -c webpack.config.mjs

样式抽离到了 css 中:

而 js 里的这个模块变为了空实现:

所以 webpack 的 style-loader + css-loader + mini-css-extract-plugin 就相当于 rollup 的 rollup-plugin-postcss 插件。

为什么 rollup 没有 loader 呢?

因为 rollup 的 plugin 有 transform 方法,也就相当于 loader 的功能了。

我们自己写一下抽离 css 的 rollup 插件:

创建 my-extract-css-rollup-plugin.mjs(注意这里用 es module 需要指定后缀为 .mjs):

const extractArr = [];

export default function myExtractCssRollupPlugin (opts{
    return {
      name'my-extract-css-rollup-plugin',
      transform(code, id) {
        if(!id.endsWith('.css')) {
          return null;
        }

        extractArr.push(code);

        return {
          code'export default undefined',
          map: { mappings'' }
        }
      },
      generateBundle(options, bundle) {

        this.emitFile({
          fileName: opts.filename || 'guang.css',
          type'asset',
          source: extractArr.join('\n/*光光666*/\n')
        })
      }
    };
  }

在 transform 里对代码做转换,这就相当于 webpack 的 loader 了。

我们在 transform 里只处理 css 文件,保存 css 代码,返回一个空的 js 文件。

然后 generateBundle 里调用 emitFile 生成一个合并后的 css 文件。

用一下:

import myExtractCssRollupPlugin from './my-extract-css-rollup-plugin.mjs';
myExtractCssRollupPlugin({
    filename'666.css'
})

删掉之前的 dist 目录,重新打包:







请到「今天看啥」查看全文