60行代码实现一个基于esbuild按需加载的loader(一)

简介: 大家好,我是Fly, 最近研究的技术就是esbuild,一个新技术的 出现必然是解决是某些问题的, 这不我就打算用它来进行项目的编译速度进行操刀了, 前端对于tsx 的编译方式 无非是两种,

大家好,我是Fly, 最近研究的技术就是esbuild,一个新技术的 出现必然是解决是某些问题的, 这不我就打算用它来进行项目的编译速度进行操刀了, 前端对于tsx 的编译方式 无非是两种,


  1. 第一种就是 使用我们 很熟悉的babel 配合对应的presets  「@babel/preset-typescript」  然后进行 编译转换js


  1. 第二种 就是 由社区提供的一些loader, 对于tsx 的文件进行编译, 比如  「ts-loader」


  1. 还有一些比较新的技术, 「可以在开发环境玩玩, 生产环境慎用」, 比如使用swc 和 esbuild 对tsx 文件进行编译。还是看公司的业务场景,esbuild 没法编译成es5 ,没法做类型检查, 然后对于ts的一些语法不支持,等缺点。


esbuild



esbuild 号称下一代前端的打包工具,我们看一张图就是知道他的速度了


image.png


对于three.js 项目的打包 esbuild 的打包速度,竟然是webpack5 的100 倍都不止, 这速度看了谁不喜欢???多快哦, 看下esbuild支持的特性:


  1. 极速,无需缓存, 这里就是 在现阶段的打包的过程中, 对于AST 的分析, 反复去做, 每一个工具链, 可能都会利用AST 做一点事情,这就会导致大量的内存占用。而esbuild共用同一份AST 节点数据,提高内存利用率。


  1. 使用go 语言开发, 充分利用cpu 多核的优势, 使用多线程打包, 线程之间共享数据。


  1. esbuild的解析器可以同时处理ES6和CommonJS的模块。它不会去区分ES6和其他的模块,因此可以让你在同一个文件中同时使用ES6和CommonJS的语法。随着esm 的流行,未来可能是一种趋势, 但是老的包有的是基于commonJS 的, 要把common js 类型的包 转成esm, 或者esm 转换成 commonjs. esm的模式 打包出来的bundle, 更加高效, 运行时开销也小。


  1. 还有 作用域提升 已经 tree-shaking 这些特性


swc


swc 本身工具的定位有类似于 babel, 不过是基于rust 写的,esbuild 定位有点类似于webpack。我们看下图:



image.png


越高表示越好, swc 的性能是 babel、typescript、esbuild 在es2015 语法 6倍多, 由于这是官网的截图, 没有经过实际项目验证, 不过我说一个数据, 对于大型react 项目, 我使用esbuild-loader 和 swc-loader 构建的速度, 整体速度的差距是不大的。swc不是本篇文章的重点, 接下来继续分析esbuild


esbuild-loader



既然esbuild, 辣么快, 那么社区肯定有人就会想到, esbuild 如何与当前前端工程化项目去做结合, 提升开发体验。现阶段我所知道的有下面这些


  1. esbuild-jest 检测单元测试, 使用esbuild的能力 来缩短跑单侧的时间


  1. esbuild-eslint  我们项目由于eslint 的规则 十分多, 子路由大概10多个, 模块关系依赖复杂,跑一次lint, 时间都是非常慢, 如果借助esbuild 的能力,美滋滋。


  1. 还有一个就是编写一个loader 去解析tsx, 解析tsx这个loader, 在webpack 的编译时间, 往往都是十分耗时的,优化这段部分,整个webpack的启动时间就会大幅度加快。


目前所做的都是开发环境的优化, 没有在生产环境去操作过, 如果使用,请慎重,等vite 啥时候生产环境 不采用rollup ,保持开发环境 和生产环境的打包方式一致了, vite 的项目 还是具有代表性的, 唯一的担心, 就是esbuild 打包出来的bundle 和 webpack 打包出来的bundle 不一致, 出现问题就背大锅了, fly 建议 内部项目 或者个人学习项目, 在技术层面可以激进点,坏了, 可以修, 但是线上项目,服务很多用户的, 还是要慎重。所以现阶段 webpack 任然是最棒的工具, 繁荣的社区, 几乎问题都可以解决,项目没有升级webpack5的, wepack5 的持久化缓存, 模块联邦、lazyCompilation、 都是令人惊艳的重大更新。


好的下面我们就开始编写一个webpack 的loader, 本篇文章不会详细介绍loader 的编写不是本文的重点, 不会编写webpack loader的同学, 请自行学习。「esbuild-import-loader」  主要是有下面有两个功能


  1. 第一个 使用esbuild 进行编译tsx


  1. 实现组件库的按需加载格式


安装相关依赖



