ข้ามไปยังเนื้อหา

การเลือกภาษา

ทุก Wasm Toolchain มีจุดหมายปลายทางเดียวกัน — ไฟล์ .wasm Binary ที่รันใน browser — แต่แต่ละตัวเดินบนเส้นทางที่ต่างกัน การเลือกที่ถูกต้องขึ้นอยู่กับ Codebase ที่มีอยู่, ทักษะของทีม และข้อจำกัดของโปรเจกต์

คำถามRustC / EmscriptenAssemblyScriptTinyGo
ทีมเขียนภาษานี้อยู่แล้วหรือ?บางทีบางทีถ้ารู้จัก TSถ้ารู้จัก Go
มี Codebase ที่ต้อง Port หรือ?ไม่ใช่ไม่ไม่
ขนาด Binary เป็นหลัก?ยอดเยี่ยมใหญ่ (libc overhead)ยอดเยี่ยมดี
GC / Runtime Overhead?ไม่มีเฉพาะ libcไม่มีน้อยมาก
JS Interop ที่สมบูรณ์ (String, Object)?ใช่ (wasm-bindgen)ผ่าน ccall/cwrapผ่าน Loaderจำกัด
ครอบคลุม Standard Library?ครบถ้วนครบถ้วน libcจำกัดบางส่วน
รองรับ Async / Concurrency?ผ่าน Futuresผ่าน Asyncifyจำกัดจำกัด
Learning Curve (เฉพาะ Wasm)?ปานกลางต่ำ (ถ้ารู้ C)ต่ำมาก (TS)ต่ำ (ถ้ารู้ Go)

เลือก Rust เมื่อคุณ เขียนโค้ด Wasm ใหม่ และต้องการ:

  • Binary ที่เล็กที่สุดเท่าที่เป็นไปได้ (ไม่มี GC, ไม่มี Runtime Heap)
  • JS Binding ที่ปลอดภัยและอัตโนมัติผ่าน wasm-bindgen
  • Crate Ecosystem ที่ออกแบบมาสำหรับ wasm32-unknown-unknown
  • แพ็กเกจ npm ที่ Publish ได้จาก wasm-pack
Terminal window
wasm-pack build --target web
# → pkg/ ready to npm publish

เลือก Emscripten เมื่อคุณ Port ไลบรารี C/C++ ที่มีอยู่ และต้องการ:

  • printf, malloc, File I/O และ POSIX API ทำงานได้โดยไม่แก้โค้ด
  • OpenGL Emulation ผ่าน WebGL (-sUSE_WEBGL2)
  • Build ขั้นตอนเดียวจาก Makefile หรือ CMake Project ที่มีอยู่
Terminal window
emcc existing_lib.c -o out.js -sEXPORTED_FUNCTIONS='["_my_api"]' -O2

เลือก AssemblyScript เมื่อทีมของคุณ เขียน TypeScript และต้องการ:

  • Learning Curve ที่ต่ำมาก (Syntax เดิม แค่ Numeric Type ต่างกัน)
  • Wasm Module เล็กและ Self-contained โดยไม่มี Dependency
  • วน Iteration เร็วโดยไม่ต้องตั้งค่า Rust หรือ C Toolchain
Terminal window
npx asc assembly/index.ts --outFile build/release.wasm --optimize

เลือก TinyGo เมื่อทีมของคุณ เขียน Go และต้องการแชร์แพ็กเกจ Pure Logic ระหว่าง Go Backend กับ Browser Frontend:

Terminal window
tinygo build -o wasm.wasm -target wasm ./main.go

หมายเหตุ: TinyGo ไม่รองรับ Go Standard Library ทั้งหมด แพ็กเกจที่ใช้ net, os หรือ Reflection อาจคอมไพล์ไม่ได้ ตรวจสอบ ตาราง Package Support ของ TinyGo ก่อนตัดสินใจ

  1. มีโค้ด C/C++ ที่ทำงานได้อยู่แล้วหรือ? → Emscripten
  2. ทีมเขียน TypeScript และต้องการเริ่มต้นเร็ว? → AssemblyScript
  3. ทีมเขียน Go หรือ? → TinyGo
  4. เขียนโค้ดใหม่และต้องการขนาด Binary ที่ดีที่สุดพร้อม JS Interop? → Rust
ตัวเลือกBenefitCost
Rust + wasm-bindgenMemory Safety จาก Ownership Model และ Binary Size เล็กมาก ไม่มี GCLearning Curve สูงกว่าตัวอื่นเพราะต้องเข้าใจ Ownership/Borrowing
C/C++ + EmscriptenEcosystem C/C++ มหาศาล Port Codebase เดิมได้เกือบทันทีGlue Code และ POSIX Emulation Layer เพิ่ม Runtime Overhead ทำให้ Binary ใหญ่
AssemblyScriptSyntax คุ้นเคยสำหรับทีม TypeScript เริ่มต้นได้เร็วที่สุดEcosystem จำกัดมาก และ ไม่ semantically เหมือน TypeScript ทุกจุด — Type System ต่างกัน
  • เลือกภาษาตามความถนัดของทีมอย่างเดียวโดยไม่ดู Binary Size ที่ตามมา เช่น ใช้ Emscripten สำหรับ Module เล็กๆ ที่ไม่มี Codebase เดิมให้ Port ทำให้ Ship Runtime ใหญ่เกินจำเป็น
  • Ship Emscripten Build แบบไม่ Optimise — ไม่ Strip POSIX Emulation Layer และไม่ใช้ Flag อย่าง -O3 หรือ -sMINIMAL_RUNTIME จน Bundle บวม
  • คิดว่า AssemblyScript คือ TypeScript ทั้งหมด แล้วใช้ Syntax หรือ Semantics ที่ asc ไม่รองรับ จนเจอ Compile Error หรือ Bug แปลกๆ ที่ตามหายาก

💡 ตัวอย่างจากของจริง

Figma เลือก C++ กับ Emscripten สำหรับ Rendering Engine ที่ Port มาจาก Desktop และวัดผลได้ว่าเวลาโหลด เร็วขึ้นมากกว่า 3 เท่า หลังเปลี่ยนมาใช้ Wasm (Figma engineering blog) ในทางกลับกัน Shopify Functions ให้ Merchant เขียน Custom Checkout Logic ที่คอมไพล์เป็น Wasm ได้ — ปัจจุบันรองรับ Rust หรือ JavaScript (เอกสาร Shopify Functions) — สองตัวอย่างที่เลือกภาษาต่างกันตาม Requirement ของตัวเอง

Toolchain ใดเหมาะที่สุดเมื่อต้อง Port ไลบรารี C ที่มีอยู่ซึ่งใช้ printf และ malloc?
Toolchain สองตัวใดที่ผลิต Wasm Binary ขนาดเล็กที่สุดเพราะไม่มี Garbage Collector หรือ Runtime Heap?
ข้อจำกัดหลักของ TinyGo เมื่อคอมไพล์โค้ด Go ไปยัง Wasm คืออะไร?
คำสั่งใด Publish โปรเจกต์ Rust Wasm เป็นแพ็กเกจ npm?