如何在浏览器运行原生代码,我们为什么要这么做,以及这种做法对 JavaScript 和 Web 未来发展的意义

无论是哪一个浏览器 - Chrome、Firefox、Edge 或者是 Safari. 代码总是被浏览器的 JavaScript 引擎(只负责运行 JavaScript 代码)解析和执行。但不幸的是,JavaScript 的运行效率并不是总是令我们满意。这就是 WebAssembly 存在的意义。
WebAssembly是可运行于现代浏览器新的代码类型。其目的是 web 性能的提升。因为他是二进制数据数据体积并不会很大,因此可以被迅速加载和执行。我们无需编写 WebAssembly ,可以通过编写其他高级语言来获取它。
汇编通常是指类似于机器代码的人类可读语言。机器码是指处理器可以明白的一系列数据。

Assembly languages and machine code
每一门高级语言会被翻译和转换为机器码最后在处理器上运行。不同的处理器结构,需要不同类型的机器码,以及不同的汇编语言。

Compiling source code for different processor architectures
WebAssembly 并不是一门汇编语言,因为它不针对特定的机器实现。它用于浏览器,并且我们可以在浏览器中执行该段代码。我们无需关系我们的代码会在哪种机器上运行。

WebAssembly as an intermediary compiler target
WebAssembly 是一种用于概念机器的语言,可以在主流机器上运行。当浏览器下载到 WebAssembly 代码时浏览器会将代码快速转换为机器码。
下面的图就展示了 WebAssembly 的运行情况。它具有易于阅读的文本格式*(.wat)*,但二进制表示是您实际交付给浏览器的内容 (.wasm)。

WebAssembly textual and binary format
WebAssembly 使您能够做的是将 C、C++ 或 Rust 代码之类的东西编译成所谓的 WebAssembly 模块。您可以将其加载到您的 Web 应用程序中并从 JavaScript 调用它。
它不是 JavaScript 的替代品,它与 JavaScript 一起工作。

WebAssembly module in an application
为什么我们需要 WebAssembly
想想你需要在浏览器之外使用软件的情况:视频游戏、视频编辑、3D 渲染或音乐制作。这些应用程序进行大量计算并且需要高度的性能。这种性能很难从 JavaScript 中获得。
JavaScript 最初是一种简单的脚本语言,旨在为充满轻量级超文本文档的网络带来一些交互性。它被设计为易于学习和编写,但它并不是为了高效而设计的。多年来,浏览器在 JavaScript 解析 的方式上增加了优化,从而带来了重大的性能改进。
随着 JavaScript 的快速发展,您可以在浏览器中执行更多的操作。新的 API 带来了交互式图形、视频流、离线浏览等等。反过来,越来越多的丰富应用程序(以前仅限于本机)开始出现在 Web 上。今天,您可以轻松地从浏览器编辑文档和发送电子邮件,但在某些领域,JavaScript 性能仍然很困难。
电子游戏尤其具有挑战性,因为它们不仅要协调音频和视频,还经常需要协调物理和人工智能。能够有效地达到在网络上运行游戏的性能将为将[许多其他应用程序](WebAssembly: How and why)带入网络打开大门,而这正是 WebAssembly 的目标。
为什么 Web 如此吸引人
web 的美妙之处在于它就像魔法 —— 它可以在任何地方使用。**无需下载,无需安装。**只需单击一下,Web 应用程序就会在您需要时立即交付。比直接在计算机上下载和运行二进制文件更安全。因为浏览器已经建立了安全策略,可以防止在其中运行的代码干扰您的系统。在网络上共享非常简单 — 链接只是可点击的字符串,您可以将其放在任何地方。
它是唯一可以让您的应用程序在任何设备上访问的真正通用平台。这还允许您维护一个单一的代码库,使更新变得简单并确保每个用户都可以访问您的应用程序。
由于这些内置的功能和网络提供的交互性,我们从超文本和小型脚本语言一路发展到一个非常强大和流行的平台,其中充满了惊人的应用程序和功能。但直到现在,它仍然从根本上由同一种脚本语言提供支持,而这种语言从来没有真正设计为首先完成所有这些工作。
WebAssembly 带来了什么
这就是让 WebAssembly 如此特别并且非常适合 web 的原因:
- 高效
- 可移植
- 灵活
WebAssembly 是为高效而设计的。它的二进制文件比文本 JavaScript 文件小得多。由于它们的大小,它们的下载速度更快,这在慢速网络上尤其重要。
它们的解码和执行速度也更快。JavaScript 是一种动态类型语言,变量类型不需要预先定义,也不需要预先编译。这使得编写起来既简单又快速,但这也意味着 JavaScript 引擎还有很多工作要做。当代码在页面上执行时,它必须解析、编译和优化代码。
解析 JavaScript 涉及将纯文本转换为称为抽象语法树 (AST) 的数据结构,并将其转换为二进制格式。WebAssembly 以二进制形式提供,解码速度更快。它是静态类型的,因此与 JavaScript 不同,引擎不需要在编译期间推测将使用哪些类型。大多数优化发生在源代码编译期间,甚至在它进入浏览器之前。内存是手动管理的,就像在 C 和 C++ 之类的语言中一样,因此也没有垃圾收集。所有这些都提供了更好、更可靠的性能。WASM 二进制文件的执行时间仅比相同本机代码的执行慢 20%。

Relative time spent processing WebAssembly in JavaScript engine
设计 WebAssembly 的主要目标之一是可移植性。要在设备上运行应用程序,它必须与设备的处理器架构和操作系统兼容。这意味着为您想要支持的操作系统和 CPU 架构的每种组合编译源代码。使用 WebAssembly 只需一个编译步骤,您的应用程序将在每个现代浏览器中运行。

Compiling native code to run on different platforms vs. compiling to WebAssembly
您不仅可以将自己的应用程序移植到 Web 上,还可以将现有的大量 C++ 库和开源应用程序移植到 Web 上。它是一种几乎所有平台都支持的语言,包括 iOS 和 Android。借助 WebAssembly,它可以用作跨 Web 和移动部署的通用语言。
WebAssembly 最令人兴奋的地方在于它为 Web 编写带来了更大的灵活性。到目前为止,JavaScript 一直是 Web 浏览器中唯一完全支持的语言。使用 WebAssembly,Web 开发人员将能够选择其他语言,更多的开发人员将能够为 Web 编写代码。JavaScript 仍然是大多数用例的最佳选择,但现在,当您确实需要提升时,可以选择偶尔使用专门的语言。UI 和应用程序逻辑之类的部分可以使用 JavaScript,核心功能在 WebAssembly 中。在优化现有 JS 应用程序的性能时,可以用更适合该问题的语言重写瓶颈。
当前完全支持的语言是 C、C++ 和 Rust,但还有许多其他语言正在开发中,包括 Kotlin 和 .NET,这两种语言都已经提供了实验性支持。
运作方式
您需要一个将源代码编译为 WebAssembly 的工具。一种方法是使用可以设置为使用不同语言的经验丰富的模块化编译器工具链 LLVM。为了编译 C 和 C++,你可以使用一个基于 LLVM 的更简单的工具 Emscripten。Rust Nightly 有自己的编译器 rustc,可以直接输出 WebAssembly。
如果你有一个用 C 编写的“Hello world”,这个 Emscripten 命令将生成在浏览器中运行它所需的文件。你得到的是一个 WebAssembly 模块以及 HTML 和 JS 文件。
emcc hello.c -s WASM=1 -o hello.html

Compiling C/C++ code to WebAssembly with Emscripten
你需要 HTML 和 JS 文件,因为 WebAssembly 不能直接访问任何平台 API —— DOM、WebGL、WebAudio 等。要使用其中任何一个,甚至要在页面上显示 WebAssembly 代码的输出,您都必须通过 JavaScript。Emscripten 创建 JS 代码来设置您的模块并使与 Web API 通信成为可能。HTML 文件加载该 JS 并在 textarea 或 canvas 元素中显示 WebAssembly 输出。
您可以将 WebAssembly 二进制文件视为常规应用程序模块:浏览器可以获取、加载和执行它们。它们具有导入和导出功能,允许您像处理 JavaScript 对象一样使用它们。您可以在 JavaScript 代码中调用 WebAssembly 函数,也可以在 WebAssembly 模块中调用 JavaScript 函数。
它只有四种基本类型,它们都是数字 — 整数和浮点数(i32、i64、f32 和 f64)。这意味着在 JavaScript 和 WebAssembly 之间传递更复杂的数据类型并不简单。例如,如果你想传递一个字符串,你必须将它编码成一个数字数组,然后传递一个指向它的指针。它只能从自己的线性内存中读写,不能直接访问外部 JavaScript 变量,除非它们被复制到内存中或通过调用堆栈传递。
现在通过 JavaScript 进行大量调用并不是很快,因为引擎每次都必须做一些设置工作。这在未来可能会改变,但现在好的建议是将 WebAssembly 视为一个独立运行的系统,并使用它来卸载大量工作。
如果您想在没有任何设置的情况下试用它,请前往 webassembly.studio 或 WebAssembly Explorer.
使用?
当然!
它就在这里,而且是真实的。 WebAssembly 在去年支持所有主流浏览器。目前支持全球 74.93% 的用户,甚至 82.92% 的桌面用户。作为旧浏览器的后备方案,您可以使用 Emscripten 编译为 asm.js — 仅使用数字(没有字符串、对象等)的 JavaScript 子集。它是一种直接导致创建 WebAssembly 的格式,它在网络上被广泛使用,例如用于将照片上传到 Facebook 时的图像压缩以及在 Adobe 的 Lightroom 中进行图像编辑。

Browsers that support WebAssembly
现实世界中已经有一些非常令人兴奋的 WebAssembly 示例。
我提到视频游戏是 WebAssembly 的一个重要目标,Unity 和虚幻引擎 4 都已经有了工作演示。你可以玩坦克游戏! WebAssembly 在 Unity 引擎中运行,Epic 有一个简短的 WebAssembly 在线演示。
Figma 是一种在浏览器中运行的界面设计工具,允许设计人员轻松协作和共享他们的工作。它主要用 C++ 编写,并有一个 2D WebGL 渲染引擎,可以处理非常大的文档。最初,他们使用 asm.js 为 Web 编译 C++ 代码。通过切换到 WebAssembly,无论文档大小如何,它们的加载时间都提高了 3 倍以上。
AutoCAD 是一种设计软件,主要用于各种工程领域,用于绘制平面图、电路、管道设计等图纸。它是用 C++ 编写的,已经存在了 35 年,比网络本身还要长。由于 WebAssembly,它现在可以作为 Web 应用程序使用,而无需用另一种语言重写如此庞大的代码库。
你可以期待越来越多的应用程序使用 WebAssembly,而且网上也有一些有趣的演示,比如视频编辑器、光线追踪器和在浏览器中运行的面部识别算法。
未来 ?
浏览器已经在开发新功能。对线程和垃圾收集的支持即将到来,这将使 WebAssembly 成为更适合编译 Java、C# 和 Go 等语言的目标。重要的目标之一是创建支持源映射的调试工具,这将允许开发人员轻松地将 WebAssembly 映射到他们的源代码。
**JavaScript 仍将在 Web 开发中占有一席之地。**它是一门很棒的语言,足够灵活,几乎可以构建任何东西,而那些它无法很好处理的空白现在可以用 WebAssembly 来填补。将 JavaScript 编译为 WebAssembly 是不可能的,而且这真的没有多大意义,因为浏览器已经被设计为直接使用 JS 并最大化其性能。
但是,即使您继续只使用 JavaScript,您仍然可以通过改进的库和框架从 WebAssembly 及其带来的速度提升中受益。很快,您就可以像使用 的 WebAssembly 实现,并且也有可能在 WebAssembly 中实现一些 React 功能。
未来可期!