用 rollup 构建一个 sdk

1,059 阅读5分钟

用 rollup 构建一个 sdk

因项目要求,要产出一个 js_sdk 给别人用,这里选择 rollup 作为构建工具。

目标:集成第三方包,解析 less、css,将图片打包成 base64 ,打包后去掉注释和 console,

生成项目

yarn add rollup global
yarn init -y

然后就可以往里面堆代码了。

最终目录结构如下

├─dist ├─node_modules ├─src │ ├─components │ ├─css │ ├─images │ ├─lib │ ├─index.js ├─rollup.config.js ├─.babelrc ├─.browserslistrc ├─.eslintrc

开发与打包命令如下,开发时开启 watch 模式

  "scripts": {
    "build": "rollup -c rollup.config.js",
    "dev": "rollup -w -c rollup.config.js"
  },

rollup.config.js 整体代码结构比较简单

export default{
    input:'',
    output:'',
    plugins:[],
    external:[]
}

rullup 插件配置

1、确定 sdk 使用目标,及使用的浏览器版本,

.browserslistrc 内容,可以修改,决定了最终产出的 sdk 代码的行数

> 1%
chrome > 50
not dead

.babelrc

{
  "presets": [
    [
      "@babel/env",
      {
        "modules": false // 设置为false,否则babel会在rollup有机会执行其操作之前导致我们的模块转化为commonjs
      }
    ]
  ]
}

2、确定输出目标,最终的使用方式为 script 标签使用

export default{
  input: 'src/index.js',   
  output: {
    file: './dist/xxx_sdk.js',
    format: 'iife', // iife  es  umd  cjs 
    name: 'XxxSdk',     // iife 模式下必加参数 ,最终导出的对象名
    // sourcemap: true,
    // sourcemapFile: './dist/bundle.js.map'
  },
}

打包后会得到一个 /dist/xxx_sdk.js,会向外暴露一个对象 XxxSdk

使用如下

<script src="./lib/xxx_sdk.js"></script>
const {yyy} = XxxSdk

3、添加 babel 插件

将代码按照 browserslistrc 中的目标进行编译,以兼容这里面的浏览器

安装
yarn add @babel/core @babel/preset-env babel-plugin-transform-remove-console rollup-plugin-babel -D
引入
import babel from 'rollup-plugin-babel'
使用
plugins:[
    // ... 其他 plugin
    babel({
      exclude: "node_modules/**",
      extensions: [".js", ".jsx", ".ts", ".tsx"],
      plugins: [
        transformRemoveConsole // 代码打包时移除 console 
      ]
    }),
]

4、添加 commonjs 插件

有时候需要使用的第三方包是用 commonjs 方式写的,这时要将其转换为 esm 方式的代码,就要使用 commonjs 插件,注意此插件要在 babel 之前调用书写

安装
yarn add rollup-plugin-commonjs -D
引入
import commonjs from 'rollup-plugin-commonjs'
使用
plugins:[
    // ...
    commonjs(), // 将 commonjs 解析成 es2015, 与 babel 同在时必须在 babel 之前调用;
]

5、windicss 支持

因为要编写 css ,这里选择使用神器 windicss,可以减少 80% 的 css 编写,也减少 css 命名的麻烦,配置简单粗暴,没有花样。

安装
yarn add rollup-plugin-windicss -D
使用
import WindiCSS from 'rollup-plugin-windicss'plugins:[
    //...
    WindiCSS(),
]

6、增加 less、css 支持

windicss 不能满足所有样式编写,还是要手动写一部分 css 代码,不安装 less 不知道可行不,没试

yarn add rollup-plugin-postcss less cssnano -D
使用
import postcss from 'rollup-plugin-postcss'
import cssnano from "cssnano";
import autoprefixer from "autoprefixer";
​
plugings:[
  postcss({
     plugins: [
         autoprefixer(), // 自动添加 css 兼容性前缀
         cssnano() ,// 压缩 css 成一行
     ],
     extensions: [".less", ".css", ".scss"], // 解析 less。css, scss
     // extract: 'css/index.css'  // 将 css 抽离成单独的文件,一般不需要开启,因为最后只要暴露一个js 出来,不想拆分
  }), 
]

