React SSR
在这篇文章中,我们将向你展示如何使用 WasmEdge 的 QuickJS 运行时来实现 React SSR 的能力。与 Docker + Linux + nodejs + v8 的方案相比,WasmEdge 要轻量得多(仅 1% 的占用),也更安全,能够提供更好的资源隔离和管理机制,并有着和非 JIT(同时是安全的)近似的性能。
本文将包含对静态渲染和流式渲染两种渲染方式的介绍。静态渲染相对容易理解和实现。而流式渲染则可以提供更好的用户体验,因为用户在浏览器前等待结果时,可以优先看到生成的部分内容。
本示例的源代码可以在 GitHub 仓库的 文件夹中找到。它展示了运行于 WasmEdge 的 JavaScript 应用程序,是如何编排 HTML 模板并将其渲染成 HTML 字符串的。
文件 component/Home.jsx 里是 React 的主页模板。
中会包含 提供的模板,作为页面的一部分。
import React from 'react';
class Page extends React.Component {
render() {
const { dataList = [] } = this.props;
return (
<div>
<div>This is page</div>
</div>
);
}
};
export default Page;
import React from 'react';
import {renderToString} from 'react-dom/server';
import Home from './component/Home.jsx';
const content = renderToString(React.createElement(Home));
console.log(content);
目录中的 rollup.config.js 文件和 文件用于把 React SSR 的所有依赖和组件打包成一个 WasmEdge 可用的 JavaScript 文件。你可以使用 npm
命令来进行构建,构建产物会输出到 dist/main.js
文件里。
npm install
npm run build
要运行这个例子,请在命令行中执行以下命令。你会看到,所有的模板成功合成了一个 HTML 字符串。
本示例的源代码可以在 GitHub 仓库的 example_js/react_ssr_stream 文件夹中找到。它展示了运行于 WasmEdge 的 JavaScript 应用程序,是如何流式地把 HTML 模板渲染成 HTML 字符串的。
文件 是 React 的主页模板。当外层的 HTML 渲染好并返回给用户 2s 之后,它才会开始“懒”加载内层的页面模板。
import React, { Suspense } from 'react';
async function sleep(ms) {
return new Promise((r, _) => {
setTimeout(() => r(), ms)
});
}
async function loadLazyPage() {
await sleep(2000);
return await import('./LazyPage.jsx');
}
class LazyHome extends React.Component {
render() {
let LazyPage1 = React.lazy(() => loadLazyPage());
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<title>Title</title>
</head>
<body>
<div>
<div> This is LazyHome </div>
<Suspense fallback={<div> loading... </div>}>
<LazyPage1 />
</Suspense>
</div>
</body>
</html>
);
}
}
export default LazyHome;
import React from 'react';
class LazyPage extends React.Component {
render() {
return (
<div>
<div>
This is lazy page
</div>
</div>
);
}
}
main.mjs 文件会启动一个异步的 HTTP 服务器,然后把 HTML 页面渲染成多段放入响应。当一个 HTTP 请求进来的时候,handle_client()
函数就会被调用来渲染 HTML,并以流的形式返回结果。
import { renderToPipeableStream } from 'react-dom/server';
import * as http from 'wasi_http';
import * as net from 'wasi_net';
import LazyHome from './component/LazyHome.jsx';
async function handle_client(s) {
let resp = new http.WasiResponse();
resp.headers = {
"Content-Type": "text/html; charset=utf-8"
}
renderToPipeableStream(<LazyHome />).pipe(resp.chunk(s));
}
async function server_start() {
print('listen 8001...');
let s = new net.WasiTcpServer(8001);
for (var i = 0; i < 100; i++) {
let cs = await s.accept();
handle_client(cs);
}
}
server_start();
目录中的 文件和 package.json 文件用于把 React SSR 的所有依赖和组件打包成一个 WasmEdge 可用的 JavaScript 文件。你可以使用 npm
命令来进行构建,构建产物会输出到 dist/main.js
文件里。
要运行这个例子,请在命令行上执行以下命令来启动服务器。
cd example_js/react_ssr_stream
nohup wasmedge --dir .:. ../../target/wasm32-wasi/release/wasmedge_quickjs.wasm dist/main.mjs &
然后通过 curl
或浏览器发送一个 HTTP 请求。
curl http://localhost:8001
结果如下所示。该服务首先返回一个 HTML 页面,里面包含一个空的内层部分(即 loading
部分)。然后在 2s 后返回内层部分的 HTML 内容,以及将它显示出来的 JavaScript 代码。
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 211 0 211 0 0 1029 0 --:--:-- --:--:-- --:--:-- 1024
100 275 0 275 0 0 221 0 --:--:-- 0:00:01 --:--:-- 220
100 547 0 547 0 0 245 0 --:--:-- 0:00:02 --:--:-- 245
100 1020 0 1020 0 0 413 0 --:--:-- 0:00:02 --:--:-- 413