یکی از موضوعات مهم و اساسی در توسعهی ابزارهای تحت وب، سرعت و کارایی است. اینکه نرم افزار های تحت وب چقدر سریع و بهینه اجرا شوند تأثیر مستقیمی بر روی مخاطبین و مصرفکنندههای نهایی نرم افزار شما دارد.
طبق بررسیهای و گزارشهای آماری شرکت گوگل، اگر وبسایت شما بیشتر ۳ ثانیه لود شود چیزی حدود ۴۰ درصد از مخاطبین خود را از دست میدهد برای همین سرعت و کارایی برنامههای نوشته شده در وب همیشه یکی از چالشهای مهم در این حوزه بوده است.
جاوا اسکرپت تنها زبان برنامهنویسی تحت وب است، هرچند تا به امروز تلاشهای زیادی توسط کمپانیهای بزرگ برای بهتر و بهینهتر شدن اجرای جاوا اسکریپت انجام شده است. کارهایی مثل مرورگرهای وب و موتورهای جاوا اسکریپت مدرن که سرعت و اجرای کدهای جاوا اسکریپ را نسبت به سالهای قبل خیلی بهینهتر و بهتر کردهاند.
کتابخانههای مدرنی مثل React یا Vue و... ، باندلرهای مدرن مثل WebPack یا Vite و... همه به نوعی در تلاش بودهاند که کارایی صفحات وب را بهینهتر و سریعتر کنند. نسبت به سالهای اولیه وب، نرمافزارهای نوشته شده با جاوا اسکریپت خیلی سریعتر و بهتر شدهاند ولی همیشه یک مشکل اساسی وجود داشته برای عبور از کارایی کدها، مخصوصاً وقتی شما در حال توسعه یک بازی تحت وب هستید و مقدار زیادی کد پردازشتی و محاسباتی دارید.
ولی پرسش اصلی با وجود جاوا اسکریپ همیشه پابرجا بوده است .آیا سرعت و کارایی بیشتر باز هم امکانپذیر است؟
برای حل این مشکل ابتدا تکنولوژی asm. js توسط موزیلا ارایه شد و بعداً مدلهای اولیه وب اسمبلی یا WASM
اگر کلیات زبانهای برنامهنویسی آشنا باشید میدونید که زبانهای برنامهنویسی برای اجرای کدهای نوشته شده دو دسته کلی دارند:
۱- زبانهای کامپایلیری که کدهای نوشته شده ابتدا توسط کامپایلر به زبان سطح ماشین (assembly) تبدیل میشوند و سپس این فایلهای باینری توسط ماشین اجرا میشوند. زبانهایی مثل C، C++، GO و...
۲- زبانهای مفسری (interpreter language ) که کدهای نوشته شده در زمان اجرا توسط یک برنامه واسط به اسم مفسر ابتدا خوانده و سپس تبدیل به کدهای اجرایی ماشین میشوند و سپس اجرا میشوند. زبانهایی مثل PHP، Python، Javascript
هر کدام از این زبانها ویژگیها و توانایی خاص خود را دارند و برای کارهای خاصی مورد استفاده قرار میگیرند ،ولی وقتی در مورد سرعت و کارایی اجرای کدها صحبت کنیم همیشه زبانهای کامپایلری بهترین کارایی و سرعت را نسبت به زبانهای مفسری دارند.
ایدهی کلی و اولیه وسم پیادهسازی چیزی شبیه به فایلهای باینری اسمبلی بوده است با این تفاوت که وابستگی خاصی به معماری ماشین نداشته باشند و در عین حال بتوانیم تجربهی در سطح ماشین داشته باشیم و به راحتی هم بتواند در محیط مرورگر اجرا شود!
وباسمبلی یا وسم (WASM ) یک دستورالعمل نوشتن فایلهای باینری است که شما میتوانید کدهای اصلی خود را به زبانهای مخلتف بنویسید و سپس به فایلهای اجرایی WASM کامپایل کنید. استفاده از WASM برای توسعه ابزارهای وب، پتانیسلهای زیادی را برای توسعه دهنده به ارمغان میآورد. فایلهای WASM به دلیل اینکه در Javascript VM اجرا میشوند، علاوه بر امنیت و اجرا شدن در محیطهای ایزوله جاوا اسکریپت، قابلیت دسترسی به APIهای اصلی مرورگر را نیز دارند.
به کمک WASM میتوانید کدهای مالتی ترد بنویسید، DOMها را تغییر دهید و با DOMها تعامل کنید.برای مثال برای پردازشهای سنگین مثل بازیهای تحت وبمیتوانید فایلهای Wasm را در WebWorker اجرا کنید تا از تمام تمان پردازشی سیستم استفاده کنید و در عین حال صفحه وب تجربه روان و سبکی را ارایه دهد. حتی سرویسهای کلودبیسی وجود دارند که می توانید کدهای WASM را در کلود اجرا کنید . ...
شما میتوانید فایلهای WASM را به زبانهای مختلفی بنویسید. در این آموزش سورسهای اصلی به زبان محبوب Golang نوشته میشوند و سپس به کمک کامپایلر Tinygo به فایلهای وسم کامپایل میشوند.
دلیل استفاده از tinygo حجم بهینه فایلهای وسم است.
برای مثال:
۱- یک برنامه سلام دنیای ساده وقتی به کمک کامپایل پیشفرض Golang کامپایل میشود حدود ۲ مگابایت است.
۲- وقتی با tinygo کامپایل شود حدود ۸۵ کیلوبایایت: )
package main import "fmt" func main() { fmt.Println("Hello world from Go :)") }
# build by default go compiler GOOS=js GOARCH=wasm go build -o hello-go.wasm ./main.go 2059063 hello-go.wasm --> KB # build by tinygo tinygo build -o hello-tinygo.wasm -no-debug -opt=2 helloWorld.go hello-tinygo.wasm ->> 85935 KB
برای شروع کار ابتدا مطمن شوید آخرین ورژن Go و TinyGo بر روی سیستم شما نصب شده باشند
(ورژن ها ی مورد استفاده در این آموزش )
❯ go version go version go1.21.0 linux/amd64 ❯ tinygo version tinygo version 0.29.0 linux/amd64 (using go version go1.21.0 and LLVM version 15.0.0)
۱- یک فایل HTML با ساختار زیر : index.html
<!doctype html> <html dir="rtl"> <head> <meta charset="utf-8" /> <title>وسم با تاینیگو</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="wasm_exec.js"> <script src="wasm.js"> </head> <body> <h1>وباسمبلی (Wasm)</h1> </body> </html>
۲- فایل wasm.js برای اجرا کردن فایل وسم :
const WASM_URL = "wasm.wasm" let wasm; const initWasm = () => { const go = new Go(); if ("instantiateStreaming" in WebAssembly) { WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then( function (obj) { wasm = obj.instance; go.run(wasm); } ); } }; initWasm();
۳- فایل wasm_exec.js را دانلود کنید (این فایل مروبط به کامپایلر Tinygo گو است )
خیلی خب ،الان چهاچوب لازم برای اجرا کردن فایل های Wasm در وب آماده شده است .
برای شروع برنامه سلام دنیا رو در Go می نویسم :
package main import "fmt" func main() { fmt.Println("Hello world from Go :)") }
و سپس به کمک دستور زیر به وسم کامپایل میکنیم :
tinygo build -o main.wasm -no-debug -opt=2 main.go
که نهایتا یک فایل چند کیلوبایتی باینری با فرمت wasm رو ایجاد میکند .
برای اجرا دقت کنید همه ی فایل ها در پوشه روت باشند .
با نصب بودن npm روی سیستمتون کامند زیر رو اجرا کنید تا وبسرور لوکال روی پورت ۳۰۰۰ اجرا بشه :
npx serve .
حالا با رفتن به آدرس
http://localhost:3000
و بازکردن کونسول پیام hello world from go رو ببینید :)
بعد از اجرا کردن برنامه سلام دنیا و اینکه با فرآیند کلی کار با وسم آشنا شدید در این مرحله یک سری داده ها رو از ورودی های html به سمت فایل وسم می فرستیم (صدا زدن توابع داخلی ) و سپس خروجی رو نمایش می دهیم :
برای شروع فایل index.html
رو به این صورت تغییر بدید :
<!doctype html> <html dir="rtl"> <head> <meta charset="utf-8" /> <title>وسم با تاینیگو</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="wasm_exec.js"> <script src="wasm.js"> </head> <body> <h1>وباسمبلی (Wasm)</h1> <p> دو عدد را وارد کنید و و نتیجه را به کمک صدا زدن تابع add در فایل Wasm محاسبه کنید . </p> <input type="number" id="a" value="2" /> + <input type="number" id="b" value="2" /> = <input type="number" id="result" /> <button>محاسبه</button> const button = document.querySelector("button"); button.addEventListener("click", (event) => { var a = parseInt(document.getElementById("a").value); var b = parseInt(document.getElementById("b").value); var res = wasm.exports.add(a, b); var sum_box = document.getElementById("result"); sum_box.value = res; }); </body> </html>
در مثال بالا به کمک
var res = wasm.exports.add(a, b);
ورودی های a و b رو به تابع add که به صورت زیر در گو نوشته شده است می فرستیم و سپس خروجی ها رو در HTML نمایش می دهیم :
package main import "fmt" func main() { fmt.Println("Hello world from Go :)") } //export add func add(x, y int) int { return x + y }
دوباره کامپایل میکنیم :
tinygo build -o main.wasm -no-debug -opt=2 main.go
و صفحه رو رفرش میکنیم : خروجی :
//export
را بنویسید مثال ://export add func add(x, y int) int { return x + y }
در این مثال به کمک کتابخانه
"syscall/js"
یک المان P را در صفحه ایجاد میکنیم و استایل بندی میکنیم :
کدهای main.go رو به این صورت تغییر بدید :
package main import ( "fmt" "syscall/js" ) // import js syscall lib func main() { fmt.Println("Hello world from Go :)") // 1- find document in page document := js.Global().Get("document") // 2 - create p p := document.Call("createElement", "p") // 3- add some text to p p.Set("innerHTML", "Hello WASM from Go!") // 4- do some style p.Set("style", "font-size:30px;border-top:1px solid #ddd;padding-top:5px") //5- finally : append P to the body document.Get("body").Call("appendChild", p) } //export add func add(x, y int) int { return x + y }
کامپایل و صفحه رو رفرش کنید :
در این آموزش کوتاه سعی کردیم یک آشنایی خیلی ابتدایی با وسم و کاربرد های اصلی اون داشته باشیم . دایره و قابلیت های وسم برای وب خیلی وسیع است و با کمی دقت و جستجو میتوانید بخش هایی از فرانت رو که نیاز به سرعت و دقت بیشتری دارد،به سمت وسم ببرید .