共计 3838 个字符,预计需要花费 10 分钟才能阅读完成。
前言
关注「Vite」底层实现的同学,我想应该分明它应用「esbuild」来实现对 .ts
、jsx
、.js
代码的转化。当然,在「Vite」之前更早应用「esbuild」的就是「Snowpack」。不过,相比拟「Vite」领有的微小社区,显然「Snowpack」的关注度较小。
「Vite」的外围是基于浏览器原生的 ES Module
。然而,相比拟传统的打包工具和开发工具而言,它做出了很多扭转,采纳「esbuild」来反对 .ts
、jsx
、.js
代码的转化就是其中之一。
那么,接下来咱们就步入明天的正题,What is esbuild, and how to use it?
1 什么是 esbuild
「esbuild」官网的介绍:它是一个「JavaScript」Bundler
打包和压缩工具,它能够将「JavaScript」和「TypeScript」代码打包散发在网页上运行。
目前「esbuild」反对的性能:
- 加载器
- 压缩
- 打包
- Tree shaking
- Source map 生成
- 将 JSX 和较新的 JS 语法移植到 ES6
- …
这里,咱们列出了几点常关注的,至于其余,有趣味的同学能够移步官网文档自行理解。
目前对于「JavaScript」语法转化不反对的个性有:
- Top-level await
- async await
- BigInt
- Hashbang 语法
须要留神的是对于不反对转化的语法会 原样输入。
2 比照现有的打包工具
「esbuild」的作者比照目前现阶段相似的工具做了 基准测试。最初的后果是:
对于这些基准测试,esbuild 比我测试的其余 JavaScript 打包程序 快至多 100 倍。
100 倍,能够说快到飞起了 … 而「esbuild」快的起因,这里我分两个层面解释:
2.1 官网解释
- 它是用「Go」语言编写的,该语言能够编译为本地代码。
- 解析,生成最终文件和生成 source maps 全副齐全并行化。
- 无需低廉的数据转换,只需很少的几步即可实现所有操作。
- 该库以进步编译速度为编写代码时的第一准则,并尽量避免不必要的内存调配。
2.2 语言层面解释
- 现阶段的相似工具,底层的实现都是基于「JavaScript」,其受限于自身是一门解释型的语言,并不能充分利用 CPU。
- 「Chrome V8」引擎尽管对「JavaScript」的运行做了优化,引进「JIT」的机制,然而局部代码实现机器码与「esbuild」全副实现机器码的模式,性能上的差距不可补救。
当然,语言层面仅仅是官网解释中的一点的开展,其余解释有工夫等后续剖析其源码实现后解说。
3 esbuild API 详解
尽管,「esbuild」早已开源和应用,然而官网文档只是简略介绍了如何应用,而对于 API 介绍局部是欠缺的,倡议读者本人去浏览源码中的定义。
「esbuild」总共提供了四个函数:transform
、build
、buildSync
、Service
。上面,咱们从源码定义的角度来认识一下它们。
3.1 transform
transform
能够用于转化 .js
、.tsx
、ts
等文件,而后输入为旧的语法的 .js
文件,它提供了两个参数:
- 第一个参数(必填,字符串),指须要转化的代码(模块内容)。
- 第二个参数(可选),指转化须要的选项,如源文件门路
sourcefile
、须要加载的loader
,其中loader
的定义:
type Loader = 'js' | 'jsx' | 'ts' | 'tsx' | 'css' | 'json' | 'text' | 'base64' | 'file' | 'dataurl' | 'binary';
transform
会返回一个 Promise
,对应的 TransformResult
为一个对象,它会蕴含转化后的旧的 js
代码、sourceMap
映射、正告信息:
interface TransformResult {
js: string;
jsSourceMap: string;
warnings: Message[];}
3.2 build
build
实现了 transform
的能力,即代码转化,并且它还会将转换后的代码压缩并生成 .js
文件到指定 output
目录。build
只提供了一个参数(对象),来指定须要转化的入口文件、输入文件、loader
等选项:
interface BuildOptions extends CommonOptions {
bundle?: boolean;
splitting?: boolean;
outfile?: string;
metafile?: string;
outdir?: string;
platform?: Platform;
color?: boolean;
external?: string[];
loader?: {[ext: string]: Loader };
resolveExtensions?: string[];
mainFields?: string[];
write?: boolean;
tsconfig?: string;
outExtension?: {[ext: string]: string };
entryPoints?: string[];
stdin?: StdinOptions;
}
build
函数调用会输入 BuildResult
,它蕴含了生成的文件 outputFiles
和提示信息 warnings
:
interface BuildResult {warnings: Message[];
outputFiles?: OutputFile[];}
然而,须要留神的是
outputFiles
只有在write
为false
的状况下才会输入,它是一个Uint8Array
。
3.3 buildSync
buidSync
顾名思义,相比拟 build
而言,它是同步的构建形式,即如果应用 build
咱们须要借助 async await
来实现同步调用,而应用 buildSync
能够间接实现同步调用。
3.4 Service
Service
的呈现是为了解决调用上述 API 时都会创立一个子进行来实现的问题,如果存在屡次调用 API 的状况呈现,那么就会呈现性能上的节约,这一点在文档中也有解说。
所以,应用了 Service
来实现代码的转化或打包,则会创立一个长期的用于共享的子过程,防止了性能上的节约。而在「Vite」中也正是应用 Service
的形式来进行 .ts
、.js
、.jsx
代码的转化工作。
Service
定义:
interface Service {build(options: BuildOptions): Promise<BuildResult>;
transform(input: string, options?: TransformOptions): Promise<TransformResult>;
stop(): void;}
能够看到,Service
的实质封装了 build
、transform
、stop
函数,只是不同于独自调用它们,Service
底层的实现是一个 长期存在可供共享 的子过程。
然而,在理论应用上,咱们并不是间接应用 Service
创立实例,而是通过 startService
来创立一个 Service
实例:
const {
startService,
build,
} = require("esbuild")
const service = await startService()
try {
const res = await service.build({entryPoints: ["./src/main.js"],
write: false
})
console.log(res)
} finally {service.stop()
}
并且,在应用 stop
的时候须要留神,它会完结这个子过程,这也意味着任何在此时处于 pending
的 Promise
也会被终止。
4 实现一个小而美的 Bundler 打包
在简略地意识「esbuild」,咱们就来实现一个小而美的 Bunder
打包:
1. 初始化我的项目和装置「esbuild」:
mkdir esbuild-bundler & npm init -y & npm i esbuild
2. 目录构造:
|——— src
|—— main.js #我的项目入口文件
|——— index.js #bundler 实现外围文件
3.index.js
:
(async () => {
const {
startService,
build,
} = require("esbuild")
const service = await startService()
try {
const res = await service.build({entryPoints: ["./src/main.js"],
outfile: './dist/main.js',
minify: true,
bundle: true,
})
} finally {service.stop()
}
})()
4. 运行一下 node index
即可体验一下闪电般的 bundler
打包!
写在最初
想必看完这篇文章,大家对「esbuild」应该建设起一个根底的认知。并且,文中的源码只是基于「Go」实现的底层能力上的,而真正的底层实现还是得看「Go」是如何实现的,因为脱离了大家熟知的前端,所以就不做介绍。那么,在一下篇文章中,我将会解说在「Vite」的源码设计中是怎么应用 esbuild
来实现 .ts
、jsx
、.js
语法解析,以及咱们如何自定义 plugin
来实现一些代码转化。最初,文章中如果存在表述不当的中央,欢送各位同学提 Issue。
❤️爱心三连击
通过浏览,如果你感觉有播种的话,能够爱心三连击!!!
前端问路人 —— 五柳 ( 微信公众号:Code center)