2024年,Rust 与 JavaScript 的较量!谁将成为编程领域的新王者?

点击下方“前端开发爱好者”,选择“设为星标第一时间关注技术干货!

如果大家有一定的 Rust Web 开发经验,可能听说过行业内关于 Rust 前端开发(通过 WASM)和 JavaScript 孰优孰劣一直是个备受争议的话题。不少从业者始终坚持认为,Rust 语言“不适合生产”或者“比 JavaScript 更慢”。

这话可能曾经正确:从历史上看,由于 WASM 无法触及 DOM,所以 JavaScript 在执行 WASM 调用时会产生一些额外开销。但当前这种影响已经相当有限。基准性能数据显示,Rust WASM 框架(例如 Leptos 和 Dioxus,后者是底层使用 Sledgehammer 的 JS 框架,也是市面上第三快的框架)已然领先于大多数 JS 框架,包括 React 和 Vue。下图所示,为原始基准测试结果。


可以看到(请原谅有点模糊),性能由高到低分别为原版 JavaScript、Sledgehammer(Dioxus 在后台实际运行的框架)、wasm-bindgen(允许 WASM 模块和 Javascript 之间进行互操作的库)、Solid.js、Vue 与 RxJS,然后是 Leptos、Dioxus、LitJS,还有 Sycamore……排在最后的才是 Vue 和 React(以及 Yew)。很明显,Rust 前端框架的性能并不差,甚至比某些最流行的 JS 框架更胜一筹。当然,大家也可以编写不用框架的纯 JS,但这么干合不合适就是另一个问题了。

我们再来看 TechEmpower 提供的后端基准测试结果:


在前 10 大顶级后端框架中,有 5 款是用 Rust 编写而成。很明显,Rust 作为后端框架的强大能力已经不言而喻,甚至能直接与 C++ 叫板。有些人可能觉得在后端服务中使用 Rust 没啥必要,但它确实能在提供更高性能的同时,帮助我们削减内存占用量、延长服务正常运行时间、降低崩溃频率。这些都是不容低估的因素,毕竟从企业经营的角度看,任何一个能节约成本的主意都值得认真考量。

但也必须承认,当我们选择一款新框架时,速度和总体性能永远不是影响决策的唯一因素。开发者体验如何?错误处理好不好用?要如何解决服务器端渲染(SSR)等问题?要想做出这个艰难的决定,这些都是必须要回答的重要问题。值得庆幸的是,Rust 已经就此给出了自己的答案。

开发者体验

不管有些人怎么想,现在 Rust 在 Web 开发方面已经相对更宽容了,不少代码已经跟 React 等 Web 框架的 JS 组件风格类似。下面,让我们快速浏览 Rust Web 框架 Leptos 中的一些组件代码:

use leptos::*;
#[component]
pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView {
// create a reactive signal with the initial value
let (value, set_value) = create_signal(cx, initial_value);
// create event handlers for our buttons
// note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures (for reference: closures are like anonymous/arrow functions in Javascript)
let clear = move |_| set_value(0);
let decrement = move |_| set_value.update(|value| *value -= 1);
let increment = move |_| set_value.update(|value| *value += 1);
// create user interfaces with the declarative `view!` macro
view! {
cx,
<div>
<button on:click=clear>"Clear"</button>
<button on:click=decrement>"-1"</button>
<span>"Value: " {value} "!"</span>
<button on:click=increment>"+1"</button>
</div>
}
}
// Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setup
pub fn main() {
mount_to_body(|cx| view! { cx, <SimpleCounter initial_value=3 /> })
}

可以看到,这些代码其实跟 JSX 之类相差不远——主要区别就是该组件不会返回任何内容,而是使用 Rust 宏来渲染 HTML;而且这里的 main 函数行为类似于 root 文件的 index.js 脚本,跟 React、Vue 或者其他可使用 JS 编写网站的 Web 框架基本一致。再来看 Dioxus 中的另一段示例:

// An example of a navbar
fn navbar(cx: Scope) -> Element {
cx.render(rsx! {
ul {
// NEW
Link { to: "/", "Home"}
br {}
Link { to: "/blog", "Blog"}
}
})
}
// An example of using URL parameters
fn get_blog_post(id: &str) -> String {
match id {
"foo" => "Welcome to the foo blog post!".to_string(),
"bar" => "This is the bar blog post!".to_string(),
id => format!("Blog post '{id}' does not exist!")
}
}

