rollup打包入门到实践

简介: rollup在业务中基本很少会有接触到,通常在我们的业务中大部分接触的脚手架,或者自己搭建项目中,我们都是用webpack,无论是vue-cli,还是react-create-app他们都是基于webpack二次封装的脚手架,所以我们对rollup更陌生一点

rollup在业务中基本很少会有接触到,通常在我们的业务中大部分接触的脚手架,或者自己搭建项目中,我们都是用webpack,无论是vue-cli,还是react-create-app他们都是基于webpack二次封装的脚手架,所以我们对rollup更陌生一点,本文是一篇关于rollup的学习笔记,希望看完在项目中有所思考和帮助。


在开始本文前,主要会从以下几点去认识了解rollup


1、基础了解rollup打包不同模式,以及如何打包成不同模式的js

2、以一个实际的例子,将工具库用rollupgulp实现任务流打包,验证打包后的js是否ok,加深对rollup的使用


npm 初始化一个基础的package.json


npm init -y


局部安装rollup


npm i rollup

然后在当前目录下创建一个index.js


index.js中写入一点测试代码

import b from './js/b.js'
// const a = require('./js/a.js');
const getName = () => {
   //  console.log('hello', a.name);
    console.log('hello', b.name);
};
getName();


npx运行局部命令


当你在当前项目安装rollup后,就可以用命令行npx执行rollup打包输出对应模式的bundle.js

// 将index.js打包输出成bundle.iife文件,iife模式
npx rollup index.js --file bundle-iife.js --format iife
// 将index.js打包输出成cjs模式
npx rollup index.js --file bundle-cjs.js --format cjs
// 将index.js打包输出成umd模式
npx rollup index.js --file bundle-umd.js --format umd
// es
npx rollup index.js --file bundle-es.js --format es

es打包后的代码是这样的,不过此时es6还未为编译成es5

const name = 'Maic';
const age = 18;
var b = {
    name,
    age
};
// const a = require('./js/a.js');
const getName = () => {
    // console.log('hello', a.name);
    console.log('hello', b.name);
};
getName();

打包前的代码

// const a = require('./js/a.js');
import b from './js/b.js'
const getName = () => {
    // console.log('hello', a.name);
    console.log('hello', b.name);
}
getName();

命令行可以输出对应文件,我们也可以用配置文件方式,因此你可以像webpack一样新建一个rollup.config.js这样的配置,内容也非常简单

export default {
    input: 'index.js', // 入口文件
    output: {
        format: 'cjs', // cjs
        file: 'bundle.js' // 打包输出后文件名
    },
}

当我们指定配置文件时,package.jsontype要指定成module,当node版本大于13时,默认是以ES Module方式,所以要给了提示,要么在package.json文件中加入type: module,要么把配置文件的后缀名改成rollup.config.mjs

"type": "module",
"scripts": {
        "build": "rollup -c rollup.config.js"
    },
Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
[!] RollupError: Node tried to load your configuration file as CommonJS even though it is likely an ES module. To resolve this, change the extension of your configuration to ".mjs", set "type": "module" in your package.json file or pass the "--bundleConfigAsCjs" flag.


es6转换成es5


在上面的例子中我们代码里有使用es6,但是打包后仍未转译,es6es5主要依赖以下几个关键插件rollup-plugin-babel,@babel/preset-env,@babel/core插件


目录下新建一个.babelrc.json,并依次安装npm i rollup-plugin-babel @babel/preset-env @babel/core --save-dev

{
    "presets": [
        ["@babel/env", {"modules": false}]
    ]
}

rollup.config.js

import commonjs from '@rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
export default [
    {
        input: 'index.js',
        output: {
            format: 'cjs',
            file: 'bundle_cjs.js'
        },
        plugins: [commonjs(), babel({
            exclude: ['node_modules/**']
        })]
    },
]


这样配置后,es6就可以成功编译成es5


我们发现还有@rollup/plugin-commonjs插件,这个插件主要是编译cjs


如果你的代码使用的是cjs,未编译前

