rollup 打包實踐

原文鏈接

簡介

rollup 是一款像 webpack 一樣的JS代碼打包工具。它特別適合類庫的維護,有了它你可以把單個複雜龐大的類庫拆分成多個文件模塊編寫,最終打包成符合UMDAMDESM等格式的單個或多個文件。它可以利用最新的ES6+ modules 規範,Tree-Shaking 不需要的代碼,這樣你就可以放心的引入你喜歡類庫中的某個方法,而不必擔心引入整個類庫。

Vue 官方使用

modules

desc

JS 模塊化演進

  • JS歷史:沒有模塊化機制
// 全局變量污染、命名衝突
var global = '全局變量';
// 污染命名空間
window.jQuery = {};

// 其他腳本中聲明同名變量導致被覆蓋
var global = {};
  • 方案1:IIFE(立即調用函數表達式)
// 利用閉包解決大量全局變量污染問題
// 最終導出的少量全局變量
window.jQuery = (function(){
    ...
    return jQuery;
})();
  • 方案2:node 服務端 CommonJS 規範
// import the entire utils object with CommonJS
// 缺陷:同步全量加載
const utils = require( './utils' );
const query = 'Rollup';
// use the ajax method of the utils object
utils.ajax(`https://api.com?search=${query}`).then(cb);

// 導出
module.exports = {
    ...
};
  • 方案3:瀏覽器端 AMD 規範
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
    exports.verb = function() {
        return beta.verb();
        //Or:
        return require("beta").verb();
    }
});

著名的AMD兩種實現,現在很少看見用了

  1. Require.js
  2. Sea.js
  • 方案4:UMD 通用模塊定義規範
// Vue.js
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ?
        module.exports = factory() :
            typeof define === 'function' && define.amd ?
                define(factory) : (global.Vue = factory());
}(this, (function () {
    'use strict';

    ...
    return Vue;

})));
  • 方案5:遲來的ES6標準: import/export

優勢:

  1. CommonJS 同步獲取模塊的方式不適合前端
  2. AMD 怪異的使用方式和各版本實現上的歧義
  3. ESM 統一標準規範形成語言特性
  4. 語法層面的靜態分析,Tree-Shaking

Tree-Shaking

  • 更快、更輕、減少複雜度
  • 便於rollup、webpack 減少代碼膨脹
// import the entire utils object with CommonJS
const utils = require( './utils' );
const query = 'Rollup';
// use the ajax method of the utils object
utils.ajax(`https://api.com?search=${query}`).then(cb);

// import the ajax function with an ES6 import statement
import { ajax } from './utils';
const query = 'Rollup';
// call the ajax function
ajax(`https://api.com?search=${query}`).then(cb);

你有遇到一下問題嗎?

  • 功能複雜,文件太大了,不想在一個文件裏維護,多個文件不想手動合併
  • 不想手寫符合UMD、AMD…規範包裹的代碼
  • 想引用其他庫某個優秀方法,不想直接拷貝過來或不想引入整個包
  • 如何把若干小文件代碼同時打包成一個符合不同規範的文件和對應壓縮包、sourcemap文件

如果你有以上困惑,rollup 統統能滿足你。

構建你的第一個包

# 全局安裝
$ npm i rollup -g

# 查看幫助
$ rollup -h
# 創建項目目錄
$ mkdir -p rollup/src
$ cd rollup

新建src/main.js

// src/main.js
import foo from './foo.js';
export default function () {
    console.log(foo);
}

新建src/foo.js

// src/foo.js
export default 'hello world!';

命令行打包 CommonJS規範, 輸出dist/bundle.js

$ rollup src/main.js -o dist/bundle.js -f cjs

dist/bundle.js:

'use strict';

var foo = 'hello world!';

function main () {
    console.log(foo);
}

module.exports = main;

命令行太長手敲太煩?使用配置文件!

在項目根目錄下新建 rollup.config.js

// rollup.config.js
export default {
    input: 'src/main.js',
    output: {
        file: 'dist/bundle.js',
        format: 'cjs'
    }
};

現在只需要輸入以下命令即可達到以上效果

$ rollup -c

結合 npm 使用 rollup

初始化 package.json

$ npm init

不全局安裝,避免全局安裝版本不統一,npx 使用 rollup

$ npm i rollup -D

$ npx rollup -c

// 在package.json 中添加

{
    ...
    "scripts": {
        ...
        "build": "rollup --config"
    }
}
$ npm run build

常見插件使用

  • 需要解析json文件?

src/main.js 修改如下

// src/main.js
import { version } from '../package.json';

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

解析 json 文件需要引入 rollup-plugin-json 包

$ npm i rollup-plugin-json -D

rollup.config.js 修改如下

// rollup.config.js
import json from 'rollup-plugin-json';

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

最終打包dist/bundle.js,多餘的無用json字段被祛除了

