服务器端渲染

¥Server-Side Rendering

服务器端渲染(通常缩写为 "SSR")允许你将应用渲染为 HTML 字符串,该字符串可以发送到客户端以缩短加载时间。除此之外,还有其他场景(例如测试),SSR 被证明非常有用。

¥Server-Side Rendering (often abbreviated as "SSR") allows you to render your application to an HTML string that can be sent to the client to improve load time. Outside of that there are other scenarios, like testing, where SSR proves really useful.



安装

¥Installation

Preact 的服务器端渲染器位于 自己的存储库 中,可以通过你选择的打包程序安装:

¥The server-side renderer for Preact lives in its own repository and can be installed via your packager of choice:

npm install -S preact-render-to-string

上面的命令完成后,我们就可以立即开始使用了。

¥After the command above finished, we can start using it right away.

HTML 字符串

¥HTML Strings

以下两个选项均返回一个 HTML 字符串,该字符串代表 Preact 应用的完整渲染输出。

¥Both of the following options return a single HTML string that represents the full rendered output of your Preact application.

renderToString

最基本、最直接的渲染方法,renderToString 将 Preact 树同步转换为 HTML 字符串。

¥The most basic and straightforward rendering method, renderToString transforms a Preact tree into a string of HTML synchronously.

import { renderToString } from 'preact-render-to-string';

const name = 'Preact User!';
const App = <div class="foo">Hello {name}</div>;

const html = renderToString(App);
console.log(html);
// <div class="foo">Hello Preact User!</div>

renderToStringAsync

等待 Promise 解析后再返回完整的 HTML 字符串。这在使用 Suspense 进行延迟加载组件或数据获取时尤其有用。

¥Awaits the resolution of promises before returning the complete HTML string. This is particularly useful when utilizing suspense for lazy-loaded components or data fetching.

// app.js
import { Suspense, lazy } from 'preact/compat';

const HomePage = lazy(() => import('./pages/home.js'));

function App() {
	return (
		<Suspense fallback={<p>Loading</p>}>
			<HomePage />
		</Suspense>
	);
}
import { renderToStringAsync } from 'preact-render-to-string';
import { App } from './app.js';

const html = await renderToStringAsync(<App />);
console.log(html);
// <h1>Home page</h1>

注意:遗憾的是,Preact v10 对 "恢复数据融合" 的实现存在一些已知的限制 - 也就是说,hydration 可能会暂停并等待 JS 块或数据下载并可用后再继续。这个问题在即将发布的 Preact v11 版本中得到了解决。

¥Note: Unfortunately there's a handful of known limitations in Preact v10's implementation of "resumed hydration" — that is, hydration that can pause and wait for JS chunks or data to be downloaded & available before continuing. This has been solved in the upcoming Preact v11 release.

目前,你需要避免使用返回 0 个或多个 DOM 节点作为子节点的异步边界,例如以下示例:

¥For now, you'll want to avoid async boundaries that return 0 or more than 1 DOM node as children, such as in the following examples:

function X() {
  // Some lazy operation, such as initializing analytics
  return null;
};

const LazyOperation = lazy(() => /* import X */);
function Y() {
  // `<Fragment>` disappears upon rendering, leaving two `<p>` DOM elements
  return (
    <Fragment>
      <p>Foo</p>
      <p>Bar</p>
    </Fragment>
  );
};

const SuspendingMultipleChildren = lazy(() => /* import Y */);

