首页
学习
活动
专区
圈层
工具
发布

Vite 学习(三) - rollup & esbuild 基础学习

本小节介绍下 rollup 和 esbuild 的基础用法,我们都知道 vite 本身使用的 rollup 打包,vite 的插件也和 rollup 的插件机制相吻合; esbuild 是用于在开发环境中对文件进行处理,也有自己的声明周期钩子函数,由于对文件的分割和 css 支持还不太友好,暂未应用到打包环节。

rollup

rollup 是成熟的构建工具, 开源类库优先选择,以 esm 标准为目标的构建工具,package.json 中提供了 module 字段,引用时以这个字段为主,不是 main,默认不支持 commonjs,需要使用插件 resolvenode 实现转换,webpack 适合企业级项目,写好的 loader, plugins,打包优化

  • tree shaking

rollup 令人津津乐道的就是 treeshaking 功能,把没有使用的代码剔除,减小打包体积

如下一段代码

代码语言:txt
复制
function test() {

    var name = 'test';

    console.log(123);

}

const name = '测试测试';

function fn() {

    console.log(name);

}



fn();

打包后没有 test 函数的代码

代码语言:txt
复制
'use strict';



const name = '测试测试';

function fn() {

    console.log(name);

}



fn();

实现原理还是依赖于 ast 解析,也是为什么需要使用 esm 标准,解析的时候,会把代码统一到一个文件的最上层,import 和 export 的字段都会打上标记,在哪里定义,在哪里使用的。自然未使用的也不会引入。

常用命令

  • npm i -g rollup 全局安装
  • rollup -i index.js 打包 index.js 文件,在控制台查看内容
  • rollup -i index.js --file dist.js 打包文件输出到 dist.js 中
  • rollup -i index.js --file dist.js --format umd 以 umd 模式打包,

还有 iife cjs umd 模式

  • rollup -i index.js a.js --dir dist 打包到dist 目录下,会自动生成index.js
  • rollup -i index.js --file dist.js --format umd --name Index (--watch) 可以全局 global 命名
  • rollup -h (--help) 查看命令

rollup.config.js

执行命令 rollup -c rollup.config.js --environment TEST:123

代码语言:txt
复制
// 命令行定义环境变量,在脚本中可以直接获取

console.log(process.env.TEST)



export default {

  input: 'index.js',

  output: {

    file: 'dist.js',

    // 打包模式可以条件判断

    format: isLocal ? 'es' : 'umd',

    name: 'Index'

  }

}

如果我们全局安装了 rollup,命令行执行操作会使用全局 rollup,如果想使用项目内的,执行 node\_modules 下的:

./node_modules/.bin/rollup -c rollup.config.js --plugin json

配置文件

rollup 不光可以打包一个文件,也可以打包多种,我们看下 vue3 打包出的种类如下,配置文件中导出数组形式即可

代码语言:txt
复制
默认 根目录 

// 模块引入可以不加后缀名或者引入目录下的 index.js 可以简写

improt resolve from '@rollup/plugin-node-resolve'

// commonjs 转为 esm

import commonjs from '@rollup/commonjs'

// 压缩

import {terser} from 'rollup-plugin-terser'

export default [

  {

    input:'index.js',

    // 不想打包进的,我们使用 cdn 引入

    external: ['react', {

      // 对象需要全局名字

      ‘react’: 'React'

    }],

    output: {

      file: 'dist/index.umd.js',

      format: 'umd',

      // 也有 plugin,编译完才用,压缩

      plugins: [

        terser()

      ],

      // 和 terser 冲突,因为这是属于注释的内容

      banner: '/\*\* hellp this is banner'

    },

    // 默认放第一个,先执行

    plugins: [

      resolve(), commonjs(), json()

    ]

  },

  {

    input:'index.js',

    output: {

      file: 'dist/index.es.js',

      format: 'es'

    }

  }

]

命令行命令默认全局, package.json 中默认本地 node\_modules 下的

rollup 插件

执行机制

input -> rollup main code -> plugin1 -> plugin2 ... -> emit file 输出到文件-> finish

我们编写插件就是对象要操作的类型的文件,根据不同 hook 在不同节点执行不同方法,完成我们想要的目的

配置对什么文件执行插件操作,什么文件不执行操作关键字:include、exclude

  • @rollup/plugin-alias 设置别名
代码语言:txt
复制
import a from '../a' // ..写法繁琐想简写如下

import a from 'a'



// 插件配置

plugins: [

  alias({

    entries: {

      a: "../a",// 页面的 ../a 用 a 替换

    },

  }),

],

源码

代码语言:txt
复制
// 最后是导出一个插件函数

...

export default function alias(options: RollupAliasOptions = {})

...



// 函数返回

