[2023.09.15]: Yew SSR模式下的条件编译问题

昨天才写了Rust的条件编译,没想到这个问题还没完。

昨天我还为它的强大而赞叹不已,自以为对它了解了八九成,然而今天我才猛然意识到,这个里面的深度远超我的想象。我估计,我现在只了解其中的冰山一角吧。

故事从客户端post数据的后端api说起。

习以为常的思维影响着我解决问题的方式,对于这种问题,我通常会寻找一个库来处理后端的 API 交互问题。因此,我在互联网上四处搜寻与 Yew SSR 开发相关的示例,并发现大部分示例都使用了 reqwest 库。但是,当时我并没有注意到一个细节,这些示例都只调用了 get 方法,却没有涉及到 post 方法的应用场景。

Yew官方给出的例子如下:

rust 复制代码
#[cfg(feature = "ssr")]
async fn fetch_uuid() -> Uuid {
    // reqwest works for both non-wasm and wasm targets.
    let resp = reqwest::get("https://httpbin.org/uuid").await.unwrap();
    let uuid_resp = resp.json::<UuidResponse>().await.unwrap();

    uuid_resp.uuid
}

#[function_component]
fn Content() -> HtmlResult {
    let uuid = use_prepared_state!((), async move |_| -> Uuid { fetch_uuid().await })?.unwrap();

    Ok(html! {
        <div>{"Random UUID: "}{uuid}</div>
    })
}

因此,我就顺着这个例子给出的reqwest,来解决我要post数据到后端api的问题。

让我们细看一下官方给出的例子代码,fetch_uuid带有条件编译#[cfg(feature="ssr")],也就是说这个函数只会在编译服务器端的target文件时才会编译进去,然后看一下调用fetch_uuid的地方,用的是use_prepared_state!宏,它也会作用于服务器端。连起来理解,就是在服务器端调用fetch_uuid函数来获取数据,并将数据"发送"到客户端。这里的"发送"实际上就是wasm的加载过程,也就是说在服务器端和客户端都可以看到这个值。理解了这段例子代码之后,我们会发现这里的过程都发生在服务器端,而不是客户端。我们要把数据post到后端api,这个场景发生在wasm加载到浏览器之后,由用户在UI上的操作发起的。

显然,这段例子代码没有涉及到我的需求,但是reqwest库中也有post的功能,能不能通过reqwest库来实现我要把前端的数据post到后端api的这个需求呢?

答案是NO。

首先聊一下SSR模式,即服务器端生成,在这个模式中,针对后端api的访问就有两种情况

  1. 在服务器的运行环境中来访问后端api;
  2. 在浏览器端的wasm环境中来访问后端api;
    明白了这两种情况后,我们就很好理解在Cargo.toml中的这段配置了。
toml 复制代码
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4"
wasm-logger = "0.2"
log = "0.4"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.29.0", features = ["full"] }
warp = "0.3"
clap = { version = "4", features = ["derive"] }

这个配置,说明wasm-bindgen-futures, wasm-logger, log这3个依赖只有在编译成wasm时才会有效,而tokio, warp, clap这个3个依赖,只有在编译成服务端代码时才会有效。

而reqwest依赖于tokio库,因此reqwest只能用于服务器端的api处理,不能用于浏览器端wasm环境中的api处理。当然,你完全不用担心如果你在开发这类需求时,会不会在这个地方配置出错,完全不用担心这个问题,因为编译器会把问题报给你。这一点总算是给Rust挽回了一点面子。

故事到这里还没有完,彩蛋还在后面。

按照上面的Cargo.toml,我最初在运行cargo clippy时总是报错,说wasm_bindgen_futures::spawn_local不存在,但后来发现运行trunk build index.html时,这个错误又没了,所以我就怀疑上了条件编译这一块。说明在运行trunk build index.html时,它产生的目标是wasm。算了吧,能够完成build也不错,先把流程走通。但是后来再运行cargo run --features=ssr --bin=ssr_server -- --dir dist时,又报wasm_bindgen_futures::spawn_local不存在的错误。我想这里编译的是服务端代码,它的编译目标应该是非wasm的。而wasm-bindgen-futures又是声明在wasm下面,因此找不到wasm_bindgen_futures。所以,我尝试把wasm-bindgen-futures一到[dependencies]下,这下子都不报错了。

因此,我觉得这里应该折射出一个问题,即Yew的SSR开发模式中,没有严格区分哪些代码要被编译成服务器端的target,哪些代码要编译成客户端wasm的target。

当然,最后的结局编译成功,运行时也没有什么问题。

总结一下,在Yew的SSR开发中,虽然代码都在一个项目之内,但是要注意区分代码和功能是哪些运行在服务器环境,哪些运行在浏览器的wasm环境。可以通过指定特定目标架构的依赖项([target.'cfg()'.dependencies]),配合编译器来帮助我们发现错误。

说了这么多,最终的代码如下:

Cargo.toml

toml 复制代码
reqwest = { version = "0.11.18", features = ["json"] }
gloo-net = { version="0.4.0", features=["json", "http"]}
wasm-bindgen-futures = "0.4"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-logger = "0.2"
log = "0.4"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.29.0", features = ["full"] }
warp = "0.3"

客户端发起post请求的代码:

rust 复制代码
    let on_save = {
        Callback::from(move |editorData: EditorData| {
            spawn_local(async move {
                if let Ok(req) = Request::post("/notes").json(&editorData) {
                    let _ = req.send().await;
                } else {
                    log!("invalid json");
                }
            })
        })
    };

今天的故事就讲到这里,里面有不对的地方,还望大家多多指教。(终于要到后端的开发了)

相关推荐
开开心心就好35 分钟前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
IvanCodes2 小时前
Python 数据处理(十三):JSON、CSV 与数据序列化
开发语言·python
Setsuna_F_Seiei3 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
aramae4 小时前
MySQL复合查询(8)
java·c语言·开发语言·后端·算法
百万蹄蹄向前冲5 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙5 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan6 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
qq_2518364576 小时前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程
kyriewen6 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理7 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程