Skip to content

Rollup

Rollup 是一个用于 JavaScript 的模块打包工具,它将小的代码片段编译成更大、更复杂的代码,例如库或应用程序。它使用 JavaScript 的 ES6 版本中包含的新标准化代码模块格式,而不是以前的 CommonJS 和 AMD 等特殊解决方案。ES 模块允许你自由无缝地组合你最喜欢的库中最有用的个别函数。 这在未来将在所有场景原生支持,但 Rollup 让你今天就可以开始这样做。 https://cn.rollupjs.org/introduction/

Rollup.js 实战学习笔记

一个真实的组件库的 rollup 打包配置

该项目支持:

打包输出文件保留原始模块结构 自动将 dependencies 依赖声明为 externals 支持处理外部 npm 依赖 支持基于 CommonJS 模块引入 支持 typescript,并导出声明文件 支持 scss,并添加前缀 支持自动清除调试代码 支持按需加载

  1. 安装
bash
npm i rollup -g

yarn add typescript postcss @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-strip @rollup/plugin-typescript rollup-plugin-postcss rollup-plugin-node-externals autoprefixer -D
  1. 配置 项目根目录下新增 rollup.config.js:
js
import path from 'node:path';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import strip from '@rollup/plugin-strip';
import typescript from '@rollup/plugin-typescript';
import autoprefixer from 'autoprefixer';
import externals from 'rollup-plugin-node-externals';
import postcss from 'rollup-plugin-postcss';

import pkg from './package.json';

export default [
  {
    input: './src/index.ts', // 入口文件
    output: [
      {
        // 出口文件
        dir: path.dirname(pkg.module),
        format: 'es', // es模块导出,支持按需加载
        name: pkg.name,
        exports: 'named', // 指定导出模式(自动、默认、命名、无)
        preserveModules: true, // 保留模块结构
        preserveModulesRoot: 'src', // 将保留的模块放在根级别的此路径下
      },
    ],
    plugins: [
      // 自动将dependencies依赖声明为 externals
      externals({
        devDeps: false,
      }),
      // 处理外部依赖
      resolve(),
      // 支持基于 CommonJS 模块引入
      commonjs(),
      // 支持 typescript,并导出声明文件
      typescript({
        outDir: 'es',
        declaration: true,
        declarationDir: 'es',
      }),
      // 支持 scss,并添加前缀
      postcss({
        plugins: [autoprefixer()],
      }),
      // 清除调试代码
      strip(),
    ],
  },
];

更新 packages.json:

json
{
  "module": "es/index.js",
  "types": "es/index.d.ts",
  "files": [
    "es"
  ],
  "scripts": {
    "build": "rimraf es && rollup -c"
  }
}

新增 tsconfig.json:

json
{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ],
  "exclude": [
    "src/**/stories.*",
    "src/**/.spec.*",
    "src/**/.mdx"
  ]
}

rollup.config.js

packages/perfect-design/rollup.config.js

js
import ts from 'rollup-plugin-typescript2';
import less from 'rollup-plugin-less';
import clear from 'rollup-plugin-clear';
import cjs from '@rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import { uglify } from 'rollup-plugin-uglify';
import copy from 'rollup-plugin-copy';

function getBaseRollupPlugins({ typescript = {} } = {}) {
  return [
    resolve(),
    cjs({
      ignoreGlobal: true,
      include: /\/node_modules\//,
      external: ['react', 'react-dom', 'styled-components']
    }),
    ts(typescript),
    terser(),
    uglify()
  ];
}

export default {
  input: ['./src/index.ts'],
  output: [
    {
      file: 'dist/cjs/index.js',
      format: 'cjs',
      name: 'cjs.js'
    },
    {
      file: 'dist/umd/index.js',
      format: 'umd',
      name: 'umd.js'
    },
    {
      file: 'dist/es/index.js',
      format: 'es',
      name: 'index.js'
    }
  ],
  plugins: [
    less({ output: './dist/style/index.css' }),
    clear({
      targets: ['dist']
    }),
    ...getBaseRollupPlugins(),
    babel({
      exclude: 'node_modules/**',
      runtimeHelpers: true
    }),
    copy({
      targets: [
        {
          src: '../../scripts/globalStyle/compiled-colors.less',
          dest: 'dist/style'
        }
      ]
    })
  ],
  external: ['react', 'react-dom']
};
text
"rollup": "^3.17.2",
		"rollup-plugin-babel": "^4.4.0",
		"rollup-plugin-clear": "^2.0.7",
		"rollup-plugin-commonjs": "^10.1.0",
		"rollup-plugin-copy": "^3.4.0",
		"rollup-plugin-delete": "^2.0.0",
		"rollup-plugin-eslint": "^7.0.0",
		"rollup-plugin-less": "^1.1.3",
		"rollup-plugin-node-resolve": "^5.2.0",
		"rollup-plugin-postcss": "^4.0.2",
		"rollup-plugin-terser": "^7.0.2",
		"rollup-plugin-typescript2": "^0.34.1",
		"rollup-plugin-uglify": "^6.0.4",

