You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#[wasm_bindgen]pubfnupdate_message(selector:&str,message:&str){let window = web_sys::window().expect("Failed to load window");let document = window.document().expect("Failed to load document");let element = document.query_selector(selector).expect("Failed to load element");ifletSome(element) = element {
element.set_inner_html(message);}else{panic!("Failed to set inner html")}}
<scripttype="module">importinit,{only_return_error_when_result}from'./pkg/hello_wasm.js';construn=async()=>{awaitinit();try{only_return_error_when_result(1);console.log('1 is ok');}catch(error){console.log('An error is reported when the input parameter is 1: ',error);}try{only_return_error_when_result(100);console.log('100 is ok');}catch(error){console.log('An error is reported when the input parameter is 100: ',error);}}run();</script>
这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了catch来捕获错误。
那么,在浏览器的控制台可以看到输出:
An error is reported when the input parameter is 1: Error: count < 10
100 is ok
<scripttype="module">importinit,{return_all_when_result}from'./pkg/hello_wasm.js';construn=async()=>{awaitinit();try{constres=return_all_when_result(1);console.log(`get ${res}`);}catch(error){console.log('An error is reported when the input parameter is 1: ',error);}try{constres=return_all_when_result(100);console.log(`get ${res}`);}catch(error){console.log('An error is reported when the input parameter is 100: ',error);}}run();</script>
是的,没错,正常获取就行了....../捂脸哭
这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了catch来捕获错误。
最后,就是在浏览器的控制台中看到:
An error is reported when the input parameter is 1: Error: count < 10
get 110
[dependencies]
wasm-bindgen = "0.2.87"web-sys = { version = "0.3.64", features = ["Window", "Document", "Element", "console"] }
js-sys = "0.3.61"
5.2 Uint8Array
首先以Uint8Array举例,在lib.rs头部引入类型:
use js_sys::Uint8Array;
然后创建一个函数,参数和返回都是Uint8Array类型:
#[wasm_bindgen]pubfnprint_uint8_array(js_arr:Uint8Array) -> Uint8Array{// new Uint8Arrayletmut arr = Uint8Array::new_with_length(3);// Uint8Array -> vecfor(index, item)in js_arr.to_vec().iter().enumerate(){console_log(format!("{} - the item in js_arr: {}", index, item));}// Avoid type conversion// Use the method of the type itselffor index in0..js_arr.length(){console_log(format!("{} - the item in js_arr: {}", index, js_arr.get_index(index)));}// vec -> Uint8Arraylet vec = vec![1, 2, 3];let arr2 = Uint8Array::from(vec.as_slice());
arr = arr2.clone();// Use the method of the type itself
arr.set_index(0,100);
arr
}
一. 前言
在上一篇文章《使用Rust和WebAssembly整花活儿(一)——快速开始》中,描述了如何创建项目和快速生成wasm并在前端中使用,迈出了整花活儿的第一步。
在开发 Web 应用程序时,使用 Rust 编写的 Wasm 模块可以提供更高的性能和更好的安全性。但是,为了与现有的 JavaScript 代码集成,必须实现 Rust 与 JS 之间的交互。Rust 与 JS 交互的主要目的是将两种语言的优势结合起来,以实现更好的 Web 应用程序。
本篇文章中,将基于上一篇文章中创建的项目来继续开发。
源码:github.com/Kuari/hello-wasm
二. 环境
三. DOM
1. 配置依赖
要操作DOM,需要引入新的依赖
web-sys
,因此,可以配置Cargo.toml
中依赖如下:你或许会好奇,这个
features
是什么,讲真,我一开始很好奇,又没看到什么特别的说明,试错才发现,原来是要手动引入功能依赖...比如说,当你需要在Rust中使用JS的console
,那么你需要在features
中加入console
。2. 获取Document
在Rust中使用
Document
,我们需要按照上一步的说明,添加features
。那么这里有一个依赖关系,首先在Rust中获取window
,然后再获取document
。因此,添加
features
后如下:然后在
lib.rs
中创建一个函数,用来调用document
:那么,现在就是在Rust中解锁了
document
,就可以在前端为所欲为了!3. 操作Element
那么开始操作一波,首先得获取到
Element
......是的,你没有想错,继续来添加
features
吧,此处要添加一个Element
:ok,那么继续。此处设定函数传入两个参数
selector
和message
,然后通过selector
获取element,更新值为message
参数的值。完整函数如下:4. 编译
将写完的Rust项目编译成wasm:
5. 在html中调用
基于上一篇文章的项目中的html,此处添加一个
div
,id为message
,添加调用wasm的update_message
函数,代码如下:6. 在浏览器验证
启动一个http server,然后在浏览器查看,可以看到在页面上出现一个
h1
标签的Hello, Rust!
。7. 发现更多方法
按照文章来写的过程中,你应该会发现一个问题——怎么这些方法没有补全?!
是的,没错的,(至少我发现)当前
web-sys
并没有补全,所以只能结合开发者优秀的前端技能和丰富的官方文档来开发了。四. Rust与JS的类型相互转换
对于wasm而言,性能固然是提升的,但是类型转换一直是个问题。当大量数据需要在wasm/js中进行类型转换时,这对性能来说,真的是个灾难。之前在使用go开发wasm时,就遇到过这样的问题,需要用官方的方法来进行手动类型转换,然而wasm处理的是一个很大的数据量......
不过好在Rust的类型支持真的挺丰富的!
1. 基础类型
基础类型挺简单的,而且Rust的范性也很好地支持了很多类型。如下是基础类型映射表:
2. 基础类型转换示例
在
lib.rs
文件中,创建一个函数,挑选几个类型作为参数传入,然后将其读取并打印:可以看到该函数传入了JS的
number
、boolean
、Uint8Array
和Array
四个类型的参数。然后编译:
接着,在前端中引入函数并调用:
最后,启动http server并打开浏览器,在控制台可以看到...看不到?!
是的,没错,Rust的
println!
只会将打印的内容发送到Rust的标准输出流,而不是前端的控制台。如果想在控制台中打印,那么需要调用JS的console
了。使用新的功能,第一步就是添加
features
,Cargo.toml
中添加console
如下:在Rust中调用
console.log()
如下:此处将其封装成一个函数:
然后,将示例函数的
println
改成console_log()
和format!
,函数代码如下:最后,编译之后,打开浏览器,就可以在控制台看到输出:
3. 通用类型
Rust中提供了一个通用的类型——JsValue,可以作为任何JS类型。
这里给一个简单的案例,设置一个函数,使用
JsValue
作为参数传入,并打印。创建函数:
然后编译成wasm文件。
在html中调用:
在html中,传入了不同类型的参数,但是在浏览器的控制台中可以看到,将所有不同类型的参数都打印出来了:
4. Result
Result
在Rust中是一个很重要的存在,经常写Rust的话,也不想在写WebAssembly时改变开发习惯。其实对于JS而言,
Result
可以直接在catch
中捕获到,只是说,这里我们需要定义好参数类型。4.1 使用Result返回报错
首先来一个只返回报错的场景:
这里返回类型是
Result
,但是仅仅返回了一个错误。值得注意的是,这里的报错使用的类型是JsError
,当然,这里也可以使用JsValue
。然后在html调用:
这里调用了两次,第一次应当是错误的,第二次应该是正确的,并且都使用了
catch
来捕获错误。那么,在浏览器的控制台可以看到输出:
4.2 使用Result返回正常值和错误
那么,如果想既返回正常值,也想返回错误呢?Rust返回一个
Result
是没有问题,那么JS怎么解析呢?直接上Rust代码:
该函数,获取到参数后,如果满足条件,加10后返回,否则报错。
那么看看html中如何调用:
是的,没错,正常获取就行了....../捂脸哭
这里的调用,依然是,第一个是错误的,第二个是正确返回值的,并且都使用了
catch
来捕获错误。最后,就是在浏览器的控制台中看到:
5. 直接引入JS类型
如果你想更直接一点,那么可以直接引入JS类型!这里主要是利用
js-sys
这个依赖,可以在官方文档上看到很多JS的类型和函数,直接引入即可使用。当然,一定场景下,直接引入的类型,是需要手动转换类型的。5.1 配置依赖
在
Cargo.toml
中添加js-sys
依赖:5.2 Uint8Array
首先以
Uint8Array
举例,在lib.rs
头部引入类型:然后创建一个函数,参数和返回都是
Uint8Array
类型:可以在代码中看到,直接引入的
Uint8Array
有自己的方法,一定场景下,需要转换类型,但是最好避免进行类型转换,而直接使用其自带的方法。这里可以简要总结下,就是最好一定场景内全部使用直接引入的JS类型,或者直接全部使用Rust类型来代替JS类型,两者都存在场景下,手动转换类型是件很糟糕的事。
5.3 Date
Date
类型,在上面的篇章中都没有提及,这里可以直接引入JS的Date
类型来使用。首先是引入类型:
然后,创建一个函数,返回时间戳:
接着,在html中调用:
最后,在浏览器的控制台中,可以看到:
五. 总结
本文中,主要讲述了如何使用Rust来实现DOM操作,读者可以根据方法自己去找到合适的方法,来实现自己的场景。其次,还讲述了Rust与JS的类型转换,从基础的各自类型的映射,到Rust独有的
Result
,到直接引入JS类型。当然这里需要注意的是,直接引入JS类型和Rust的基础类型映射JS类型这两种方法尽量不要混用,混用会导致需要手动类型转换,造成性能损耗。至此,又向Rust和WebAssembly整花活儿迈进了一步~😼
The text was updated successfully, but these errors were encountered: