【应用实践】esbuild 代码调用

专栏中的代码均在 github.com/sunny-117/e...

命令行的操作当需要配置的内容过多是,太繁琐了。因此我们当然可以像其他工具一样,进行配置化的操作。

Esbuild 对外暴露了一系列的 API,主要包括两类: Build APITransform API,我们可以在 Nodejs 代码中通过调用这些 API 来使用 Esbuild 的各种功能。

项目打包------Build API

Build API主要用来进行项目打包,包括buildbuildSynccontext三个方法。

我们可以在项目根目录下创建esbuild.config.mjs文件来进行处理

javascript 复制代码
import esbuild from "esbuild";
esbuild.build({
  // 入口文件列表,为一个数组
  entryPoints: ['src/app.tsx'],
  // 是否需要打包,一般设为 true
  bundle: true,
  // 是否进行代码压缩
  minify: false,
  // 是否生成 SourceMap 文件
  sourcemap: true,
  // 指定语言版本和目标环境
  target: ['es2020', 'chrome58', 'firefox57', 'safari11'],
  // 指定输出文件
  outfile: './public/dist/app.js',
  // 指定loader
  loader: {
    ".svg": "dataurl",
  }
})

buildSyncbuild唯一的不一样就是这个方法是同步的,个人并不推荐使用buildSync这种同步的 API,它们会导致两方面不良后果。一方面容易使 Esbuild 在当前线程阻塞,丧失并发任务处理的优势。另一方面,Esbuild 所有插件中都不能使用任何异步操作,这给插件开发增加了限制。

为了让大家看到完整的效果,我们可以重新处理一下代码:

javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client'
import Comp1 from 'components/Comp1';
import Comp2 from 'components/Comp2';

const App = () => (<div>
  <h1>Hello World!</h1>
  <Comp1 />
  <Comp2 />
</div>);

ReactDOM
  .createRoot(document.getElementById('root')!)
  .render(<App />)

为了看到效果,多写了2个组件

javascript 复制代码
// components/Comp1.tsx
import React from 'react';
export default () => { 
  return (<>
    <h1>Comp1</h1>
    <ul>
      <li>vite</li>
      <li>esbuild</li>
      <li>rollup</li>
    </ul>
  </>)
}
javascript 复制代码
// components/Comp2.tsx
import React from 'react';
import logo from "../assets/react.svg"

export default () => { 
  return (<>
    <h1>Comp2</h1>
    <img src={logo} />
  </>)
}

然后我们就可以利用node去运行esbuild.config.mjs文件,然后对文件进行打包处理了

javascript 复制代码
node esbuild.config.mjs

打包好之后的文件,我们可以用index.html去调用对应的js文件即可

javascript 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="root"></div>
  <script src="dist/app.js"></script>
</body>
</html>

利用http-server运行一下

javascript 复制代码
 npx http-server -o -c-1
// -o 立即打开浏览器
// -c-1 清空http-server缓存

引入css

javascript 复制代码
// src/style.css
body{
  color:black;
  font-size:26px;
}
.title{
  color:blue;
  font-size:40px;
}
javascript 复制代码
// app.tsx
import "./style.css"

const App = () => (<div>
  <h1 className="title">Hello World!!</h1>
  <Comp1 />
  <Comp2 />
</div>);

最后打包出来的css文件名和app组件同名,所以最好css的名字不要和组件同名。

和打包之后的js一样,并不会自动放到index.html上,需要我们自己放上去

javascript 复制代码
<link rel="stylesheet" href="dist/app.css">

如果是在其他组件内引入的css,一样会打包到app.css中,比如在Comp1.tsx中引入css

javascript 复制代码
// components/comp.css
ul{
  list-style: none;
}
li{
  border: 1px solid #ccc;
}

// components/Comp1.tsx
import React from 'react';
import "./comp.css"

export default () => { 
  return (<>
    <h1>Comp1</h1>
    <ul>
      <li>vite</li>
      <li>esbuild</li>
      <li>rollup</li>
    </ul>
  </>)
}

我们也可以引入CSS modules预处理器,以便组件的css样式与全局css样式冲突

javascript 复制代码
// components/comps.module.css
.title{
  color:yellow;
}

// components/Comp2.tsx
import React from 'react';
import logo from "../assets/react.svg"
import comps from "./comps.module.css";

export default () => { 
  return (<>
    <h1 className={comps.title}>Comp2</h1>
    <img src={logo} />
  </>)
}

需要在配置中加上css moduleloader加载器

javascript 复制代码
loader: {
	".module.css": "local-css",
},

引入Html

之前都是直接将html写死在打包好的文件夹中,其实也可以直接打包已有的html文件

在src目录下创建index.html文件

javascript 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="./app.css" rel="stylesheet">
</head>
<body>
  <div id="root"></div>