return {

    name: 'alias',// 插件名,报错查看

    // inputOptions 对应 rollup.config.js 写的配置 导出的值

    // 构建之前处理

    async buildStart(inputOptions) {

      await Promise.all(

        [...(Array.isArray(options.entries) ? options.entries : []), options].map(

          ({ customResolver }) =>

            customResolver &&

            typeof customResolver === 'object' &&

            typeof

    

    // 路径映射  a          ../a   

    resolveId(importee, importer) {

      // 返回真正的 路径

    }

其他相对重要的钩子函数如:

  • transform
  • 类型: Function
  • 形式: ( source, id ) => (code|{ code, map, dependencies }|Promise)

可以被用来改变每个模块代码。在 --watch 模式中, 会监听 dependencies 数组中所有文件或者目录的变化。

例如我们使用 replace 插件,对文件内容进行替换

具体大家可以去官网学习

esbuild

因为 vite 内部文件编译使用了 esbuild,他是 go 语言写的,完全命令行使用,能够解析 js ,但是不能运行 js。代码中也是间接通过 node 执行命令行工具操作。我们需要了解 esbuild 的插件执行机制,我们在编写 vite 插件的时候,如果写自己插件有需要的话,也要同时兼容 rollup 和 esbuild

使用

npx esbuild index.js

npx esbuild index.js --ourfile=dist.js 输出

想打包的话(所有代码打包到一起)加上 --bundle,内部有 treeshaking

--target=esnext 指定编译目标,注意 esbuild 对 es5 编译不完整,const 依旧会 va

--platform=node, browser node 环境或者浏览器环境

--format=esm cjs

--watch

--define:TEST=12 环境变量

如果项目中使用了图片,需要使用 loade

代码语言:txt
复制
import a from './logo.png'

--loader:.png=dataurl // 转成了 base64

esbuild 插件

在 esbuild 中,插件被设计为一个函数,该函数需要返回一个对象(Object),对象中包含 name 和 setup 等 2 个属性

build 对象上会暴露整个构建过程中非常重要的 2 个函数:onResolve 和 onLoad,它们都需要传入 Options(选项)和 CallBack(回调)等 2 个参数。

代码语言:txt
复制
// build.js



let exampleOnLoadPlugin = {

  name: "example", // 名字为了报错警告提示

  setup(build) {

    let fs = require("fs");



    // 输入的 options

    console.log(build.initialOptions);

    // 我们可以在操作中修改配置  build.initialOptions.outdir = 'lib'

    /\*\*

      build.onStart({}, () => {

          // 不能再 onstart 中修改配置

      })

    \*/

    

    // 处理哪类文件

    build.onResolve(

      {

        filter: /\.txt$/,

      },

      async (args) => ({

        path: args.path,

        namespace: "txt", // 区分文件加载,标识

      })

    );



    build.onLoad(

      {

        // 在 go 里执行 正则

        filter: /\.\*$/, //什么类型文件执行



        namespace: "txt",

      },

      async (args) => {

        // 模块的文件路径

        let text = await fs.promises.readFile(args.path, "utf-8");

        return {

          // 文件内容预处理

          contents: `export default ${JSON.stringify([

            ...text.split(/\s+/),

            "----------",

          ])}`,

          // loader: "json",

        };

      }

    );

  },

};



require("esbuild")

  .build({ // 命令行参数

    entryPoints: ["index.js"],

    bundle: true,

    outdir: "dist",

    loader: {

      ".png": "dataurl",

    },

    plugins: [exampleOnLoadPlugin],

  })

  .catch(() => process.exit(1));

onResolve 的回调函数

onResolve 函数的回调函数会在 esbuild 构建每个模块的导入路径(可匹配的)时执行。

onResolve 函数的回调函数需要返回一个对象,其中会包含 path、namespace、external 等属性。

通常,该回调函数会用于自定义 esbuild 处理 path 的方式,例如:

重写原本的路径,例如重定向到其他路径

将该路径所对应的模块标记为 external,即不会对改文件进行构建操作(原样输出)

onLoad 的回调函数

onLoad 函数的回调函数会在 esbuild 解析模块之前调用,主要是用于处理并返回模块的内容,并告知 esbuild 要如何解析它们。并且,需要注意的是 onLoad 的回调函数不会处理被标记为 external 的模块。

onLoad 函数的回调函数需要返回一个对象,该对象总共有 9 个属性。这里我们来认识一下较为常见 2 个属性:

contents 处理过的模块内容

loader 告知 esbuild 要如何解释该内容(默认为 js)。例如,返回的模块内容是 CSS,则声明 loader 为 css

本节对 rollup 和 esbuild 中的常见使用方式做了介绍,包括常用命令和插件的使用,下一节会介绍下 vite 插件的学习使用,如果有问题欢迎留言,谢谢阅读!

下一篇
举报
领券