// import b from './js/b.js'
const a = require('./js/a.js');
const getName = () => {
    console.log('hello', a.name);
    // console.log('hello', b.name);
};
getName();

编译打包后

'use strict';
var _01 = {};
var name = 'Web技术学苑';
var age = 18;
var a$1 = {
  name: name,
  age: age
};
var a = a$1;
var getName = function getName() {
  console.log('hello', a.name);
};
getName();
module.exports = _01;

rollup默认就是esModule方式,所以你会看到你配置的输出文件都是export default方式输出的。


当我们简单的了解一些rollup的知识后,我们尝试打包一个我们自己写的工具库试一试


rollup打包一个工具库


在很早之前写过一篇关于webpack打包工具库,可以参考这篇文章webpack5构建一个通用的组件库,今天用rollup实现一个webpack5打包一样的功能,对应文章源码参考nice_utils


准备基础库


首先我们把nice_utils[1]仓库下拷贝出src目录


目录大概就是下面这样

9b19bf102914896cb4c49c6fc4635dc2.png

因为项目是支持ts的所以也需要安装typescript


执行以下命令,然后初始化tsconfig.json

npm i typescript --save-dev
npx tsc --init

npx tsc --init主要是默认生成ts配置文件

{
    "compilerOptions": {
      "baseUrl": ".",
      "outDir": "dist",
      "sourceMap": true,
      "target": "es5",
      "module": "ESNext",
      "moduleResolution": "node",
      "newLine": "LF",
      "strict": true,
      "allowJs": true,
      "noImplicitAny": false,
      "noImplicitThis": false,
      "noUnusedLocals": true,
      "experimentalDecorators": true,
      "resolveJsonModule": true,
      "esModuleInterop": true,
      "removeComments": false,
      "jsx": "preserve",
      "lib": ["esnext", "dom", "dom.iterable"],
    },
  }

这里注意一点lib配置需要加上dom.iterable,不加这个会打包编译报错,因为我们的工具函数里有用到entries迭代器,所以lib上需要加上这个,默认生成的配置会比较多,关键的几个,特别注意lib,target,jsx即可


rollup.config.js


在根目录下新建rollup.config.js

import path, { dirname } from 'path';
import { fileURLToPath } from 'url'
import commonjs from '@rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import alias from '@rollup/plugin-alias';
import ts from 'rollup-plugin-typescript2';
const resolve = (p) => {
    return path.resolve(dirname(fileURLToPath(import.meta.url)), p)
};
const builds = {
    'runtime-cjs-prod': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'cjs',
        env: 'production',
        external: []
    },
    'runtime-esm-prd': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'esm',
        env: 'production',
        external: []
    },
    'runtime-umd-prd': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'umd',
        env: 'production',
        external: []
    }
}
const getConfig = (name) => {
    const opts = builds[name];
    const config = {
        input: opts.entry,
        external: opts.external,
        plugins: [
            commonjs(),
            babel(),
            // 设置全局路径别名
            alias({
                entries: {
                    'src': resolve('src'),
                }
            }),
            ts({
                tsconfig: resolve('./tsconfig.json')
            })
        ].concat(opts.plugins, []),
        output: {
            file: opts.dest(name),
            format: opts.format,
            name: opts.name || 'Nice_utils',
        }
    }
    return config;
}
export default Object.keys(builds).map(getConfig)

以上一段代码看似好长,但实际上输出的就是一个数组配置,本质上就是输出

export default [
    {
        input: '',
        dest: '',
        format: 'cjs',
        env: 'production',
        external: []
    }
    ...
]

我们注意到resolve这个方法有些特殊,主要是获取路径,我们以前可能不会这么做,我们会path.resove(__dirname, p),因为此时rollup是默认ESModule所以,__dirname就会报错,__dirname只有在cjs中才可以正确使用,所以这里只是换了一种方式,但实际上的作用并没有发生变化

import path, { dirname } from 'path';
import { fileURLToPath } from 'url'
const resolve = (p) => {
    return path.resolve(dirname(fileURLToPath(import.meta.url)), p)
};
const builds = {
    'runtime-cjs-prod': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'cjs',
        env: 'production',
        external: []
    },
    ...
}