</body>
<script src="./app.js"></script>
</html>
javascript 复制代码
import esbuild from 'esbuild';
esbuild.build({
  // 入口文件列表,为一个数组
  entryPoints: ['src/app.tsx','src/index.html'],
  // 是否需要打包,一般设为 true
  bundle: true,
  // 是否进行代码压缩
  minify: false,
  // 是否生成 SourceMap 文件
  sourcemap: true,
  // 指定语言版本和目标环境
  target: ['es2020', 'chrome58', 'firefox57', 'safari11'],
  // 是否生成打包的元信息文件
  metafile: true,
  // 指定输出文件
  outdir: './public/dist/',
  // 指定loader
  loader: {
    ".html": "copy",
    ".svg": "dataurl",
    ".module.css": "local-css",
  }
})

插件

Esbuild 的时候难免会遇到一些需要加上自定义插件的场景,并且 Vite 依赖预编译的实现中大量应用了 Esbuild 插件的逻辑,你可以到现有 esbuild 插件列表中去查找已有的esbuild插件,比如,之前对于图片和css的处理。内联图像插件css插件

javascript 复制代码
// 导入
npm install esbuild-plugin-inline-image
npm install -D esbuild-plugin-class-modules

// 使用
import inlineImage from "esbuild-plugin-inline-image";
import classModules from "esbuild-plugin-class-modules";

esbuild.build({
  ...
  plugins: [
    ...
    inlineImage(),
    classModules()
  ]
  ...
});

元数据分析

esbuild.build是一个异步函数,我们还可以拿到这个函数的返回值,根据这个返回值,我们还能进行元数据的分析

javascript 复制代码
import esbuild from "esbuild";
import inlineImage from "esbuild-plugin-inline-image";
(async () => { 
  const result = await esbuild.build({
    // 入口文件列表,为一个数组
    entryPoints: ['src/app.tsx'],
    // 是否需要打包,一般设为 true
    bundle: true,
    // 是否进行代码压缩
    minify: false,
    // 是否生成 SourceMap 文件
    sourcemap: true,
    // 指定语言版本和目标环境
    target: ['es2020', 'chrome58', 'firefox57', 'safari11'],
    // 是否生成打包的元信息文件
    metafile: true,
    // 指定输出文件
    outfile: './public/dist/app.js',
    // 指定loader
    // loader: {
    //   ".svg": "dataurl",
    // },
    plugins: [
      inlineImage()
    ]
  })

  console.log(result);

  // 打印详细的元信息
  const text = await esbuild.analyzeMetafile(result.metafile, {
    verbose: true, 
  });

  console.log(text);
})();

context

在项目打包方面,除了buildbuildSync,Esbuild 还提供了另外一个比较强大的 API------context

context为我们提供了三种可以增量构建的API,注意context和下面的API都是异步的

  • Watch mode 简单来说就是监听模式,当我们修改源文件的时候,会自动帮我们重建
  • Serve mode 启动本地开发服务器,提供最新构建的结果。注意,Serve mode会自动帮我们构建打包源文件,但是并不支持热重载
  • Rebuild mode 允许手动调用构建。当将 esbuild 与其他工具集成时这非常有用。
javascript 复制代码
import esbuild from "esbuild";
import inlineImage from "esbuild-plugin-inline-image";
import classModules from "esbuild-plugin-class-modules";

(async () => {
  const ctx = await esbuild.context({
    // 入口文件列表,为一个数组
    entryPoints: ["src/app.tsx","src/index.html"],
    // 是否需要打包,一般设为 true
    bundle: true,
    // 是否进行代码压缩
    minify: false,
    // 是否生成 SourceMap 文件
    sourcemap: true,
    // 指定语言版本和目标环境
    target: ["es2020", "chrome58", "firefox57", "safari11"],
    // 指定输出文件
    // outfile: "./dist/app.js",
    outdir: "./dist",
    loader: {
      ".html": "copy",
    },
    plugins: [inlineImage(),classModules()],
  });

  // await ctx.watch();

  ctx
    .serve({
      servedir: "./dist",
      port: 8000,
      host: "localhost",
    })
    .then((server) => {
      console.log(`server is running at ${server.host}:${server.port}`);
    });
})();

Live reload

实时重新加载是一种开发方法,您可以在浏览器与代码编辑器同时打开并可见。当您编辑并保存源代码时,浏览器会自动重新加载,并且重新加载的应用程序版本包含您的更改。这意味着您可以更快地迭代,因为您不必在每次更改后手动切换到浏览器、重新加载,然后切换回代码编辑器。

不过,esbuild并没有给我们提供实时重新加载的API,但是可以通过组合监视模式Watch mode(和服务模式Serve mode加上少量客户端 JavaScript 来构建实时重新加载仅在开发期间添加到应用程序的代码。

配置代码还是之前的不变,只是watch()serve()函数同时打开,然后只需要在客户端html中加入下面的js代码即可

javascript 复制代码
<script type="module">
	new EventSource('/esbuild').addEventListener('change', () => location.reload())
</script>
相关推荐
Martin -Tang29 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发30 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂2 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽5 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新6 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端