rollup配置及使用

https://segmentfault.com/a/1190000041180789#item-3-6

项目配置

babel

引入依赖 首先运行以下命令安装babel相关:

sh
yarn add @babel/core @babel/cli @babel/preset-env -D

配置babel.config.js

js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: '> 0.25%, not dead'
      }
    ]
  ]
};

搭配@babel/plugin-transform-runtime和core-js:

sh
yarn add core-js @babel/runtime
yarn add @babel/plugin-transform-runtime -D

修改babel.config.js如下:

js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: '> 0.25%, not dead',
        useBuiltIns: 'usage',
        corejs: '3.6.5'
      }
    ]
  ],
  plugins: ['@babel/plugin-transform-runtime']
};

增加 npm scripts

text
"scripts:" {
  "babel": "babel ./src/index.js -o ./dist/index.js"
}

Typescript

面向未来,所以这里引入typescript,统一用 ts 进行开发

sh
yarn add typescript@4.3.5 -D
yarn add @babel/preset-typescript -D

修改babel配置如下:

js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: '> 0.25%, not dead',
        useBuiltIns: 'usage',
        corejs: '3.6.5'
      },
      '@babel/preset-typescript'
    ]
  ],
  plugins: ['@babel/plugin-transform-runtime']
};

rollup

项目是纯粹的Javascript项目,没有vue、react相关的业务性代码,所以使用 rollup 进行打包。

引入依赖

sh
yarn add rollup @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-terser rollup-plugin-typescript2 tslib -D

配置rollup.config.js

js
import babel from '@rollup/plugin-babel'; // 引入babel
import commonjs from '@rollup/plugin-commonjs'; // 引入cjs插件,支持 commonjs 语法
import { nodeResolve } from '@rollup/plugin-node-resolve'; // 引入resolve,告诉 Rollup 如何查找外部模块
import typescript from 'rollup-plugin-typescript2'; // ts
import { terser } from 'rollup-plugin-terser'; // 压缩打包文件

const extensions = ['.js', '.ts'];

const pkg = require('./package.json'); // 从package.json引入

const version = pkg.version; // 项目版本
const license = pkg.license; // 协议
const author = pkg.author; // 作者

// 打包文件的头部声明
const banner
  = '/*!\n'
  + ` * ${pkg.name} v${version}\n`
  + ` * (c) 2020-${new Date().getFullYear()} ${author}\n`
  + ` * Released under the ${license} License.\n`
  + ' */';

module.exports = {
  input: 'src/index.ts',
  output: [
    // 文件输出配置
    {
      file: 'dist/index.umd.js', // 打包后生产的文件位置,及文件名
      format: 'umd',
      name: 'utools', // 包的全局变量名称
      banner
    },
    {
      file: 'dist/index.esm.js', // 打包后生产的文件位置,及文件名
      format: 'esm',
      name: 'utools', // 包的全局变量名称
      banner
    }
  ],
  plugins: [
    nodeResolve({
      extensions,
      modulesOnly: true
    }),
    commonjs(),
    typescript(),
    babel({
      babelHelpers: 'runtime',
      include: 'src/**',
      exclude: 'node_modules/**',
      extensions
    }),
    terser()
  ]
};

增加 npm scripts

text
"scripts": {
  "build": "rollup -c", // -c参数即 config 使用配置文件
  "dev": "rollup -c -w" // -w参数监听源文件是否有改动,如果有改动,重新打包
},

package.json 如下

json
{
  "name": "module name",
  "version": "module version",
  "description": "module desc",
  "main": "dist/index.umd.js",
  "types": "types/index.d.ts",
  "module": "dist/index.esm.js",
  "scripts": {
    "test": "jest",
    "babel": "babel ./src/index.js -o ./dist/index.js",
    "build": "rimraf dist/* && rollup -c",
    "release": "release-it",
    "release:beta": "release-it major --preRelease=beta",
    "fix:src": "npx eslint src --fix --ext .ts",
    "fix:test": "npx eslint test --fix --ext .js",
    "lint": "npm run fix:src && npm run fix:test"
  },
  "repository": {
    "type": "git"
  },
  "author": "module author",
  "license": "MIT",
  "devDependencies": {},
  "dependencies": {}
}

