首页>国内 > 正文

Rollup打包入门到实践,你学会几分?

2022-10-28 09:55:25来源:Web技术学苑

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


(资料图片)

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

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

2、以一个实际的例子,将工具库用rollup与gulp实现任务流打包,验证打包后的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// esnpx 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.json的type要指定成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,但是打包后仍未转译,es6转es5主要依赖以下几个关键插件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目录。

目录大概就是下面这样。

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

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

npm i typescript --save-devnpx 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必须要安装,不安装就会提示需要安装此插件。

并且我们看到了es6转es5所需要的@babel/core,@babel/preset-env以及rollup-plugin-babel,还有@rollup/plugin-commonjs,这个插件会将内部模块中如果有用到cjs会给我们转译成es6,因为在浏览器是不识别require这样的关键字的。

当我们运行npm run build时。

测试打包后的js

我们新建了一个example文件,在该目录下新建一个index.html。

                  example        
<script src="../dist/runtime-umd-prd.js"></script>

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

我们新建一个gulpfile.js文件。

// gulpfile.jsimport 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.jsimport 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时,就会打包,并同时打开浏览器。

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

总结

了解rollup[2]的基础使用,对于工具库来说,rollup打包比起webpack配置要简单得多,但是远远没有webpack的生态强大,两者比较使用起来rollup比webpack要简单得多,我们也可以参考学习vue2[3]源码,vue2源码是如何通过rollup打包的。

以一个简单的例子结合gulp配和rollup打包对应不同模式的js,从而加深对rollup的理解。

本文示例code example[4]。

参考资料

[1]nice_utils:https://github.com/maicFir/nice_utils

[2]rollup:https://rollupjs.org/guide/en/

[3]vue2:https://github.com/vuejs/vue

[4]code example:https://github.com/maicFir/lessonNote/tree/master/rollup/02

关键词: 配置文件 需要安装 根目录下 输出文件 正确使用

相关新闻

Copyright 2015-2020   三好网  版权所有 联系邮箱:435 22 640@qq.com  备案号: 京ICP备2022022245号-21