主要的依赖有


  1. esbuild   这个主要是核心依赖, 主要利用 transform 这个api  esbuild 提供的AST 接口少之又少, 如果你的项目重度依赖 babel , 那么在技术选型的过程中, 不建议使用 esbuild, 因为esbuild, 提供的AST 接口很少, esbuild 的作者本身 追求极致性能,  你可以使用swc, swc 你可以理解为 更快一点的 babel, 不过是基于rust, 相对于js 有着天然的优势 。可以看下官网的移植指南,结合项目综合去考虑。

image.png


  1. 由于esbuild, 没有提供AST 的接口, 所以对于AST 的 处理 我们 还是使用babel 相关的包 去做处理。 「@babel/parser , @babel/traverse, @babel/type  @babel/generator」


定义接口类型




关于接口的设计其实很简单,  在 esbuild Transform 接口的类型上, 进行增加


import { TransformOptions } from 'esbuild'
export type EsbuildImportLoader = Omit<TransformOptions, 'sourcemap' | 'sourcefile'> & {
  libraryName: string
  customStyle?: (importName: string) => string
  customName: (importName: string) => string
}


T除了 sourcemap  和 sourcefile  这两个属性


「sourcefile」 这个属性:


当使用没有文件名的输入时,此选项设置文件名。当使用转换 API 和使用带有标准输入的构建 API 时会发生这种情况。配置的文件名反映在错误消息和源映射中。如果未配置,则文件名默认为


let fs = require('fs')
let js = fs.readFileSync('app.js', 'utf8')
require('esbuild').transformSync(js, {
  sourcefile: 'example.js',
  sourcemap: 'inline',
})


sourcemap  就是我们前端理解的 源码映射, 我们写loader 完全没必要 由外面传了,

直接通过loader 的上下文拿对应的map其实就可以了。


TransformOptions 到底有哪些 类型呢 ???

有很多类型, 感兴趣的同学 去 这个网址 进行 查看  https://esbuild.github.io/api/#sourcemap

我选几个有代表性的属性 进行讲解:


Format


定义转换的文件 格式: 有  iife、commonjs 、esm。我们一一进行试验。


IIFE


let js = 'alert("我是FLY")'
let out = require('esbuild').transformSync(js, {
  format: 'iife',
})
process.stdout.write(out.code)


我们执行 ts-node 看控制台的输出


image.png

CommonJS


继续实验commonjs 的


let js = 'export default "test"'
let out = require('esbuild').transformSync(js, {
  format: 'cjs',
})
process.stdout.write(out.code)


image.png


打包结果 是对 esm 用一个 「toCommonJS」 进行包裹


ESM


let js = 'module.exports = "test"'
let out = require('esbuild').transformSync(js, {
  format: 'esm',
})
process.stdout.write(out.code)


image.png


但是 同样一段代码 esm 打包出来的 代码更少,用了一个 commonJS 函数包裹  这也证明了 esbuild  在esm 和commonjs 之间的能力。


相关文章
|
Ubuntu 关系型数据库 MySQL
M1 macos docker获取x86 x64 amd 等指定架构版本linux ubuntu mysql 容器并启动容器
M1 macos docker获取x86 x64 amd 等指定架构版本linux ubuntu mysql 容器并启动容器
|
前端开发 Java 程序员
【Java基础】前端传一个数组或者集合后台怎么接受(案例详解)
hello本期继续以实际案例的形式分享Java基础之 Java后台接受数组和集合的案例,分享给初学者
1597 0
【Java基础】前端传一个数组或者集合后台怎么接受(案例详解)
|
IDE TensorFlow 算法框架/工具
成功解决 OSError: [WinError 193] %1 不是有效的 Win32 应用程序
成功解决 OSError: [WinError 193] %1 不是有效的 Win32 应用程序
|
10月前
|
缓存 监控 算法
提高 Webpack 热更新的性能
【10月更文挑战第23天】还可以进一步深入探讨热更新性能优化的具体案例、不同场景下的优化策略,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解热更新性能优化的方法和技巧,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的热更新技术和最佳实践。
|
11月前
|
SQL JavaScript 安全
代码审查
【10月更文挑战第13天】
|
存储 分布式计算 负载均衡
什么是 HBase?其组件起什么作用?
【8月更文挑战第12天】
1605 4
|
前端开发 CDN
React 在 html 中 CDN 引入(包含useState、antd、axios ....)
React 在 html 中 CDN 引入(包含useState、antd、axios ....)
1539 0
|
存储 安全 数据安全/隐私保护
移动APP安全加固技术深度解析
【7月更文挑战第12天】移动APP安全加固技术是保障移动应用安全的重要手段。通过对Android和iOS两大主流平台的安全加固,可以有效防止逆向分析、动态调试、数据篡改等安全威胁。在实际应用中,我们需要结合静态层面、动态层面和数据层面的加固技术,全方位地提升APP的安全性。同时,随着技术的不断发展,我们也需要不断关注新的安全威胁和加固技术,确保移动应用的安全性和稳定性。
|
自然语言处理 JavaScript 前端开发
Vue 3的编译器是什么
【8月更文挑战第15天】Vue 3的编译器是什么
142 0