The Power of WebAssembly — possibly a silver bullet to server-side rendering (SSR/PSSR) problems
I first learned about WebAssembly in the middle of 2019. I must admit that this theory piqued my interest at first, and I began following thousands of discussion threads, viewpoints, blogs, and other online resources. I’ve put a lot of time into it, and it’s incredibly intriguing. In reality, I’ve been working on my own for a while now to accomplish something. When I published my last two server-side rendering articles on LinkedIn, one of my acquainted netizens wrote some suggestions to me. That motivated me to write this article to elaborate on many aspects of WebAssembly. Please check out the following links if you haven’t already: Island Architecture , Progressive Server Side Rendering .
Introduction Of WebAssembly?
WebAssembly is a binary instruction format for a virtual machine that is designed to be executed in web browsers. It is a low-level, assembly-like language that provides a compact and efficient representation of code that can be executed at near-native speed. WebAssembly is intended to be used as a target language for compilers of other programming languages, such as C/C++, Rust, and Go. It was designed to address the limitations of JavaScript, which is the primary language used for client-side web development. While JavaScript is a versatile and powerful language, it has certain limitations when it comes to performance and security. WebAssembly is designed to complement JavaScript and provide a more efficient and secure alternative for certain types of tasks, such as compute-intensive operations, audio and video processing, and 3D graphics rendering.
The following MDN?article ?is recommended to skim through to attain initial interest.
How does it work?
WebAssembly (Wasm) is a low-level binary format that is designed to be executed by a virtual machine (VM) inside a web browser. The Wasm binary code is generated by a compiler from a high-level programming language, such as C, C++, Rust, or AssemblyScript.
When a Wasm module is loaded into a web browser, it is parsed and validated by the browser’s Wasm engine. The validation process checks the binary format for compliance with the Wasm specification and ensures that the module’s code is safe to execute.
Once validated, the Wasm module is executed by the Wasm engine inside a sandboxed environment. The Wasm engine includes a just-in-time (JIT) compiler that converts the Wasm code into machine code that can be executed directly by the CPU. The JIT compiler optimizes the Wasm code for performance, taking advantage of the underlying hardware architecture of the user’s device. During execution, the Wasm module can interact with the host environment (i.e., the web browser) through a set of interfaces called “bindings.” These bindings provide access to the browser’s DOM, APIs, and other resources that the module may need to interact with. Please refer to the following diagram.
Let me also write a pseudo code to describe how WebAssembly interacts with JIT to execute routine tasks on the CPU. WebAssembly studio can be used as an online IDE to play around.
// Define a Wasm module
module = createWasmModule(code);
// Validate the module
if (validateWasmModule(module)) {
// Initialize the Wasm engine
engine = createWasmEngine();
// Load the module into the engine
instance = loadWasmModule(engine, module);
// Get the function to execute from the module instance
func = getWasmFunction(instance, "myFunction");
// Get the JIT compiler
jitCompiler = createJITCompiler();
// Compile the Wasm function using the JIT compiler
compiledFunction = compileWasmFunction(jitCompiler, func);
// Execute the compiled function on the CPU
result = executeCompiledFunction(compiledFunction);
// Use the result of the function execution
handleResult(result);
} else {
// Handle invalid module error
handleError("Invalid Wasm module");
}
Usage of WebAssembly
I assume so far we have grasped the fundamentals of it. Now the follow-up question comes as to where exactly it can be applied. Well, there are many potential use cases. Let me state a few of them as follows:
1.?Web applications:?It allows developers to write complex web applications that can run in the browser with near-native performance. Some of the existing problems with Server-side rendering might be resolved by this.
2.?Server-side/Mobile/IoT applications: As WebAssembly can be executed headlessly, can also be used to write high-performance server-side/mobile/Internet of Things (IoT) applications. , cloud computing, big data processing, scientific computing, sensor networks, smart home devices, and industrial automation might seek benefits from it.
3.?Blockchain: WebAssembly can be used to create smart contracts for blockchain applications. Because WebAssembly modules are small and efficient, they can be used to create contracts that run on resource-constrained devices.
Underlying Challenges
There are many examples of companies using WebAssembly in production, such as Figma, Autodesk, and GitHub. Still, it owns some demerits as follows:
1.?Limited?tooling: The tooling is still evolving, and there may be some gaps in the tooling.
2.?Difficult debugging: Debugging the WebAssembly code can be more challenging than debugging traditional JavaScript code. This is because WebAssembly code is compiled code, which means that it is not as human-readable as JavaScript code.
3.?Potential security risks: WebAssembly code runs outside of the JavaScript sandbox, which means that it has more direct access to system resources. This can make it more vulnerable to security exploits if not properly secured.
4.?Potential risks in cross-browser compatibility: While it is supported in all major browsers, there may be some differences in implementation across browsers that can cause compatibility issues.
5.?Stepper?learning curve: WebAssembly is a relatively new technology, and there may be a learning curve involved in getting up to speed with how it works and how to use it effectively.
Nodejs and Browser Support for WebAssembly
If you’ve read this far, I’m assuming you have a rudimentary knowledge of what it’s capable of. As a frontend developer, you may have a follow-up question such as: What about browser support? Is it supported by NodeJs?
Presently, it is being supported by every major browser. As a result, it can be used to build cross-platform applications that run on any device with a modern web browser. It is also supported by many other development tools and platforms, such as Rust, Go, and Node.js, etc. Check the compatibility chart?here . For Node.js particularly, we can use the built-in WebAssembly API. There is no need to install any external npm packages.
If you want to use WebAssembly in a browser environment, you can use the `WebAssembly.instantiateStreaming()` method, which is part of the standard JavaScript API. There are no external npm packages required for this either.
领英推荐
That being said, there are many npm packages available that provide utilities and tools for working with WebAssembly, such as `wasm-pack ` for building, testing, and publishing Rust-generated WebAssembly packages, and `assemblyscript` for compiling TypeScript-like code to WebAssembly. These packages can be useful if there are any complex development requirements.
There is good news! Nextjs has started supporting WebAssembly recently. You may find this nextjs-wasm library interesting.
WebAssembly To Solve Server-side Rendering Challenges
Well! Now we have reached a crucial juncture in this article where I am about to illustrate the most fascinating aspect of WebAssembly, which can potentially unravel a burning issue in Web development related to server-side rendering. That’s why I chose to articulate WebAssembly so elaboratively at first.
WebAssembly can potentially help to solve the hydration issue in server-side rendering (SSR) by providing a more efficient way to execute JavaScript code on the server side.
When a web application is rendered on the server side, the server needs to generate the initial HTML markup and send it to the client. To provide an interactive and dynamic user interface, the client-side JavaScript code needs to be executed on the browser after the HTML is received. This process is called hydration.
One of the challenges of hydration is that the JavaScript code needs to be loaded and executed on the client side, which can result in longer load times and slower rendering. This is especially true for large JavaScript bundles that contain a lot of code. Now, let’s explore the possibilities:
— WebAssembly can potentially help to address this issue by providing a more efficient and faster way to execute JavaScript code on the server side.
— Also, it is possible to generate a more optimized and efficient bundle that can be sent to the client.
— In addition, WebAssembly can be used to pre-render parts of the application on the server side by calculating the initial states, which can further reduce the amount of work that needs to be done on the client side. This can lead to faster rendering times and improved performance.
Boo to boring theories! Let’s get some snippets
I’ve tried to write a sample working snippet. Let’s study them below.
// greet.c file
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <emscripten.h>
// greet function to be used for printing the given name
EMSCRIPTEN_KEEPALIVE
char* greet(int length, char* name) {
char* result = malloc(sizeof(char) * (length + 8));
strcpy(result, "Hello, ");
strncat(result, name, length);
strcat(result, "!");
return result;
}
// de-allocating memory for the greet function
EMSCRIPTEN_KEEPALIVE
void deallocate(char* ptr, int length) {
free(ptr);
}
// EMSCRIPTEN_KEEPALIVE macro is used to tell the Emscripten compiler
// to export these functions as WebAssembly exports.
// This allows us to call them from our JavaScript code.
2. Now, we have to compile this C code into WebAssembly. We can use the?Emscripten compiler . Here’s an example command to compile the?greet.c?file into a WebAssembly module.
emcc greet.c -s WASM=1 -o greet.wasm
3. let’s create a JavaScript function that calls this WebAssembly function using the?WebAssembly.instantiateStreaming?method and updates the browser’s DOM with the rendered component.
// wasm-module.js
const wasmModule = WebAssembly.instantiateStreaming(fetch('greet.wasm'), {});
const createWasm = async () => {
const wasmInstance = await wasmModule;
return wasmInstance.exports;
};
export { createWasm };
4. Now, let’s write a React snippet to render the server-side component for printing a given name by a WebAssembly function.
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import fs from 'fs';
import { createWasm } from './wasm-module'; // assume this file exports a function to create a WebAssembly module
const App = ({ name }) => {
const greet = (name) => {
const wasm = createWasm(); // create a new WebAssembly module
const resultPointer = wasm.exports.greet(name.length, name); // call a function in the WebAssembly module
const result = wasm.exports.getStringFromMemory(resultPointer); // get the result from memory
wasm.exports.deallocate(resultPointer, result.length + 1); // free the memory
return result;
};
return (
<div>
<h1>Hello {name}!</h1>
<p>{greet(name)}</p>
</div>
);
};
const render = async () => {
const name = 'World';
const html = ReactDOMServer.renderToString(<App name={name} />);
return `<!DOCTYPE html><html><head><title>Hello World</title></head><body>${html}</body></html>`;
};
// Render the HTML string
render().then((html) => {
console.log(html);
});
So, did you try to execute these snippets and find out what happens? If yes, let me know your experience in the comment section.
Is it a silver bullet to all SSR issues?
Generally, WebAssembly is not a silver bullet solution to the hydration issue in SSR. Undoubtedly it has the potential to provide a more efficient and faster way to execute JavaScript code on the server side, which can eventually help to improve performance and reduce load times. Still, there is an uphill task to do.
“With great power comes great responsibility”
As WebAssembly possesses the power to execute CPU routines directly, it beholds great responsibility as well. As of today, the frontend fraternity is not well equipped to leverage this technology. This is still at a nascent stage. I wish down the line, the open-source community would add more values to ease the related development. Please put your thoughts in the comment section.