{T}

组件库开发

项目初始化

bash
npm init -y

# 安装 rollup 打包插件
npm i -D rollup

新建 src/index.js 文件

javascript
console.log("Hello World!!")

export default {}

创建配置文件 rollup.config.dev.js

javascript
const path = require("path")
const inputPath = path.resolve(__dirname, "./src/index.js")
const outputPath = path.resolve(__dirname, "./dist/datav.js")
console.log("inputPath", inputPath)

module.exports = {
  input: inputPath,
  output: {
    file: outputPath,
    format: "umd",
    name: "datav"
  }
}

添加脚本并执行 pnpm dev

json
{
  "name": "datav-libs",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "rollup -wc rollup.config.dev.js",
    "build": "rollup -c rollup.config.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "packageManager": "pnpm@9.12.2+sha1.3012e6dd27e70ec4e185be062e8a124523dccfc4",
  "devDependencies": {
    "rollup": "^4.42.0"
  }
}

模块化标准

原打包文件 src/index.js

javascript
console.log("Hello World!!")

export default {}
  1. umd 标准
javascript
;(function (global, factory) {
  typeof exports === "object" && typeof module !== "undefined"
    ? (module.exports = factory())
    : typeof define === "function" && define.amd
    ? define(factory)
    : ((global = typeof globalThis !== "undefined" ? globalThis : global || self), (global.datav = factory()))
})(this, function () {
  "use strict"

  console.log("Hello World!!")

  var index = {}

  return index
})

html 使用的方式

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="../dist/datav.js"></script>
  </head>
  <body></body>
</html>
  1. cjs 标准
javascript
"use strict"

console.log("Hello World!!")

var index = {}

module.exports = index

打包后的 cjs 模块,浏览器不可以直接使用,需要使用 webpack 工具打包成 umd 文件

  1. es
javascript
console.log("Hello World!!")

var index = {}

export { index as default }

html 使用

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="../dist/datav.js" type="module"></script>
  </head>
  <body></body>
</html>

rollup-plugin-node-resolve

rollup-plugin-node-resolve 是 Rollup 的核心插件,用于解析第三方模块的路径(如 node_modules 中的依赖),确保 Rollup 能够正确找到并打包这些模块。它解决了 Rollup 默认不处理 node_modules 依赖的问题,是构建复杂项目时的必备工具

  1. 解析 node_modules 中的模块 Rollup 默认只处理相对路径(如 ./utils.js),而无法直接识别 import lodash from 'lodash' 这样的模块引用。该插件会按照 Node.js 的模块解析规则(如 mainmoduleexports 字段)定位依赖。
  2. 支持模块扩展名自动补全 自动尝试添加 .js.json.node 等扩展名(类似 Webpack 的 resolve.extensions
  3. 处理子路径和深层依赖 例如 import 'lodash/get' 或嵌套的 node_modules 依赖
  4. 自定义主入口字段 可优先选择 module(ESM)或 main(CJS)等字段,优化 Tree Shaking

基本使用

bash
npm install -D rollup-plugin-node-resolve

在 Rollup 配置文件中引入插件:

JavaScript
import resolve from 'rollup-plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve()
  ]
};

常用配置选项:

选项说明
mainFields指定模块入口字段的优先级(默认 ['module', 'main'])。
extensions解析时尝试的扩展名(默认 ['.js', '.json', '.node'])。
modulesOnly是否仅解析 ES Modules(默认 false,也支持 CJS)。
jail将解析限制在指定目录内(如 jail: '/src')。
dedupe强制使用同一版本的依赖(如 dedupe: ['lodash'])。
preferBuiltins是否优先使用 Node.js 内置模块(如 fs,默认 true)。

示例:自定义配置:

JavaScript
resolve({
  mainFields: ['module', 'main'],  // 优先使用 ESM 入口
  extensions: ['.mjs', '.js', '.jsx'], // 支持更多扩展名
  modulesOnly: true,               // 仅打包 ESM 模块
  dedupe: ['react', 'react-dom']   // 避免重复依赖
})

与其他插件的协作

  1. @rollup/plugin-commonjs 搭配 如果依赖是 CommonJS 格式(如 Lodash),需先用 commonjs 插件转换为 ESM:

    JavaScript
    import commonjs from '@rollup/plugin-commonjs';
    import resolve from 'rollup-plugin-node-resolve';
    
    plugins: [
      resolve(),
      commonjs()
    ]
  2. rollup-plugin-babel 搭配 在解析模块后使用 Babel 转译:

    JavaScript
    import babel from 'rollup-plugin-babel';
    
    plugins: [
      resolve(),
      babel({ presets: ['@babel/preset-env'] })
    ]

