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

2023-09-15 23:39:01

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

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

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

Yew官方给出的例子如下:

#[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中的这段配置了。
[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

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请求的代码:

    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");
                }
            })
        })
    };

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

更多推荐

量子计算基础知识—Part1

1.什么是量子计算机?量子计算机是基于量子力学原理构建的机器,采用了一种新的方法来处理信息,从而使其具有超强的功能。量子计算机使用Qubits处理信息。2.什么是量子系统?一个量子系统指的是由量子力学规则描述和控制的物理系统。在量子力学中,物理系统的状态不再是经典物理中的确定性值,而是由一个称为波函数的数学对象描述的概

接入国家能源平台MQTT应用案例

一、项目介绍随着国家对节能环保措施的力度不断加大,基于物联网技术搭建的国家能源平台在国家相关部门的建设下逐渐成熟。致力于利用实际能耗数据建立能效仿真模型,通过实时寻优运算,获得当前的最优化运行策略,并将控制指令下发控制系统,快速帮助能源全面实现自身能源管理的信息化、流程化、可视化和可操作性。二、项目所面临的问题1.常规

Mac电脑系统怎么样才能干干净净地卸载应用程序?

Mac系统怎么样才能干干净净地卸载应用程序,不留下隐私数据和用户信息呢?如果有方法的话,那么该方法对于Mac电脑小白是否友好呢?CleanMyMac就是一款用于清理Mac系统下应用程序的一款清理工具,其内置了应用程序的安全卸载功能,保证了软件的彻底卸载。下面小编使用CleanMyMacX版本,带大家看看如何使用它干净地

Scotch: Combining SGX and SMM to Monitor Cloud Resource Usage【TEE的应用】

目录摘要引言贡献背景SMMXenCreditScheduler与资源核算SGX威胁模型SchedulerattacksResourceinterferenceattacksVMEscapeattacks架构ResourceAccountingWorkflowCostofAccounting具体的部署和评估见论文相关工作

自监督学习之对比学习:MoCo模型超级详解解读+总结

文章目录一、MoCo简介1.1整体思想1.2动量1.3正负样本如何选取二、动态字典2.1query和key2.2字典特点三、编码器的动量更新3.1编码器的更新规则3.2使用动量更新的原因四、实验过程4.1目标函数:infoNCE4.1.1softmax4.1.2交叉熵损失4.1.3交叉熵损失函数和softmax的关系4

比特币 ZK 赏金系列:第 2 部分——查找哈希冲突

在我们的零知识赏金(ZKB)系列的第二部分中,我们将其应用于解决哈希冲突难题。在这样的谜题中,两个不同的输入散列到相同的输出。此类赏金可用于:充当煤矿中的金丝雀,给我们一个有价值的提醒。存在冲突是散列函数较弱的标志,因此我们可以尽早升级以减轻损失。资助研究以发现哈希函数中的漏洞,特别是对于MiMC等新函数。碰撞攻击历史

前端工程化面试题

下面的模块导出了什么结果?exports.a='a';module.exports.b='b';this.c='c';module.exports={d:'d'}参考答案:{d:'d'}说一下你对前端工程化,模块化,组件化的理解?参考答案:这三者中,模块化是基础,没有模块化,就没有组件化和工程化模块化的出现,解决了困扰

4 vCPU 实例达成 100 万 JSON API 请求/秒的优化实践

“性能工程”(Performanceengineering)是个日渐流行的概念。顾名思义“性能工程”是包含在系统开发生命周期中所应用的一个技术分支,其目的就是确保满足非功能性的性能需求,例如:性能、可靠性等。由于现代软件系统变得日益复杂,我们在对抗性能这个凸显的挑战的时候往往显得无措手足,或者照本宣科的尝试一些偏方,寄

人脸修复祛马赛克算法CodeFormer——C++与Python模型部署

一、人脸修复算法1.算法简介CodeFormer是一种基于AI技术深度学习的人脸复原模型,由南洋理工大学和商汤科技联合研究中心联合开发,它能够接收模糊或马赛克图像作为输入,并生成更清晰的原始图像。算法源码地址:https://github.com/sczhou/CodeFormer这种技术在图像修复、图像增强和隐私保护

OceanBase开源获信通院认可:开源300万行核心代码、社区答疑超3万次

昨日,在由中国信息通信研究院主办的“2023OSCAR开源产业大会”上,蚂蚁集团旗下的自研原生分布式数据库OceanBase荣获“2023OSCAR尖峰开源项目”、“2023OSCAR尖峰开源企业(开源运营与生态建设)”两个奖项。同时,完成可信开源社区评估,获得“可信开源社区”评估证书。OceanBase自2021年6

从0搭建夜莺v6基础监控告警系统(二):采集数据、打通夜莺显示

文章目录1.写在前面1.1.categraf采集数据1.2.官方文档传送门2.配置过程2.1.打通夜莺和VictoriaMetrics2.2.配置Categraf2.3.验证结果2.4.配置仪表盘3.部署总结3.1.操作总结3.2.仪表盘展示上一操作我们已经安装好了所需的基础服务,接下来需要打通各组件之间的数据推送和监

热文推荐