最后我们在package.json中配置打包命令

{
    "name": "02",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "type": "module",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "rollup -c rollup.config.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@babel/core": "^7.19.6",
        "@babel/preset-env": "^7.19.4",
        "@rollup/plugin-alias": "^4.0.2",
        "@rollup/plugin-commonjs": "^23.0.2",
        "@types/node": "^18.11.6",
        "rollup": "^3.2.3",
        "rollup-plugin-babel": "^4.4.0",
        "rollup-plugin-typescript2": "^0.34.1",
        "typescript": "^4.8.4"
    }
}

顺带我们看下,我们使用到的一些插件,注意@types/node必须要安装,不安装就会提示需要安装此插件

0ebfee47d519ea32093c48c45abbbc5d.png

并且我们看到了es6es5所需要的@babel/core,@babel/preset-env以及rollup-plugin-babel,还有@rollup/plugin-commonjs,这个插件会将内部模块中

如果有用到cjs会给我们转译成es6,因为在浏览器是不识别require这样的关键字的


当我们运行npm run build

5dee596123ee2639119436baf7fc9ace.png


测试打包后的js


我们新建了一个example文件,在该目录下新建一个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>example</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="../dist/runtime-umd-prd.js"></script>
  </body>
</html>

我们需要借助一个类似webpack-dev-server的第三方插件才行,这里我们结合gulpbrowser-sync两个插件


我们新建一个gulpfile.js文件

// gulpfile.js
import browserSync from 'browser-sync';
import gulp from 'gulp';
import { rollup } from 'rollup';
import { builds, getConfig } from './config.js';
const buildTask = (keyName) => {
    gulp.task('build', () => {
        const { input, output, plugins } = getConfig(keyName);
        return rollup({
            input,
            plugins
        })
            .then(bundle => {
                return bundle.write({
                    ...output,
                    sourcemap: true
                });
            });
    });
}
const devServer = () => {
    const server = browserSync.create();
    const defaultOption = {
        port: '8081', //设置端口
        open: true,  // 自动打开浏览器
        files: `src/*`, // 当dist文件下有改动时,会自动刷新页面
        server: {
            baseDir: '.' // 基于当前根目录
        },
        serveStatic: ['.', './example'],
    }
    gulp.task('server', () => {
        server.init(defaultOption)
    })
}
const start = async () => {
    const keyName = Object.keys(builds)[2]; // 输出umd模式
    await buildTask(keyName);
    await devServer();
}
start();

我们所用到的就是gulp,并结合rollup打包我们的仓库代码


在引入的config.js主要是把之前的相关配置提了出去

// config.js
import path, { dirname } from 'path';
import { fileURLToPath } from 'url'
import commonjs from '@rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import alias from '@rollup/plugin-alias';
import ts from 'rollup-plugin-typescript2';
export const resolve = (p) => {
    return path.resolve(dirname(fileURLToPath(import.meta.url)), p)
};
export const builds = {
    'runtime-cjs-prod': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'cjs',
        env: 'production',
        external: [],
        plugins: []
    },
    'runtime-esm-prd': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'esm',
        env: 'production',
        external: [],
        plugins: []
    },
    'runtime-umd-prd': {
        entry: resolve('src/index.ts'),
        dest: name => `dist/${name}.js`,
        format: 'umd',
        env: 'production',
        external: [],
        plugins: []
    }
}
export const getConfig = (name) => {
    const opts = builds[name];
    const config = {
        input: opts.entry,
        external: opts.external,
        plugins: [
            commonjs(),
            babel(),
            // 设置全局路径别名
            alias({
                entries: {
                    'src': resolve('src'),
                }
            }),
            ts({
                tsconfig: resolve('./tsconfig.json')
            })
        ].concat(opts.plugins, []),
        output: {
            file: opts.dest(name),
            format: opts.format,
            name: opts.name || 'Nice_utils',
        }
    }
    return config;
}