@babel/node 全局使用

@babel/node 是 Babel 提供的命令行工具,它允许你直接使用 Babel 转译和运行 Node.js 脚本(支持 ES6+ 语法、JSX、TypeScript 等)。它相当于一个集成了 Babel 的 node 替代品,适合开发阶段的快速调试和原型开发。主要用途就是将 es6 的代码转换为 es5 的代码,babel 官网

  1. 实时转译 在运行 .js.jsx.ts 等文件时,自动通过 Babel 转译代码(基于项目中的 Babel 配置,如 .babelrcbabel.config.js
  2. 支持最新语法 允许直接使用 ES Modules (import/export)、装饰器、类属性等尚未被 Node.js 原生支持的语法
  3. REPL 环境 提供交互式 REPL 环境(类似原生 node 的 REPL),支持实验性语法

全局安装模块:

bash
npm i -g @babel/node
npm i -g @babel/core

注意@babel/node 依赖 @babel/core 需同时安装

基本用法

  1. 直接运行文件 通过 babel-node 命令执行脚本:

    bash
    babel-node script.js
  2. REPL 模式 启动支持 Babel 转译的交互式环境:

    bash
    zhangzhengyang@zhangzhengyang datav-libs % babel-node
    babel > require("./src/index.js")
    Hello World!!
    {}
    babel > require("./src/index.js")
    {}
    babel > require("./src/index.js")
    {}
    babel >
  3. 结合 Babel 配置 确保项目根目录有 Babel 配置文件(如 .babelrc),例如:

    bash
    {
      "presets": ["@babel/preset-env"]
    }

常见选项:

选项说明
--presets指定使用的 Babel preset(覆盖配置)
--extensions指定处理的文件扩展名(默认 .js, .jsx, .es6, .es, .mjs
--ignore忽略的文件/目录(如 --ignore "node_modules"

示例:

bash
npx babel-node --presets @babel/preset-env,@babel/preset-react src/app.jsx
DANGER

不适用于生产环境 @babel/node 每次运行都会实时转译代码,性能较差。生产环境应先用 babel-cli 或构建工具(如 Webpack)预编译代码,再用原生 node 运行

ts-node 的区别:

  • @babel/node:通过 Babel 转译(支持 JS/TS,但类型检查需额外配置)
  • ts-node:专为 TypeScript 设计,集成类型检查,通常对 TS 项目更友好

.babelrc 文件

需要在项目根目录创建 .babelrc 文件和安装 @babel/preset-env 模块

bash
npm i -D @babel/preset-env

.babelrc 文件内容:

shell
{
  "presets": [
    "@babel/env"
  ]
}

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime 是 Babel 的一个核心插件,用于优化代码转译过程中的**辅助函数(helper functions)内置特性(如 PromiseSymbol 等)**的复用,从而减少代码体积、避免全局污染,并提升兼容性。它通常与 @babel/runtime 配合使用

核心功能

  1. 复用辅助函数
  • 问题:Babel 转译语法(如 classasync/await)时会自动生成辅助函数(如 _classCallCheck_asyncToGenerator)。默认情况下,这些函数会直接插入到每个文件中,导致代码冗余
  • 解决transform-runtime 将这些辅助函数改为从 @babel/runtime 中按需引入,减少重复代码
  1. 避免全局污染
  • 问题:转译 PromiseSymbol 等新 API 时,Babel 默认会通过全局注入 polyfill(如 core-js),可能污染全局环境
  • 解决transform-runtime 以模块化方式引入这些 API,避免直接修改全局对象
  1. 支持沙箱环境
  • 适合开发库(Library)或工具包,确保代码不会因 polyfill 影响宿主环境

需同时安装插件和运行时依赖:

bash
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime  # 必须作为生产依赖

注意:@babel/runtime 是生产依赖,因为转译后的代码会直接引用它

修改配置文件 rollup.config.dev.js

js
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3,          // 可选:指定 core-js 版本(默认 false)
        "helpers": true,      // 是否复用辅助函数(默认 true)
        "regenerator": true   // 是否复用 generator 函数(默认 true)
      }
    ]
  ]
}

关键配置选项:

选项说明
corejs使用 core-js 的版本(false23)。需额外安装 @babel/runtime-corejs3
helpers是否复用 Babel 的辅助函数(如 _classCallCheck),默认 true
regenerator是否复用 regenerator-runtime(转译 async/await),默认 true
useESModules是否使用 ES Modules 语法引入 helpers(优化 Tree Shaking)

示例对比

未使用 transform-runtime,转译后的代码直接插入辅助函数:

javascript
// 输入代码
class Foo {}

