5 个不稳定版本
0.3.0 | 2022 年 1 月 8 日 |
---|---|
0.2.1 | 2022 年 1 月 1 日 |
0.1.2 | 2021 年 12 月 22 日 |
#1246 in 异步
每月 85 下载
26KB
352 行
按需加载
yew-interop
的使用方式是,当消费组件请求资源时,才会按需请求每个资源。
如果您使用 wasm-bindgen 的 JS-snippet 方法 包含库,或者直接在 index.html
中插入 <script/>
或 <link/>
,即使没有消费组件,资源也会在每次请求时加载。这可能导致拥塞和浪费数据。
一次性加载,到处使用
每个资源只请求一次。如果一个资源在一个组件中已经被请求,其他消费组件不会触发重新加载。对同一资源的其他请求将等待加载完成,或者如果资源已加载,则立即返回。
示例
示例文件夹 包含使用 yew-interop
构建的演示网站
下面的 gif 展示了前两个用例,出于演示目的,加载速度被限制。
安装
主分支包含最新开发的代码。
yew-interop = {git="https://github.com/Madoshakalaka/yew-interop.git", branch="master", features=["yew-stable"]}
yew-stable
功能与在 crates.io 上发布的最新 Yew 版本兼容,目前为 0.19。如果您正在使用 yew-next(Yew 的主分支),请将 yew-stable
功能更改为 yew-next
。
或者,您可以安装在 crates.io 上发布的最新版本,它使用 Yew 0.19。
yew-interop = "0.2"
请注意,yew-next
/yew-stable
功能仅在主分支中存在,因为已发布的 crate 不能有 git 依赖。
API
异步加载 CSS 或 JavaScript 库
如果你的JavaScript库公开了你想在Rust中使用的函数或对象,那么选择 yew_interop::declare_resources!
是正确的。
首先,你想要创建一个单独的模块 interop
并在那里声明你的依赖。
// alternatively, create a separate file `interop.rs`,
// and add `mod interop;` to `main.rs` to have tidier code.
mod interop{
use yew_interop::declare_resources;
declare_resources!(
library_a
"https://my-cdn.com/library-a.min.js"
library_b
"https://my-cdn.com/library-b.min.js"
"https://my-cdn.com/library-b.min.css"
library_c
"/static/library-c.min.js"
"/static/library-c.min.css"
);
}
这个宏会展开成一个 <ResourceProvider/>
组件。你需要在应用根组件中包裹这个提供者
use yew::prelude::*;
use interop::ResourceProvider;
#[function_component(App)]
pub fn app() -> Html {
html! {
<ResourceProvider>
// the rest of your app
</ResourceProvider>
}
}
该宏还会通过在资源名称前添加 "use" 来展开成钩子,在这个例子中,宏会展开成 pub fn use_library_a() -> bool
和 pub fn use_library_b() -> bool
在你的消费组件中,你可以使用这些钩子来异步等待库加载
use yew::prelude::*;
use interop::use_library_a;
#[function_component(Consumer)]
pub fn consumer() -> Html {
let library_a_ready = use_library_a(); // <-- generated hook
html! {
if library_a_ready{
// use library a here
}else{
<p>{"please wait..."}</p>
}
}
}
对于JavaScript库,在Rust中使用该库之前,你还需要使用
wasm-bindgen
和js-sys
编写一些存根。wasm-bindgen书籍中有关于此的良好章节。你还可以查看我们的演示网站并查看如何实现
显式资源类型
declare_resources!
宏需要知道一个URL是JavaScript还是CSS。当你提供类似于上面的字符串字面量时,宏会从最后一段路径的后缀推导出信息。它期望.js或.css,并且足够智能,可以排除查询参数或片段。
当路径段不以.js或.css结尾,或者当你提供其他表达式,如宏调用或标识符时,你需要手动通过在URL前添加自定义关键字js/css来指定URL类型。
declare_resources!
将接受任何具有实现 Into<Cow<'static, str>>
返回类型的表达式的表达式,所以 &'static str
、String
、Cow<'static, str>
都是可以的。
这里有一个更复杂的例子
use yew_interop::declare_resources;
const MY_LIB_JS: &str = "https://cdn.com/my_lib.js";
declare_resources!(
my_lib
js MY_LIB_JS
"https://cdn.com/my_lic_b.css" // <-- when a string literal is provided, script type is determined from the suffix
"/static/snippet.js"
js concat!("https://a.com/", "b.js")
my_lib_b
css "/somehow/ends/with/.js" // <-- explicit type css overrides the suffix
my_lib_c
js String::from("https://a.com/test.js")
);
副作用JavaScript
在这里,副作用脚本指的是在onload事件中运行某些内容的JavaScript,而不是公开函数和类的库。
如果你的JavaScript是副作用脚本,你想要启用 script
功能。
# change yew-stable to yew-next if you use yew's master branch
yew-interop = {git="https://github.com/Madoshakalaka/yew-interop.git", features=["yew-stable", "script"]}
或者
yew-interop = {version = "0.2", features = ["script"]}
你需要用感叹号 (!) 预先标识脚本标识符。每个标识符只允许一个脚本URL,以下是一个例子
use yew_interop::declare_resources;
declare_resources!(
lib // <- normal library
"https://cdn.com/lib.js"
"/static/lib.css"
! my_script // <- exclamation mark for side effect scripts
"https://cdn.com/script.js"
);
由于只允许JavaScript,你不需要显式指定资源类型。
与之前的示例相同,这将扩展为一个 use_<identifier>
钩子。不同的是,钩子返回的是一个 Option<Script>
,当脚本正在加载时返回 None
。
要运行脚本,您需要渲染一个 <ScriptEffect/>
组件并将脚本对象传递给该组件。这允许您自由控制脚本何时以及是否运行。该 <ScriptEffect/>
组件是文档 <head/>
元素的 门控,所以它不会在其位置渲染任何内容,它只会渲染时运行脚本。
mod interop{
use yew_interop::declare_resources;
declare_resources!(
! my_script
"https://cdn.com/script.js"
);
}
use yew::prelude::*;
use yew_interop::ScriptEffect;
use interop::use_my_script; // <-- generated hook
/// this example simply runs the script on every re-render, if the script is ready.
#[function_component(MyComp)]
pub fn my_comp() -> Html {
let script = use_my_script(); // <-- returns Option<Script>
// ...snip
html! {
if let Some(script) = script{
<ScriptEffect {script}/>
}else{
<p>{"Please wait..."}</p>
}
}
}
如果您的脚本依赖于其他组件的渲染,例如演示中的第四个示例 中,其中脚本向渲染的元素添加 onclick 处理程序,您需要确保脚本在所有依赖项之后渲染。
Yew 从底部到顶部按广度优先顺序渲染子节点,这不是最直观的渲染顺序。
确保正确渲染顺序的一种方法是将 <ScriptEffect/>
组件作为 最深依赖项的兄弟元素 放置。
例如,假设您的脚本依赖于两个组件 <ComponentA/>
和 <ComponentB/>
。
下面的示例显示了正确的放置,其中 A 和 B 拥有相同的深度,
html!{
<>
<ScriptEffect {script}/>
<ComponentA/>
<ComponentB/>
// <ScriptEffect {script}/> !!! do not place here, otherwise it would render first
</>
}
这里的渲染顺序是 B -> A -> ScriptEffect。
这是一个更复杂的情况,其中 B 深度更深,因此我们将组件放置在 B 的顶部
#[derive(Properties, PartialEq)]
pub struct ContainerProps {
children: Children
}
#[function_component(Container)]
pub fn container(props: &ContainerProps) -> Html {
// --snip--
html! {
{for props.children.iter()}
}
}
html!{
<>
<ComponentA/>
<Container>
<ScriptEffect {script}/>
<ComponentB/>
</Container>
<ComponentC/>
</>
}
渲染顺序是 C -> Container -> A -> B -> ScriptEffect。
贡献
欢迎您的 pull request!CI 中有广泛的测试。请务必查看我们的 开发指南。
依赖项
~15MB
~293K SLoC