精读《Rust 是 JS 基建的未来》
时间:2023-03-28 13:14:44
HTML
RustIsTheFutureofJavaScriptInfrastructure本文介绍了Rust在JS基础设施圈中开始流行的事实:Webpack、Babel、Terser、Prettier、ESLint,这些都是近几年才开始流行的工具,都有Rust替代品,性能提升了10到100倍。前端基础设施的迭代浪潮从未停止过。当以上工具为Gulp、js-beautify、tslint等工具盖棺材时,新一代基于Rust的构建工具已经悄悄将webpack、babel、prettier、terser、eslint的棺材盖在了他们的头上,它总有一天会被覆盖。原文已经有了很好的中文翻译。值得一提的是,原文中部分英文术语对应具体的中文解释,记录如下:low-levelprogramming:底层编程。人体工学:Ergonomics人体工学。opinionated:固执己见,固执己见。Criticaladoption:关键采用技术选择的关键点。精读本文不会介绍如何使用Rust,而是着重介绍原文中提到的Rust工具链的一些基本用法。如果您有兴趣,可以立即更换现有的工具库!swcswc是基于Rust开发的一系列编译、打包、压缩等工具,被广泛应用于更多更高层次的JS基础设施中,极大地促进了Rust在JS基础设施中的影响力,所以先介绍一下。swc提供了一系列的原子能力,涵盖构建和运行时:@swc/cli@swc/cli可以同时构建js和ts文件:consta=1npmi-D@swc/clinpxswc./main.ts#output:#使用swc成功编译了1个文件。#vara=1;具体功能类似于babel,可以让浏览器支持高级语法或者ts,但是@swc/cli至少比babel快20倍。自定义配置可以通过.swcrc文件完成。@swc/core您可以使用@swc/core来制作更高级别的构建工具,因此它是@swc/cli的开发人员调用版本。基础API来自官网开发者文档:constswc=require("@swc/core");swc.transform("sourcecode",{//.swcrc文件名中有些选项不能指定:"input.js",sourceMaps:true,//输入文件默认被视为模块。isModule:false,//下面的所有选项都可以通过.swcrcjsc:{parser:{syntax:"ecmascript",},transform:{},},}).then((output)=>{output.code;//转换后的代码output.map;//sourcemap(instring)});其实就是把cli调用改成了node调用。@swc/wasm-web@swc/wasm-web可以在浏览器运行时调用wsm版本的swc,以获得更好的性能。下面是官方的例子:import{useEffect,useState}from"react";从“@swc/wasm-web”导入initSwc,{transformSync};导出默认函数App(){const[初始化,setInitialized]=useState(false);useEffect(()=>{asyncfunctionimportAndRunSwcOnMount(){awaitinitSwc();setInitialized(true);}importAndRunSwcOnMount();},[]);函数编译(){如果(!初始化){返回;}constresult=transformSync(`console.log('hello')`,{});控制台日志(结果);}return(Compile
);}这个例子可以在浏览器运行的时候做一些类似babel的事情,无论是低-代码平台或在线编码平台,可用于运行时编译。@swc/jest@swc/jest提供了一个Rust版本的jest实现,使jest运行得更快。使用方法也很简单,先安装:npmi@swc/jest然后在jest.config.js配置文件中,将ts文件指向@swc/jestcompile:module.exports={transform:{"^.+\\.(t|j)sx?$":["@swc/jest"],},};swc-loaderswc-loader是webpack的加载器插件,替代了babel-loader:module:{rules:[{test:/\.m?js$/,exclude:/(node_modules)/,use:{//`.swcrc`可以用来配置swcloader:"swc-loader"}}];}swcpack增强了很多将文件打包成一个文件的功能,基本上可以看成是swc版的webpack,当然性能上会更进一步与swc-loader解决方案相比有所改进。截至目前,该功能还在测试阶段,只要安装@swc/cli即可使用。创建spack.config.js并执行npxspack后,就可以像webpack一样运行了。DenoDeno的linter、代码格式化器和文档生成器是用swc构建的,因此属于Rust阵营。Deno是一个新的js/ts运行时,所以我们总是喜欢用node来类比。quickjs也是如此。这三个都是js语言的运算符。作为开发者,要求永远是更好的性能、兼容性和生态。三者几乎缺一不可,所以虽然目前还不能完全替代Nodejs,但是作为高性能的替代品,还是很香的。你可以基于它们制作一些跨终端和跨平台的解析器。比如kraken是一个基于quickjs+flutter的高性能网页渲染引擎。它是网络浏览器的替代品。跨端解决方案。esbuildesbuild是较早被广泛使用的新一代JS基础设施,是一款JS打包压缩工具。虽然是用Go写的,但是性能和Rust不相上下,可以和Rust的趋势一起看。esbuild目前有两个功能:编译和压缩,理论上可以分别替代babel和terser。编译函数的基本用法:require('esbuild').transformSync('letx:number=1',{loader:'ts',})//'letx=1;\n'压缩函数的基本用法:require('esbuild').transformSync('fn=obj=>{returnobj.x}',{minify:true,})//'fn=n=>n.x;\n'压缩功能比较稳定,适用于生产环境,编译功能需要兼容webpack的地方太多,成熟稳定后才能用于生产环境。事实上,很多新项目已经在生产环境中使用了esbuild的编译功能。编译功能与@swc类似,但由于Rust支持编译为wsm,@swc提供了web运行时编译能力,esbuild暂未见此功能。RomeRome是Babel作者打造的基于Nodejs的前端基础设施全家桶,包括但不限于Babel、ESLint、webpack、Prettier、Jest。目前有使用Rust进行重构的计划,虽然还没有实现,但我们可以暂且考虑将Rome作为Rust的一员。rome是一个全家桶API,所以只需要yarnaddrome就可以完成所有的环境准备工作。romebundle打包项目。romecompile编译单个文件。romedevelop调试项目。romeparse解析文件的抽象语法树。romeanalyzeDependencies分析依赖关系。Rome还将文件格式化和Lint合并到romecheck命令中,并提供友好的UI终端提示。其实我不太看好Rome,因为它太累赘了,测试、编译、Lint、格式化、压缩、打包等琐碎的事情太多了。将每件作品交给社区可能会更好。重构,一根头发,牵一发而动全身。NAPI-RSNAPI-RS提供了一个高性能的Rust-to-Node连接层,可以将Rust代码编译成Node可调用文件。下面是官网的例子:#[js_function(1)]fnfibonacci(ctx:CallContext)->Result
{letn=ctx.get::(0)?.try_into()?;CTX。env.create_int64(fibonacci_native(n))}上面写了一个斐波那契数列函数,直接调用fibonacci_native函数实现。为了让Node调用此方法,首先安装CLI:npmi@napi-rs/cli。由于环境麻烦,我们需要用这个脚手架来初始化一个workbench,我们在里面写Rust,然后用固定脚本发布npm包。执行napinew创建一个项目,我们发现入口文件必须是js,毕竟必须要被node引用,看起来像这样(我创建了一个myLib包):const{loadBinding}=require('@node-rs/helper')/***__dirname表示从当前目录加载本机插件*'myLib'是本机插件的名称*第二个参数由`package.json`中的`napi.name`字段决定*第三个参数由`package.json`中的`name`字段决定*`loadBinding`助手将首先从`__dirname`加载`myLib.[PLATFORM].node`*如果加载插件失败,它将回退到从`myLib加载-[PLATFORM]`*/module.exports=loadBinding(__dirname,'myLib','myLib')所以loadBinding是入口,项目文件夹下有3个系统环境包,不同系统环境调用:@cool/core-darwin-x64macOSx64平台。@cool/core-win32-x64Windowsx64平台。@cool/core-linux-arm64-gnuLinuxaarch64平台。@node-rs/helper这个包的作用是引导node执行预编译好的二进制文件,loadBinding函数会尝试加载当前平台识别的二进制包。将src/lib.rs的代码改成上面斐波那契数列的代码后,执行npmrunbuild编译。注意编译前需要安装rust开发环境,一行脚本即可安装,详见rustup.rs。然后将当前项目整体发布为一个节点包。发布后,您可以在节点代码中引用它:import{fibonacci}from'myLib'functionhello(){letresult=fibonacci(10000)console.log(result)returnresult}NAPI-RS作为Rust之间的桥梁和Node,很好的解决了Rust逐渐取代现有JS工具链的问题。Rust+WebAssemblyRust+WebAssembly表明Rust具有编译为wsm的能力。虽然编译后的代码性能会变得稍微慢一些,但还是比js快很多。同时由于wsm的可移植性,Rust也变得可移植。其实Rust支持编译成WebAssembly并不奇怪,因为WebAssembly的定位之一就是作为其他语言的目标编译产物,然后它本身就支持跨平台,这样就可以很好的完成使命的沟通。WebAssembly是一个基于栈的虚拟机(stackmachine),因此具有一流的跨平台能力。将Rust编译成wsm,除了安装Rust开发环境外,还需要安装wasm-pack。安装后要编译,只需执行wasm-packbuild。更多用法请参考API文档。dprintdprint是一个用rust编写的js/ts格式化工具,并提供了dprint-node版本,可以直接作为node包使用,通过npm安装。从源码可以看出,它是使用NAPI-RS实现的。dprint-node可以直接在Node中使用:constdprint=require('dprint-node');dprint.format(文件路径,代码,选项);参数文档。严格来说,Parcel是上一代JS基础设施。它出现在Webpack之后和Rust趋势之前。不过既然在SWC中重写了,也算是跟上了潮流。综上所述,前端全家桶已经有了一套完整的Rust实现,但是现有项目的编译准确性需要大量验证,我们还需要时间等待这些库的成熟。但毋庸置疑的是,Rust语言对JS基础设施的支持比较完善,剩下的只是工具层逻辑覆盖的问题,随着时间的推移可以解决。Rust语言重写逻辑带来的巨大性能提升,将为社区注入强大的活力。正如原文章所说,前端社区可以引入Rust语言以获得巨大的性能提升,即使这可能会导致为社区做贡献的门槛。提升。讨论地址是:Jingdu《Rust 是 JS 基建的未来》·Issue#371·dt-fe/weekly想参与讨论的请点这里,每周都有新话题,周末或周一发布。前端精读——帮你过滤靠谱的内容。关注前端精读微信公众号版权声明:免费转载-非商业-非衍生-保留署名(知识共享3.0许可)