docs/guide/blog/ssr.zh-CN.md
服务端渲染(Server-Side Rendering),是指由服务侧完成页面的 HTML 结构拼接的页面处理技术。一般用于解决 SEO 问题和首屏加载速度问题。
由于 SSR 是在非浏览器环境执行 JS 代码,所以会出现很多问题。本文主要介绍 React Hooks 在 SSR 模式下常见问题及解决方案。
更多关于 SSR 的介绍可以看 UmiJS 的文档《服务端渲染(SSR)》。
SSR 是在 node 环境下运行 React 代码,而此时 window、document、navigator 等全局属性没有。如果直接使用了这些属性,就会报错 window is not defined, document is not defined, navigator is not defined 等。
常见的错误用法是在 Hooks 执行过程中,直接使用了 document 等全局属性。
import React, { useState } from 'react';
export default () => {
const [state, setState] = useState(document.visibilityState);
return state;
};
import React, { useState, useEffect } from 'react';
export default () => {
const [state, setState] = useState();
useEffect(() => {
setState(document.visibilityState);
}, []);
return state;
};
isBrowser 来做环境判断import React, { useState } from 'react';
function isBrowser() {
return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
}
export default () => {
const [state, setState] = useState(isBrowser() && document.visibilityState);
return state;
};
如果使用了 useLayoutEffect,在 SSR 模式下,会出现以下警告
⚠️ Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://fb.me/react-uselayouteffect-ssr for common fixes.
import { useLayoutEffect, useEffect } from 'react';
const useIsomorphicLayoutEffect = isBrowser() ? useLayoutEffect : useEffect;
export default useIsomorphicLayoutEffect;
isBrowser 判断是否在浏览器环境执行import React, { useState } from 'react';
import { useEventListener } from 'ahooks';
export default () => {
const [value, setValue] = useState(0);
const clickHandler = () => {
setValue(value + 1);
};
useEventListener(
'click',
clickHandler,
{
- target: document.getElementById('click-btn')
+ target: () => document.getElementById('click-btn')
}
);
return (
<button id="click-btn" type="button">
You click {value} times
</button>
);
};
useIsomorphicLayoutEffect 来代替 useLayoutEffect