精读《Rust 是 JS 基建的将来》

2021年11月22日 阅读数:9
这篇文章主要向大家介绍精读《Rust 是 JS 基建的将来》,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

Rust Is The Future of JavaScript Infrastructure 这篇文章讲述了 Rust 正在 JS 基建圈流行的事实:WebpackBabelTerserPrettierESLint 这些前些年才流行起来的工具都已有了 Rust 替代方案,且性能有着 10~100 倍的提高。javascript

前端基建的迭代浪潮从未停歇,当上面这些工具给 Gulp、js-beautify、tslint 等工具盖上棺材盖时,基于 Rust 的新一代构建工具已经悄悄将棺材盖悬挂在 webpack、babel、prettier、terser、eslint 它们头上,不知道哪天就会盖上。前端

原文已经有了不错的 中文翻译,值得一提的是,原文一些英文名词对应着特定中文解释,记录以下:java

  • low-level programming:低级编程 底层编程。
  • ergonomics:人体工程学 人机工程学。
  • opinionated:自觉得是,执拗的 开箱即用的。
  • critical adoption:批判性采用 技术选型临界点。

精读

本文不会介绍 Rust 如何使用,而会重点介绍原文提到的 Rust 工具链的一些基本用法,若是你感兴趣,能够马上替换现有的工具库!node

swc

swc 是基于 Rust 开发的一系列编译、打包、压缩等工具,而且被普遍应用于更多更上层的 JS 基建,大大推进了 Rust 在 JS 基建的影响力,因此要第一个介绍。react

swc 提供了一系列原子能力,涵盖构建与运行时:linux

@swc/cli

@swc/cli 能够同时构建 js 与 ts 文件:webpack

const a = 1
npm i -D @swc/cli
npx swc ./main.ts

# output:
# Successfully compiled 1 file with swc.
# var a = 1;

具体功能与 babel 相似,均可以让浏览器支持先进语法或者 ts,只是 @swc/cli 比 babel 快了至少 20 倍。能够经过 .swcrc 文件作 自定义配置web

@swc/core

你能够利用 @swc/core 制做更上层的构建工具,因此它是 @swc/cli 的开发者调用版本。基本 API 来自官网开发者文档:typescript

const swc = require("@swc/core");

swc
  .transform("source code", {
    // Some options cannot be specified in .swcrc
    filename: "input.js",
    sourceMaps: true,
    // Input files are treated as module by default.
    isModule: false,

    // All options below can be configured via .swcrc
    jsc: {
      parser: {
        syntax: "ecmascript",
      },
      transform: {},
    },
  })
  .then((output) => {
    output.code; // transformed code
    output.map; // source map (in string)
  });

其实就是把 cli 调用改为了 node 调用。npm

@swc/wasm-web

@swc/wasm-web 能够在浏览器运行时调用 wsm 版的 swc,以获得更好的性能。下面是官方的例子:

import { useEffect, useState } from "react";
import initSwc, { transformSync } from "@swc/wasm-web";

export default function App() {
  const [initialized, setInitialized] = useState(false);

  useEffect(() => {
    async function importAndRunSwcOnMount() {
      await initSwc();
      setInitialized(true);
    }
    importAndRunSwcOnMount();
  }, []);

  function compile() {
    if (!initialized) {
      return;
    }
    const result = transformSync(`console.log('hello')`, {});
    console.log(result);
  }

  return (
    <div className="App">
      <button onClick={compile}>Compile</button>
    </div>
  );
}

这个例子能够在浏览器运行时作相似 babel 的事情,不管是低代码平台仍是在线 coding 平台均可以用它作运行时编译。

@swc/jest

@swc/jest 提供了 Rust 版本的 jest 实现,让 jest 跑得更快。使用方式也很简单,首先安装:

npm i @swc/jest

而后在 jest.config.js 配置文件中,将 ts 文件 compile 指向 @swc/jest 便可:

module.exports = {
  transform: {
    "^.+\\.(t|j)sx?$": ["@swc/jest"],
  },
};