非常明显,RSX(也就是 Dioxus 中的 React JSX)的编写非常简单,甚至可能比使用 Leptos 还要轻松。更明显的是,React 组件哲学在 Rust 这里也有体现、超越了特定编程语言,而且对接得相当顺畅。我们甚至可以把这些函数跟单元结构结合起来,从而获取各种函数的命名空间,用以捆绑 API 调用等内容。例如:

// this is pseudocode and is just to showcase bundling functions together in a unit struct, which is a struct that doesn't hold any data
pub struct APICalls;

// we make an implementation of our struct and insert the functions we want to use in it - then we can just import the struct and it'll import all of the associated methods.
Impl APICalls {
pub async fn get_dog_api_data() -> Json<Dog> {
... some code here
// this should probably return some json data
}
pub async fn get_cat_api_data() -> Json<Cat> {
... some code here
// this should probably return some json data
}
}

fn navbar (cx: Scope) -> Element {
// now we can call the data like this, or something similar
let dogs = APICalls::get_dog_api_data().await;
}

如所见,尽管 Rust 拥有自己的一套独特设计逻辑,但仅仅触及其表面就足以在 Web 开发中获得不错的进展。而且这里的真正亮点其实是 Rust 的错误处理,它比 JS 甚至是 TypeScript 的同类机制要强得多。一般来说,在使用 TypeScript 进行编码时,我们只有两个选择:要么类型检查,要么使用 try-catch 块。但任何一位有经验的开发者都知道,不断把代码打包进 try-catch 块绝不是什么好主意,而且 TypeScript 仍然会被编译为 JavaScript。所以一旦稍不小心,JS 的问题仍然会照常出现(CJS 还是 ECMAscript、运行时中没完没了的随机错误等等)。

下面我们来看最基本的 Rust 错误处理过程:

async fn foo() -> Result<String, String>{
let bar = String::from("foobar!");
// due to Rust being an expressive language, return is not explicitly required here - we can just write the variable
match bar.trim() {
"foobar!" => Ok(bar),
_ => Err("Was not foobar!".to_string())
}
}

#[tokio::main]
fn main() -> Result<String, String> {
let Ok(res) = foo().await else {
return Err("Was not foobar :(".to_string());
}

println!("The string was: {res}!");
}

本示例中展示了两种情况:我们可以使用基本模式匹配来确定字符串内容是什么。如果结果匹配则返回 OK;如果结果不匹配(因为多了下划线),则返回一个 String 类型的错误(这里也实现了 std::error::Error,所以可以将其作为 Error 类型使用)。我们还可以为变量编写声明,借此强调该变量必须为 Result 类型,如果不是则执行其他操作(本示例中为提前返回)。之后我们就能直接使用 res 本体了,因为它会被声明为 Result 当中包含的值。

生态系统

虽然 JS 的生态系统(通过 Node/npm)比 Rust 大得多,但 Rust 这边也完全可以满足大多数项目的需求。Rust 目前已经为数据库、Redis 及其他多种服务提供强大支持。大家可以将这些服务引入任何 Web 应用程序当中,而且不受具体编程语言的影响。

如果打算构建 SaaS,Rust 几乎可以提供所需要的一切:SMTP 方面有 lettre ,Stripe 支付方面有 async-stripe,社交网络账户登录的 OAuth 回调有 oauth2,数据库方面有 SQLx 甚至 airtable(如果倾向 ORM,也可以选择 Diesel 或者 SeaORM),对接 GPT-3 则可以用 openai_api。在 SaaS 启动之后,Rust 甚至可以使用 lapin 来支持 RabbitMQ,用 rs-rdkafka 支持 Kafka。从这个角度看,如果大家希望打造坚如磐石的高性能服务,Rust 完全有与 JS 一战的实力。

正所谓“磨刀不误砍柴工”,Rust 这边提供的好工具也所在多有。例如,你可以安装 clippy,这是一款出色的实用程序,无需初始化即可使用。输入 cargo clippy 就会开始运行,可以帮助检测非必要借用等问题,同时提供代码优化建议。更重要的是,如果需要在不同项目之间使用同一套特定配置,也可以直接在 root 目录下创建一个 clippy.toml 文件,用它根据需求进行配置。

由于 Rust 实际上并不是属于 Web 编程语言,所以往往得不到最好的 crate 支持,包括使用服务 API 之类。但目前大多数服务 API 其实已经提供 HTTP REST Web 服务的形式和可用端点,大家完全可以使用 reqwest 这类 crate 检索自己需要的数据。

部署