'use strict';

var version = "1.0.0";

// src/main.js

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

module.exports = main;
  • 需要引入CommonJS規範包?

修改 src/main.js 如下

// src/main.js
import { version } from '../package.json';
import answer from 'the-answer';

export default function () {
    console.log('the answer is ' + answer);
    console.log('version ' + version);
}

安裝依賴測試包 the-answer

$ npm i the-answer

最終打包結果,the-answer 沒有打進bundle.js,在node環境可用。

'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var answer = _interopDefault(require('the-answer'));

var version = "1.0.0";

// src/main.js

function main () {
    console.log('the answer is ' + answer);
    console.log('version ' + version);
}

命令行窗口出現警告

(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
the-answer (imported by src/main.js)

CommonJS包需要 rollup-plugin-node-resolve 插件

$ npm i rollup-plugin-node-resolve -D

修改 rollup.config.js

// rollup.config.js
import json from 'rollup-plugin-json';
import resolve from 'rollup-plugin-node-resolve';

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

打包結果,發現 the-answer 被打進去了

'use strict';

var version = "1.0.0";

var index = 42;

// src/main.js

function main () {
    console.log('the answer is ' + index);
    console.log('version ' + version);
}

module.exports = main;
  • 需要使用最新JS語法,babel 轉碼?

安裝 rollup-plugin-babel

$ npm i -D rollup-plugin-babel rollup-plugin-node-resolve

rollup.config.js 修改如下

import babel from 'rollup-plugin-babel';

export default {
    input: 'src/main.js',
    output: {
        file: './dist/bundle.js',
        format: 'cjs'
    },
    plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**' // only transpile our source code
        })
    ]
};

新建babel配置文件 src/.babelrc

{
    "presets": [
        ["@babel/env", {"modules": false}]
    ]
}
// "modules": false 避免在rollup處理之前,被babel轉成CommonJS格式

安裝babel核心包:@babel/core@babel/preset-env

$ npm i -D @babel/core @babel/preset-env

在 src/main.js 中使用 ES6 的箭頭函數

// src/main.js
import answer from 'the-answer';

export default () => {
    console.log(`the answer is ${answer}`);
}

打包結果

'use strict';

var index = 42;

// src/main.js
var main = (function () {
    console.log("the answer is ".concat(index));
});

module.exports = main;
  • 需要壓縮代碼?

安裝 rollup-plugin-uglify

$ npm i -D rollup-plugin-uglify

修改 rollup.config.js 如下

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import { uglify } from "rollup-plugin-uglify";

export default {
    input: 'src/main.js',
    output: {
        file: './dist/bundle.js',
        format: 'cjs'
    },
    plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**' // only transpile our source code
        }),
        uglify()
    ]
};
  • 需要 sourcemap ?

rollup 內置支持,但默認關閉

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import { uglify } from "rollup-plugin-uglify";

export default {
    input: 'src/main.js',
    output: {
        file: './dist/bundle.js',
        format: 'cjs',
        sourcemap: true, // 開啓 sourcemap
    },
    plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**' // only transpile our source code
        }),
        uglify()
    ]
};
  • 根據不同文件生成不同包 ?

例如根據主入口文件 src/index.js 生成 UMD、ESM 規範的包文件

import babel from 'rollup-plugin-babel';
import { uglify } from "rollup-plugin-uglify";
import { terser } from "rollup-plugin-terser";

export default [{
  input: 'src/index.js',
  output: {
    file: 'dist/traceKit.es.js',
    format: 'esm'
  }
}, {
  input: 'src/index.js',
  output: {
    file: 'dist/traceKit.es.min.js',
    format: 'esm',
    sourcemap: true
  },
  plugins: [
    terser(),
  ]
}, {
  input: 'src/index.js',
  output: {
    file: 'dist/traceKit.js',
    name: 'TraceKit',
    format: 'umd'
  },
  plugins: [
    babel(),
  ]
}, {
  input: 'src/index.js',
  output: {
    file: 'dist/traceKit.min.js',
    name: 'TraceKit',
    format: 'umd',
    sourcemap: true,
    // sourcemapFile: 'traceKit.min.js.map',
  },
  plugins: [
    babel(),
    uglify(),
  ]
}]

注意: uglify 不支持 es6,用 rollup-plugin-terser

官方推薦插件

建議最好都打個ESM包並在 package.json 指明,如 Vue。
這樣做的好處是便於webpack、rollup 利用最新特性 Tree-Shaking 靜態解析代碼,讓你的包更小。
也讓大家能更好的向es6+規範過渡。

{
    ...
    "module": "dist/vue.runtime.esm.js",
    ...
}

更多特性請移步官方文檔

  • 不侷限於類庫,打包你的 app 應用
  • 實時監聽代碼更新打包
  • 代碼分片
  • 根據不同環境配置
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章