swc-loader

swc-loader 是针对 webpack 的 loader 插件,代替 babel-loader

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules)/,
      use: {
        // `.swcrc` can be used to configure swc
        loader: "swc-loader"
      }
    }
  ];
}

swcpack

加强了多文件 bundle 成一个文件的功能,基本能够认为是 swc 版本的 webpack,固然性能也会比 swc-loader 方案有进一步提高。

截至目前,该功能还在测试阶段,只要安装了 @swc/cli 就可以使用,经过建立 spack.config.js 后执行 npx spack 便可运行,和 webpack 的使用方式同样。

Deno

Deno 的 linter、code formatter、文档生成器采用 swc 构建,所以也算属于 Rust 阵营。

Deno 是一种新的 js/ts 运行时,因此咱们总喜欢与 node 进行类比。quickjs 也同样,这三个都是一种对 js 语言的运行器,做为开发者,需求永远是更好的性能、兼容性与生态,三者几乎缺一不可,因此当下虽然不能彻底代替 Nodejs,但做为高性能替代方案是很香的,能够基于他们作一些跨端跨平台的解析器,好比 kraken 就是基于 quickjs + flutter 实现的一种高性能 web 渲染引擎,是 web 浏览器的替代方案,做为一种跨端方案。

esbuild

esbuild 是较早被普遍使用的新一代 JS 基建,是 JS 打包与压缩工具。虽然采用 Go 编写,但性能与 Rust 不相上下,能够与 Rust 风潮放在一块儿看。

esbuild 目前有两个功能:编译和压缩,理论上分别可代替 babel 与 terser。

编译功能的基本用法:

require('esbuild').transformSync('let x: number = 1', {
  loader: 'ts',
})

// 'let x = 1;\n'

压缩功能的基本用法:

require('esbuild').transformSync('fn = obj => { return obj.x }', {
  minify: true,
})

// 'fn=n=>n.x;\n'

压缩功能比较稳定,适合用在生产环境,而编译功能要考虑兼容 webpack 的地方太多,在成熟稳定后才考虑能在生产环境使用,目前其实已经有很多新项目已经在生产环境使用 esbuild 的编译功能了。

编译功能与 @swc 相似,但由于 Rust 支持编译到 wsm,因此 @swc 提供了 web 运行时编译能力,而 esbuild 目前尚未看到这种特性。

Rome

Rome 是 Babel 做者作的基于 Nodejs 的前端基建全家桶,包含但不限于 Babel, ESLint, webpack, Prettier, Jest。目前 计划使用 Rust 重构,虽然尚未实现,但咱们姑且能够把 Rome 看成 Rust 的一员。

rome 是个全家桶 API,因此你只须要 yarn add rome 就完成了全部环境准备工做。

  • rome bundle 打包项目。
  • rome compile 编译单个文件。
  • rome develop 调试项目。
  • rome parse 解析文件抽象语法树。
  • rome analyzeDependencies 分析依赖。

Rome 还将文件格式化与 Lint 合并为了 rome check 命令,并提供了友好 UI 终端提示

其实我并不太看好 Rome,由于它负担过重了,测试、编译、Lint、格式化、压缩、打包的琐碎事情太多,把每一块交给社区可能会作得更好,这不如今还在重构中,牵一发而动全身。

NAPI-RS

NAPI-RS 提供了高性能的 Rust 到 Node 的衔接层,能够将 Rust 代码编译后成为 Node 可调用文件。下面是官网的例子:

#[js_function(1)]
fn fibonacci(ctx: CallContext) -> Result<JsNumber> {
  let n = ctx.get::<JsNumber>(0)?.try_into()?;
  ctx.env.create_int64(fibonacci_native(n))
}

上面写了一个斐波那契数列函数,直接调用了 fibonacci_native 函数实现。为了让这个方法被 Node 调用,首先安装 CLI:npm i @napi-rs/cli