在部署方面,Shuttle 是迄今为止最简单的 Rust 部署方法。后端部署确实要麻烦一点,要么需要鼓捣配置文件、要么通过网站上的 GUI 添加环境变量来接入需要使用的服务,或者是提供相应的静态文件。

Shuttle 使用代码基础设施理念配合代码注释,因此能够快速上手,这就避免了用 Rust 宏在主函数中声明配置文件的麻烦。我们可以用它来交付数据库并支持静态文件,以通过各种 JS 框架(包括 Next.js、React 或者任何其他可以编译为静态资产的框架)添加编译前端,如下所示:

// main.rs
#[shuttle_runtime::main]
pub async fn axum (
#[shuttle_shared_db::Postgres] postgres: PgPool,
#[shuttle_secrets::Secrets] secrets: SecretStore,
#[shuttle_static_folder] static: PathBuf
) -> shuttle_axum::ShuttleAxum {
// carry out database migrations (this assumes migrations are idempotent)
sqlx::migrate!().run(&postgres).await.expect("Migrations failed :(");

let hello_world = secrets.get("MY_VARIABLE").expect("Couldn't get MY_VARIABLE, is it set in Secrets.toml?");

// Make a router serving API routes that require a DB connection
let api_router = create_api_router(postgres);

// Add a compiled frontend (like e.g. from Next.js, React, Vue etc) to the router
let router = Router::new()
.nest("/api", api_router)
.nest_service("/", get_service(ServeDir::new(static).handle_error(handle_error));

// return the router (Rust returns implicitly so writing "return" is not required)
Ok(router.into())
}

总结

综上所述,Rust 无疑是种值得在 Web 开发中一试的优秀语言。凭借着内存占用小、性能水平高、正常运行时间长和运维成本低等优势,Rust 将帮助您在前端领域节约下宝贵的时间和金钱。

原文链接:

https://joshmo.hashnode.dev/can-rust-beat-javascript-in-2023


欢迎长按图片加好友,我会第一时间和你分享前端行业趋势面试资源学习途径等等。

添加好友备注【进阶学习】拉你进技术交流群

关注公众号后,在首页:

  • 回复面试题,获取最新大厂面试资料。
  • 回复简历,获取 3200 套 简历模板。
  • 回复React实战,获取 React 最新实战教程。
  • 回复Vue实战,获取 Vue 最新实战教程。
  • 回复ts,获取 TypeScript 精讲课程。
  • 回复vite,获取 Vite 精讲课程。
  • 回复uniapp,获取 uniapp 精讲课程。
  • 回复js书籍,获取 js 进阶 必看书籍。
  • 回复Node,获取 Nodejs+koa2 实战教程。
  • 回复数据结构算法,获取数据结构算法教程。
  • 回复架构师,获取 架构师学习资源教程。
  • 更多教程资源应有尽有,欢迎关注获取

相关推荐

  • 阿里家属:程序员老公10年薪资变化,从12年月薪三千到现在年入百万以上
  • 纯CSS实现跑马灯效果,CSS动画知识是该补一补了~
  • 开源 2 年、打磨 13 年、300 万行代码的开源项目
  • 分享一位27岁的前端,从二本到澳洲🦘的故事
  • 分享7个有用的Node.js库,让你事半功倍
  • 卧槽,又来一个 Java 神器!!
  • 计算,为了无法计算的价值
  • 拜登政府发布AI行政命令;国内超一半大模型公司跑在阿里云上;ChatGPT被曝参数规模为200亿丨AIGC大事日报
  • 苹果亮出全球首个3nm PC芯片!920亿晶体管,功耗直接砍半?MBP换芯不换面
  • 通义千问2.0来了!实测编程打败8成Python用户,阿里云大模型「全家桶」炸场
  • [开源]一款终端仿真软件,支持多种后端协议,无依赖跨平台使用
  • 10种顶流聚类算法Python实现
  • 如何设计一个安全好用的OpenApi
  • 究竟是用什么工具让郭德纲讲起了英文相声,马斯克做中文演讲?
  • 1031.AI日报:关于AI可能对人类未来构成威胁,有人挺开放性监管,有人警告垄断
  • 最容易出错的 Hive Sql 详解
  • 事件图谱构建如何进行事件标注:Duee等代表性事件标注数据集解析与Marktool事件标注动手实现
  • 阿里终于放大招了?!
  • Monzo 采用有针对性的流量削峰策略,以抵御移动应用引发的惊群效应
  • 前端“秀肌肉”,云端 Photoshop 亮相