WebAssembly (WASM) предоставляет возможность запускать высокопроизводительный код в браузере, а фреймворк Fresh на JavaScript упрощает интеграцию таких модулей в веб-приложения. Работа с существующими WASM библиотеками требует понимания структуры модуля, способов его импорта и взаимодействия с JavaScript.
Существует два основных способа подключения WASM-библиотеки: динамический и статический импорт.
Динамический импорт
const wasmModule = await import("./my_module.wasm");
Этот метод удобен, когда модуль загружается по запросу, например, при взаимодействии пользователя с интерфейсом. Динамический импорт возвращает объект, содержащий экспортированные функции WASM, которые можно вызывать напрямую.
Статический импорт
import wasmModuleUrl from "./my_module.wasm?url";
const response = await fetch(wasmModuleUrl);
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
Статический импорт подходит для случаев, когда модуль нужен при инициализации страницы. Этот подход позволяет получить полный контроль над памятью и экспортами модуля.
WASM использует собственную память, поэтому часто необходимо
создавать объекты типа Uint8Array для передачи данных между
JavaScript и модулем.
const memory = new WebAssembly.Memory({ initial: 256, maximum: 512 });
const importObject = { env: { memory } };
const { instance } = await WebAssembly.instantiate(bytes, importObject);
Передача строк или сложных структур требует преобразования данных:
function passStringToWasm(str, memory, malloc) {
const encoder = new TextEncoder();
const encoded = encoder.encode(str);
const ptr = malloc(encoded.length);
const memBuffer = new Uint8Array(memory.buffer, ptr, encoded.length);
memBuffer.set(encoded);
return ptr;
}
Экспортированные функции WASM доступны через объект
instance.exports. При вызове функций необходимо учитывать
типы данных — WASM поддерживает только базовые числовые типы
(i32, i64, f32,
f64).
const result = instance.exports.add(5, 3); // пример вызова функции add
Если функция возвращает указатель на память, необходимо преобразовать данные обратно в Jav * aScript:
function getStringFromWasm(ptr, memory, length) {
const memBuffer = new Uint8Array(memory.buffer, ptr, length);
const decoder = new TextDecoder();
return decoder.decode(memBuffer);
}
В Fresh используется рендеринг на сервере и клиенте, поэтому WASM можно подключать как в компоненте на сервере, так и на клиентской части. Для клиента важно загружать модуль асинхронно, чтобы не блокировать интерфейс:
export default function MyComponent() {
const [result, setResult] = useState(null);
useEffect(() => {
(async () => {
const wasm = await import("../wasm/my_module.wasm");
setResult(wasm.add(10, 20));
})();
}, []);
return <div>Результат: {result}</div>;
}
Для серверного использования WASM модуль можно импортировать синхронно в обработчике рендеринга страницы:
const { instance } = await WebAssembly.instantiate(
await Deno.readFile("./wasm/my_module.wasm")
);
const value = instance.exports.compute(42);
free из модуля, если они
предоставлены.Многие популярные библиотеки имеют WASM-сборки, например,
ffmpeg.wasm, opencv.js,
zstd.wasm. Для их интеграции необходимо:
instance.exports или
предоставленные обёртки JavaScript.ffmpeg.wasm с Freshimport { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
const ffmpeg = createFFmpeg({ log: true });
export default function VideoProcessor({ file }) {
useEffect(() => {
(async () => {
await ffmpeg.load();
ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(file));
await ffmpeg.run('-i', 'input.mp4', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const url = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
console.log(url);
})();
}, [file]);
return <div>Видео обрабатывается...</div>;
}
Эта схема показывает, как существующие WASM-библиотеки могут быть полностью интегрированы в Fresh, сохраняя высокую производительность и асинхронность работы.