// 输出代码
function _classCallCheck(instance, Constructor) {
  /*...*/
}
var Foo = function Foo() {
  _classCallCheck(this, Foo)
}

使用 transform-runtime,辅助函数从 @babel/runtime 引入:

JavaScript
// 输出代码
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
var Foo = function Foo() {
  _classCallCheck(this, Foo);
};

tree-shaking 机制

Rollup 的 Tree-Shaking(摇树优化)是一种通过静态分析移除 JavaScript 代码中未使用部分(dead code)的机制。它依赖于 ES Modules(ESM)的静态结构特性,能够显著减少打包体积,提升运行效率

核心原理

静态分析的基础:ES Modules

  • ESM 的静态特性import/export 语句必须在模块顶层声明(不能动态生成),这使得 Rollup 在打包时能明确追踪模块的依赖关系和导出/导入情况。
  • 对比 CommonJS:CommonJS 的 require() 是动态的,无法在构建时确定依赖关系,因此传统工具(如 Webpack 4 之前)难以优化。

作用域与副作用分析

  • 作用域隔离:Rollup 分析每个模块的导入导出,标记未被引用的导出。
  • 副作用检测:通过静态代码分析(或手动注解)判断模块是否有副作用(如修改全局变量、触发 IIFE 等)。无副作用的代码更容易被移除

工作流程

  1. 依赖图谱构建 Rollup 从入口文件出发,递归分析所有 import 语句,生成完整的依赖关系图
  2. 标记活跃代码
    • 从入口开始,标记所有被直接或间接引用的导出(如函数、变量、类)
    • 未被引用的导出标记为“未使用”(例如未使用的工具函数或冗余代码)
  3. 副作用检查
    • 如果模块未被标记为“有副作用”(如 package.json"sideEffects": false),则安全移除未引用的代码
    • 无法确定副作用的代码默认保留(保守策略)
  4. 生成最终包 仅包含被标记的活跃代码和必要的依赖,剔除未使用的部分

关键配置与优化技巧

  1. 启用 Tree-Shaking 的条件

使用 ESM 格式:确保源码和依赖的第三方库提供 ESM 版本(优先 "module" 字段)。修改 Rollup 配置:默认启用,无需额外设置,但需配合以下优化:

JavaScript
// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm', // 输出 ESM 格式以支持下游进一步优化
  },
  treeshake: true, // 默认开启,可配置详细规则
};
  1. 手动标记副作用

package.json 中声明:

