現在越來越多的 single-page website 稱只要打開這個網頁就可以使用了!而且越來越多的框架嘗試這麼寫,但是我作為一個逆流而上的愣頭青非常反對這個.
使用者體驗#
用過美團,餓了嗎,淘寶,支付寶的都知道,別說了.
體驗奇差無比
網頁就真的跨平台了嗎?#
眾所周知,HTML5 CSS3 JS262 webassembly 和 WebGPU 等均為瀏覽器標準,有按照標準的,有制定標準的,有事實標準也有完全不按照套路出牌的,而且越新的技術標準的執行就越混亂,標準就越混亂,脫離標準的 UB 就越離奇,所以,這些 SPA 都會採用各種各樣的 pollyfill 的 node 模組去嘗試解決這個問題,無論是昨天的 polyfill.js, babel, 還是今天的 eslint, 而伴隨著越來越多的前端工具的誕生,前端社區項目往往帶來過高的期待,以及日新月異的所謂的工作流.
前端開發者不得不兩年更換一次自己的框架來保證自己緊跟這個時代,這往往是非常痛苦的,而傳統的 APP 開發者如 Qt 和 Gtk, 又或者是 Android, Winform 等都並不會像 web 一樣變更頻繁,你甚至可以在最新的 windows 11 上運行當年拿 Delphi 開發的 GUI 軟體,甚至是為 windows 3 寫的,拿 turbo pascal 包的對 WinAPI 直接調用的 GUI 軟體.
而你的上個時代的 Flash 的 Action script 在現在的瀏覽器甚至已經不可以加載了.
安全性就真的更好嗎?#
與傳統的 App 不同的是 WPA 既要在這個小小的網頁裡維護一套自己的生命週期組件的同時保證較高的渲染速度的同時還要處理與後端的數據交互,先不談他的性能問題,就算是安全也變得尤為棘手.
我們都知道我們有時候會使用什麼 cookies 和什麼 web storage (當然有的瀏覽器支持的並不好😄) 存各種 session 數據來認證你確實是你,然而各種 XSS 攻擊的套路也層出不窮,甚至現在 XSS 攻擊已經成為了既緩衝區溢出攻擊 (哦,我的蘋果,你的 M1 加密芯片在第一天就被 shellcode 幹爆了) 之後最大的攻擊手段,先不談對伺服器的影響,先談談對客戶的影響.
假如有一個腳本能獲取到你的 cookies 並且成功的猜出了你的 cookies 的 pattern 並對其進行了一定的變換使得後端以為你真的在登錄,那麼你帳號裡面的任何的數據將變得非常的危險.
其次就是後端,很多 SPA 通過使用前端驗證的方式來降低後端的開銷,我們也知道 JS 是多爛的語言,萬一時候遇到腦癱開發者並沒有對某個信息進行嚴格驗證,某個別出心裁的用戶就可以在用戶名上填入這種信息...
這也是曾經我最喜歡幹的事情之一,而且成功打趴了很多 php 網頁 (
這對於後端也是非常大的威脅,我們同時知道 JS 這門爛語言有 eval, 我們可以甚至一邊 XSS 一邊注入一邊在用戶名裡面填什麼 eval...
而本地的 App 如果開發者訓練有素通過各種各樣加殼的手法進行反逆向後... 這將對這類攻擊造成極大的挑戰,而 SPA 面對這的就是一個個按 F12
就能打開調試器的瀏覽器,這對黑客的學習成本... 打了骨折.
犧牲的性能真的不要緊嗎?#
不得不說,谷歌的工程師你們太厲害了,把 JS 這一坨大粪能 JIT 成這樣
但是,我們知道原生的操作系統提供的 API 往往都是 native 語言寫的,例如 C/C 艹 / Swift/ObjC, 這些語言再爛也就頂多是 3x 優化最好的 C 的速度,而 JS 雖然速度這麼快 (5xC) 但瀏覽器上面可不止搭建了一個抽象層,先就是 2D 繪畫,瀏覽器採用的是 Skia (Chrome), windows 所提供的默認的 2D 繪圖是 GDI, 或者是 DirectX 等更加全面的 API, Skia 將會對自身的調用轉譯成提供類似功能的 OpenGL/Vulkan, 然後再用諸如 Angle 這類的進行跨平台調用,這僅僅是渲染層面上.
多線程上 JS 本身是一個單線程語言,它並不具備什麼同步語義之類的東西,WebWorker 這種簡陋的接口在面對複雜需求時往往是力不從心的,這裡我們可以看到 HHLAB 的 CPU 計算性能被各大科學計算框架吊打的事實,先忽略掉算法層面的问题,人家 native 語言如 C 艹有什麼 MPI 可以高舉 SIMD 和多核大棒,你憑什麼跟人家打?
我的藍牙,USB 設備,外設呢?#
現實當中,大部分軟體雖然都沒有直接訪問硬體或者對硬體的接口大家都已經做的差不多了,但還是有一些軟體需要跟驅動打交道,如最近挺火的區塊鏈錢包的硬體錢包.
現在我們的 Chrome 已經要放棄 napi 了不知道以後拿什麼對本機的 USB 端口也好,藍牙也好進行溝通.
有人可能會說,欸你用 USB 和藍牙的 JS 接口啊!
這就給大家看看這些接口有多難用!
先看看相容性
然後再看看鬼畜的語法
let button = document.getElementById('request-device');
button.addEventListener('click', async () => {
let device;
try {
device = await navigator.usb.requestDevice({ filters: [{
vendorId: 0xABCD, // emm
classCode: 0xFF, // 行吧
protocolCode: 0x01 // 好! 你贏了
}]});
while (true) {
let result = await device.transferIn(1, 6);
if (result.data && result.data.byteLength === 6) {
console.log('Channel 1: ' + result.data.getUint16(0)); // 看這美麗的raw
console.log('Channel 2: ' + result.data.getUint16(2));
console.log('Channel 5: ' + result.data.getUint16(4));
}
if (result.status === 'stall') { // 看這愚蠢的字符串
console.warn('Endpoint stalled. Clearing.');
await device.clearHalt(1); // 看著抽搐的await
}
}
} catch (err) {
// No device was selected.
}
if (device !== undefined) {
// Add |device| to the UI.
}
});
目前應該沒幾家對這個接口落地了,這是描述文檔
然後假如你想在 windows 上連接 USB 設備呢?(*nix 設備包括安卓都統一使用 libusb)
你可以通過簡單的查閱 MSDN 獲得你想要的文檔,然後使用 VS 寫出優美的 C#, 然後隨便調試還不限制什麼功能.
protected override async void OnLaunched(LaunchActivatedEventArgs args){
// 找 vid 和 pid
UInt32 vid = 0x045E;
UInt32 pid = 0x078F;
string aqs = UsbDevice.GetDeviceSelector(vid, pid);
var myDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs, null);
if (myDevices.Count == 0){
ShowError("Device not found!");
return;
}
UsbDevice device = await UsbDevice.FromIdAsync(myDevices[0].Id);
// 控制信號
UsbSetupPacket initSetupPacket = new UsbSetupPacket() {
Request = initRequest,
RequestType = new UsbControlRequestType()
{
Recipient = UsbControlRecipient.DefaultInterface,
ControlTransferType = UsbControlTransferType.Vendor
}
};
await device.SendOutControlTransferAsync(initSetupPacket);
}
藍牙... 你想用 web 操作藍牙?
別妄想了!
那就更別說什麼 PCIE 設備了
JS 語言從一開始就不是給你寫 App 的,只不過得益於現在的 DOM 系統提供的跨平台渲染一致性大家都想更輕鬆的寫出各平台都凑合能跑的軟體.#
我個人還是崇尚於使網頁作為信息的展示和交流平台就足夠了,其他的功能並一定要讓瀏覽器完成,使用操作系統原生的或者是跨平台的原生語言寫的框架不論是易用性還是安全性都要遠遠的高於使用前端技術.
現在產生這個的原因不是因為原生平台功能不足,而是在易用性上,原生的操作系統和 GUI 軟體廠商並沒有抱著面向用戶和面向開發者的觀念努力優化學習曲線,會的人少自然寫出的軟體就少,效果就差強人意.
我所希望看到的是一秒鐘之內我能夠在這個網頁上快速的獲取我所需要的信息而不是等待這個網頁加載一些雜七雜八雜如 App 的組件和目錄.