大家好,很高兴又见面了,我是”高级前端进阶“,由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端进阶
前言
本文主要和大家讨论 asm-dom,即通过 WebAssembly 技术 C++ 率先支持虚拟DOM。在年初,我也的确 使用 WebAssembly 将客户端应用成功移植到了 Web,这也是为什么我一直对 WebAssembly 充满好奇的缘由。我甚至在头条上开了一个合集《WebAssembly 前沿技术》来专门探讨 WebAssembly ,并将持续关注 WebAssembly 的最新动态。
下面是已发布部分文章传送门(更多文章可以阅读我的头条专题):
- 《 2023 年让 WebAssembly 大火的 10+应用!》
- 《 万字长文!2023 年 WebAssembly 各个运行时性能对比!》
- 《 让 JavaScript 在 WebAssembly 上加速运行!》
- 《全网最火的5+优秀 WebAssembly 运行时!》
- 《 在线表格再添一员猛将excelize,支持 wasm! 》
正如大家所看到的,当我们还在迟疑是否要在日常开发中引入 WebAssembly 的时候,许多优秀的应用、工具已经开始吃 WebAssembly 的红利了,而且取得了不错的成就,这可能也是为什么各个浏览器厂商、开发者如此热衷 WebAssembly 的缘由吧。
1.什么是 asm-dom
asm-dom 是用于构建 C++ SPA 单页应用程序的最小 WebAssembly 虚拟 DOM。 基于 asm-dom,开发者可以使用 C++ 编写整个 SPA,然后使用 Emscripten 将其编译为 WebAssembly(或作为后备的 asmjs),asm-dom 将自动处理 DOM API 调用,从而生成一个旨在通过利用通用硬件功能以本机速度执行的应用程序。

asm-dom项目
此外,开发者可以基于原有 C/C++ 代码而无需任何更改,列如创建绑定层。 这相当于使用 C++ 中创建一个应用程序,在必要的时候会自动调用 Javascript,而不是通过 Javascript 反向调用 C++。 asm-dom 允许开发者只用 C++ 编写代码一次,并在桌面、移动应用程序和 Web 端实现代码共享。
目前 asm-dom 在 Github 上通过 MIT 协议开源,有超过 2.7k 的 star、0.1k 的 fork、是一个值得关注的开源项目。可以将 asm-dom 的典型特征归纳为以下几个核心要点,包括:
灵活性
asm-dom 是一个底层虚拟 DOM 库,对于开发者应该如何构建自己的应用程序并无偏好。
共享 C++ 代码库
只用 C++ 编写代码一次,并与桌面/移动应用程序和 Web 端最大限度的实现代码共享。
速度飞快
asm-dom 允许开发者使用 C++ 编写整个 SPA,并使用 Emscripten 将其编译为 WebAssembly,同时保证应用速度。这对于基于浏览器、WebView的应用程序来说绝对是福音。
类似 JSX 的语法
借助于 gccx,即一个允许简单语法(CPX)的解析器(和 JSX 有细微差异),asm-dom 会自动将 CPX 语法转换为标准 C++。 通过这种方式,开发者可以编写出与 HTML 超级类似的文件,从而提供开发人员体验。
VNode* vnode = (
<div
onclick={[](emscripten::val e) -> bool {
emscripten::val::global("console").call<void>("log", emscripten::val("clicked"));
return true;
}}
>
<span style="font-weight: bold">This is bold</span>
and this is just normal text
<a href="/foo">I'll take you places!</a>
</div>
);
服务器端渲染
asm-dom 支持服务器端渲染,开发者可以用 C++ 编写服务器并使用 WebAssembly 在 Node.js 上运行。只需要两个核心步骤:
- 使用 toHTML 在服务器上生成 HTML 并将其发送到客户端以加快页面加载速度,并允许搜索引擎出于 SEO 目的抓取页面
- 在服务器渲染的节点上调用 toVNode 并使用在客户端创建的 vnode 对其进行 patch(类似补水)。 通过这种方式,asm-dom 将保留它并仅附加事件处理程序,从而提供出色的首次加载体验
下面是服务端渲染的典型示例:
// 返回视图的函数,用于客户端和服务器
VNode* view() {
return (
h("div",
Data(
Attrs {
{"id", "root"}
}
),
Children {
h("h1", std::string("Title")),
h("button",
Data(
Attrs {
{"class", "btn"}
},
Callbacks {
{"onclick", onButtonClick}
}
),
"Click Me!"
)
}
)
);
}
// 服务端渲染
VNode* vnode = view();
std::string appString = toHTML(vnode);
std::string html = (
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>My Awesome App</title>"
"<link rel="stylesheet" href="/index.css" />"
"</head>"
"<body>"
+ appString +
"</body>"
"<script src="/bundle.js"></script>"
"</html>"
);
// 客户端渲染
VNode* oldVNode = toVNode(
emscripten::val::global("document").call<emscripten::val>("getElementById", emscripten::val("root"))
);
VNode* vnode = view();
patch(oldVNode, vnode);
// 附加事件处理器
2.如何使用 asm-dom 构建应用程序
asm-dom 是一个底层虚拟 DOM 库,与如何构建应用程序无关。asm-dom 初始诞生于非游戏、VR、AR 或图像/视频编辑的常见用例中测试 WebAssembly 的强劲功能的想法。不过不幸的是,目前 GC/DOM Integration 依旧是一个面向未来的特性 ,所以,asm-dom 并不是完全在 wasm 中开发的。
asm-dom 中所有与 DOM 的交互都是用 Javascript 编写的, 这是一个很大的缺点,由于需要 JS 和 WASM 之间额外的绑定开销。当然,未来 asm-dom 会更强劲。
如果想在没有任何配置的情况下开始使用 asm-dom,可以思考 asm-dom-boilerplate(查看文末资料),这是一个超级简单的项目,包含需要的所有内容,只需克隆并运行它。如果使用的是 webpack,为了使用 asm-dom,必须准备环境。
第一需要安装 arraybuffer-loader:
npm install --save-dev arraybuffer-loader
然后在 Webpack 的 loader 中添加下面的内容:
{
test: /.wasm$/,
loaders: ['arraybuffer-loader'],
}
另外,如果 fs 有一些问题,可以将下面代码添加到 webpack 配置中:
node: {
fs: 'empty',
},
之后,可以使用 cpp 文件夹中的源代码构建应用程序:
- asm-dom.hpp 和 asm-dom-server.hpp
- asm-dom.cpp 和 asm-dom-server.cpp(或包含两者的 asm-dom.a)
并使用 emscripten (emcc cli) 编译它,下面是关于此步骤的一些提示:
- 请确保在编译期间使用 –bind 选项,否则 asm-dom 将无法工作。
- emcc 有许多可以优化构建的设置,可以查看官方文档。
- 提议将应用程序编译为 .bc,然后使用它来生成一个 WebAssembly 版本和一个 asm.js 版本(作为回退版本)
编译后就可以在项目中导入应用程序,但是需要注意的是如果使用了 Babel,则需要忽略编译后的文件:
{
test: /.js$/,
loaders: ['babel-loader'],
exclude: [
/node_modules/,
/compiled/, // folder that contains the compiled code (wasm and asmjs)
/.asm.js$/ // ignore all .asm.js files
],
}
3.asm-dom 的示例
3.1 使用 gccx 集成类 JSX
asm-dom 对类 JSX 语法的支持是通过 gccx 实现的,在使用之前需要导入它,然后就可以像 JSX 那样编写代码。列如下面的例子:
import gccx from 'gccx';
const code = `
#include "../asm-dom/asm-dom.hpp"
#include <emscripten/val.h>
#include <string>
using namespace asmdom;
int main() {
VNode* vnode = <h1>Hello world!</h1>;
// jsx语法
patch(
emscripten::val::global("document").call<emscripten::val>(
"getElementById",
std::string("root")
),
vnode
);
return 0;
};
`;
const compiled = gccx.parse(code);
// 编译代码为字符串
3.2 官方示例
下面是 asm-dom 官方提供的一个示例。
#include "asm-dom.hpp"
using namespace asmdom;
int main() {
Config config = Config();
init(config);
// 借助于 gccx,asm-dom 可以与类似 JSX 的语法一起使用
VNode* vnode = (
<div
onclick={[](emscripten::val e) -> bool {
emscripten::val::global("console").call<void>("log", emscripten::val("clicked"));
return true;
}}
>
<span style="font-weight: bold">This is bold</span>
and this is just normal text
<a href="/foo">I'll take you places!</a>
</div>
);
//插入到空的 DOM 元素,这会修改 DOM 作为副作用
patch(
emscripten::val::global("document").call<emscripten::val>(
"getElementById",
std::string("root")
),
vnode
);
// 不借助于 gccx的语法
VNode* newVnode = h("div",
Data(
Callbacks {
{"onclick", [](emscripten::val e) -> bool {
emscripten::val::global("console").call<void>("log", emscripten::val("another click"));
return true;
}}
}
),
Children {
h("span",
Data(
Attrs {
{"style", "font-weight: normal; font-style: italic"}
}
),
std::string("This is now italic type")
),
h(" and this is just normal text", true),
h("a",
Data(
Attrs {
{"href", "/bar"}
}
),
std::string("I'll take you places!")
)
}
);
// Second `patch` invocation
patch(vnode, newVnode);
// asm-dom 有效地将旧视图更新为新状态
return 0;
};
4.本文总结
随着 Web 的不断发展,WebAssembly 会在 Web 应用程序的开发中发挥越来越重大的作用,许多高级语言如:C++/C、Rust、Go、Zig、C#、PHP、Java等等都已经能将 WebAssembly 作为编译目标 。
同时在基于 WebAssembly 的高级语言 Web 框架层面也是层出不穷,列如 Rust生 态的Yew,Sycamore、percy、seed、MoonZoon 等等,又列如 Go 生态的 Vecty、Vugu、go-app、vue等等。这对于希望扩展技能并保持技术领先地位的开发人员是时候思考学习 WebAssembly 并解锁其威力了。
由于篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
参考资料
https://mbasso.github.io/asm-dom/
https://github.com/mbasso/asm-dom-boilerplate
https://github.com/mbasso/gccx
https://mbasso.github.io/asm-dom/docs/inline-example.html
https://www.geeksforgeeks.org/explain-dirty-checks-in-react-js/
封面图版权:作者 swapnil074 的《Explain dirty checks in React.js》




意义在哪里?生态在哪里?
没有一个好的string类,面向工程都白扯
好奇点,如何控制web页面本地存取文件?安全如何控制的!
造一个程序把网页套进去么?这个路绕得多了
rust不服,rust乃宇宙语,整个宇宙都是rust的
收藏了,感谢分享
最好的方式是用react写代码,编译成wsm
一直到都在互相渗透,互相竞争,看看Node. js为什么出来?技术战争本身是好事,会更进步的
不适合设计页面 适合一次性渲染的应用页面 频繁动态更新css样式的 慢的要死 我曾上过当 用wasm设计了一个表单设计器 慢的要死 又改成JS了 速度差别很大
C++还是专注于自己擅长的领域吧,Web不是未来
都好几年没更新了
c++ 后端框架有推荐吗?
都是用老外的轮子,哪天人家不给用了直接完蛋,跟阿里云一样,飘走了
不应该是Rust + Yew么?