Stimulus — это минималистичный JavaScript-фреймворк, который не заменяет HTML, а расширяет его поведение с помощью контроллеров. Взаимодействие с WebAssembly (Wasm) в контексте Stimulus позволяет создавать высокопроизводительные веб-приложения, сохраняя чистую и понятную структуру кода.
Контроллеры являются центральным элементом фреймворка. Они представляют собой классы, где описываются:
static targets.data-action.connect(), disconnect() и
initialize().Пример базового контроллера:
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["output"];
connect() {
console.log("Контроллер подключен");
}
UPDATE(event) {
this.outputTarget.textContent = event.target.value;
}
}
Здесь outputTarget указывает на DOM-элемент с атрибутом
data-target="controllerName.output".
WebAssembly — это бинарный формат для выполнения кода в браузере с почти нативной производительностью. Основные шаги интеграции:
.wasm
файл.Пример загрузки модуля:
const response = await fetch("module.wasm");
const buffer = await response.arrayBuffer();
const module = await WebAssembly.instantiate(buffer);
const { exportedFunction } = module.instance.exports;
exportedFunction();
Для полной интеграции создается контроллер, который загружает и инициализирует Wasm-модуль при подключении к DOM:
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["input", "output"];
async connect() {
const response = await fetch("/wasm/module.wasm");
const buffer = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(buffer);
this.wasmModule = instance;
}
calculate() {
const value = parseInt(this.inputTarget.value, 10);
const result = this.wasmModule.exports.compute(value);
this.outputTarget.textContent = result;
}
}
Ключевые моменты:
.wasm происходит один раз при
connect().Wasm использует собственную память (WebAssembly.Memory).
Для взаимодействия с массивами и строками требуется создание
TypedArray:
const memory = new Uint8Array(this.wasmModule.exports.memory.buffer);
memory.se t([1, 2, 3, 4], 0);
const result = this.wasmModule.exports.sum(0, 4);
Важно учитывать:
Stimulus позволяет связывать события DOM напрямую с методами, которые взаимодействуют с Wasm:
<input data-controller="calculator" data-action="input->calculator#calculate" data-calculator-target="input">
<div data-calculator-target="output"></div>
В контроллере calculate берет значение из поля ввода,
передает его в функцию WebAssembly и обновляет DOM. Такой подход
гарантирует:
Для больших модулей или длительных операций загрузка может быть
асинхронной. В Stimulus это решается через async connect()
и промисы:
async connect() {
this.outputTarget.textContent = "Загрузка...";
const response = await fetch("/wasm/heavy_module.wasm");
const buffer = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(buffer);
this.wasmModule = instance;
this.outputTarget.textContent = "Готово";
}
Преимущества:
try/catch.Можно подключать несколько .wasm модулей в одном
контроллере, разделяя их по функциональности:
async connect() {
const modules = await Promise.all([
fetch("/wasm/math.wasm").then(r => r.arrayBuffer()).then(WebAssembly.instantiate),
fetch("/wasm/string_utils.wasm").then(r => r.arrayBuffer()).then(WebAssembly.instantiate)
]);
this.mathModule = modules[0].instance;
this.stringModule = modules[1].instance;
}
Такой подход:
Для передачи строк или структур используют:
TypedArray с
указанием смещения и длины.Пример передачи строки:
function passStringToWasm(str, wasmInstance) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
const ptr = wasmInstance.exports.allocate(bytes.length);
const memory = new Uint8Array(wasmInstance.exports.memory.buffer);
memory.set(bytes, ptr);
return ptr;
}
Экспортированные функции модуля могут обрабатывать данные напрямую из памяти, что позволяет достигать высокой скорости.
Использование WebAssembly не ограничивается вычислениями. Модуль может возвращать данные, которые контроллер Stimulus использует для обновления интерфейса:
updateUI() {
const dataPtr = this.wasmModule.exports.generateData();
const memory = new Uint8Array(this.wasmModule.exports.memory.buffer);
const result = memory.slice(dataPtr, dataPtr + 100); // пример
this.outputTarget.textContent = Array.from(result).join(",");
}
Подход обеспечивает:
Stimulus в сочетании с WebAssembly открывает путь к созданию высокопроизводительных веб-приложений с минимальной нагрузкой на JavaScript, сохраняя чистую архитектуру и простую работу с DOM.