Rollup炼金术:打造NPM包提高开发效率!

前言

1.在云在前端公众号中发送rollup领取源码地址

2.本章内容涉及到package.json,如果对其相关字段不熟悉,可以先参考我的上一篇文章# 中高级前端必须掌握的package.json最新最全指南

起步

1. rollup与webpack区别

Rollup 更适合构建库和组件,追求更高的代码优化和性能;

Webpack 更适合构建复杂的应用,提供了更多的功能和灵活性。

2. 安装 Rollup

  1. 初始化 package.json

yarn init -y

  1. 安装 rollup

yarn add rollup -D

  1. 新建src/foo.js
js 复制代码
export default 'hello world!';
  1. 新建src/index.js
js 复制代码
import foo from './foo.js';
export default function () {
    console.log(foo);
}
  1. 修改 package.json
js 复制代码
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"browser": "dist/index.umd.js",
"scripts": {
  "build": "rollup -c",
  "serve": "rollup -c -w"
},

-c:代表读取配置去打包,默认读取根目录下的rollup.config.mjs

-w:代表了watch监听,调试的时候可以用

  1. 新建 rollup.config.mjs
js 复制代码
import { defineConfig } from "rollup";
import pkg from "./package.json" assert { type: "json" }; //断言导出json模块

export default defineConfig([
  {
    input: "src/index.js", //入口文件
    output: [
      {
        file: pkg.main, //出口文件
        format: "cjs", //打包成CommonJS模块
      },
      {
        file: pkg.module, //出口文件
        format: "es", //打包成es module模块
      },
      {
        name: "myUtils", //打包成UMD模式,需提供name
        file: pkg.browser, //出口文件
        format: "umd", //打包成UMD模块
      },
    ],
  },
]);
  1. 运行 yarn run build 进行打包,项目根目录会生成一个dist文件夹。

  2. 根目录新建index.html引入打包后的文件,进行测试

js 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 测试es module模块 -->
    <script type="module">
      import main from "./dist/index.es.js";
      main();
    </script>
    <!-- 测试umd模块 -->
    <script src="./dist/index.umd.js"></script>
    <script>
      window.myUtils();
    </script>
  </body>
</html>

提示:使用esm模块时,直接访问,浏览器会报跨域错误。

解决方式:安装vscode扩展 Live Server,然后使用open with Live Server打开。可以看到浏览器打印出两个hello world!,至此使用rollup已打包成功。

插件

插件列表:github.com/rollup/awes...

rollup有丰富的插件,可以让我们做更多的处理,这里列举常用的插件使用方式。

1. @rollup/plugin-json(处理JSON文件)

  1. 安装

yarn add @rollup/plugin-json -D

  1. 修改src/index.js 文件
js 复制代码
import { version } from "../package.json";

export default function () {
  console.log("version " + version);
}
  1. 在 rollup.config.mjs 文件中加入 JSON plugin
js 复制代码
import json from '@rollup/plugin-json';

export default defineConfig([
  {
    //...
    plugins: [
      json(),
    ],
  },
]);
  1. 使用 yarn run build 运行 Rollup。dist/index.cjs.js文件内容应该如下所示
js 复制代码
'use strict';

var version = "1.0.0";

function index () {
    console.log('version ' + version);
}

module.exports = index;

结果中JSON文件已成功被处理,并且只导入了我们实际需要的数据version,其他内容例如name、devDependencies都被忽略了。这就是 tree shaking 的作用

2. @rollup/plugin-terser(压缩文件)

  1. 安装

yarn add @rollup/plugin-terser -D

  1. 修改rollup.config.mjs
js 复制代码
import terser from "@rollup/plugin-terser";

export default defineConfig([
  {
    //...
    plugins: [terser()],
  },
]);
  1. 使用 yarn run build 进行打包,生成的打包文件将全部被压缩

3. @rollup/plugin-node-resolve(处理外部依赖)

  1. 安装

yarn add @rollup/plugin-node-resolve -D

  1. 修改rollup.config.mjs
js 复制代码
import resolve from "@rollup/plugin-node-resolve";

export default defineConfig([
  {
    //...
    plugins: [resolve()],
  },
]);
  1. 运行 yarn add lodash-es,安装lodash-es包进行测试

  2. 修改src/index.js

js 复制代码
import { add } from "lodash-es"; //引入第三方包

export default function () {
  console.log("sum " + add(2 + 4));
}
  1. 使用 yarn run build 打包。这里如果不使用@rollup/plugin-node-resolve,会报错:Uncaught TypeError: Cannot read properties of undefined (reading 'add')

