Flutter 使用 Skia 构建,这是一个用 C++ 编写的 2D 图形库。 Flutter 的 Skia 引擎使其能够跨各种平台(包括 Web)创建可移植且高性能的应用程序。

大多数网络浏览器都理解 JavaScript 语言; 但是,Skia C++ API 可以通过 WebAssembly 在 Web 浏览器上运行。 WebAssembly 允许原生代码在浏览器中运行,方法是将其编译成浏览器可以理解的语言。

在本教程中,我们将介绍 WebAssembly 并了解它的好处。 我们还将了解 Flutter 代码如何与 WebAssembly 绑定进行交互。

  • 什么是 WebAssembly?

  • 我们为什么要使用 WebAssembly?

  • Dart 和 WebAssembly 的互操作性

  • 在 Flutter Web 应用程序中使用 WebAssembly

    • 使用 AssemblyScript 生成 WebAssembly 模块

    • 在 Flutter 应用中使用 WebAssembly 模块

什么是 WebAssembly?

WebAssembly (Wasm) 是一种低级语言,以接近本机的性能运行。 它是一种具有紧凑二进制格式的虚拟堆栈机器,旨在成为高级语言的编译目标。

WebAssembly 使工程师能够使用 C、C++、Rust 和其他在浏览器中运行的高级语言编写代码。 Wasm 将用高级语言编写的代码编译为 WebAssembly 模块。 然后将它们加载到 Web 应用程序中并使用 JavaScript 调用。

浏览器可以快速将 WebAssembly 模块转换为任何机器的指令。

我们为什么要使用 WebAssembly?

JavaScript 一直是浏览器理解的主要语言。 但是,在运行 3D 游戏等资源密集型应用程序时,JavaScript 往往会变得迟缓。 对于此类应用程序,需要接近原生的体验。 这就是 Wasm 的用武之地。

WebAssembly 与 JavaScript 一起工作,为我们的应用程序提供接近原生的速度。 由于模块体积小,Wasm 加载和执行速度更快,从而在 Web 上实现了高性能应用程序。

Wasm 使我们能够构建快速、高性能、可移植和内存安全的应用程序。 这是一个开放标准,旨在在其他平台上运行,而不仅仅是网络。 许多流行的语言至少对 WebAssembly 有一些支持。


超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →


Dart 和 WebAssembly 的互操作性

Dart Web 平台使 Dart 代码能够在 JavaScript 支持的平台上编译和运行。 我们还可以在 Dart 代码中调用现有的 JavaScript 代码,这可以通过 js包裹。

从 Dart 代码调用 JavaScript 代码和从 JavaScript 代码调用 Dart 代码的能力称为“Dart-JavaScript 互操作性”。

这 js包提供注释和函数,让我们指定 Dart 代码如何与 JavaScript 代码连接。 JavaScript API 具有 WebAssemblyobject,所有 WebAssembly 相关函数的命名空间,允许加载 WebAssembly 模块、创建新的内存和表实例以及处理 WebAssembly 错误。

WebAssembly 有两种文件格式:

  • .wasm: 包含二进制的汇编代码,是可执行文件

  • .wat: 包含人类可读的文本格式 .wasm文件并编译为 .wasm. 它仅用于编辑或调试

编写 WebAssembly 代码可能很痛苦。 大多数语言都支持从我们的源代码生成 Wasm 模块,然后我们可以使用提供的绑定加载和调用这些模块。

我们可以通过 Dart-web 代码中的 JavaScript WebAssembly 对象使用 WebAssembly,使用 js绑定。

为了利用 js在我们的 Dart 代码中绑定,用 @JS并添加 external它的关键字:

@JS('WebAssembly.instantiate')
external Object instantiate(Object bytesOrBuffer, Object import);

在 Flutter Web 应用程序中使用 WebAssembly

我们可以使用各种语言来创建可以加载到 Flutter 应用程序中的 Wasm 模块。 在本文中,我们将使用 AssemblyScript(一种用于 WebAssembly 的类似 TypeScript 的语言 )来生成 Wasm 模块。

使用 AssemblyScript 生成 WebAssembly 模块

首先,我们需要安装 Node.js。 您可以从 Node 的官方站点 下载 Node 。

接下来,安装 npx,一个 npm 包运行器,使用以下命令:

npm i -g npx

创建一个新目录和一个 package.json文件。 然后安装 assemblyscript和 assemblyscript/loader使用以下命令:


来自 LogRocket 的更多精彩文章:

  • 不要错过 The Replay 来自 LogRocket 的精选时事通讯

  • 使用 React 的 useEffect 优化应用程序的性能

  • 之间切换 在多个 Node 版本

  • 了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画

  • 探索 Tauri ,一个用于构建二进制文件的新框架

  • 比较 NestJS 与 Express.js

  • 发现 TypeScript 领域中使用的流行 ORM


mkdir wasm && cd wasm
npm init
npm i --save-dev assemblyscript
npm i --save @assemblyscript/loader

接下来,运行以下命令来搭建一个新项目:

npx asinit .

该命令将生成 assembly和 build文件夹。 我们将在 index.ts文件并将生成的 Wasm 代码放在 build文件夹。

