[Tools] Esbuild

esbuild 命令行调用

使用 Esbuild 有 2 种方式,分别是 命令行调用代码调用

无论如何我先npm init -y创建一个新项目,然后通过如下的命令完成 Esbuild 的安装:

npm i esbuild

查看esbuild版本

./node_modules/.bin/esbuild --version

创建ts文件

const sayHello = (text:string) => { 
  console.log(`hello ${text}`);
}
sayHello('world!');

ts文件编译,指定输出文件位置

./node_modules/.bin/esbuild src/index.ts --outfile=dist/index.js

对比tsc的编译

tsc src/index.ts

默认转成的是ES5语法

var sayHello = function (text) {
    console.log("hello ".concat(text));
};
sayHello('world!');

可以给一些参数

tsc src/index.ts --target es6 --outFile dist/index.js

其实esbuild也能指定ES的转换版本

./node_modules/.bin/esbuild src/index.ts  --outfile=dist/index.js --target=esnext

但是注意,esbuild只支持es6以上的版本,因此,如果你指定es5的版本,就会报出如下的错误

./node_modules/.bin/esbuild src/index.ts  --outfile=dist/index.js --target=es5

esbuild 代码调用

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

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

项目打包——Build API

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

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

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 所有插件中都不能使用任何异步操作,这给插件开发增加了限制。

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

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个组件

// components/Comp1.tsx
import React from 'react';
export default () => { 
  return (<>
    <h1>Comp1</h1>
    <ul>
      <li>vite</li>
      <li>esbuild</li>
      <li>rollup</li>
    </ul>
  </>)
}
// 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文件,然后对文件进行打包处理了

node esbuild.config.mjs

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

<!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运行一下

 npx http-server -o -c-1
// -o 立即打开浏览器
// -c-1 清空http-server缓存

引入css

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

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

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

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

<link rel="stylesheet" href="dist/app.css">

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

// 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样式冲突

// 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加载器

loader: {
	".module.css": "local-css",
},

esbuild 新版本已经对css module进行了处理,所以这可以省略

引入Html

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

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

<!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>
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插件

// 导入
npm install esbuild-plugin-inline-image

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

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

使用了inlineImage插件,loader中的.svg对应的处理则不再需要

元数据分析

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

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 与其他工具集成时这非常有用。
import esbuild from "esbuild";
import inlineImage from "esbuild-plugin-inline-image";

(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()],
  });

  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代码即可

<script type="module">
	new EventSource('/esbuild').addEventListener('change', () => location.reload())
</script>
posted @   Zhentiw  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2023-02-25 [Typescript] Wrap an function with Identity function to provide clean Type API
2023-02-25 [Typescript] Constraint inputs types
2021-02-25 [Graphql] Apollo Client Example
2021-02-25 [Graphql + Netlify] Solve @Apollo/client CORS problem
2020-02-25 [Unit testing Angular] Complete component code
2020-02-25 [Javascript] Objects and Functions
2020-02-25 [HTML5] Element id binding
点击右上角即可分享
微信分享提示