7、将图片打包成 base64

因为最后只能暴露出一个 js 文件,此时需要将图片打包成内联的 base64,找了好几个插件都不够满意,最后这个感觉挺好用

安装
yarn add @rollup/plugin-url -D
使用
// 引入
import url  from '@rollup/plugin-url'// 配置
plugins:[
    // ...
    url({
      limit: 300*1024, // 300k 以内的图片都转成内联的 base64
      exclude: 'node_modules/**'
    }),
]

8、集成第三方包

需要使用第三方包,如果不解析,就只会在代码中有一行

import 'xxxx' 

而 xxxx 的内容代码并没有打包集成到输出文件中,这里选用 官方的 插件 @rollup/plugin-node-resolve ;

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

使用如下,配合 extenal 属性,可以将一些包不集成到输出文件中,比如不将 lodash 集成到 输出文件中,最后需要用户自行安装 lodash ;

import resolve from '@rollup/plugin-node-resolve';
​
plugins: [
    // ...
    resolve(), // 使用时要比 commonjs 和 babel 早调用
    commonjs(),
    babel()
],
external: ['lodash']

这里我遇到一个坑,第三方包嵌套较深,有四层,最后只要开启了 resolve() 打包就会卡死,只有前面成功过一次,后面一直卡死,无奈将第三方包拆了,将其代码复制到 lib 目录中,最后也能成功;

9、代码压缩

打包后的js 代码需要压缩减小体积,压缩后的代码会自动去掉 debugger 断点

安装
yarn add rollup-plugin-terser -D
使用
import { terser } from 'rollup-plugin-terser'
​
plugins:[
    // ...
    terser({
      ecma: 6,
      output: {
        comments: false // 移除注释
      }
    }),
]

还可以添加 eslint 插件,rollup-plugin-eslint ,配合 .eslintrc.js 使用

rollup.config.js 完整代码

import babel from 'rollup-plugin-babel'
import postcss from 'rollup-plugin-postcss'
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser'
import commonjs from 'rollup-plugin-commonjs'
import WindiCSS from 'rollup-plugin-windicss'
import clear from "rollup-plugin-clear";
import url  from '@rollup/plugin-url'
import transformRemoveConsole from "babel-plugin-transform-remove-console";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
import { eslint } from "rollup-plugin-eslint";
export default {
  input: 'src/index.js',   
  output: {
    file: './dist/xxx.js',
    format: 'iife', // iife  es    
    name: 'xxxSdk',     // iife 必加参数  
    // sourcemap: true,
    // sourcemapFile: './dist/bundle.js.map'
  },
  plugins: [
    // 每次编译之前清空历史编译目录
    // clear({
    //   targets: ["dist"],  // 项目打包编译生成的目录
    //   watch: true,  // 实时监听文件变化
    // }),
​
    resolve(), // 识别集成第三方包
    commonjs(), // 将 commonjs 解析成 es2015, 与 babel 同在时必须在 babel 之前调用;
​
    babel({
      exclude: "node_modules/**",
      extensions: [".js", ".jsx", ".ts", ".tsx"],
      plugins: [
        transformRemoveConsole // 移除 console 
      ]
    }),
    url({
      limit: 300*1024, // 300k 以内的图片都转成内联的 base64
      exclude: 'node_modules/**'
    }),
    WindiCSS(),
    postcss({
      plugins: [
        // autoprefixer(), // 自动添加 css 兼容性前缀
        cssnano() ,// 压缩 css 
      ],
      extensions: [".less", ".css"],
      // use: ["less"],
      // extract: 'css/index.css'  // 将 css 抽离成单独的文件,一般不需要开启
    }), // 解析 less。css, scss
    // 将代码压缩,去掉注释
    terser({
      ecma: 6,
      output: {
        comments: false
      }
    }),
    // 会自动加载文件根目录的 `.eslintrc.js` 配置文件
    eslint({
      fix: true,
      throwOnError: true, // 有错误时会抛出
      throwOnWarning: true, // 有警报时会抛出
      include: ['src'],
      exclude: [], // 默认值 node_modules/**
      formatter: () => { }
      // https://www.npmjs.com/package/rollup-plugin-eslint
    }) 
  ],
}