Rollup is a JavaScript module wrapper that compiles small pieces of code into large, complex pieces. Rollup is based on ES6 packaging as opposed to previous AD hoc solutions such as CommonJS and AMD. Unlike Webpack, which focuses on application packaging, Rollup. js is more focused on Javascript class library packaging. Many well-known frameworks or libraries such as Vue and React are packaged through rollup.js.

A, chooserollupThe reason?

Webpack and Rollup can be used to their advantage in different scenarios. Webpack has “built-in advantages” for code splitting and static resource imports, and supports hot module replacement (HMR), which Rollup does not.

So webPack is preferred when developing applications, but Rollup has algorithmic advantages for tree-shaking code and ES6 modules. If the project only needs to package a simple package and is developed based on ES6 modules, use Rollup.

Webpack has supported tree-shaking since 2.0, and packaging of ES6 Modules is supported when using Babel-Loader. In fact, Rollup has gradually lost its edge. However, it has not been abandoned. Instead, it is favored by many library developers due to its simple API and usage. React, Vue, etc., all use rollup as a build tool.

2. Package the tool kit common to all projectsgdpg-utils

Here, gdPG-utils, the package used by various projects, is used as an example to explain rollup. The details of the process are not detailed, but are presented in the form of file codes. If necessary, you can understand the configuration according to the official website.

1. Final directory structure:

NPM I tree-node-cli -g tree- L 4 -i "node_modules" > dir.md // The directory is generated by tree-node-cliCopy the code
GDPG - utils ├ ─ ─ CHANGELOG. Md ├ ─ ─ LICENSE ├ ─ ─ the README. Md ├ ─ ─ dist/directory/packaging production │ ├ ─ ─ gdpg-utils.com mon. Js │ ├ ─ ─ GDPG - utils. Esm. Js │ ├ ─ ─ GDPG - utils. Js │ └ ─ ─ index. The js ├ ─ ─ index. The HTML / / test HTML ├ ─ ─ package - lock. Json ├ ─ ─ package. The json ├ ─ ─ a rollup. Config. Js / / a rollup configuration file ├ ─ ─ scripts │ └ ─ ─ the publish. Js / / pushed to the remote directory script ├ ─ ─ SRC / / packaging code │ ├ ─ ─ but ts │ ├ ─ ─ Modules / / modules folder │ │ ├ ─ ─ array │ │ │ └ ─ ─ but ts │ │ ├ ─ ─ brower │ │ │ └ ─ ─ but ts │ │ ├ ─ ─ method │ │ │ └ ─ ─ index. The ts │ │ ├ ─ ─ native │ │ │ ├ ─ ─ android. Ts │ │ │ ├ ─ ─ but ts │ │ │ └ ─ ─ PC. Ts │ │ ├ ─ ─ number │ │ │ └ ─ ─ but ts │ │ ├ ─ ─ object │ │ │ └ ─ ─ index. Ts │ │ ├ ─ ─ storage │ │ │ └ ─ ─ but ts │ │ ├ ─ ─ string │ │ │ └ ─ ─ but ts │ │ └ ─ ─ tool │ │ └ ─ ─ index. The ts │ ├─ └─ tsconfig.json // typescript config ├── ├.html yarn.lockCopy the code

1.1 package. Json