json
{
  "sideEffects": false, // 整个库无副作用
  // 或指定有副作用的文件
  "sideEffects": ["**/*.css", "**/*.global.js"]
}
  • 代码注释:在模块顶部添加 /*#__PURE__*/ 标记纯函数调用(如 /*#__PURE__*/ someFunction()
  1. 第三方库的优化
  • 优先选择 ESM 版本:如 lodash-es 替代 lodash
  • 排除无 Tree-Shaking 的库:某些库(如 jQuery)因大量副作用需手动添加到 external

实际效果示例:

  1. 未优化前的代码
JavaScript
// utils.js
export const usedFunc = () => console.log('I am used');
export const unusedFunc = () => console.log('I am unused');

// main.js
import { usedFunc } from './utils';
usedFunc();
  1. 使用 Tree-Shaking 后输出,unusedFunc 被完全移除,因为它未被引用且无副作用
JavaScript
// dist/bundle.js
const usedFunc = () => console.log('I am used');
usedFunc();

常见问题与解决

为什么某些代码未被移除?

  • 原因:模块被标记为有副作用,或 Rollup 无法静态分析动态代码(如 eval()
  • 解决:检查 sideEffects 配置,或使用 /*#__PURE__*/ 标记纯函数

如何验证 Tree-Shaking 效果?

  • 使用 Rollup 的 output.hoistTransitiveImports: false 查看详细依赖
  • 分析输出文件(如通过 rollup-plugin-visualizer 生成依赖图)

如何处理 CommonJS 依赖?

  • 使用 @rollup/plugin-commonjs 转换 CJS 为 ESM,但可能损失部分优化效果

external 属性

external 是 Rollup 配置中的重要属性,用于声明某些模块不打包到最终的输出文件中,而是作为外部依赖(如通过 <script> 标签引入的 CDN 资源或 Node.js 的 require/import)。它适用于以下场景:

  • 避免重复打包(如 reactlodash 等公共库)
  • 依赖由运行时环境提供(如浏览器全局变量 jQuery 或 Node.js 内置模块 fs
  • 优化构建速度(跳过大型库的解析和打包)

基本用法

在 Rollup 配置中,external 可以接受以下类型的值:

  • 字符串(匹配模块名或路径)
  • 正则表达式(匹配一类模块)
  • 函数(动态判断是否外部化)

示例配置:

JavaScript
// rollup.config.js
export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  external: [
    'react',                  // 直接声明模块名
    /^lodash\/.+/,            // 正则匹配子路径(如 lodash/get)
    (id) => id.includes('jquery') // 函数动态判断
  ]
};
  1. 字符串匹配
  • 精确匹配模块名external: ['react', 'lodash'] 会排除所有 import 'react'import 'lodash' 的引用
  • 路径匹配external: ['src/utils'] 排除本地文件
  1. 正则表达式匹配
  • 排除所有 lodash 的子路径:external: [/^lodash\/.+$/]。效果:import 'lodash/get' 会被外部化,但 import 'lodash' 不会(需单独声明)
  1. 函数动态判断
  • 根据模块 ID 动态决策:

    JavaScript
    external: (id) => {
      // 排除 node_modules 中的依赖或 Node.js 内置模块
      return id.startsWith('node:') || /node_modules/.test(id);
    }

不同场景下的配置

  1. 浏览器环境(CDN 引入)

假设 reactlodash 通过 <script> 标签全局挂载:

html
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/lodash@4/lodash.min.js"></script>

Rollup 配置:

JavaScript
external: ['react', 'lodash'],
output: {
  format: 'iife',
  globals: {
    react: 'React',      // 将 import 'react' 映射到全局变量 React
    lodash: '_'          // 将 import 'lodash' 映射到全局变量 _
  }
}
  1. Node.js 内置模块

排除 fspath 等内置模块:

javascript
external: ["fs", "path", "node:events"]
  1. peerDependencies(库开发)

开发一个库时,声明 peerDependencies 不打包:

JavaScript
external: Object.keys(require('./package.json').peerDependencies)

注意事项

  1. 必须配合 output.globals(UMD/IIFE 格式)

如果输出格式为 umdiife,需通过 globals 指定外部模块的全局变量名,否则运行时会报错:

JavaScript
output: {
  format: 'umd',
  globals: {
    react: 'React',
    lodash: '_'
  }
}
  1. commonjs 插件的协作

如果依赖是 CommonJS 模块(如 lodash),需先通过 @rollup/plugin-commonjs 转换,再外部化:

JavaScript
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';

export default {
  plugins: [
    resolve(),
    commonjs()
  ],
  external: ['lodash']
};
  1. 避免误排除

错误的 external 配置会导致运行时缺失依赖。可通过以下方式调试:

  • 检查打包后的文件是否包含 requireimport 语句
  • 使用 rollup-plugin-visualizer 分析依赖图

示例:修改 rollup.js 的配置文件:

javascript
import resolve from "rollup-plugin-node-resolve"

export default {
  input: "./src/plugin/main.js",
  output: [
    {
      file: "./dist/index-plugin-cjs.js",
      format: "cjs"
    },
    {
      file: "./dist/index-plugin-es.js",
      format: "es"
    }
  ],
  plugins: [resolve()],
  external: ["vue"]
}

重新打包:

javascript
rollup -c rollup.plugin.config.js

查看 dist/index-plugin-es.js,可以看到虽然使用 resolve 插件,vue 库仍被当做外部库处理

@rollup/plugin-commonjs 插件

@rollup/plugin-commonjs 是 Rollup 的核心插件,用于将 CommonJS (CJS) 模块转换为 ES Modules (ESM),使 Rollup 能够正确解析和打包依赖项。由于 Rollup 原生仅支持 ESM,而许多 npm 包(如 lodashreact 等)仍采用 CommonJS 格式,因此该插件在 Rollup 生态中至关重要

bash
npm install @rollup/plugin-commonjs -D

基本使用

基本配置:

JavaScript
// rollup.config.js
import commonjs from '@rollup/plugin-commonjs';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    commonjs() // 默认配置
  ]
};

常用配置选项:

选项说明
include指定需要转换的模块(如 include: /node_modules/)。
exclude排除不需要转换的模块(如 exclude: /node_modules\/lodash-es/)。
extensions指定解析的文件扩展名(默认 ['.js', '.cjs'])。
ignoreGlobal是否忽略全局变量(如 processBuffer),默认 false
sourceMap是否生成 sourcemap,默认 true
transformMixedEsModules是否转换混合 ESM/CJS 模块(如 import + module.exports),默认 false
dynamicRequireTargets指定动态 require() 的目标模块(如 ['node_modules/**/*.js'])。

示例:高级配置

JavaScript
commonjs({
  include: /node_modules/, // 仅转换 node_modules 中的 CJS 模块
  exclude: ['node_modules/lodash-es'], // 排除已为 ESM 的 lodash-es
  extensions: ['.js', '.cjs'], // 处理 .js 和 .cjs 文件
  ignoreGlobal: true, // 不注入全局变量
  dynamicRequireTargets: [
    'node_modules/debug/**/*.js' // 处理 debug 模块的动态 require
  ]
})

常见问题与解决

  1. 动态 require() 报错

问题:某些库(如 debug)使用动态 require(),Rollup 默认无法解析 ​解决​:通过 dynamicRequireTargets 指定目标模块:

JavaScript
commonjs({
  dynamicRequireTargets: ['node_modules/debug/**/*.js']
})
  1. 混合 ESM/CJS 模块报错

问题:模块同时使用 importmodule.exports,导致转换失败 ​解决​:启用 transformMixedEsModules

JavaScript
commonjs({
  transformMixedEsModules: true
})
  1. 全局变量缺失(如 process

问题:浏览器环境缺少 Node.js 全局变量(如 process)。 ​解决​:

  1. 使用 rollup-plugin-inject 注入变量:

    JavaScript
    import inject from '@rollup/plugin-inject';
    
    plugins: [
      commonjs(),
      inject({ process: 'process' }) // 注入 process
    ]
  2. 或通过 @rollup/plugin-replace 替换:

    javascript
    import replace from "@rollup/plugin-replace"
    
    plugins: [replace({ "process.env.NODE_ENV": JSON.stringify("production") })]

@rollup/plugin-babel

@rollup/plugin-babel 是 Rollup 的官方插件,用于在打包过程中通过 Babel 转译 JavaScript 代码,支持 ES6+ 语法、JSX、TypeScript 等新特性,同时兼容旧浏览器或 Node.js 环境。它是 Rollup 生态中实现代码兼容性的核心工具之一

  1. 语法降级 将 ES6+ 代码(如 箭头函数async/await类属性)转换为 ES5 语法
  2. JSX/TSX 转换 支持 React/Vue 的 JSX 或 TypeScript 语法转译
  3. 按需 Polyfill 配合 @babel/preset-env 按目标环境自动引入必要的 polyfill
  4. 代码优化 移除类型注解(TypeScript)、死代码(Dead Code Elimination)
  5. Sourcemap 支持 生成准确的 Sourcemap,便于调试
bash
npm i @rollup/plugin-babel -D

修改配置文件,增加 babel 插件的引用:

javascript
const path = require("path")

// 将项目中的依赖打包进包中
const resolve = require("rollup-plugin-node-resolve")
const vuePlugin = require("rollup-plugin-vue")
const postcss = require("rollup-plugin-postcss")
const { babel } = require("@rollup/plugin-babel")
// const commonjs = require("rollup-plugin-commonjs")
const json = require("rollup-plugin-json")

const inputPath = path.resolve(__dirname, "./src/index.js")
const outputUmdPath = path.resolve(__dirname, "./dist/datav.js")
const outputEsPath = path.resolve(__dirname, "./dist/datav.es.js")
console.log("inputPath", inputPath)

module.exports = {
  input: inputPath,
  output: [
    {
      file: outputUmdPath,
      format: "umd",
      name: "datav",
      globals: {
        vue: "Vue" // 指定全局变量
      }
    },
    {
      file: outputEsPath,
      format: "es"
    }
  ],
  plugins: [
    resolve(),
    // commonjs(),
    babel({
      exclude: "node_modules/**",
      babelHelpers: "runtime", // 替换为新的配置
      plugins: [
        [
          "@babel/transform-runtime",
          {
            regenerator: true // 默认false
          }
        ]
      ]
    }),
    json(),
    vuePlugin(),
    postcss({
      plugins: []
    })
  ],
  external: ["vue"]
}

rollup-plugin-json 插件

默认情况下 rollup.js 不支持导入 json 模块,需要使用 json 插件来支持

bash
npm i -D rollup-plugin-json

修改配置文件:

javascript
import resolve from "rollup-plugin-node-resolve"
import commonjs from "rollup-plugin-commonjs"
import babel from "rollup-plugin-babel"
import json from "rollup-plugin-json"

export default {
  input: "./src/plugin/main-json.js",
  output: [
    {
      file: "./dist/index-plugin-cjs.js",
      format: "cjs"
    },
    {
      file: "./dist/index-plugin-es.js",
      format: "es"
    }
  ],
  plugins: [resolve(), commonjs(), babel(), json()]
}

重新打包:rollup -c rollup.plugin.config.js。查看dist/index-plugin-cjs.js源码,可以看到 json 文件被解析为一个对象进行处理

javascript
var name = "rollup-test"
var version = "1.0.0"
var description = ""
var main = "index.js"
var scripts = {
  test: 'echo "Error: no test specified" && exit 1'
}
var author = ""
var license = "ISC"
var devDependencies = {
  "@babel/core": "^7.1.6",
  "@babel/plugin-external-helpers": "^7.0.0",
  "@babel/preset-env": "^7.1.6",
  rollup: "^0.67.3",
  "rollup-plugin-babel": "^4.0.3",
  "rollup-plugin-commonjs": "^9.2.0",
  "rollup-plugin-json": "^3.1.0",
  "rollup-plugin-node-resolve": "^3.4.0"
}
var dependencies = {
  epubjs: "^0.3.80",
  loadsh: "^0.0.3",
  "sam-test-data": "^0.0.4",
  "sam-test-data-cjs": "^0.0.1",
  "sam-test-data-es": "^0.0.1",
  "sam-test-data-umd": "^0.0.1"
}
var json = {
  name: name,
  version: version,
  description: description,
  main: main,
  scripts: scripts,
  author: author,
  license: license,
  devDependencies: devDependencies,
  dependencies: dependencies
}

console.log(json.name, json.main)

@rollup/plugin-terser

@rollup/plugin-terser 是 Rollup 的官方插件,用于压缩(Minify)和混淆(Obfuscate)JavaScript 代码,显著减少打包体积并提升运行时性能。它基于 Terser(UglifyJS 的现代替代品),支持 ES6+ 语法,是生产环境构建的必备工具

  1. 代码压缩
    • 移除空格、注释、无效代码。
    • 缩短变量名(如 longVariableNamea
    • 优化表达式(如 !!aa
  2. Dead Code Elimination 结合 Rollup 的 Tree-Shaking 移除未使用的代码
  3. ES6+ 支持 正确处理 箭头函数const/let等新语法
  4. Sourcemap 生成 生成压缩后的 Sourcemap,便于调试生产环境代码
  5. 多线程压缩 支持并行处理,提升构建速度。
bash
npm install --save-dev @rollup/plugin-terser

基本使用

在 Rollup 配置中引入

JavaScript
// rollup.config.js
import { terser } from '@rollup/plugin-terser';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.min.js',
    format: 'esm'
  },
  plugins: [
    terser() // 默认配置
  ]
};

通常在其他转换插件(如 Babel)之后使用:

JavaScript
import babel from '@rollup/plugin-babel';
import { terser } from '@rollup/plugin-terser';

plugins: [
  babel(), // 先转译
  terser() // 后压缩
]

关键配置选项:

选项说明
format输出格式(默认继承 Rollup 的 output.format)。
mangle是否混淆变量名(默认 true)。
compress压缩配置(可禁用或细化规则)。
keep_classnames是否保留类名(默认 false,对依赖类名的库设为 true)。
keep_fnames是否保留函数名(默认 false,对依赖函数名的库设为 true)。
module是否输出 ES Module(默认 false,设为 true 可优化 Tree-Shaking)。
sourceMap是否生成 Sourcemap(默认 false,生产环境建议关闭)。
maxWorkers最大线程数(默认 4,提升多核 CPU 的压缩速度)。

示例:自定义配置

javascript
terser({
  mangle: {
    properties: true, // 混淆对象属性名
    reserved: ["$", "exports"] // 保留特定名称
  },
  compress: {
    drop_console: true, // 移除 console.log
    dead_code: true // 移除死代码
  },
  keep_classnames: false,
  format: {
    comments: false // 移除注释
  }
})

性能优化建议

  1. 启用多线程压缩;通过 maxWorkers 加速构建:
JavaScript
terser({
  maxWorkers: 4 // 根据 CPU 核心数调整
})
  1. 排除开发依赖;在开发构建中禁用压缩
JavaScript
// rollup.config.js
const isProduction = process.env.NODE_ENV === 'production';

export default {
  plugins: [
    isProduction && terser() // 仅生产环境启用
  ].filter(Boolean)
};
  1. 避免过度压缩

某些库依赖特定的变量名或类名(如 ReactRedux),需通过 reserved 保留:

JavaScript
terser({
  mangle: {
    reserved: ['React', 'Redux']
  }
})

常见问题与解决

  1. Sourcemap 错误

问题:压缩后 Sourcemap 不准确。 ​解决​:确保 Rollup 的 output.sourcemaptersersourceMap 配置一致:

JavaScript
output: {
  sourcemap: true // Rollup 生成 Sourcemap
},
plugins: [
  terser({ sourceMap: true }) // Terser 生成压缩后的 Sourcemap
]
  1. 变量名混淆导致运行时错误

问题:压缩后变量名被缩短,但某些代码依赖动态属性名(如 obj[key]) ​解决​:禁用特定属性的混淆:

JavaScript
terser({
  mangle: {
    properties: {
      regex: /^_/, // 不混淆以 _ 开头的属性
      reserved: ['propName'] // 保留特定属性名
    }
  }
})
  1. 压缩后代码体积未显著减少

原因:可能未启用 Tree-Shaking 或存在未优化的依赖。 ​检查步骤​:

  1. 确认 Rollup 的 output.formatesmcjsiife/umd 不易优化)

  2. 使用 rollup-plugin-visualizer 分析打包体积:

    bash
    npm install --save-dev rollup-plugin-visualizer

    配置:

    JavaScript
    import { visualizer } from 'rollup-plugin-visualizer';
    
    plugins: [
      visualizer() // 生成 stats.html
    ]

