一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 2 天,点击查看活动详情。
大家好,我是小欲望。我上一篇博客: 为什么选择Vite的学习 。当我看到Vite 将会使用 esbuild 预构建依赖。因此去查看了esbuild是什么?就查阅到了自己的新知识esbuild-loader。它就是本文的主人公。
我喜欢先知道怎么用,然后再去了解它的优势与原理之类的。
我项目中的性能优化
使用前(项目启动)
使用后(项目启动)
使用前(项目打包)
使用后(项目打包)
Vue-cli 中使用
安装
npm i -D esbuild-loader
使用vue.config.js文件中
// 在文件的顶部加载esbuild-loader的 ESBuildMinifyPlugin
const { ESBuildMinifyPlugin } = require('esbuild-loader');
在chainWebpack 函数中配置以下代码。如果文件中未有此函数可以敲一个,看vue-cli。
// 使用 esbuild 编译 js 文件
const rule = config.module.rule('js');
// 清理自带的 babel-loader
rule.uses.clear();
// 添加 esbuild-loader
rule
.use('esbuild-loader')
.loader('esbuild-loader')
// 删除底层 terser, 换用 esbuild-minimize-plugin
config.optimization.minimizers.delete('terser');
// 使用 esbuild 优化 css 压缩
config.optimization
.minimizer('esbuild')
.use(ESBuildMinifyPlugin, [{ minify: true, css: true }]);
由于我这个项目比较老,@vue/cli-service: 3.6.0 导致运行出错。因此升级@vue/cli-service: 4.5.0
因我项目中未使用到ts,如果有用到ts的可以使用
// 使用 esbuild 编译 js 文件
const rule = config.module.rule('js');
// 清理自带的 babel-loader
rule.uses.clear();
// 添加 esbuild-loader
rule
.use('esbuild-loader')
.loader('esbuild-loader')
.options({
loader: 'ts', // 如果使用了 ts, 或者 vue 的 class 装饰器,则需要加上这个 option 配置, 否则会报错: ERROR: Unexpected "@"
target: 'es2015',
tsconfigRaw: require('./tsconfig.json')
})
// 删除底层 terser, 换用 esbuild-minimize-plugin
config.optimization.minimizers.delete('terser');
// 使用 esbuild 优化 css 压缩
config.optimization
.minimizer('esbuild')
.use(ESBuildMinifyPlugin, [{ minify: true, css: true }]);
如果想看webpack的配置:性能优化使用 esbuild 为你的构建提速
Esbuild 为什么快
语言上的差异
大部分的打包工具都是使用JavaScript 编写的。但esbuild是使用Go语言编写。两种语言都有自己擅长的领域,但是在打包这种GPU密集的场景下面,GO更占据优势。
Javascript 是一种 解释型语言, Go是编译型语言。Go少了JavaScript的解释代码的时间。
解释型语言与编译型语言的区别
因为我们电脑执行的语言是机器语言,因此我们都需要把这些高级语言转成机器语言。编译型语言是在运行的时候已经把代码转成成了机器语言有可执行的二进制文件。当使用的时候直接执行这个二进制文件。
解释型语言使用解释器是只有在执行到对应的语句时才会将源代码一行一行的解释成机器语言,给计算机来执行,所以使用解释器来执行的语言也被称为动态语言;
例子: 编译型语言就是一个书本翻译官。英文作品全部翻译完,变成一个中文的译文。解释型语言就是你出国随身携带一个漂亮的美女翻译官,对方讲一句,小姐姐翻译一句给你听。
多线程优势
Go 天生具有多线程运行能力,而 JavaScript 本质上是一门单线程语言,直到引入 WebWorker 规范之后才有可能在浏览器、Node 中实现多线程操作。
我曾经研读过 Rollup、Webpack 的代码,就我熟知的范围内两者均未使用 WebWorker 提供的多线程能力(这个范文杰大佬说的Esbuild 为什么那么快 , 我可没读过这两个源码,我就是个菜鸟)。反观 Esbuild,它最核心的卖点就是性能,它的实现算法经过非常精心的设计,尽可能饱和地使用各个 CPU 核,特别是打包过程的解析、代码生成阶段已经实现完全并行处理。
WebWorker 是什么?
JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
应用场景
Web Worker我们可以当做计算器来用,需要用的时候掏出来摁一摁,不用的时候一定要收起来~
-
加密数据
有些加解密的算法比较复杂,或者在加解密很多数据的时候,这会非常耗费计算资源,导致UI线程无响应,因此这是使用Web Worker的好时机,使用Worker线程可以让用户更加无缝的操作UI。 -
预取数据
有时候为了提升数据加载速度,可以提前使用Worker线程获取数据,因为Worker线程是可以是用XMLHttpRequest的。 -
预渲染
在某些渲染场景下,比如渲染复杂的canvas的时候需要计算的效果比如反射、折射、光影、材料等,这些计算的逻辑可以使用Worker线程来执行,也可以使用多个Worker线程,这里有个射线追踪的示例。 -
复杂数据处理场景
某些检索、排序、过滤、分析会非常耗费时间,这时可以使用Web Worker来进行,不占用主线程。 -
预加载图片
有时候一个页面有很多图片,或者有几个很大的图片的时候,如果业务限制不考虑懒加载,也可以使用Web Worker来加载图片,可以参考一下这篇文章的探索,这里简单提要一下。 -
TensorFlowJS
目前前端也是可以使用TensorFlowJS做一些机器学习相关的事情,我认为Web Worker在肯定也能做出一些功能优化。
这些应用场景我也是没有使用过,之后可能会看些相关应用场景的文章,写些demo出来。如果有大佬知道其他的应用场景,求指教(小弟在此拜过)。
总结
语言和多线程是我认为esbuild最大的优势。还有些其他细节大家可以看下大佬的文章。
解决热更新慢的问题
npm install babel-plugin-dynamic-import-node
.babelrc 或者 babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
'env': {
'development': {
'plugins': ['dynamic-import-node']
}
}
}
解决 vue热加载编译速度慢问题,引入按需加载插件 dynamic-import-node
解决项目栈溢出,有可能会加快项目的启动
最后
esbuild-loader 是我从为什么选择Vite 中第一次认识到。也应用到了自己的项目中去。减少了自己的开发时长。再此篇博客中我认识到解释型语言与编译型语言、以及WebWorker。WebWorker对我学习Threejs 项目的性能优化肯定有所帮助。
写博客的自我想法:
文章有些复制了大佬们的段落,之前我都是阅读然后再边看边总结,但是发现总结的还不如大佬们写的,本来的目的是为了让自己加深印象,但想起自己写的第一篇博客似乎随着时间的漫长也忘记具体内容。之前还有认为我这样复制缝合起来的博客有什么意义。
现在有了目的让看到我文章的人可以从我这里认识那些大佬,以及自己这块知识点所阅读过的文章汇总,方便后期的知识回顾。