WebAssembly dalam TypeScript
Artikel ini menerangkan tentang WebAssembly dalam TypeScript.
Kami akan menerangkan kaedah praktikal dan mudah difahami untuk mengintegrasikan TypeScript dan WebAssembly.
YouTube Video
WebAssembly dalam TypeScript
WebAssembly (Wasm) ialah runtime format binari yang berjalan hampir pada kelajuan asli dalam pelayar. Dengan memanggil Wasm dari TypeScript, anda boleh menggunakan proses yang memerlukan pengiraan tinggi dan pustaka asli sedia ada yang ditulis dalam C/C++ atau Rust secara berkesan.
Aliran Pelaksanaan Asas
Di sini, kami akan menerangkan aliran pelaksanaan asas untuk Wasm. TypeScript (atau pelayar) mengambil fail .wasm, mewujudkannya (instantiate), dan memanggil fungsi yang dieksport.
-
- Cipta binari .wasm menggunakan AssemblyScript, Rust, C++, atau sediakan yang sedia ada.
-
- Ambil fail .wasm dalam TypeScript (atau dalam pelayar) dan instantiate secara segerak atau tidak segerak.
-
- Panggil fungsi yang dieksport dan kongsi memori menggunakan
WebAssembly.Memoryjika perlu.
- Panggil fungsi yang dieksport dan kongsi memori menggunakan
WebAssembly.instantiateStreaming
Seterusnya, kami akan menunjukkan contoh asas pemuatan fail Wasm dan memanggil fungsi yang dieksport. Pelayar perlu menyokong instantiateStreaming.
Kod berikut ialah contoh mendapatkan simple.wasm dari pelayan dan memanggil fungsi add.
1// TypeScript: load-and-call.ts
2// Fetch and instantiate a wasm module and call its exported `add` function.
3async function run() {
4 const response = await fetch('http://localhost:3000/simple.wasm');
5 // Use instantiateStreaming when available for efficiency.
6 const { instance } = await WebAssembly.instantiateStreaming(response, {});
7 // @ts-ignore
8 const result = instance.exports.add(2, 3);
9 console.log('2 + 3 =', result);
10}
11run().catch(console.error);- Fungsi di dalam Wasm disimpan dalam
instance.exports. - Oleh kerana TypeScript tidak menerima maklumat jenis, anda perlu menggunakan
@ts-ignoreatau mencipta definisi jenis anda sendiri.
Aliran Kerja Menggunakan AssemblyScript
AssemblyScript membolehkan anda menulis Wasm dalam sintaks yang serupa dengan TypeScript, menjadikannya pilihan yang mudah diakses untuk pembangun TypeScript. Di sini, kami menyediakan satu fungsi ringkas, membinanya ke dalam .wasm dan .d.ts, serta memanggilnya dari TypeScript.
1// assembly/index.ts (AssemblyScript)
2// npm install --save-dev assemblyscript
3
4// Export a simple function to add two integers.
5export function add(a: i32, b: i32): i32 {
6 return a + b;
7}- Dengan menggunakan
asc(penyusunAssemblyScript), anda boleh menjana fail.wasmdan, jika perlu, fail takrifan jenis.d.ts. Untuk mencubanya secara tempatan, pasangassemblyscriptdengan npm dan bina.
1# build commands
2# npm install --save-dev assemblyscript
3npx asc assembly/index.ts -o build/simple.wasm -t build/simple.wat --bindings esm --exportTable --sourceMap
4
5# optionally generate d.ts with --exportRuntime or use as-bind / loader toolsBerikut ialah contoh pengambilan dan pemanggilan dari sisi TypeScript.
1// ts client that loads AssemblyScript-generated wasm
2async function runAssemblyScript() {
3 const res = await fetch('http://localhost:3000/build/simple.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 console.log('AssemblyScript add:', instance.exports.add(10, 7));
7}
8runAssemblyScript().catch(console.error);- AssemblyScript memerlukan pengendalian model memori dan rentetan yang teliti, tetapi amat mudah digunakan untuk pengiraan nombor asas.
Rust + wasm-bindgen (Pilihan berkuasa dan biasa digunakan)
Bahagian ini menerangkan aliran kerja menulis Wasm dalam Rust dan menghubungkannya dengan JavaScript atau TypeScript menggunakan wasm-bindgen. Di sini, kami menggunakan fungsi Fibonacci ringkas sebagai contoh untuk menunjukkan cara mengimport modul yang dijana sebagai modul ES.
Eksport fungsi dari sisi Rust menggunakan wasm-bindgen.
1// src/lib.rs (Rust)
2// install wasm-pack from https://drager.github.io/wasm-pack/installer/
3use wasm_bindgen::prelude::*;
4
5// Export a function to JavaScript using wasm-bindgen.
6#[wasm_bindgen]
7pub fn fib(n: u32) -> u32 {
8 if n <= 1 { return n; }
9 let mut a = 0;
10 let mut b = 1;
11 for _ in 2..=n {
12 let tmp = a + b;
13 a = b;
14 b = tmp;
15 }
16 b
17}- Apabila anda bina dengan
wasm-packatau CLIwasm-bindgen, definisi jenis untuk TypeScript dan pembungkus JS dijana, membolehkan anda terus mengimportnya sebagai ESM.
1# build with wasm-pack
2# install wasm-pack from https://drager.github.io/wasm-pack/installer/
3wasm-pack build --target nodejs --out-dir pkgDi pihak TypeScript, import dan gunakan modul ES dari pkg.
1// Node.js: import WASM module built with --target web
2// import init, { fib } from '../pkg/my_wasm_module.js';
3// Node.js: import WASM module built with --target nodejs
4import wasm from '../pkg/my_wasm_module.js';
5
6async function run() {
7 //await init(); // --target web
8 console.log('fib(10)=', wasm.fib(10));
9}
10
11run().catch(console.error);wasm-packmenjana pembungkus JavaScript dan definisi jenis.d.ts, memudahkan penggunaan dari TypeScript. Sila ambil perhatian bahawa apabila anda menetapkanwebuntuk pilihan--targetdalam perintahwasm-pack, inisialisasi secara asinkron diperlukan.
Contoh Pengkongsian Memori Dunia Sebenar: Pemindahan dan Pemprosesan Array (Peringkat Rendah)
Apabila bertukar data dalam jumlah besar dengan Wasm, pengkongsian ArrayBuffer untuk pertukaran data yang cekap adalah penting. Di sini kami tunjukkan contoh menggunakan AssemblyScript, tetapi prinsip yang sama juga terpakai untuk wasm-bindgen Rust.
Di pihak AssemblyScript, sediakan fungsi eksport untuk menulis ke memori. Sebagai contoh, fungsi untuk mendarab dua setiap elemen dalam array akan kelihatan seperti berikut.
1// assembly/array_ops.ts (AssemblyScript)
2// Square values in place in the wasm linear memory starting at `ptr` for `len` elements.
3export function square_in_place(ptr: usize, len: i32): void {
4 // Treat memory as a pointer to 32-bit integers.
5 for (let i = 0; i < len; i++) {
6 let offset = ptr + (i << 2); // i * 4 bytes
7 let value = load<i32>(offset);
8 store<i32>(offset, value * value);
9 }
10}Untuk menentukan tetapan memori yang digunakan oleh AssemblyScript, sediakan asconfig.json berikut.
1{
2 "options": {
3 "memoryBase": 0,
4 "importMemory": false,
5 "initialMemory": 1,
6 "maximumMemory": 10
7 }
8}1 npx asc assembly/array_ops.ts -o build/array_ops.wasm -t build/array_ops.wat --bindings esm --exportTable --sourceMap- Untuk memanggil fungsi ini, anda perlu menyalin
ArrayBufferke ruang memori Wasm dan menghantar penunjuk.
Di bawah ialah contoh penggunaan WebAssembly.Memory dalam TypeScript untuk menyalin data dan memanggil fungsi.
1// TypeScript: use memory to pass array to wasm
2async function runArrayOps() {
3 const res = await fetch('http://localhost:3000/build/array_ops.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 const memory: WebAssembly.Memory = instance.exports.memory;
7 // Create a view into wasm memory.
8 const i32View = new Int32Array(memory.buffer);
9
10 // Example data
11 const input = new Int32Array([1, 2, 3, 4]);
12 // Choose an offset (in i32 elements) to copy data to (simple example: at index 0).
13 const offset = 0;
14 i32View.set(input, offset);
15
16 // Call wasm function: ptr in bytes, len in elements
17 // @ts-ignore
18 instance.exports.square_in_place(offset * 4, input.length);
19
20 // Read back result
21 const result = i32View.slice(offset, offset + input.length);
22 console.log('squared:', result);
23}
24runArrayOps().catch(console.error);memory.bufferialah memori linear yang dikongsi; meminimumkan salinan dapat meningkatkan kelajuan pemprosesan sebanyak mungkin. Ambil perhatian juga bahawa penunjuk merujuk kepada kedudukan dalam bait, manakalaTypedArraydiurus berdasarkan bilangan elemen, jadi berhati-hati agar tidak mengelirukan perbezaan ini.
Pengendalian Selamat Jenis: Sediakan Definisi Jenis TypeScript
Eksport Wasm ialah objek JavaScript, jadi menyediakan definisi jenis pada bahagian TypeScript akan memudahkan pembangunan. Berikut ialah contoh ringkas fail definisi jenis.
Berikut menunjukkan definisi jenis minimum yang boleh anda cipta secara manual sebagai simple.d.ts.
1// simple.d.ts
2export function add(a: number, b: number): number;
3export const memory: WebAssembly.Memory;- Meletakkan ini dalam
typeRootsfailtsconfig.jsonanda atau menggunakandeclare moduleakan membolehkan penyemakan jenis.wasm-packdengan mudah menjana fail.d.tssecara automatik, jadi ia sangat berguna untuk digunakan.
Pola Inisialisasi Semasa Runtime: Segerak vs Tidak Segerak
Oleh kerana modul Wasm memerlukan I/O (pengambilan) dan pengkompilan, inisialisasi tidak segerak adalah lazim. Namun, terdapat juga pola di mana anda menyimpan WebAssembly.Module lebih awal dan instantiate secara segerak.
Di bawah ialah struktur kod asas untuk inisialisasi WebAssembly secara tidak segerak. Dalam projek sebenar, pola ini digalakkan.
1// async init pattern
2async function initWasm(url: string) {
3 const res = await fetch(url);
4 const { instance, module } = await WebAssembly.instantiateStreaming(res, {});
5 return instance;
6}- Inisialisasi tidak segerak membolehkan pengendalian ralat dan lazy loading dimasukkan dengan lebih fleksibel, menjadikannya paling mudah dalam pembangunan sebenar. Tambahan pula, kod yang dijana oleh
wasm-packtermasuk APIinit()untuk inisialisasi, jadi membiasakan diri dengan aliran ini akan memudahkan kerja anda.
Pertimbangan Prestasi Praktikal
Berikut adalah beberapa perkara yang perlu diambil perhatian untuk penambahbaikan prestasi ketara. Sila rujuk tip pengoptimuman ini apabila menggabungkan TypeScript dan WebAssembly.
- Apabila panggilan fungsi berlaku dengan sangat kerap, beban antara pemanggilan JavaScript dan Wasm boleh menjadi halangan (bottleneck). Kami mengesyorkan pengelompokan data dan memprosesnya sekaligus sebanyak mungkin.
- Peruntukan dan penyalinan memori meningkatkan beban pemprosesan. Gunakan penimbal (buffer) dikongsi dan penunjuk untuk meminimumkan operasi-operasi ini.
- Berhati-hati semasa mengendalikan nombor apungan (floating-point). Dalam TypeScript, ia menjadi jenis
number, tetapi anda boleh mengendalikannya secara tepat dengan memadankan jenis di bahagian Wasm.
Ringkasan
Dengan menggabungkan TypeScript dan WebAssembly, anda boleh mencapai prestasi hampir asli di dalam pelayar. Ini amat berkesan untuk tugasan yang memerlukan pengiraan tinggi atau apabila anda ingin menggunakan aset asli sedia ada. Gabungan ini adalah pilihan yang sangat berkuasa apabila anda ingin meningkatkan prestasi aplikasi web anda.
Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.