前端工具库开发
lwcai

前言

本文将介绍如何开发一个前端工具库,并使用rollup对代码进行打包,最终将工具库发布到npm

Rollup 是一个 JavaScript 模块打包工具,可以将多个小的代码片段编译为完整的库和应用。文档

想要完整地发布一个工具库,分为以下步骤:

  1. 初始化项目,并安装相关依赖
  2. 修改package.json中的相关配置
  3. 如果你的工具包是使用typescript编写的,还需要额外配置一下tsconfig.json
  4. 使用rollup打包代码
  5. 发布npm

安装依赖

  • rollup:用于打包 JavaScript 应用程序和库的模块打包器
  • rollup-plugin-typescript2:用于在 Rollup 构建过程中编译 TypeScript 代码
  • typescript:rollup-plugin-typescript2 的对等依赖项
  • tslib:rollup-plugin-typescript2 的对等依赖项
  • @rollup/plugin-json:用于将 .json 文件转换为 ES6 模块
npm i rollup rollup-plugin-typescript2 tslib typescript @rollup/plugin-json -D

配置文件

配置package.json

 {
"name": "my-lib",
"version": "1.0.0",
"description": "...",
"main": "lib/index.js", // 项目入口文件, commonjs 规范的代码
"module": "es/index.js", // esm 规范的代码的入口文件,优先级比 main 高
"type": "module", // 定义 package.json 文件和该文件所在目录根目录中.js文件和无拓展名文件的处理方式。值为 moduel 则当作 es 模块处理;值为 commonjs 则被当作commonJs模块处理
"typings": "index.d.ts", // .d.ts声明文件的入口
"files": [
"es",
"lib"
], // 项目中要发布的文件列表
"scripts": {
"build": "rollup -c rollup.config.ts --configPlugin typescript --environment NODE_ENV=production",
"dev": "rollup -c rollup.config.ts --configPlugin typescript"
},
"devDependencies": {...}
}

配置rollup.config.ts

import json from '@rollup/plugin-json'
import typescript from 'rollup-plugin-typescript2'
import { readFileSync } from 'fs'

export default {
input: 'src/index.ts',
output: [
{
name: pkg.name,
file: pkg.main,
format: 'umd',
sourcemap: !isProduction,
},
{
name: pkg.name,
file: pkg.module,
format: 'es',
sourcemap: !isProduction,
}
],
plugins: [
json(),
typescript(),
]
}

使用第三方类库

我们在开发过程中,可能会依赖一些外部类库,如:lodashreact

为了保证其中的模块能够正常的,通常都会需要使用到@rollup/plugin-commonjs。该插件支持将 CommonJS 模块转换为ES6,可以帮助我们在项目中使用commonjs规范的文件/模块。

另外,我们在打包的时候应该注意保持这些类库的外部引用状态,并告诉rollup如何正确的处理这些依赖。

最后,为了确保这些外部类库的代码不会被共同打包到bundle.js文件中。可以使用external属性,告诉rollup哪些是外部的库,不需要对这些外部类库进行打包。

安装:

npm install @rollup/plugin-commonjs @rollup/plugin-node-resolve --save-dev

使用:

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

export default {
input: 'src/index.js',
output: [
{
// ...
format: 'umd',
globals: {
'lodash': 'lodash'
}
}
],
plugins: [
nodeResolve(),
commonjs()
],
external: ['lodash'],
};

Tree Shaking & 副作用代码

在使用rollup进行生产构建的时候,会自动开启tree shaking,其本质是借助ES Module的静态分析来消除无用的js代码。无用代码有以下特征:

  • 代码不会被执行到
  • 代码执行的结果不会被用到
  • 代码只影响死变量

为了更直观的体会tree shaking的作用,我们可以看看rollup官方示例:

main.js

// TREE-SHAKING
import { cube } from './maths.js';

console.log(cube(5)); // 125

maths.js

// maths.js

// This function isn't used anywhere, so
// Rollup excludes it from the bundle...
export const square = x => x * x;

// This function gets included
// rewrite this as `square(x) * x`
// and see what happens!
export const cube = x => x * x * x;

// This "side effect" creates a global
// variable and will not be removed.
window.effect1 = 'created';

const includeEffect = false;
if (includeEffect) {
// On the other hand, this is never
// executed and thus removed.
window.effect1 = 'not created';
}

bundle comes out:

// maths.js

// This function gets included
// rewrite this as `square(x) * x`
// and see what happens!
const cube = x => x * x * x;

// This "side effect" creates a global
// variable and will not be removed.
window.effect1 = 'created';

// TREE-SHAKING

console.log(cube(5)); // 125

可以看到,没有被使用到的代码都被’摇’掉了。可是产生了副作用的一段代码仍然被保留了下来:

window.effect1 = 'created';

以下操作同样会产生副作用:

  • global, document, window, 全局变量修改,如window.xxx = xx
  • console.loghistory.pushState等方法调用
  • 未声明的变量,如a = 1

处理副作用代码

有时候,我们并不希望意外的副作用代码被保留下来,以此避免产生一些不必要的错误。

treeshake.annotations

在代码注释中忽略副作用

/*@__PURE__*/ console.log('side-effect');

class Impure {
constructor() {
console.log('side-effect');
}
}

/*@__PURE__*/ new Impure();

treeshake.moduleSideEffects

忽略模块的副作用

// input file
import { unused } from 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.moduleSideEffects === true
import 'external-a';
import 'external-b';
console.log(42);
// output with treeshake.moduleSideEffects === false
console.log(42);

更多选项请参考官方文档。使用配置项:

// rollup.config.js

export default {
// ...
treeshake: {
propertyReadSideEffects: false,
moduleSideEffects: false,
// ...
}
}

提示:使用SideEffects前请确保你的代码真的没有副作用,否则打包的时候会误删掉那些有副作用的代码

生成声明文件

由于我们使用了typescript,在发布代码之前,也应该让同样使用typescript的用户能够进行类型提示。

这里我们使用rollup-plugin-dts,该插件能够汇总你的.d.ts定义文件。该插件会自动将任何外部库(@types例如)标记为external,因此这些库将被排除在捆绑之外。

安装:

npm install --save-dev rollup-plugin-dts

将其添加到rollup.config.js

export default [
//...
{
input: "./my-input/index.d.ts",
output: [{ file: "dist/my-library.d.ts", format: "es" }],
plugins: [dts()],
},
]

并且修改package.json配置项:

{
"types": "dist/my-library.d.ts", // 用于指定包含项目的类型声明文件的目录
}

// 或者

{
"typings": "dist/my-library.d.ts" // 用于指定包含项目类型声明文件的文件名
}

执行构建命令

npm run build
{
"scripts": {
"build": "rollup -c rollup.config.ts --environment NODE_ENV=production --configPlugin typescript"
},
}

发布

npm publish
 Comments