譯文聲明本文是翻譯文章,文章原作者Kevin J. Lynagh,文章來源:http://keminglabs.com原文地址:https://keminglabs.com/blog/building-a-fast-electron-app-with-rust/
譯文聲明本文是翻譯文章,文章原作者Kevin J. Lynagh,文章來源:http://keminglabs.com
當我研發Finda時,我非常希望它能夠做到快速,最好能在16毫秒內相應所有用戶輸入。
在深入了解技術細節之前,我們首先要了解Finda自身的設計目標。
鑒於上述要求,我重新考慮了我的選項。
除了渲染之外,我發現操作系統集成方面也存在著一些困難點:
Electron用語用戶界面層,Rust作為二進位執行並處理所有其他內容
1) 瀏覽器調用一個文檔onKeyDown監聽器,該監聽器將JavaScript keydown事件翻譯為表示事件的普通JavaScript對象,就像是:
{name: "keydown", key: "C-g"} 2) 這個JavaScript對象被傳遞給Rust(之後會傳遞更多),Rust返回另一個表示整個應用程序狀態的普通JavaScript對象: { query: "search terms", results: [{label: "foo", icon: "bar.png"}, ...], selected_idx: 2, show_overlay: false, ... }
3) 然後將這個JavaScript對象傳遞給React.js,它使用<divs> 和<ols>將器實際呈現給DOM。
其中比較有趣的是第二個步驟,如果從JavaScript調用Rust,那會是什麼樣子?
var Native = require("Native"); var new_app = Native.step({name: "keydown", key: "C-g"});
Rust中這個函數有一些複雜,我們來具體分析一下:
pub fn step(call: Call) -> JsResult<JsObject> { let scope = call.scope; let event = &call.arguments.require(scope, 0)?.check::<JsObject>()?; let event_type: String = event .get(scope, "name")? .downcast::<JsString>() .unwrap() .value();
pub fn step(call: Call) -> JsResult<JsObject> {
JavaScript有幾種語義不能完美映射到Rust的語言語義(例如,參數對象和動態變數)。
match event_type.as_str() { "blur" => finda::step(&mut app, finda::Event::Blur), "hide" => finda::step(&mut app, finda::Event::Hide), "show" => finda::step(&mut app, finda::Event::Show),
"keydown" => { let s = event .get(scope, "key")? .downcast::<JsString>() .unwrap() .value(); finda::step(&mut app, finda::Event::KeyDown(s)); }
... 這些分支還會調用finda :: step函數,它將實際更新應用程序狀態以響應事件,例如:更改查詢並返回相關結果、打開選定結果、隱藏Finda等等。 (我會在以後的博客文章中詳細講解Rust,希望大家繼續關注我的博客,或者關注@lynaghk) 在應用程序狀態更新之後,它需要返回到Electron端進行渲染。這個過程看起來與其他方案都很相似,但實際是在另一個方向上,它是將Rust數據結構翻譯成JavaScript數據結構: let o = JsObject::new(scope);
o.set("show_overlay", JsBoolean::new(scope, app.show_overlay))?; o.set("query", JsString::new(scope, &app.query).unwrap())?; o.set( "selected_idx", JsNumber::new(scope, app.selected_idx as f64), )?; 在這裡,我們首先創建JavaScript對象,該對象將返回到Electron並將一些鍵與某些基本類型相關聯。 返回結果(一個對象類型數組)需要更多的限制:數組大小需要事先聲明、Rust結構必須明確列舉出來。但整體來說,還不算太糟糕: let rs = JsArray::new(scope, app.results.len() as u32);
for (idx, r) in app.results.iter().enumerate() { let jsr = JsObject::new(scope); jsr.set("label", JsString::new(scope, &r.label).unwrap())?;
if let Some(ref icon) = r.icon { jsr.set("icon", JsString::new(scope, &icon.pathname).unwrap())?; }
rs.set(idx as u32, jsr)?; }
o.set("results", rs)?;
最後,在該函數結束時返回JavaScript對象:
Ok(o)
Neon處理所有的細節,並將其傳遞給JavaScript端的調用者。
那麼,在實踐中它們的性能表現得如何呢? 在Chrome DevTools的「性能」選項卡(內置於Electron中)中,我們可以看到,這是一個單一keypress的典型曲線:
其中的每個步驟都被標記:1)將按鍵轉換為事件,2)在Rust中處理事件,3)使用React渲染結果。
2、根據質量啟發式(匹配數量、是否出現在詞語邊界等)對所有這些結果進行排序。
考慮到這些性能指標,我們可以通過刪除React(也可能是整個DOM)來縮短響應時間,而不是使用<canvas>元素手動處理布局並進行渲染。
Electron可以輕鬆構建和分發桌面應用程序,讓我擺脫繁瑣的字體渲染細節、低級操作系統熱鍵和窗口API。
原文鏈接:本文翻譯自:keminglabs.com 如若轉載,請註明出處:keminglabs.com安全客 - 有思想的安全新媒體
原文鏈接:
如若轉載,請註明出處:keminglabs.com安全客 - 有思想的安全新媒體
推薦閱讀: