În acest al patrulea episod din seria WebAssembly pentru începători, ne vom concentra pe relația dintre WebAssembly și JavaScript.
Vei învăța aici cum să integrezi WebAssembly în proiectele tale JavaScript. Mai mult, vom analiza detaliile API-ului JavaScript WebAssembly.
WebAssembly, un standard deschis în format binar, permite dezvoltatorilor să ruleze aplicații cu performanțe aproape native direct în browser. Dacă ești nou în acest domeniu, îți recomandăm să consulți și episoadele anterioare ale acestui ghid.
Să începem analiza.
Integrarea WebAssembly cu JavaScript
În primul tutorial WebAssembly, am discutat despre mecanismele interne ale WASM. Pentru a obține performanțe superioare în aplicațiile tale web, trebuie să folosești funcțiile și API-urile WASM din JavaScript. Am menționat, de asemenea, cum diverse framework-uri JavaScript pot beneficia de WASM pentru a construi aplicații extrem de eficiente.
Momentan, modulele WASM nu pot fi încărcate direct ca module ES6 folosind <script type="module">
. Aici intervine JavaScript, facilitând încărcarea și compilarea WASM în browser. Pașii necesari sunt următorii:
- Încarcă octeții .wasm într-un ArrayBuffer sau un array tipizat.
- Folosește
WebAssembly.Module
pentru a compila octeții. - Instanțiază
WebAssembly.Module
cu importuri pentru a obține exporturi care pot fi apelate.
Prin urmare, ai nevoie de un modul WASM pre-compilat. Ai la dispoziție diverse opțiuni, cum ar fi Rust, C/C++, AssemblyScript sau chiar TinyGo (Go), pentru a-ți scrie codul și a-l transforma ulterior într-un modul .wasm.
WebAssembly este, în esență, o țintă de compilare pentru limbaje. Acest lucru înseamnă că va trebui să scrii codul în limbajul preferat și să folosești codul binar rezultat în aplicația ta (web sau non-web). Pentru utilizarea pe servere, va fi necesar WASI pentru interacțiunea cu sistemele.
Deoarece WebAssembly utilizează memoria liniară printr-un array extensibil, atât JavaScript, cât și WASM pot accesa sincron, ceea ce permite crearea de aplicații rapide și cu multe funcționalități.
Exemple de integrare WebAssembly cu JavaScript
Vom folosi exemple concrete pentru a înțelege mai bine cum se poate utiliza WASM alături de JavaScript.
După cum am menționat, este necesar un modul WASM pre-compilat. În acest exemplu, vom folosi Emscripten (C/C++). Datorită formatului binar eficient al WASM, putem rula codul generat alături de JavaScript sau alte limbaje.
Configurarea instrumentelor
Pentru a folosi Emscripten, este necesar să obținem instrumentul emsdk, care ne va permite să compilăm codul C/C++ în cod .wasm.
Execută următoarea comandă în terminalul tău. Dacă nu ai instalat GIT, urmează ghidul Open Source 101: Sistemul de control al versiunilor și Git pentru a face acest lucru.
git clone https://github.com/emscripten-core/emsdk.git cd emsdk
#Ieșire [email protected]:~/Projects/WASM2$ git clone https://github.com/emscripten-core/emsdk.git Clonare în 'emsdk'... remote: Enumerarea obiectelor: 3566, gata. remote: Contorizare obiecte: 100% (62/62), gata. remote: Compresie obiecte: 100% (49/49), gata. remote: Total 3566 (delta 31), reutilizate 38 (delta 13), reambalate 3504 Primire obiecte: 100% (3566/3566), 2,09 MiB | 2,24 MiB/s, gata. Rezolvare delte: 100% (2334/2334), gata. [email protected]:~/Projects/WASM2$ cd emsdk [email protected]:~/Projects/WASM2/emsdk$
În folderul emdsk, executăm o altă comandă pentru a obține cea mai recentă versiune Emscripten pregătită pentru utilizare.
Pentru aceasta, trebuie să execuți următoarele comenzi.
./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh
#Ieșire [email protected]:~/Projects/WASM2/emsdk$ ./emsdk install latest Rezolvare alias SDK 'latest' la '3.1.31' Rezolvare versiune SDK '3.1.31' la 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit' Instalare SDK 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'.. Instalare instrument 'node-14.18.2-64bit'.. Descărcare: /home/nitt/Projects/WASM2/emsdk/zips/node-v14.18.2-linux-x64.tar.xz din https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v14.18.2-linux-x64.tar.xz, 21848416 Bytes Dezarhivare '/home/nitt/Projects/WASM2/emsdk/zips/node-v14.18.2-linux-x64.tar.xz' în '/home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit' Instalare finalizată a instrumentului 'node-14.18.2-64bit'. Instalare instrument 'releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'.. Descărcare: /home/nitt/Projects/WASM2/emsdk/zips/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-wasm-binaries.tbz2 din https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a/wasm-binaries.tbz2, 349224945 Bytes Dezarhivare '/home/nitt/Projects/WASM2/emsdk/zips/1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-wasm-binaries.tbz2' în '/home/nitt/Projects/WASM2/emsdk/upstream' Instalare finalizată a instrumentului 'releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'. Instalare finalizată a SDK 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit'. [email protected]:~/Projects/WASM2/emsdk$ ./emsdk activate latest Rezolvare alias SDK 'latest' la '3.1.31' Rezolvare versiune SDK '3.1.31' la 'sdk-releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit' Setare următoarele instrumente ca active: node-14.18.2-64bit releases-1eec24930cb2f56f6d9cd10ffcb031e27ea4157a-64bit Următorii pași: - Pentru a accesa cu ușurință instrumentele emsdk din linia de comandă, ia în considerare adăugarea următoarelor directoare în PATH: /home/nitt/Projects/WASM2/emsdk /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin /home/nitt/Projects/WASM2/emsdk/upstream/emscripten - Acest lucru poate fi realizat pentru shell-ul curent prin rulare: source "/home/nitt/Projects/WASM2/emsdk/emsdk_env.sh" - Configurează emsdk în scripturile de pornire ale shell-ului tău rulând: echo 'source "/home/nitt/Projects/WASM2/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile
Ultima comandă, source ./emsdk_env.sh
, asigură că variabila de mediu PATH include calea către compilatorul emcc Emscripten, permițând astfel utilizarea acestuia pentru compilarea codului.
#Ieșire [email protected]:~/Projects/WASM2/emsdk$ source ./emsdk_env.sh Setare mediu EMSDK (ascunde aceste mesaje cu EMSDK_QUIET=1) Adăugare directoare în PATH: PATH += /home/nitt/Projects/WASM2/emsdk PATH += /home/nitt/Projects/WASM2/emsdk/upstream/emscripten PATH += /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin Setare variabile de mediu: PATH = /home/nitt/Projects/WASM2/emsdk:/home/nitt/Projects/WASM2/emsdk/upstream/emscripten:/home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin EMSDK = /home/nitt/Projects/WASM2/emsdk EMSDK_NODE = /home/nitt/Projects/WASM2/emsdk/node/14.18.2_64bit/bin/node [email protected]:~/Projects/WASM2/emsdk$
Acum, generăm codul wasm rulând următoarea comandă.
emcc hello-tipstrick.ro.c -o hello-tipstrick.ro.js
#Ieșire [email protected]:~/Projects/WASM2$ emcc hello-tipstrick.ro.c -o hello-tipstrick.ro.js shared:INFO: (Emscripten: Rulează verificări de integritate) cache:INFO: generare sistem activ: symbol_lists/1c683af19e290d0b5ca7a8747d74a76f63dcb362.txt... (acesta va fi stocat în cache în "/home/nitt/Projects/WASM2/emsdk/upstream/emscripten/cache/symbol_lists/1c683af19e290d0b5ca7a8747d74a76f63dcb362.txt" pentru compilări ulterioare) cache:INFO: - ok [email protected]:~/Projects/WASM2$ dir emsdk hello-tipstrick.ro.c hello-tipstrick.ro.js hello-tipstrick.ro.wasm [email protected]:~/Projects/WASM2$
După cum se poate observa, obținem fișierele „hello-tipstrick.ro.js” și „hello-tipstrick.ro.wasm”. Putem verifica fișierele rulând comanda dir în directorul proiectului.
Ambele fișiere sunt esențiale. „hello-tipstrick.ro.wasm” conține codul compilat, iar fișierul „hello-tipstrick.ro.js” conține codul JavaScript necesar pentru a-l rula. Deoarece Emscripten suportă execuția web și node.js, îl putem testa cu Node.
node hello-tipstrick.ro.js
#Ieșire [email protected]:~/Projects/WASM2$ node hello-tipstrick.ro.js Salut, tipstrick.ro! [email protected]:~/Projects/WASM2$
Pentru a vedea funcționând pe web, se poate genera un fișier HTML cu Emscripten. Pentru a face asta, execută următoarea comandă.
emcc hello-tipstrick.ro.c -o hello-tipstrick.ro.html
#Ieșire [email protected]:~/Projects/WASM2$ emcc hello-tipstrick.ro.c -o hello-tipstrick.ro.html [email protected]:~/Projects/WASM2$
Pentru a rula fișierul HTML, se poate folosi Python 3 HTTP Server rulând următoarea comandă:
python3 -m http.server 8000
Apoi, accesează http://localhost:8000/hello-tipstrick.ro.html
pentru a vedea rezultatul.
Notă: Majoritatea sistemelor vin cu Python preinstalat. Dacă nu este cazul, se poate instala cu ușurință înainte de a încerca rularea serverului Python3.
Folosirea API-ului JavaScript pentru a interacționa cu WASM
În această secțiune, ne vom concentra pe API-ul JavaScript WASM, învățând cum să încărcăm și să executăm cod WASM. Mai întâi, analizăm codul de mai jos.
fetch('hello-tipstrick.ro.wasm').then( response => response.arrayBuffer()) .then (bytes => WebAssembly.instantiate(bytes)) .then(result=> alert(result.instance.exports.answer()))
Codul de mai sus utilizează următoarele API-uri JavaScript:
- fetch() API browser
- WebAssembly.instantiate
În afară de acestea, există și alte API-uri demne de menționat, cum ar fi:
- WebAssembly.compile
- WebAssembly.instance
- WebAssembly.instantiate
- WebAssembly.instantiateStreaming
fetch() Browser API
API-ul fetch()
încarcă resursa de rețea .wasm. Dacă încerci să încarci resursa local, va trebui să dezactivezi partajarea resurselor între origini. Alternativ, poți folosi un server Node pentru a gestiona acest aspect. Pentru a instala și rula un server Node, execută următoarea comandă:
sudo apt install npm
Apoi, execută următoarea comandă pentru a porni serverul.
npx http-server -o
#Ieșire http-server version: 14.1.1 http-server settings: CORS: disabled Cache: 3600 seconds Connection Timeout: 120 seconds Directory Listings: visible AutoIndex: visible Serve GZIP Files: false Serve Brotli Files: false Default File Extension: none Available on: http://127.0.0.1:8080 http://192.168.0.107:8080 Hit CTRL-C to stop the server Open: http://127.0.0.1:8080 [2023-01-28T19:22:21.137Z] "GET /" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70" (node:37919) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated (Use `node --trace-deprecation ...` to show where the warning was created) [2023-01-28T19:22:21.369Z] "GET /favicon.ico" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70" [2023-01-28T19:22:21.369Z] "GET /favicon.ico" Error (404): "Not found"
Această comandă va deschide browserul web, unde se pot vedea toate fișierele proiectului.
Acum deschide fișierul hello-tipstrick.ro.html și deschide instrumentele pentru dezvoltatori web. Acolo, deschide consola și scrie următoarea comandă:
fetch("hello-tipstrick.ro.wasm");
Va returna următoarea promisiune:
#Ieșire Promise {<pending>} [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: Response body: (...) bodyUsed: false headers: Headers {} ok: true redirected: false status: 200 statusText: "OK" type: "basic" url: "http://127.0.0.1:8080/hello-tipstrick.ro.wasm" [[Prototype]]: Response
De asemenea, poți scrie următorul script și să-l rulezi prin HTML.
Pentru a rula modulele wasm pe server, trebuie să folosești următorul cod în Node.js.
const fs = require('fs'); const run = async() => { const buffer = fs.readFileSync("./hello-tipstrick.ro.wasm"); const result = await WebAssembly.instantiate(buffer); console.log(result.instance.exports.answer()); }; run();
Este recomandat să consulți documentația API-ului WebAssembly JavaScript pentru a obține mai multe informații.
Comparație între JavaScript și WASM
Pentru a înțelege mai bine relația dintre WASM și JavaScript, este util să le comparăm. WASM se remarcă prin viteza superioară și formatul binar optimizat pentru compilare, în timp ce JavaScript este un limbaj de nivel înalt. Codul binar al WASM poate fi dificil de învățat, dar există modalități eficiente de a lucra cu el.
Diferențele cheie dintre WASM și JavaScript includ:
- WASM este un limbaj compilat, în timp ce JS este un limbaj interpretat. Browserul trebuie să descarce și să analizeze codul JavaScript în timpul rulării, în timp ce codul WASM este gata de execuție datorită codului pre-compilat.
- WebAssembly este un limbaj de nivel scăzut, în timp ce JavaScript este un limbaj de nivel înalt. Fiind la nivel înalt, JS este mai ușor de folosit. Cu toate acestea, WASM, fiind la nivel scăzut, poate executa mult mai rapid decât JavaScript.
- JavaScript beneficiază de o comunitate vastă, ceea ce oferă un mediu de dezvoltare mai bun. WebAssembly, fiind relativ nou, nu are încă același nivel de resurse.
Ca dezvoltator, nu este necesar să te îngrijorezi în legătură cu alegerea unuia dintre ele. Atât JS, cât și WASM, lucrează împreună.
Astfel, dacă dezvolți o aplicație performantă, se poate opta pentru WebAssembly pentru părțile care necesită performanță maximă. API-ul JavaScript facilitează preluarea și utilizarea modulelor WASM direct în codul JavaScript.
Concluzii
WebAssembly reprezintă un instrument excelent pentru JavaScript, permițând dezvoltatorilor să creeze aplicații performante atât pentru web, cât și pentru alte platforme, fără a înlocui JavaScript.
Va evolua WASM într-un pachet complet, înlocuind JavaScript? Este puțin probabil, dar nu complet imposibil, având în vedere obiectivele WebAssembly. Totuși, ideea ca WebAssembly să înlocuiască complet JavaScript în viitor nu este exclusă.
Pentru a construi aplicații moderne, consultă cele mai bune biblioteci JavaScript (JS) UI disponibile.