翻译:Using Web Assembly to speed up your Angular Application
作者:Lukas Marx
Web程序集可以由多种语言生成,如C、C++或Rust。甚至还有一个将TypeScript编译成Web程序集的实验项目。对于本教程,我们将保持其简单性并编译一个非常基本的C程序。
创建新的Angular项目
我们可以使用angular cli执行此操作:
ng new angular-wasm
我们还需要Web程序集JavaScript API的类型。让我们安装它们:
npm install @types/webassembly-js-api --dev --save
将C编译为WASM
在编译任何东西之前,我们需要先创建C程序。为此,请继续在项目根目录中创建一个名为“wasm”的文件夹。在该文件夹中,创建一个名为“fibonacci.c”的文件。没错!我们的示例将计算任何数字的斐波那契数,因为这是一项非常昂贵的任务。尤其是对于更高的数字。
int fibonacci(int n)
{
if (n == 0 || n == 1)
return n;
else
return (fibonacci(n - 1) + fibonacci(n - 2));
}
向JavaScript公开函数
我们在这里要做的是使斐波那契函数可以从JavaScript代码中调用。因此,我们需要用一种特殊的装饰物来标记它。对于这个decorator,我们需要包括emscripten.h之后,程序如下所示:
#include <emscripten.h>
int EMSCRIPTEN_KEEPALIVE fibonacci(int n)
{
if (n == 0 || n == 1)
return n;
else
return (fibonacci(n - 1) + fibonacci(n - 2));
}
将C编译为Web程序集(WASM)
要使用此函数,我们需要先将其编译为Web Assembly,因为浏览器不理解C。为此,我们使用之前安装的编译器。执行此操作的命令如下所示:
emcc wasm/fibonacci.c -Os -s WASM=1 -s MODULARIZE=1 -o wasm/fibonacci.js
Os选项定义了执行的优化级别。我们这里用的是很高的等级。
除了WASM之外,编译器还将生成一个JavaScript文件。该文件包含一些胶水代码,用于处理WASM和JavaScript之间的通信。使用module=1选项,我们告诉编译器将代码包装到模块中。这样,在我们的angular应用程序中消费就容易多了。
该命令的结果应该是两个新文件:fibonacci.js和fibonacci.wasm。
在Angular Service中包装Web Assembly
现在我们可以在角度中使用WASM函数。这些实用风格的函数的最佳位置是在单独的服务中。因此,我们将创建一个名为WasmService的新服务。
要让angular cli为我们生成该服务,请使用以下命令:
ng generate service wasm
不幸的是,使用WASM模块并不像使用普通JavaScript模块那么容易。
我们不仅需要导入WASM JavaScript粘合代码模块
src/app/wasm.service.ts
import * as Module from './../../wasm/fibonacci.js'
但是还必须使用文件加载器导入was文件本身
import '!!file-loader?name=wasm/fibonacci.wasm!../../wasm/fibonacci.wasm';
不幸的是,这样WASM文件就不包括在捆绑包中,而是作为单独的文件提供。
实例化Web程序集
要在运行时使用WASM文件,必须从提供的URL获取该文件并将其转换为字节数组。
为此,我们在服务内部创建了一个名为“InstanceWasm”的新方法。
src/app/wasm.service.ts
private async instantiateWasm(url: string){
// fetch the wasm file
const wasmFile = await fetch(url);
// convert it into a binary array
const buffer = await wasmFile.arrayBuffer();
const binary = new Uint8Array(buffer);
// create module arguments
// including the wasm-file
const moduleArgs = {
wasmBinary: binary,
onRuntimeInitialized: () => {
// TODO
}
};
// instantiate the module
this.module = Module(moduleArgs);
}
请注意,我们还需要在服务上创建一个名为“module”的属性。此属性将包含模块及其所有函数,包括斐波那契函数。
现在,我们可以在服务的构造函数中调用该方法:
src/app/wasm.service.ts
constructor() {
this.instantiateWasm('wasm/fibonacci.wasm');
}
为了方便地将斐波那契函数提供给角度分量,我们将在服务中创建一个同名的方法。
在该方法内部,我们只是调用WASM函数。
src/app/wasm.service.ts
public fibonacci(input: number): number{
return this.module._fibonacci(input)
}