要是再不發(fā)點(diǎn)有關(guān)的技術(shù)文章,我都差點(diǎn)忘記,我除了是個(gè)段子手以外,還是一個(gè)技術(shù)博主了…… 某月某日,蝦神本蝦接到了這樣一個(gè)需求,前端獲取數(shù)據(jù)之后,在渲染之前,要對(duì)數(shù)據(jù)進(jìn)行分類,分類的方法可以選擇常用幾種,例如等距法、自然斷點(diǎn)法、標(biāo)準(zhǔn)差分類法等……問(wèn):為什么要在前端?這種功能不是一般都是在后臺(tái)實(shí)現(xiàn)的么?雖然JavaScript號(hào)稱能夠重寫一切,但是這種寫算法的事情,本身不是它的領(lǐng)域啊。答:前端的數(shù)據(jù)來(lái)源可能是很多不同的系統(tǒng),組合之后得到的,不一定來(lái)源于一個(gè)系統(tǒng)或者一個(gè)庫(kù)表,如果讓后臺(tái)做,前后臺(tái)之間的數(shù)據(jù)傳遞就太繁雜了,所以甲方想在前端把這事情給做了,再說(shuō):所以呢,蝦神你不是做算法的么?來(lái)來(lái)來(lái),幫忙給寫一個(gè)……雖然我手動(dòng)寫過(guò)自然斷點(diǎn)法的計(jì)算方法,但是我不會(huì)JavaScript啊……你要不讓我先花兩個(gè)月,學(xué)習(xí)一下JavaScript?雖然自然斷點(diǎn)的算法還是比較簡(jiǎn)單的,我也寫過(guò):(見(jiàn)以前的文章: 探索性數(shù)據(jù)分析:自然斷點(diǎn)法算法原理及Python實(shí)現(xiàn)
但是對(duì)于這種重復(fù)造輪子的活動(dòng),我一般是努力拒絕的:所以我果斷的把Python推薦給了他,畢竟Python里面已經(jīng)有好幾個(gè)身經(jīng)百戰(zhàn)的自然斷點(diǎn)法的實(shí)現(xiàn)了,然后就毫無(wú)意外被拍回來(lái)了……好吧,作為碼農(nóng),不能說(shuō)不行……我雖然不會(huì)寫JavaScript,但是我會(huì)寫Rust啊……好巧不巧,Rust寫的wasm,就正好能在前端用。wasm: WebAssembly的簡(jiǎn)寫,是一種新型的瀏覽器端代碼: 用JavaScript的運(yùn)行原理來(lái)說(shuō),它實(shí)際上是在JS的編譯器中動(dòng)態(tài)編譯,然后在JS的VM中執(zhí)行的,那么wasm可以讓C/C++/Rust一類的高性能編譯語(yǔ)言,轉(zhuǎn)換成一種稱之為IR的虛擬指令集,在需要的時(shí)候,在轉(zhuǎn)換成JS VM可以運(yùn)行的機(jī)器指令:這種IR的編譯指令,能夠最大化的利用客戶端的底層(如CPU\內(nèi)存\顯卡等)硬件,所以很多時(shí)候,比原生態(tài)的JavaScript性能更高。2019年的時(shí)候,wasm就已經(jīng)正式成為了W3C標(biāo)準(zhǔn),成為了Web開發(fā)的“第四門語(yǔ)言”有關(guān)wasm的其他介紹,大家有興趣的可以查閱其他資料,反正一句話:這玩意兒就是一個(gè)可以運(yùn)行在前端瀏覽器上的編譯級(jí)語(yǔ)言功能。所以,我們就可以利用一些Rust寫的東西,編譯成JS可以用的腳本了。秉承著有的輪子,我們就不用自己去造的原則,首先我們?nèi)タ纯碦ust的官方倉(cāng)庫(kù)http://crates.io里面,有沒(méi)有我們需要的東西:很快,我告訴你,真的很快啊,就讓我找到了這個(gè)東西:我們直接去全球最大的同性交友網(wǎng)站gayhub……阿不,github,把這個(gè)包c(diǎn)lone下來(lái)(或者你直接下載zip,然后解壓也行),因?yàn)樽髡咭呀?jīng)把所有的wasm相關(guān)代碼都寫好,所以你只需要運(yùn)行編譯打包就可以了,注意,官方文檔上說(shuō)-features如下:wasm-pack build --release -- -features js (不好使)在我這里最新的Rust版本里面已經(jīng)不好使了,直接編譯為web就行,命令行如下:wasm-pack build --release target web然后看著cargo自動(dòng)安裝一堆東西,自動(dòng)下載一堆東西,自動(dòng)編譯一堆東西,直到顯示:然后可以了,我們可以看見(jiàn),在工程根目錄下,會(huì)得到一個(gè)pkg包,里面有我們需要的wasm文件:其中,index.html是調(diào)用的示例文件,搞前端的同學(xué)一眼就明白:然后我們啟動(dòng)一個(gè)小http服務(wù)器,就可以看見(jiàn)效果了,我這里啟動(dòng)的是python自帶的http服務(wù)器:我們可以簡(jiǎn)單解析一下這個(gè)工程(如果你Rust沒(méi)有基礎(chǔ)也沒(méi)有興趣,就可以跳過(guò)這一部分了),首先在src/jenks.rs文件中,寫了一個(gè)函數(shù),做了算法的實(shí)現(xiàn)://src/jenks.rs pub fn get_jenks_breaks<T: ToPrimitive>(num_bins: usize, data: &[T]) -> Vec<f64> { //jenks的算法見(jiàn)文章開頭的鏈接,這里不解釋了 //這里的就是用Rust把jenks的實(shí)現(xiàn)過(guò)程寫了一遍。 }
這個(gè)函數(shù)在Rust工程里面是可以直接用的了,我們可以在下面寫一個(gè)測(cè)試方法,來(lái)看看效果:但是我們要在前端調(diào)用它,所以必須還要封裝成wasm,所以還需要一個(gè)對(duì)外封裝的接口:src/wasm.rs:前面的#[wasm_bindgen]特性,就是聲明該方法,是一個(gè)wasm的綁定,這樣這個(gè)方法編譯之后,就可以被前端調(diào)用了。#[wasm_bindgen] pub fn get_jenks_breaks(no_bins: usize, data: &[f64]) -> Box<[f64]> { let breaks = crate::jenks::get_jenks_breaks(no_bins, data); breaks.into_boxed_slice() }
接下去,編譯完成之后,會(huì)生成.wasm文件,這個(gè)文件是一個(gè)二進(jìn)制的文件: 也就是我們前面說(shuō)的IR(中間過(guò)程)文件。然后前端需要調(diào)用的話,還得將它給調(diào)度到JS VM里面去,所以Rust的wasm工具包還會(huì)編譯出方便前端調(diào)用的js/ts接口:這樣,你就得到了一個(gè)標(biāo)準(zhǔn)的Javascript接口,那么前端同學(xué)要調(diào)用,就沒(méi)有任何的難度了……至此,一個(gè)用后臺(tái)高性能編譯型語(yǔ)言編寫的算法,就完成了前端封裝和調(diào)用。如果你有興趣研究一下webassembly的話,這個(gè)例子我覺(jué)得可以當(dāng)成hello world還好懂……起碼你不用寫一行代碼,而具體里面的技術(shù)細(xì)節(jié),有興趣的同學(xué)可以查閱:https://developer.mozilla.org/zh-CN/docs/WebAssembly
|