现在越来越多的 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 的组件和目录.