接下来,将以下方法添加到 index.ts文件。 这 plusOne函数向计数器加一,而 minusOne函数从计数器中减一。

// The entry file of your WebAssembly module.
export function plusOne(n: i32): i32 {
  return n+1;
}
export function minusOne(n:i32):i32{
  return n - 1;
}

通过运行生成 WebAssembly 模块 npm run asbuild在根目录中。 该命令生成 .wasm和 .wat中的文件 build文件夹。 我们将利用 release.wasm文件在我们的 Flutter 应用程序中。

在 Flutter 应用中使用 WebAssembly 模块

要使用生成的 Wasm 模块,我们将添加 release.wasm文件作为我们的 Flutter 应用程序的资产。 我们还将使用 wasm_interop 包,它为我们处理 JavaScript WebAssembly 绑定,并使我们能够通过调用公开的方法与 WebAssembly 交互。

首先,在里面新建一个 Flutter 应用 wasm文件夹使用 flutter create .命令。 然后,新建一个 assets/wasm文件夹并添加生成的 release.wasm文件。 更新 pubspec.yaml文件以包含资产文件夹和 wasm_interop包裹:

dependencies:
  wasm_interop: ^2.0.1
flutter:
  assets:
    - assets/wasm/

跑 flutter pub get添加依赖项。

更新 MyHomePage小部件在 main.dart文件如下图:

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() {
  }
  void _decrementCounter() {
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You current count is:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
            const SizedBox(
              height: 100,
            ),
            Wrap(
              spacing: 100,
              children: [
                ElevatedButton(
                    onPressed: _incrementCounter,
                    child: const Text('Increment')),
                ElevatedButton(
                    onPressed: _decrementCounter,
                    child: const Text('Decrement'))
              ],
            )
          ],
        ),
      ),
    );
  }
}

跑 flutter run -d chrome在根目录中以在 Chrome 上提供应用程序。 我们的应用程序具有 增量 和 减量按钮。 与Wasm 函数挂钩的

创建一个新的 wasm_loader.dart文件并添加 WasmLoader班级。 这 WasmLoader类包含我们的 Dart 到 Wasm 互操作性逻辑。

import 'package:flutter/services.dart' show rootBundle;
import 'package:wasm_interop/wasm_interop.dart';
​
class WasmLoader {
  WasmLoader({required this.path});
  late Instance? _wasmInstance;
  final String path;
  Future<bool> initialized() async {
    try {
      final bytes = await rootBundle.load(path);
      _wasmInstance = await Instance.fromBufferAsync(bytes.buffer);
      return isLoaded;
    } catch (exc) {
      // ignore: avoid_print
      print('Error on wasm init ${exc.toString()}');
    }
    return false;
  }
  bool get isLoaded => _wasmInstance != null;
  Object callfunction(String name, int input) {
    final func = _wasmInstance?.functions[name];
    return func?.call(input);
  }
}

灵动鸟App,苹果14系列灵动岛效果插件,完美体验iPhone14Pro!

上面的代码片段执行以下操作:

  • 在类构造函数中需要一个 Wasm 模块路径

  • 将 Wasm 模块文件加载到 initialized方法

  • 使用异步编译和实例化 Wasm 代码 Instance.fromBufferAsync方法。 该方法利用 WebAssembly.instantiate()JavaScript API

  • 返回 isLoaded中的状态 initialized成功初始化 Wasm 代码时的方法

  • 添加一个 callfunction需要一个函数名和参数的方法,然后调用该函数

最后,更新 MyHomePage小部件在 main.dart文件以使用 WasmLoader:

  late WasmLoader loader;
  int _counter = 0;
​
  @override
  void initState() {
    super.initState();
    _init();
  }
​
  Future<void> _init() async {
    loader = WasmLoader(path: 'assets/wasm/release.wasm');
    final isLoaded = await loader.initialized();
    if (isLoaded) {
      setState(() {});
    }
  }
​
  void _incrementCounter() {
    _counter = loader.callfunction('plusOne', _counter) as int;
    setState(() {});
  }
​
  void _decrementCounter() {
    _counter = loader.callfunction('minusOne', _counter) as int;
    setState(() {});
  }

上面的代码片段执行以下操作:

  • 创建一个实例 WasmLoader随着我们的道路 .wasm文件

  • 初始化 WasmLoader并在初始化后更新应用程序的状态

  • 更新 _counter属性与调用的结果 plusOne和 minusOneWasm 模块中的函数

重新运行应用程序并单击 递增 和 递减 按钮 — 计数器会相应更新。 你在 Flutter 应用中成功使用了 WebAssembly 代码!

结论

在本教程中,我们讨论了 WebAssembly 并研究了它在提高应用程序性能方面的一些好处。 我们还研究了 Flutter 如何与 JavaScript 绑定进行交互。 最后,我们使用 AssemblyScript 生成连接到 Flutter Web 应用程序的 WebAssembly 模块。

有了它,您现在可以在 Flutter Web 应用程序中使用 WebAssembly 并提高它们的性能。 本文中的所有代码都可以在 GitHub 上找到。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