4. @rollup/plugin-commonjs(将第三方包CommonJS转ES)

  1. 安装

yarn add @rollup/plugin-commonjs -D

  1. 修改rollup.config.mjs
js 复制代码
import commonjs from "@rollup/plugin-commonjs";

export default defineConfig([
  {
    //...
    plugins: [commonjs()],
  },
]);
  1. 运行 yarn add ms,安装ms包进行测试

  2. 修改src/index.js

js 复制代码
import ms from "ms"; //引入CommonJS类型包

export default function () {
  console.log(ms("2 days"));
}
  1. 使用 yarn run build 进行打包

5. @rollup/plugin-alias(路径别名)

  1. 安装

yarn add @rollup/plugin-alias -D

  1. 修改rollup.config.mjs
js 复制代码
import alias from "@rollup/plugin-alias";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export default defineConfig([
  {
    //...
    plugins: [
      //之前的插件保留,在plugins数组末尾添加alias
      alias({
        entries: [{ find: "@", replacement: path.resolve(__dirname, "src") }],
      }),
    ],
  },
]);
  1. 新建src/utils/index.js
js 复制代码
export function Add(a, b) {
  return a + b;
}
  1. 修改src/index.js
js 复制代码
import { Add } from "@/utils";

export default () => {
  console.log(Add(1, 2));
};
  1. 使用 yarn run build 进行打包

代码分割

1. 自动拆分代码块

rollup对于使用import()方式引入的文件,会自动将代码拆分成块,并以chunk-hash.js的格式命名文件,其中hash是基于内容的哈希字符串。

由于umd模块不支持代码分割,并且打包后从一个文件变为一个文件夹,因此需要做一些调整

  1. 修改package.json
js 复制代码
"main": "dist/cjs", 
"module": "dist/es",
  1. 修改rollup.config.mjs
js 复制代码
export default defineConfig([
  {
    //...
    output: [
      {
        dir: pkg.main, //出口文件夹
        format: "cjs", //打包成CommonJS模块
      },
      {
        dir: pkg.module, //出口文件夹
        format: "es", //打包成es module模块
      },
    ],
  },
]);
  1. 修改src/index.js
js 复制代码
export default function () {
  import("./foo.js").then(({ default: foo }) => console.log(foo));
}
  1. 运行yarn run build

  2. 修改index.html,进行测试

js 复制代码
<!-- 测试es module模块 -->
<script type="module">
  import main from "./dist/es/index.js";
  main();
</script>

2. 显式拆分代码块

通过配置 output.manualChunks 显式地将模块拆分成单独的块。常用于拆分第三方包。

  1. 修改rollup.config.mjs
js 复制代码
export default defineConfig([
  {
    //...
    output: [
      {
        dir: pkg.main, //出口文件
        format: "cjs", //打包成CommonJS模块
        manualChunks: {
          lodash: ["lodash-es"],
        },
      },
      {
        dir: pkg.module, //出口文件
        format: "es", //打包成es module模块
        manualChunks: {
          lodash: ["lodash-es"],
        },
      },
    ],
  },
]);
  1. 修改src/index.js
js 复制代码
import { add } from "lodash-es";

export default function () {
  console.log("sum " + add(2 + 4));
}
  1. 使用 yarn run build 运行 Rollup,可以看到lodash已被拆分出来,形成一个单独的文件

使用 babel

将es6代码转es5,以兼容旧版浏览器、特定的移动设备等

  1. 安装

@rollup/plugin-babel:在 Rollup 打包过程中使用 Babel 进行代码转换

@babel/core:babel核心库

@babel/preset-env:将ES6转换为向后兼容的JavaScript

@babel/plugin-transform-runtime:处理async,await、import()等语法关键字的帮助函数

js 复制代码
yarn add @rollup/plugin-babel -D
yarn add @babel/core -D
yarn add @babel/preset-env -D
yarn add @babel/plugin-transform-runtime -D
  1. 修改rollup.config.mjs
js 复制代码
import { babel } from "@rollup/plugin-babel";

export default defineConfig([
  {
    //...
    plugins: [
      babel({
        babelHelpers: "runtime",
        presets: ["@babel/preset-env"],
        plugins: [["@babel/plugin-transform-runtime", { useESModules: true }]],
      }),
    ],
  },
]);

注意:如果使用了@rollup/plugin-commonjs,@rollup/plugin-commonjs一定要在@rollup/plugin-babel之前调用