最后我们在package.json添加运行命令

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rollup -c rollup.config.js",
    "server": "gulp build && gulp server"
},

注意我们server实际上有两个任务,所以必须要依次执行两个任务才行


当我们运行npm run server时,就会打包,并同时打开浏览器

743e3b871af733d81aa59adf8184f5c4.png


OK了,证明我们打包后的js就生效了


总结


  • 了解rollup[2]的基础使用,对于工具库来说,rollup打包比起webpack配置要简单得多,但是远远没有webpack的生态强大,两者比较使用起来rollupwebpack要简单得多,我们也可以参考学习vue2[3]源码,vue2源码是如何通过rollup打包的
  • 以一个简单的例子结合gulp配和rollup打包对应不同模式的js,从而加深对rollup的理解
  • 本文示例code example[4]



相关文章
|
6月前
|
缓存 前端开发 JavaScript
Vite 打包优化:全面解析与实践
Vite 作为新一代前端构建工具,以其快速开发体验和高效打包能力著称。然而,在实际项目开发中,为了进一步提升性能和用户体验,我们仍需对 Vite 打包进行优化。本文将深入探讨 Vite 打包优化策略,涵盖代码拆分、资源压缩、缓存利用、构建配置等多个方面,并提供实践案例和最佳实践建议,帮助开发者充分释放 Vite 的潜力。
1598 1
|
6月前
|
前端开发 UED
探索前端工程化之路:Webpack、Rollup等构建工具对比与实践
在现代前端开发中,工程化成为不可或缺的一环。本文将深入探讨常用的前端构建工具Webpack和Rollup,并比较它们在实践中的优劣势。通过对功能、性能、插件生态等方面的评估,帮助读者选择适合自己项目需求的构建工具。
92 1
|
6月前
|
JSON 前端开发 JavaScript
深入了解rollup(四)插件开发示例
Rollup是一个JavaScript模块打包器,它可以将多个模块打包成一个单独的文件,以便在浏览器中使用。与其他打包工具相比,Rollup的主要优势在于它可以生成更小、更快的代码。在本文中,我们将深入了解Rollup的插件开发。
82 1
|
前端开发 JavaScript 开发者
前端工程化打包工具之Rollup
Rollup是一个非常流行的前端工程化打包工具,它可以帮助开发者快速构建具有可重用性和可维护性的前端项目,并且提供了完善的依赖管理和打包机制。
182 3
|
自然语言处理 JavaScript 前端开发
从 rollup 初版源码学习打包原理
从 rollup 初版源码学习打包原理
46 0
|
缓存 JSON JavaScript
30分钟搞懂Rollup+Typescript工程构建(一)
最近在研究一个ngptcommit命令行工具,然后想通过Rollup+Typescript去编译的时候,发现对Rollup和Typescript的编译配置有点陌生,所以希望通过本文能够对其有个系统的认知。
223 0
|
JSON JavaScript 前端开发
30分钟搞懂Rollup+Typescript工程构建(二)
在本文中不讨论Typescript的具体用法,我们将学习如何将Typescript代码转为JavaScript。
494 0
|
JavaScript 前端开发 API
Vite 是如何使用 Rollup 进行构建的
Vite 是如何使用 Rollup 进行构建的
326 0
|
JavaScript 前端开发 算法
rollup 是什么?如何使用?
继 Webpack、Vite 等前端工具链系列的了解之后,又碰到了 Rollup,我之前对 Rollup 的了解仅停留在 Vite 打包时使用、组里的大佬使用 Rollup 写过一个静态官网页面,为了
|
Web App开发 编解码 自然语言处理
rollup 实战第二节 搭建开发环境
在上一篇博客中,我简单的描述了 rollup 怎么使用,配置文件的使用。这一篇,来一起学习一下 rollup 怎么搭建开发服务,这里不包含任何的框架代码,我们需要 实现的是 ,我 在代码中修改任何地方,rollup可以自己监听到,并且给我给我更新浏览器就行。 这里的代码包括 css, 以及js等。
rollup 实战第二节 搭建开发环境