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 之间的能力。


相关文章
|
7月前
|
数据采集 前端开发 JavaScript
我是如何使用 Next.js14 + Tailwindcss 重构个人项目的
这篇文章介绍了作者在学习React和Nest时受到大佬imsyy项目DailyHot的启发,基于React开发了一个项目,并因为这个项目获得了少量流量而进行了优化。作者随后因为想要优化SEO和深入学习Next.js14,决定重构这个项目。文章详细介绍了项目的信息、特性、演示图、运行环境、Vercel本地部署步骤以及责任声明。作者还感谢了为本项目提供支持与灵感的项目,并承诺会记录下开发过程中遇到的问题及解决方法以帮助他人。
我是如何使用 Next.js14 + Tailwindcss 重构个人项目的
|
1月前
|
JavaScript 测试技术 UED
解决 Vue 项目中 Tree shaking 无法去除某些模块
【10月更文挑战第23天】解决 Vue 项目中 Tree shaking 无法去除某些模块的问题需要综合考虑多种因素,通过仔细分析、排查和优化,逐步提高 Tree shaking 的效果,为项目带来更好的性能和用户体验。同时,持续关注和学习相关技术的发展,不断探索新的解决方案,以适应不断变化的项目需求。
|
前端开发 JavaScript Java
前端——使用RequireJS的r.js打包压缩模块
前端——使用RequireJS的r.js打包压缩模块
|
7月前
|
JavaScript 前端开发
vue-loader是什么?使用它的用途有哪些?怎么使用?
vue-loader是什么?使用它的用途有哪些?怎么使用?
115 0
|
Web App开发 前端开发 JavaScript
UMD 被淘汰了吗?不考虑的 UMD 的库如何在纯 UMD 前端项目中运行?
UMD 被淘汰了吗?不考虑的 UMD 的库如何在纯 UMD 前端项目中运行?
241 0
|
JavaScript CDN
vue_按需引入elment、echarts和路由懒加载,减少打包体积
vue_按需引入elment、echarts和路由懒加载,减少打包体积
215 0
|
前端开发
60行代码实现一个基于esbuild按需加载的loader(二)
设计 整个插件的构建思路 就是 读取配置, 然后判断是否需要 按需加载, 如果需要 就利用的 babel 能力, 去做转化, 转换完成用 esbuild 去做编译就好了。
60行代码实现一个基于esbuild按需加载的loader(二)
|
缓存 JavaScript CDN
Vue.js项目加载速度性能优化
Vue.js项目加载速度性能优化
233 0
|
JavaScript 索引 开发者
Lodash 扩展JS通用方法
版权声明:本文首发 http://asing1elife.com ,转载请注明出处。 https://blog.csdn.net/asing1elife/article/details/82848345 Lodash是一个著名的javascript原生库,不需要引入其他第三方依赖。
1790 0