鞋网站建设,网站app程序制作企业,建设官方网站的主要作用,做百度企业网站有什么好处大家好#xff0c;我是若川。持续组织了5个月源码共读活动#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与#xff0c;每周大家一起学习200行左右的源码#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。今天分享一篇esbui… 大家好我是若川。持续组织了5个月源码共读活动感兴趣的可以点此加我微信 ruochuan12 参与每周大家一起学习200行左右的源码共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。今天分享一篇esbuild的文章~Hello我是三元同学。之前停更了一段时间因为得了流感一直在家养病没来得及更新文章跟读者朋友们先说声抱歉~今天给大家带来的是我最近写的原创文章由于近段时间一直在研究前端构建相关的领域像 Esbuild、Vite 这些都接触得比较多了而且这些工具现在在前端圈也比较热门备受业界关注因此我想我有必要把我研究过的一些东西分享给大家希望能对你有所帮助。什么是 Esbuild?Esbuild 是由 Figma 的 CTO 「Evan Wallace」基于 Golang 开发的一款打包工具相比传统的打包工具主打性能优势在构建速度上可以快 10~100 倍。架构优势1. Golang 开发采用 Go 语言开发相比于 单线程 JIT 性质的解释型语言 使用 Go 的优势在于 :一方面可以充分利用多线程打包并且线程之间共享内容而 JS 如果使用多线程还需要有线程通信(postMessage)的开销另一方面直接编译成机器码而不用像 Node 一样先将 JS 代码解析为字节码然后转换为机器码大大节省了程序运行时间。2. 多核并行内部打包算法充分利用多核 CPU 优势。Esbuild 内部算法设计是经过精心设计的尽可能充分利用所有的 CPU 内核。所有的步骤尽可能并行这也是得益于 Go 当中多线程共享内存的优势而在 JS 中所有的步骤只能是串行的。3. 从零造轮子从零开始造轮子没有任何第三方库的黑盒逻辑保证极致的代码性能。4. 高效利用内存一般而言在 JS 开发的传统打包工具当中一般会频繁地解析和传递 AST 数据比如 string - TS - JS - string这其中会涉及复杂的编译工具链比如 webpack - babel - terser每次接触到新的工具链都得重新解析 AST导致大量的内存占用。而 Esbuild 中从头到尾尽可能地复用一份 AST 节点数据从而大大提高了内存的利用效率提升编译性能。与 SWC 对比速度下面拿纯 Esbuild 和 SWC 来编译代码作为 Transformer 来转换 800 个 tsx 文件不写任何的 JS 胶水代码(如 esbuild-register、esbuild-loader、swc-loader 本身为了适配相应的宿主工具会写一堆 JS 胶水代码影响判断)。EsbuildSWCTSC第一次138 ms217 ms8640 ms第二次154 ms206 ms8400 ms第三次142 ms258 ms8480 ms平均144.7 ms227 ms8507 ms耗时倍率x1x 1.58x 58.8从这个例子可以看出Esbuild 与 SWC 在性能上是在一个量级的这里通过仓库的例子 Esbuild 略快但不排除其他例子里面 SWC 比 Esbuild 略快的场景。兼容性Esbuild 本身的限制包括如下没有 TS 类型检查不能操作 AST不支持装饰器语法产物 target 无法降级到 ES5 及以下意味着需要 ES5 产物的场景只用 Esbuild 无法胜任。相比之下SWC 的兼容性更好产物支持 ES5 格式支持装饰器语法可以通过写 JS 插件操作 AST应用场景对于 Esbuild 和 SWC很多时候我们都在对比两者的性能而忽略了应用场景。对于前端的构建工具来说主要有这样几个垂直的功能:BundlerTransformerMinimizer从上面的速度和兼容性对比可以看出Esbuild 和 SWC 作为 transformer 性能是差不多的但 Esbuild 兼容性远远不及 SWC。因此SWC 作为 Transformer 更胜一筹。但作为 Bundler 以及 MinimizerSWC 就显得捉襟见肘了首先官方的 swcpack 目前基本处于不可用状态Minimizer 方面也非常不成熟很容易碰到兼容性问题。而 Esbuild 作为 Bundler 已经被 Vite 作为开发阶段的依赖预打包工具同时也被大量用作线上 esm CDN 服务比如esm.sh等等作为 Minimizer Esbuild 也已足够成熟目前已经被 Vite 作为 JS 和 CSS 代码的压缩工具用上了生产环境。综合来看SWC 与 Esbuild 的关系类似于当下的 Babel 和 Webpack前者更适合做兼容性和自定义要求高的 Transformer(比如移动端业务场景)而后者适合做 Bundler 和 Minimizer以及兼容性和自定义要求均不高的 Transformer。插件机制esbuild 插件就是一个对象里面有name和setup两个属性name是插件的名称setup是一个函数其中入参是一个 build 对象这个对象上挂载了一些钩子可供我们自定义一些构建逻辑。以下是一个简单的esbuild插件示例:let envPlugin {name: env,setup(build) {// 文件解析时触发// 将插件作用域限定于env文件并为其标识命名空间env-nsbuild.onResolve({ filter: /^env$/ }, args ({path: args.path,namespace: env-ns,}))// 加载文件时触发// 只有命名空间为env-ns的文件才会被处理// 将process.env对象反序列化为字符串并交由json-loader处理build.onLoad({ filter: /.*/, namespace: env-ns }, () ({contents: JSON.stringify(process.env),loader: json,}))},
}require(esbuild).build({entryPoints: [app.js],bundle: true,outfile: out.js,// 应用插件plugins: [envPlugin],
}).catch(() process.exit(1))使用如下:*// 应用了env插件后构建时将会被替换成process.env对象*import { PATH } from envconsole.log(PATH is ${PATH})不过在编写插件的时候有一些需要注意的地方Esbuild 插件机制只可作用于 build API而不适用于 transformAPI这意味着 webpack 当中的 esbuild-loader 这种只使用 Esbuild transform 功能的地方无法利用 Esbuild 的插件机制。插件中的 filter 正则是使用 go 原生的正则实现的用来过滤文件为了不使性能过于劣化规则应该尽可能严格。同时它本身和 JS 的正则也有所区别比如前瞻(?)、后顾(?)和反向引用(\1)就不支持。实际的插件应该考虑到自定义缓存(减少 load 的重复开销)、sourcemap 合并(源代码正确映射)和错误处理。可以参考 Svelte plugin。虚拟模块支持与 Rollup 对比作为打包器一般需要两种形式的模块一种存在于真实的磁盘文件系统中另一种并不在磁盘而在内存当中也就是虚拟模块。Rollup 本身就天然支持虚拟模块Vite 基于它的插件机制也重度使用了虚拟模块的功能以 wasm 文件的处理为例:const wasmHelperId /__vite-wasm-helper
// helper 函数实现
const wasmHelper async (opts {}, url: string) {// 省略具体实现
}
export const wasmPlugin (config: ResolvedConfig): Plugin {return {name: vite:wasm,resolveId(id) {if (id wasmHelperId) {return id}},async load(id) {if (id wasmHelperId) {return export default ${wasmHelperCode}}if (!id.endsWith(.wasm)) {return}const url await fileToUrl(id, config, this)// 虚拟模块return
import initWasm from ${wasmHelperId}
export default opts initWasm(opts, ${JSON.stringify(url)})
}}
}但 Rollup 的虚拟模块也有一些限制为了与真实模块区分开默认约定要在路径前面拼上一个\0。这样会对路径产生一定的入侵性直接放到浏览器进行 import 会出问题(Vite 内部也将 \0 替换成 __xx 这种形式以免直接将 带\0 路径放到浏览器中 import):image.pngEsbuild 中对于虚拟模块的支持更加友好一些直接通过 namespace 来区分真实模块和虚拟模块这样也不会有 \0 这样 hack 操作。编译能力使用 Esbuild 的虚拟模块可以完成很丰富的功能除了上述插件实例中在内存中计算出 env 的值作为模块内容还可以模块名当做一个函数来进行编译甚至可以在编译阶段实现函数递归的过程。比如这个 Esbuild 插件{name: fibo,setup(build) {build.onResolve({ filter: /^fib\(\d\)/ }, args {return { path: args.path, namespace: fib }})build.onLoad({ filter: /^fib\(\d\)/, namespace: fib }, args {const match /^fib\((\d)\)/.exec(args.path);n Number(match[1]);console.log(n);let contents n 2 ? export default ${n1} : import n1 from fib(${n - 1})import n2 from fib(${n - 2})export default n1 n2return { contents }})}
}引入这个插件可以解析如下的 import 语句import fib5 from fib(5)console.log(fib5)// 13所有的模块都是虚拟模块在真实文件系统中并不存在另外还能借助虚拟模块来进行 URL Import支持如下的 import 代码:import React from https://esm.sh/react17这也可以在插件当中实现可参考示例。落地场景1. 代码压缩工具Esbuild 的代码压缩功能非常优秀可以甩开传统的压缩工具一个量级以上的性能差距。Vite 在 2.6 版本也官宣在生产环境中直接使用 Esbuild 来压缩 JS 和 CSS 代码。2. 代替 ts-node社区已经有了相应的方案 esno: https://github.com/antfu/esnots-node index.ts
// 替换为
esno hello.ts3. 代替 ts-jest使用 esbuild-jest 代替ts-jest我曾经尝试在某些大型包中使用 esbuild-jest 来作为 transformer相比 ts-jest整体大概提升 3 倍测试效率。Github 地址https://github.com/aelbore/esbuild-jest4. 第三方库 BundlerVite 中在开发阶段使用 Esbuild 来进行依赖的预打包将所有用到的第三方依赖转成 ESM 格式 Bundle 产物并且未来有用到生产环境的打算。同时业界也有一些平台基于纯 Esbuild 来做线上 cjs - esm 的 CDN 服务比如 esm.sh 和 skypack:5. 打包 Node 库为什么要打包 Node 库:减少 node_modules 代码避免业务安装一大堆 node_modules 的代码减少安装体积提高启动速度所有代码打到一个文件减少了大量的文件 io 操作更安全。所有代码打包也是锁定依赖版本的一种方式可以避免之前出现的 coa 包导致的大面积 CI 挂掉的问题可参考云谦的这篇文章。这方面 Esbuild 的作用跟现在 vercel 团队出品的 ncc 差不多但会对代码的写法有一些限制无法分析动态 require 或者 import 语句含有变量的情况:6. 小程序编译对于小程序的场景也可以使用 Esbuild 来代替 Webpack大大提升编译速度对于 AST 的转换则通过 Esbuild 插件嵌入 SWC 来实现实现快速编译。详见 132 的分享 esbuild 上生产。7. Web 构建Web 场景就显得比较复杂了对于兼容性和周边工具生态的要求比较高比如低浏览器语法降级、CSS 预编译器、HMR 等等如果要用纯 Esbuild 来做还需要补充很多能力。之前三元同学基于 Esbuild 实现了一套 Web 开发脚手架 ewas已经在 Github 开源并且已成功落地到我之前的小册项目当中相比 create-react-app 启动速度提升了 100 倍以上(30s - 0.3s)。仓库地址: https://github.com/sanyuan0704/ewas。如今 Remix 1.0 正式发布底层使用 Esbuild 构建带来了极致的性能体验成为 Next.js 强有力的竞争对手。但总体来说目前 Esbuild 对于真实的 Web 场景还有很多能力不支持还有一些硬伤包括语法不支持降级到ES5拆包不灵活、不支持 HMR对于真正能作为 Webpack 一样的构建工具来讲还有很长的路要走。················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇在知乎、掘金收获超百万阅读。从2014年起每年都会写一篇年度总结已经写了7篇点击查看年度总结。同时最近组织了源码共读活动帮助3000前端人学会看源码。公众号愿景帮助5年内前端人走向前列。识别上方二维码加我微信、拉你进源码共读群今日话题略。分享、收藏、点赞、在看我的文章就是对我最大的支持