Create React Doc is a markdown document site generator that uses React. Previously, Create React Doc introduced the pre-rendering technology to pre-generate static pages of the corresponding routes, so that the document sites built based on it can enjoy SEO(Search Engine Optimization) and speed up the first-screen access load.
A new challenge
Create React Doc uses pre-rendering technology to obtain the DOM structure of each page route to generate the corresponding HTML files, and stores the static files in gh-Pages service (you can choose other storage services) to speed up the loading of first-screen access and SEO. See the blue wireframe flowchart below:
The following figure shows the static directory file stored by the GP-Pages service:
When accessing a document created by Create React Doc, the page rendering cycle can be divided into first-screen rendering, bridging, and interactionable phases.
First screen rendering stage: Take the quick Start section as an example, when the user enters muyunyun.cn/create-reac… , the gP-Pages service will push pre-rendered pages, and users can get a very fast first-screen experience 😁.
It should be noted, however, that pre-rendered pages only generate static HTML pages, so the user cannot interact with the page during the first screen rendering stage.
Interface stage: The interface stage is the intermediate stage between the first screen rendering stage and the page interactive stage, in which JavaScript logic is executed to change the page from non-interactive to interactive. However, the observation found that from the pre-rendered page to the page can be interactive, there are loading pages that interfere with the experience, the experience is very bad 😭.
The undesired intermediate load page (see figure above) occurs because both the pre-rendered page and the client rendered page use reactdom.render and specify the same root path node (root in this case). After accessing the first pre-rendered page and executing the JavaScript logic, React removes the existing HTML structure and restarts rendering based on the root node, which inevitably leads to undesired page loading or page thrashing.
ReactDOM.render(
<RouterRoot />.document.getElementById('root'),Copy the code
Interactivity phase: This phase allows users to interact with the page. For example, click the left menu button to expand and fold.
Prerendering first screen straight out scheme based on SSR
Document-based sites are mostly static content, and a small percentage are dynamically interactive. Abstract the following feasibility ideas:
-
Idea 1: Adjust the interactive layout to reduce the interaction of dynamic nodes. For example, replace multi-level menus with breadcrumb components and tiled menu structures, or explore more elegant CSS interactions.
-
Idea 2: Decouple the rendering timing of static nodes and dynamic interactive nodes. Most static pages are rendered in pre-rendering, and dynamic logical nodes are executed in bridging stage. The pseudocode is as follows:
if(! ifProdRender) {// Prerender static nodes
ReactDOM.render(
<QuietNode />.document.getElementById('quietNode'),)}else {
// The interface stage completes the rendering of dynamic interaction nodes
ReactDOM.render(
<DynamicNode />.document.getElementById('dynamicNode'),)}Copy the code
Based on the above code, static page nodes and dynamic interaction nodes can be separately rendered. However, the defect of this scheme is that the connection between static nodes and dynamic interactive nodes is completely cut off, and nodes rendered in the bridging stage cannot affect static page nodes, such as page layout and route jump.
- Idea 3:
Decouple the effective time of static node rendering and dynamic interaction to ensure the connection between static node and dynamic interaction node rendering
. On the basis of the second idea, it is further associated with the server side rendering (static page is displayed on the first screen of the server side, and interactive logic is injected on the client side) can be perfectly supportedStatic nodes and dynamic interaction are isolated from each other, and the page jitter is not ensured in the connection stage
Yao. However, the server here can use GH-Pages service to store nodes pre-rendered based on SSR.
The code that performs different render logic depending on the environment is shown below, and the complete changes can be seen in Mr.
if (ifDev) {
// dev render
document.getElementById('root').innerHTML = ReactDOMServer.renderToString(<RouterRoot />)
ReactDOM.hydrate(
<RouterRoot />.document.getElementById('root'),)}else if (ifPrerender) {
// prerender
document.getElementById('root').innerHTML = ReactDOMServer.renderToString(<RouterRoot />)}else {
// prod render
ReactDOM.hydrate(
<RouterRoot />.document.getElementById('root'),)}Copy the code
At this point, the unfriendly jitter problem in the bridging phase (undesired page loading) is solved, the user no longer feels the unfriendly experience due to page jitter when visiting the site, and the interface from the first rendering page to the interactive page is smoother.
summary
In addition to first screen loading speed and SEO, the experience of the intermediate state from the first screen (non-interactive) to the interactive stage is also important in a static content dominated document site. Based on the premise of React technology ecology, this paper gives a solution of pre-rendering first screen straight out based on SSR, which solves the problem of page jitter in cohesive state relatively perfectly. With React 18, we’ll be able to make node interactions more responsive in real time to further optimize the user experience. Let’s see.