有关已知问题以及我们如何解决这些问题的更全面描述,请参阅 Hydration 2.0 (preactjs/preact#4442)

¥For a more comprehensive write up of the known problems and how we have addressed them, please see Hydration 2.0 (preactjs/preact#4442)

HTML 流

¥HTML Streams

流式渲染是一种渲染方法,它允许你在 Preact 应用的各个部分准备就绪时将其发送到客户端,而不是等待整个渲染完成。

¥Streaming is a method of rendering that allows you to send parts of your Preact application to the client as they are ready rather than waiting for the entire render to complete.

renderToPipeableStream

renderToPipeableStream 是一种利用 Node.js 流 渲染应用的流式方法。如果你不使用 Node,则应改为使用 renderToReadableStream

¥renderToPipeableStream is a streaming method that utilizes Node.js Streams to render your application. If you are not using Node, you should look to renderToReadableStream instead.

import { renderToPipeableStream } from 'preact-render-to-string/stream-node';

// Request handler syntax and form will vary across frameworks
function handler(req, res) {
	const { pipe, abort } = renderToPipeableStream(<App />, {
		onShellReady() {
			res.statusCode = 200;
			res.setHeader('Content-Type', 'text/html');
			pipe(res);
		},
		onError(error) {
			res.statusCode = 500;
			res.send(
				`<!doctype html><p>An error ocurred:</p><pre>${error.message}</pre>`
			);
		}
	});

	// Abandon and switch to client rendering if enough time passes.
	setTimeout(abort, 2000);
}

renderToReadableStream

renderToReadableStream 是另一种流式方法,类似于 renderToPipeableStream,但设计用于支持标准化 Web 流 的环境。

¥renderToReadableStream is another streaming method and similar to renderToPipeableStream, but designed for use in environments that support standardized Web Streams instead.

import { renderToReadableStream } from 'preact-render-to-string/stream';

// Request handler syntax and form will vary across frameworks
function handler(req, res) {
	const stream = renderToReadableStream(<App />);

	return new Response(stream, {
		headers: {
			'Content-Type': 'text/html'
		}
	});
}

自定义渲染器输出

¥Customize Renderer Output

我们通过 /jsx 模块提供了许多选项,用于针对一些常见用例自定义渲染器的输出。

¥We offer a number of options through the /jsx module to customize the output of the renderer for a handful of popular use cases.

JSX 模式

¥JSX Mode

如果你正在进行任何类型的快照测试,JSX 渲染模式尤其有用。它渲染输出,就好像它是用 JSX 编写的一样。

¥The JSX rendering mode is especially useful if you're doing any kind of snapshot testing. It renders the output as if it was written in JSX.

import renderToString from 'preact-render-to-string/jsx';

const App = <div data-foo={true} />;

const html = renderToString(App, {}, { jsx: true });
console.log(html);
// <div data-foo={true} />

漂亮模式

¥Pretty Mode

如果你需要以更人性化的方式获得渲染输出,我们可以满足你的需求!通过传递 pretty 选项,我们将保留空格并按预期缩进输出。

¥If you need to get the rendered output in a more human friendly way, we've got you covered! By passing the pretty option, we'll preserve whitespace and indent the output as expected.

import renderToString from 'preact-render-to-string/jsx';

const Foo = () => <div>foo</div>;
const App = (
	<div class="foo">
		<Foo />
	</div>
);

const html = renderToString(App, {}, { pretty: true });
console.log(html);
// <div class="foo">
//   <div>foo</div>
// </div>

浅层模式

¥Shallow Mode

出于某些目的,通常最好不要渲染整个树,而只渲染一层。为此,我们有一个浅渲染器,它将按名称打印子组件,而不是它们的返回值。

¥For some purposes it's often preferable to not render the whole tree, but only one level. For that we have a shallow renderer which will print child components by name, instead of their return value.

import renderToString from 'preact-render-to-string/jsx';

const Foo = () => <div>foo</div>;
const App = (
	<div class="foo">
		<Foo />
	</div>
);

const html = renderToString(App, {}, { shallow: true });
console.log(html);
// <div class="foo"><Foo /></div>

XML 模式

¥XML Mode

对于没有子元素的元素,XML 模式将改为将它们渲染为自闭合标签。

¥For elements without children, XML mode will instead render them as self-closing tags.

import renderToString from 'preact-render-to-string/jsx';

const Foo = () => <div></div>;
const App = (
	<div class="foo">
		<Foo />
	</div>
);

let html = renderToString(App, {}, { xml: true });
console.log(html);
// <div class="foo"><div /></div>

html = renderToString(App, {}, { xml: false });
console.log(html);
// <div class="foo"><div></div></div>