rollup-plugin-postcss

rollup-plugin-postcss 是 Rollup 生态中用于处理 CSS 的核心插件,它集成了 PostCSS 的强大功能,支持 CSS 预处理(Sass/Less)、模块化(CSS Modules)、自动前缀(Autoprefixer)、代码压缩 等特性,是现代前端项目打包 CSS 资源的首选工具

功能说明
CSS 预处理支持 Sass、Less、Stylus 等编译为 CSS。
CSS Modules局部作用域 CSS(生成哈希类名避免冲突)。
Autoprefixer自动添加浏览器前缀(如 -webkit-)。
CSS 压缩通过 cssnano 移除空格和注释。
CSS 代码拆分支持生成独立的 .css 文件(而非内联 JS)。
Sourcemap 生成开发环境调试友好。
Tree-Shaking结合 purgecss 移除未使用的 CSS。

需安装插件及常用配套工具:

bash
# 基础安装
npm install -D rollup-plugin-postcss postcss

# 可选:预处理、Autoprefixer、CSS Modules
npm install -D sass autoprefixer cssnano postcss-modules

# 或使用 Less/Stylus
npm install -D less stylus

基本使用

在 Rollup 中引入

JavaScript
// rollup.config.js
import postcss from 'rollup-plugin-postcss';