(!) Module level directives cause errors when bundled, 'use client' was ignored.

https://github.com/rollup/rollup/issues/4699

https://github.com/Ephem/rollup-plugin-preserve-directives/tree/main

热更新插件

划重点!!!在开发中怎么能少了热更新呢,众所周知,rollup 是不支持热更新的,但这么好用的功能,与时俱进的 rollup 怎么能没有呢?插件 rollup-plugin-hotreload 实现了这个功能!

sh
npm install --save-dev rollup-plugin-hotreload

在 rollup.config.js 中配置如下:

text
import { rph, rphMultibundles } from "rollup-plugin-hotreload";
import path from "path";
import "your_server_path"; // import your server path;

export default rphMultibundles({
  //...
  plugins: [
    rph({
      templateHtmlPath: "src/index.html", // template html path relative to rootDir
      isStopRPH: false, // stop hotreload or not
      rootDir: __dirname, // rootDir
      rootBuildDir: "build", // build root path relative to rootDir
      buildPaths: [
        // first one is relative path to rootDir...
        ["js/index.min.js", "src/index.js"]
        // as many as you want ...
      ]
    })
  ]
});

区分开发环境和生产环境

在开发环境我们需要sourcemap开启,配置热更新和本地服务,在生产环境我们需要sourcemap关闭,不需要热更新和本地服务,需要代码压缩等,所以需要区分。

拆分 roullup.config.dev.js和rollup.config.prod.js 将rollup.config.js拆分成两个rollup.config.dev.js和rollup.config.build.js

修改 package.json 中的打包命名即可。 配置 package.json 最后在package.json中配置命名(这里做了修改,区分了开发环境和生产环境)

配置rollup.config.dev.js

本地服务器 + source-map + 热更新

两个本地服务器插件 rollup-plugin-serve or rollup-plugin-dev

这两个插件相比:

rollup-plugin-dev 使用koa服务器实现,存在依赖 具有可能有用的附加功能

安装插件

先来安装一个插件开启本地服务器

安装rollup-plugin-serve or rollup-plugin-dev

sh
npm install rollup-plugin-serve --save-dev

or

npm install rollup-plugin0dev --save-dev

开启热更新

安装rollup-plugin-livereload插件

sh
npm install rollup-plugin-livereload --save-dev

在开发环境的rollup.config.dev.js中配置插件中配置rollup-plugin-livereload() 即可。

配置rollup.config.dev.js 在rollup.config.dev.js的打包输出 output 中配置 sourcemap: true,这样调试代码会更方便。

js
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import json from 'rollup-plugin-json';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
import pkg from '../package.json'; // 引用文件路径要记得修改!!

export default [
  {
    input: pkg.main,
    output: [
      {
        name: 'W',
        file: pkg.browser,
        format: 'umd',
        sourcemap: true,
      }
    ],
    plugins: [
      resolve(), // so Rollup can find `ms`
      commonjs(), // so Rollup can convert `ms` to an ES module
      json(), // so Rollup can read file `.json`
      livereload(),
      serve({
        open: true,
        port: 8082,
        contentBase: ''
      })
    ]
  }
];

引用库

在根目录下建一个index.html文件来引用js类库,npm run dev即可自动打开浏览器页面。

配置rollup.config.prod.js

生产环境下不需要以上配置,主要需要代码压缩,安装插件:

sh
import { terser } from "rollup-plugin-terser";

export default [
  {
    input: pkg.main,
    output: [
      {
        name: 'W',
        file: pkg.browser,
        format: 'umd'
      }
    ],
    plugins: [
        resolve(), // so Rollup can find `ms`
        commonjs(), // so Rollup can convert `ms` to an ES module
        json(), // so Rollup can read file `.json`
        terser() // so minify
    ]
    }
];

rollup-plugin-visualizer

rollup一般用于npm包的构建,相比webpack更轻量。rollup-plugin-visualizer功能和webpack-bundle-analyzer类似,但ui设计上提供了更多的template可以调用。

sh
# NPM
npm install --save-dev rollup-plugin-visualizer

# Yarn
yarn add -D rollup-plugin-visualizer
js
const { visualizer } = require('rollup-plugin-visualizer');

export default {
  input: './src/main.js',
  output: {
    file: 'bundle.js',
    format: 'es'
  },
  plugins: [
    // 放在所有插件最后
    visualizer({
      filename: 'report.html',
      open: true,
      gzipSize: true,
      brotliSize: true,
    })
  ]
};

Contributors

作者:Long Mo
字数统计:2.3k 字
阅读时长:10 分钟
Long Mo
文章作者:Long Mo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Longmo Docs