js 复制代码
import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';

const config = {
  ...
  plugins: [
    commonjs(),
    babel(...)
  ],
};
  1. 修改src/index.js
js 复制代码
import foo from "./foo";

export default () => {
  console.log(foo);
};
  1. 使用 yarn run build 运行 Rollup,在dist/es/index.js文件已将es6转es5

处理 Sass

1. 打包支持sass文件

rollup-plugin-postcss 默认集成了对 scss、less、stylus 的支持

  1. 安装
js 复制代码
yarn add sass -D
yarn add postcss rollup-plugin-postcss -D
  1. 修改rollup.config.mjs
js 复制代码
import postcss from "rollup-plugin-postcss";

export default defineConfig([
  {
    //...
    plugins: [
      postcss(),
    ],
  },
]);
  1. 新建src/foo.scss
js 复制代码
$color: red;

body {
  background-color: $color;
  display: flex;
}
  1. 修改src/index.js
js 复制代码
import foo from "./foo.js";
import "./foo.scss";

export default function () {
  console.log(foo);
}
  1. 使用 yarn run build 运行 Rollup,在dist/es/index.js中可以看到样式被打包了进去

2. css加前缀

  1. 安装

yarn add autoprefixer -D

  1. 更新 packages.json
js 复制代码
"browserslist": [
    "defaults",
    "not ie < 8",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]
  1. 修改rollup.config.mjs
js 复制代码
import postcss from "rollup-plugin-postcss";
import autoprefixer from "autoprefixer";

export default defineConfig([
  {
    //...
    plugins: [
      postcss({
        plugins: [autoprefixer()],
      }),
    ],
  },
]);

3. css压缩

  1. 安装

yarn add cssnano -D

  1. 修改rollup.config.mjs
js 复制代码
import postcss from "rollup-plugin-postcss";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";

export default defineConfig([
  {
    //...
    plugins: [
      postcss({
        plugins: [autoprefixer(), cssnano()],
      }),
    ],
  },
]);

4. 抽离单独的css文件

修改rollup.config.mjs

js 复制代码
export default defineConfig([
  {
    //...
    plugins: [
      postcss({
        plugins: [autoprefixer(), cssnano()],
        extract: "css/index.css",
      }),
    ],
  },
]);

使用 yarn run build 运行 Rollup,在dist/es中可以看到样式已被拆分出去,单独在css文件夹下

处理 Typescript

1. typescript 插件

  1. 安装

yarn add rollup-plugin-typescript2 typescript tslib -D

  1. 新建src/bar.ts
js 复制代码
const str = "hello ts!";

export default str;
  1. 修改src/index.js
js 复制代码
import bar from "./bar.ts";
import "./foo.scss";

export default function () {
  console.log(bar);
}
  1. 修改rollup.config.mjs
js 复制代码
import typescript from "rollup-plugin-typescript2";

export default defineConfig([
  {
    //...
    plugins: [typescript()]
  },
]);

注意点:

  1. 如果使用了@rollup/plugin-node-resolve,@rollup/plugin-node-resolve要在rollup-plugin-typescript2之前调用
js 复制代码
const config = {
    ...
    plugins: [
      resolve(),
      typescript(...)
    ],
  };
  1. 如果使用了@rollup/plugin-babel,需配置babel扩展
js 复制代码
import { DEFAULT_EXTENSIONS } from '@babel/core';

export default defineConfig([
  {
    //...
    plugins: [
      babel({
        babelHelpers: 'runtime',
        presets: ["@babel/preset-env"],
        plugins: [["@babel/plugin-transform-runtime", { useESModules: true }]],
        extensions: [...DEFAULT_EXTENSIONS, ".ts", ".tsx"], //增加配置
      }),
    ],
  },
]);

2. 导出类型声明文件

新建tsconfig.json

js 复制代码
{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "declaration": true, //生成声明文件
    "outDir": "dist",
    "rootDir": "src"
  },
  "exclude": ["node_modules", "dist"]
}

tsconfig.json配置会与typescript()配置合并,并覆盖其默认配置

使用 yarn run build 运行 Rollup,在dist/cjs与dist/es下会分别生成一份ts声明文件

优化

1. 打包前清空原打包目录

  1. 安装 rimraf 和 rollup-plugin-delete
js 复制代码
yarn add rimraf -D //删除打包目录
yarn add rollup-plugin-delete -D //设置要删除的文件或目录
  1. 修改rollup.config.mjs