export default {
  input: 'src/main.js',
  output: { dir: 'dist', format: 'esm' },
  plugins: [
    postcss({
      plugins: [],       // PostCSS 插件(如 autoprefixer)
      extract: true,     // 提取为独立 CSS 文件
      modules: true,     // 启用 CSS Modules
      sourceMap: true,   // 开发环境启用 Sourcemap
    })
  ]
};

配合预处理语言(Sass/Less)

JavaScript
postcss({
  plugins: [],
  use: ['sass'], // 使用 Sass(需安装 sass)
  extract: true
})

关键配置选项:

选项类型说明
extractboolean是否提取 CSS 为独立文件(默认 false,内联到 JS 中)。
modulesboolean启用 CSS Modules(默认 false)。
sourceMapboolean生成 Sourcemap(默认 false)。
pluginsarrayPostCSS 插件列表(如 [autoprefixer(), cssnano()])。
usearray预处理语言(如 ['sass', 'less'])。
injectboolean将 CSS 注入 JS 并动态插入 <style> 标签(默认 false)。
minimizeboolean是否压缩 CSS(默认 production 环境为 true)。
configboolean是否读取 postcss.config.js(默认 true)。

高级用法

  1. 使用 CSS Modules
JavaScript
// rollup.config.js
postcss({
  modules: true,
  generateScopedName: '[name]__[local]___[hash:base64:5]' // 自定义类名格式
})

// 在 JS 中引用
import styles from './styles.module.css';
console.log(styles.myClass); // 输出哈希类名(如 `_myClass_1f2j3`)
  1. 自定义 PostCSS 插件
JavaScript
// postcss.config.js
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';

export default {
  plugins: [
    autoprefixer({ overrideBrowserslist: '> 0.5%' }),
    cssnano() // 生产环境压缩
  ]
};
  1. 按需引入 CSS(Tree-Shaking);结合 purgecss 移除未使用的 CSS:
JavaScript
import purgecss from '@fullhuman/postcss-purgecss';

postcss({
  plugins: [
    purgecss({
      content: ['./src/**/*.html', './src/**/*.js'] // 扫描 HTML/JS 中的类名
    })
  ]
})

与其他插件的协作

  1. @rollup/plugin-image 配合处理 CSS 中的图片资源:
JavaScript
import image from '@rollup/plugin-image';

plugins: [
  image(),
  postcss()
]
  1. rollup-plugin-livereload 配合开发环境实时刷新 CSS:
JavaScript
import livereload from 'rollup-plugin-livereload';

plugins: [
  postcss({ extract: true }),
  livereload({ watch: 'dist' }) // 监听 dist 目录变化
]

rollup 按需加载

直接引用使用的组件即可,这样在 build 时不会将整个组件库打包

注意:需要 babel 支持,参考:https://element.eleme.cn/#/zh-CN/component/quickstart

rollup-plugin-vue

rollup-plugin-vue 是 Rollup 的官方插件,用于直接打包 Vue 单文件组件,支持 Vue 2 和 Vue 3 的模板、样式和逻辑代码的解析与优化

功能说明
Vue SFC 支持解析 .vue 文件中的 <template><script><style>
模板编译将 Vue 模板转换为渲染函数(支持 Vue 2 的 compiler 和 Vue 3 的 @vue/compiler-sfc
CSS 处理支持 <style> 块的预处理(Sass/Less)、作用域 CSS(Scoped CSS)和 CSS Modules
热更新(HMR)开发环境下支持模块热替换
Tree-Shaking结合 Rollup 移除未使用的代码
Sourcemap 生成支持调试编译后的代码

安装:

bash
# Vue 2 项目
npm install --save-dev rollup-plugin-vue@5 vue-template-compiler

# Vue 3 项目
npm install --save-dev rollup-plugin-vue@6 @vue/compiler-sfc

注意rollup-plugin-vue 的 v5 对应 Vue 2,v6 对应 Vue 3,需匹配版本。

基本使用

Vue 2 配置示例

JavaScript
// rollup.config.js
import vue from 'rollup-plugin-vue';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve(), // 解析 node_modules 依赖
    commonjs(), // 转换 CommonJS 模块
    vue({
      css: true, // 提取 CSS 为独立文件
      compileTemplate: true // 显式启用模板编译
    })
  ]
};

Vue 3 配置示例

JavaScript
import vue from 'rollup-plugin-vue';
import { nodeResolve } from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: { dir: 'dist', format: 'esm' },
  plugins: [
    nodeResolve(),
    vue({
      target: 'browser', // 指定目标环境(browser/node)
      css: 'dist/vue-components.css' // 提取所有 CSS 到指定文件
    })
  ]
};

关键配置选项:

选项说明
css控制 CSS 处理方式: - true:提取为独立文件。 - false:内联到 JS 中。 - 字符串:指定提取的 CSS 文件名。
compileTemplate是否编译模板(默认 true)。
target目标环境:'browser'(默认)或 'node'(SSR 场景)。
style配置 <style> 块的处理: - postcss:启用 PostCSS 插件。 - trim:移除空格。
preprocessStyles预处理 <style> 块(支持 Sass/Less)。
preprocessOptions预处理器的配置(如 Sass 的 includePaths)。
hmr是否启用热更新(默认开发环境为 true)。
customBlocks处理自定义块(如 <docs>)。

常见问题与解决

  1. 模板编译报错(Vue 2)

问题Error: Cannot find module 'vue-template-compiler'。 ​解决​:确保安装了 vue-template-compiler 且版本与 vue 一致:

code
npm install vue-template-compiler@2.6.14 --save-dev
  1. 样式未提取为独立文件

问题css: true 无效。 ​解决​:确保 Rollup 的 output.dir(而非 output.file)已设置:

code
output: { dir: 'dist', format: 'esm' } // 必须为目录格式
  1. Vue 3 的 <script setup> 语法支持

问题<script setup> 未被正确处理。 ​解决​:确保使用 rollup-plugin-vue@6 并安装 @vue/compiler-sfc

code
npm install @vue/compiler-sfc@3 --save-de

eslint 配置

bash
npm i -D eslint

# 执行 下面的命令后,会出现一个交互式的命令行
./node_modules/.bin/eslint --init


✔ What do you want to lint? · javascript, css
✔ How would you like to use ESLint? · syntax
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · vue
✔ Does your project use TypeScript? · no / yes
✔ Where does your code run? · browser
The config that you've selected requires the following dependencies:

eslint, globals, typescript-eslint, eslint-plugin-vue, @eslint/css
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · npm

配置 eslint 脚本:

json
{
  "lint": "eslint ./src"
}

注:eslint 无需全局安装

本地使用

在组件库执行的命令

bash
npm link

使用组件库的项目,手动在 package.json 中指定模块名和版本号

bash
npm link 模块名

npm 发布

bash
npm login
npm publish

如果使用 @group/npmName 这种发布时,请注意是否有 @group 发布权限,如果没有,可以创建 @group 后再发布,建议个人用户不要用 @group/npmName 这种格式