{
  "name": "gdpg-utils"."version": "1.0.10"."description": "gdpg web js utils"."keywords": [
    "utils"."tool"]."main": "dist/gdpg-utils.common.js"."module": "dist/gdpg-utils.esm.js"."browser": "dist/gdpg-utils.js"."scripts": {
    "dev": "rollup --config rollup.config.js --watch --environment ENV:dev"."build": "rollup --config rollup.config.js --environment ENV:prod"."pub": "node scripts/publish.js"
  },
  "author": ""."license": "ISC"."publishConfig": {
    "registry": "http://127.0.0.1:8081/repository/npm-hosted/"
  },
  "devDependencies": {
    "@babel/core": "^ 7.15.0"."@babel/preset-env": "^ 7.15.0"."@rollup/plugin-alias": "^ 3.1.5." "."@rollup/plugin-babel": "^ 5.3.0." "."@rollup/plugin-commonjs": "^ 20.0.0"."@rollup/plugin-json": "^ 4.1.0." "."@rollup/plugin-node-resolve": "^ 13.0.4"."@rollup/plugin-typescript": "^ 8.2.5"."commander": "^ 8.1.0"."dayjs": "^ 1.10.6"."js-cookie": "^ 3.0.0"."lodash": "^ 4.17.21"."rollup": "^ 2.56.0"."rollup-plugin-dev": "^ 1.1.3." "."rollup-plugin-livereload": "^ at 2.0.5." "."rollup-plugin-replace": "^ 2.2.0." "."rollup-plugin-terser": "^ 7.0.2"."rollup-plugin-visualizer": "^ 5.5.2." "."shelljs": "^ 0.8.4"."store": "^ 2.0.12"."tslib": "^ 2.3.0." "."typescript": "^ 4.3.5." "}}Copy the code

1.2 a rollup. Config. Js

import typescript from '@rollup/plugin-typescript' / / typescript plug-in
import json from '@rollup/plugin-json'; // Import data from JSON
import nodeResolve from '@rollup/plugin-node-resolve' // help find packages in node_modules
import commonjs from '@rollup/plugin-commonjs' // Make non-ES6 syntactic packages available for ES6
import babel from '@rollup/plugin-babel' // Rollup Babel plugin, ES6 to ES5
import dev from 'rollup-plugin-dev'; // Start the local server
import livereload from 'rollup-plugin-livereload'; // Enable hot update
import {
  terser
} from 'rollup-plugin-terser';
import {
  visualizer
} from 'rollup-plugin-visualizer';

import pkg from './package.json'

export default {
  input: "src/index.ts".// Import file
  output: [{ // Different types of export files
      file: pkg.main,
      format: 'cjs'.// CommonJS
      exports: 'auto'
    },
    {
      file: pkg.module,
      format: 'es'.// ES module file
      exports: 'auto'
    },
    {
      file: pkg.browser,
      format: 'umd'.// Common module definition, with AMD, CJS and IIFE as one
      name: 'gdpg-utils'.exports: 'auto'},].plugins: [
    json(),
    typescript(),
    nodeResolve({
      browser: true.main: true
    }),
    commonjs(),
    babel({
      exclude: 'node_modules/**'./ / ignore node_modules
      babelHelpers: true.// Enable volume optimization
    }),
    process.env.ENV === 'prod' ? terser() : null,
    process.env.ENV === 'dev' ? livereload() : null,
    process.env.ENV === 'dev' ? dev({
      port: 8888.dirs: ' ',}) :null,
    process.env.ENV === 'prod' ? visualizer() : null,].watch: {
    exclude: 'node_modules/**'.include: 'src/**'}};Copy the code

1.3 the format field

You may not understand the format field here, especially what CJS stands for; Because JS can be modularized in a variety of ways, Rollup can package different files for different module specifications, with the following five options:

  • Amd: Asynchronous module definition for module loaders such as RequireJS
  • CJS: CommonJS for Node and Browserify/Webpack
  • Es: es module file
  • Iife: self-executing module, suitable for browser environmentscriptThe label
  • Umd: Generic module definition, combining AMD, CJS and IIFE

1.4 the plugins field

Plug-ins extend Rollup’s ability to handle other types of files and function somewhat like a combination of Webpack’s Loader and plugin; However, the configuration is much simpler than in WebPack. Instead of declaring each file to be processed by each plug-in, you just declare it in plugins and it is automatically loaded when the corresponding file type is introduced. The project uses the following plug-ins

Import typescript from '@rollup/plugin-typescript' import json from '@rollup/plugin-json'; Import nodeResolve from '@rollup/plugin-node-resolve' // help find package import commonjs from node_modules Import Babel from '@rollup/plugin-babel' import Babel from '@rollup/plugin-babel' import Babel from '@rollup/plugin-babel' Import dev from 'rollup-plugin-dev'; Import livereload from 'rollup-plugin-livereload'; // Enable hot updateCopy the code

2. Tool code for the SRC directory

The index of 2.1 SRC/ts

import * as array from './modules/array'
import * as brower from './modules/brower'
import * as method from './modules/method'
import * as number from './modules/number'
import * as object from './modules/object'
import * as string from './modules/string'
import * as native from './modules/native'
import * as tool from './modules/tool'

export default{... array, ... brower, ... method, ... number, ... object, ... string, ... native, ... tool, }Copy the code

2.2 Modules folder contains a lot of content, which is briefly described heresrc/modules/number/index.tsandsrc/modules/tool/index.tsAn example of

// src/modules/number/index.ts

/ * * *@description Generates the specified range of random decimals */
export const randomNumberInRange = (min: number, max: number) = > Math.random() * (max - min) + min;

/ * * *@description Computes the sum of arrays or numbers */
export const sum = (. arr) = > [...arr].reduce((acc, val) = > acc + val, 0);
Copy the code
// src/modules/tool/index.ts

/ * * *@description Deep copy * /
export function deepClone(data: any) :any {
  const type = Object.prototype.toString.call(data);
  let result: {
    [key: string]: any
  };
  if (type === '[object Object]') {
    result = {};
  } else if (type === '[object Array]') {
    result = [];
  } else {
    return data;
  }
  Object.keys(data).forEach((key) = > {
    const value = data[key];
    result[key] = deepClone(value);
  });
  return result;
}

/ * * *@description Async await elegant processing */
export constawaitWrap = <T, U = any>(promise: Promise<T>): Promise<[U | null, T | null]> => promise .then<[null, T]>((data: T) => [null, data]) .catch<[U, null]>((err) => [err, null]); /** * @description const unique = (sourceArr: Array<any>, data: Array<any>, key: string) Array<any> => { const arr = [...data, ...sourceArr]; return arr.reduce((acc: Array<any>, cur: any) => { const ids = acc.map((item) => item[key]); return ids.includes(cur[key]) ? acc : [...acc, cur]; } []); };Copy the code

3. Run the development command and package command

3.1 Local Development

npm run dev
// "dev": "rollup --config rollup.config.js --watch --environment ENV:dev"
// --config rollup.config.js
Copy the code
  • –config rollup.config.js // Use rollup.config.js configuration file
  • –watch // Monitor (monitor rollup.config.js watch configuration) SRC directory transformation, dynamic package update dist directory
  • –environment ENV:dev // Pass the environment variable ENV:dev

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial-scale =1.0"> <title>gdpg-utils test page</title> </head> <body> <h1>gdpg-utils</h1> <script type="module"> import  utils from '.. /dist/gdpg-utils.esm.js'; The console. The log (' ⧭ randomNumberInRange % c ', 'color: # 1 d5673, utils. RandomNumberInRange); A 翍 翍 ('deepClone % C a 翍 ', 'color: # 00BF00 ', utils.deepclone); console.log('deepClone % C a 翍 ', 'color: # 00BF00 ', utils.deepclone); </script> </body> </html>Copy the code

About debugging: I used two types of debugging during the development of the package:

  • Method 1: Directly through the index.html debugging, (principle: withrollup-plugin-devandrollup-plugin-livereloadSet up the debugging environment in this way)

  • Approach 2: Debug the package under development in the project via NPM link (soft link)

Execute NPM link in package directory;

Run NPM link gdpg-utils in the project directory

The package can be used (NPM unlink gdpg-utils removes the package link);

3.2 packaging

npm run build
Copy the code

Packing results:Package structure analysis: uncompressed in37kbOr so

3.3 Push to NPM private library

Push script scripts/publish.js

const path = require('path');
const shelljs = require('shelljs');
const program = require('commander');

const targetFile = path.resolve(__dirname, '.. /package.json');
const packagejson = require(targetFile);
const currentVersion = packagejson.version;
const versionArr = currentVersion.split('. ');
const [mainVersion, subVersion, phaseVersion] = versionArr;

// The default version number
const defaultVersion = `${mainVersion}.${subVersion}.${+phaseVersion+1}`;

let newVersion = defaultVersion;

// Get the version number from the command line argument
program
  .option('-v, --versions <type>'.'Add release version number', defaultVersion);

program.parse(process.argv);

if (program.versions) {
  newVersion = program.versions;
}

console.log('newVersion:', newVersion);

function publish() {
  shelljs.sed('-i'.'"name": "ktools"'.'"name": "@kagol/ktools"', targetFile);
  shelljs.sed('-i'.`"version": "${currentVersion}"`.`"version": "${newVersion}"`, targetFile);
  shelljs.exec('npm run build');
  shelljs.exec('npm publish');
}

publish();
Copy the code
npm run pub
Copy the code

3.4 Introduction and use in VUE project

Package structure analysis: code compression +gzip compression after the size in5kbOr so// TODO: unit test, document preview, commit specification

Third, the Tree Shaking

Because Rollup itself supports the ES6 modularity specification, Tree Shaking is done without additional configuration

Four, code segmentation

Rollup code splits, like parcels, are imported on demand; But our output format, Format, cannot use Iife because iIFE self-executing functions put all modules in one file, either through AMD or CJS or other specifications.

export default {
  input: "./index.ts".output: {
    // Output folder
    dir: "dist".format: "amd",}};Copy the code

This way, the code we import dynamically through import() is split into separate JS and imported on demand at call time; However, the files of this AMD module cannot be directly referenced in the browser, and must be loaded through a library that implements THE AMD standard, such as require.js.

Five, the summary

Rollup has the following advantages:

  • Simple configuration and fast packaging
  • Automatically remove unreferenced code (built-in Tree shaking)

But he also has the following shortcomings that can not be ignored:

  • Development server can not achieve module hot update, debugging cumbersome
  • Code splitting in the browser environment depends on AMD
  • Loading third-party modules is complicated