因为环境比较麻烦,所以须要利用这个脚手架初始化一个工做台,咱们在里面写 Rust,而后再利用固定的脚本发布 npm 包。执行 napi new 建立一个项目,咱们发现入口文件确定是个 js,毕竟要被 node 引用,大概长这样(我建立了一个 myLib 包):

const { loadBinding } = require('@node-rs/helper')

/**
 * __dirname means load native addon from current dir
 * 'myLib' is the name of native addon
 * the second arguments was decided by `napi.name` field in `package.json`
 * the third arguments was decided by `name` field in `package.json`
 * `loadBinding` helper will load `myLib.[PLATFORM].node` from `__dirname` first
 * If failed to load addon, it will fallback to load from `myLib-[PLATFORM]`
 */
module.exports = loadBinding(__dirname, 'myLib', 'myLib')

因此 loadBinding 才是入口,同时项目文件夹下存在三个系统环境包,分别供不一样系统环境调用:

  • @cool/core-darwin-x64 macOS x64 平台。
  • @cool/core-win32-x64 Windows x64 平台。
  • @cool/core-linux-arm64-gnu Linux aarch64 平台。

@node-rs/helper 这个包的做用是引导 node 执行预编译的二进制文件,loadBinding 函数会尝试加载当前平台识别的二进制包。

src/lib.rs 的代码改为上面斐波那契数列的代码后,执行 npm run build 编译。注意在编译前须要安装 rust 开发环境,只要一行脚本便可安装,具体看 rustup.rs。而后把当前项目总体看成 node 包发布便可。

发布后,就能够在 node 代码中引用啦:

import { fibonacci } from 'myLib'

function hello() {
  let result = fibonacci(10000)
  console.log(result)
  return result
}

NAPI-RS 做为 Rust 与 Node 的桥梁,很好的解决了 Rust 渐进式替换现有 JS 工具链的问题。

Rust + WebAssembly

Rust + WebAssembly 说明 Rust 具有编译到 wsm 的能力,虽然编译后代码性能会变得稍慢,但仍是比 js 快不少,同时因为 wsm 的可移植性,让 Rust 也变得可移植了。

其实 Rust 支持编译到 WebAssembly 也不奇怪,由于原本 WebAssembly 的定位之一就是做为其余语言的目标编译产物,而后它自己支持跨平台,这样它就很好的完成了传播的使命。

WebAssembly 是一个基于栈的虚拟机 (stack machine),因此跨平台能力一流。

想要将 Rust 编译为 wsm,除了安装 Rust 开发环境外,还要安装 wasm-pack

安装后编译只需执行 wasm-pack build 便可。更多用法能够查看 API 文档

dprint

dprint 是用 rust 编写的 js/ts 格式化工具,并提供了 dprint-node 版本,能够直接做为 node 包,经过 npm 安装使用,从 源码 能够看到,使用 NAPI-RS 实现。

dprint-node 能够直接在 Node 中使用:

const dprint = require('dprint-node');
dprint.format(filePath, code, options);

参数文档

Parcel

Parcel 严格来讲算是上一代 JS 基建,它出如今 Webpack 以后,Rust 风潮以前。不过因为它已经采用 SWC 重写,因此姑且算是跟上了时髦。

总结

前端全家桶已经有了一整套 Rust 实现,只是对于存量项目的编译准确性须要大量验证,咱们还须要时间等待这些库的成熟度。

但毫无疑问的是,Rust 语言对 JS 基建支持已经较为完备了,剩下的只是工具层逻辑覆盖率的问题,均可以随时间而解决。而用 Rust 语言重写后的逻辑带来的巨幅性能提高将为社区注入巨大活力,就像原文说的,前端社区能够为了巨大性能提高而引入 Rust 语言,即使这可能致使为社区贡献门槛的提升。

讨论地址是: 精读《Rust 是 JS 基建的将来》· Issue #371 · dt-fe/weekly

若是你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

版权声明:自由转载-非商用-非衍生-保持署名( 创意共享 3.0 许可证