js 复制代码
import { rimrafSync } from "rimraf";
import del from 'rollup-plugin-delete';

rimrafSync("dist");// 删除打包目录

export default defineConfig([
  {
    //...
    plugins: [
      del({ targets: "dist/*" }),
    ],
  },
]);

2. 打包产物清除调试代码

  1. 安装

yarn add @rollup/plugin-strip -D

  1. 修改rollup.config.mjs
js 复制代码
import strip from "@rollup/plugin-strip";

export default defineConfig([
  {
    //...
    plugins: [
      strip()
    ],
  },
]);
  1. 修改src/index.js
js 复制代码
import bar from "./bar.ts";
import "./foo.scss";

export default function () {
  console.log(bar);
  document.querySelector("body").innerHTML = bar;
}

使用 yarn run build 运行 Rollup,可以看到console.log部分的代码,在打包的时候已被删除

调试

1. 建立链接

例如当前项目名为rollup,想作为一个第三方包

在项目中运行npm link,将rollup项目创建成本地依赖包

在项目例如名为my-app中,运行 npm link rollup 建立软链接,link后面的rollup为rollup项目中的package.json的name值

建立软链接之后,可以在项目my-app 的node_models中找到rollup,之后修改rollup包里的内容会实时更新,不用再运行npm link

在项目my-app中使用

js 复制代码
import main from 'rollup'; 
main();

2. 解除链接

解除项目my-app 的rollup依赖,在项目 my-app 中运行

npm unlink rollup

发布

1. 发布到npm

  1. 如果以前改过npm的镜像地址,比如使用了淘宝的镜像,就先改回来

npm config set registry https://registry.npmjs.org/

  1. 如果没有npm账户,先去官网注册www.npmjs.com/ ,注册完去邮箱点击链接,完成认证

  2. 在项目终端登录 npm login,输入用户信息后会提示二次验证,最后把邮件的验证码输入即可

  3. 修改package.json

js 复制代码
"files": [
    "dist/es/*",
    "dist/cjs/*"
  ],

配置files,设置需要推送到npm的文件

  1. 进行发布

npm publish

(如果包名有@符号,例如@username/component,默认为私有包,需要加上 --access public参数)

备注:这里包名为rollup,肯定会与npm上现有的包名重复,发布时会报错。需要修改package.json的name属性,重新设置属于自己的包名

  1. 可在npm个人主页查看上传的包

上传成功后,使用npm进行下载,可在node_modules中看到我们只上传了打包后的代码

2. 发布npm包时可能会遇到的一些坑

  1. 邮箱未验证
js 复制代码
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! you must verify your email before publishing a new package: https://www.npmjs.com/email-edit : your-package

去邮箱验证

  1. 没有权限发布
js 复制代码
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You do not have permission to publish "your-package". Are you logged in as the correct user? : your-package

包和别人的包重名了。修改包名

  1. 需要登录
js 复制代码
npm ERR! code ENEEDAUTH
npm ERR! need auth auth required for publishing
npm ERR! need auth You need to authorize this machine using `npm adduser`

使用 npm login 登录

  1. 只有管理员才有权限发布
js 复制代码
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! [no_perms] Private mode enable, only admin can publish this module [no_perms] Private mode enable, only admin can publish this module: your-package

源设置成第三方源,比如设置了淘宝镜像。只要把源设为默认的即可

npm config set registry http://registry.npmjs.org

  1. 包名过于类似
js 复制代码
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! Package name too similar to existing packages; try renaming your package to '@hopgoldy/auto-git' and publishing with 'npm publish --access=public' instead : your-package
  1. 无法发布私有包
js 复制代码
npm ERR! publish Failed PUT 402
npm ERR! code E402
npm ERR! You must sign up for private packages :

这个当你的包名为@your-name/your-package时才会出现,原因是当包名以@your-name开头时,npm publish会默认发布为私有包,但是 npm 的私有包需要付费,所以需要添加如下参数进行发布:

npm publish --access public

或者在package.json中添加

js 复制代码
"publishConfig": {
    "access": "public"
  },
  1. 包的命名空间

lerna ERR! E404 Scope not found

点击链接创建包的命名空间:www.npmjs.com/org/create

结尾

  1. 文章首发于云在前端公众号,未经许可禁止转载!

  2. 分享不易,点赞支持

相关推荐
To_OC8 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic8 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy8 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志9 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble10 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯11 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜11 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司12 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g12 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森12 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http