一文彻底搞懂 React 服务端渲染

474次阅读  |  发布于2年以前

1、前言

在前端项目需要首屏渲染速度优化或SEO的场景下,大家或多或少都听过到过服务端渲染( SSR ),但大多数人对服务端渲染具体实现和底层原理还是比较陌生的。本文基于公司官网开发时做了服务端渲染改造基础上,系统理解和梳理这套体系的模式和逻辑,并写了一些笔记和Demo(文后链接)便于深入理解。这次我们来以React为例,把服务端渲染彻底讲弄明白。本文主要有以下内容:

1.1 什么是服务端渲染?

服务端渲染, SSR (Server-side Rendering) ,顾名思义,就是在浏览器发起页面请求后由服务端完成页面的HTML结构拼接,返回给浏览器解析后能直接构建出有内容的页面。

用 node 实现一个简单的 SSR

我们使用Koa框架来创建node服务:

//  demo1
var Koa = require("koa");
var app = new Koa();

// 对于任何请求,app将调用该函数处理请求:
app.use(async (ctx) => {
  // 将HTML字符串直接返回 
  ctx.body = `
    <html>
      <head>
         <title>ssr</title>
        </head>
        <body>
        <div id="root">
            <h1>hello server</h1>
            <p>word</p>
        </div>
      </body> 
      </html>`;
});
//监听
app.listen(3001, () => {
  console.log("listen on 3001 port!");
});

启动服务后访问页面,查看网页源代码是这样:

npx create-react-app my-app

上面的例子就是一个简单的服务端渲染,其服务侧直接输出了有内容的HTML,浏览器解析之后就能渲染出页面。与服务端渲染对应的是客户端渲染 ,CSR(Client Side Rendering),通俗的讲就是由客户端完成页面的渲染。其大致渲染流程是这样:在浏览器请求页面时,服务端先返回一个无具体内容的HTML,浏览器还需要再加载并执行JS,动态地将内容和数据渲染到页面中,才能完成页面具体内容的显示。目前主流的React ,Vue, Angular 等SPA页面未经特殊处理均采用客户端渲染。最常见脚手架create-react-app 生成的项目就是客户端渲染:

上面采用客户端渲染的HTML页面中

中无内容,需在浏览器端加载并执行bundle.js后才能构建出有内容页面。

1.2 为什么用服务端渲染?

1.2.1 服务端渲染的优势

相比于客户端渲染,服务端渲染有什么优势?我们可以从下图对比一下这两种不同的渲染模式。

首屏时间更短

采用客户端渲染的页面,要进行JS文件拉取和JS代码执行,动态创建 DOM 结构,客户端逻辑越重,初始化需要执行的 JS 越多,首屏性能就越慢;客户端渲染前置的第三方类库/框架、polyfill 等都会在一定程度上拖慢首屏性能。Code splitting、lazy-load等优化措施能够缓解一部分,但优化空间相对有限。相比而言,服务端渲染的页面直接拉取HTMl就能显示内容,更短的首屏时间创造更多的可能性。

利于 SEO

在别人使用搜索引擎搜索相关的内容时,你的网页排行能靠得更前,这样你的流量就有越高,这就是SEO的意义所在。那为什么服务端渲染更利于爬虫爬你的页面呢?因为对于很多搜索引擎爬虫(非google)HTML返回是什么内容就爬什么内容,而不会动态执行JS代码内容。对客户端渲染的页面来说,简直无能为力,因为返回的HTML是一个空壳。而服务端渲染返回的HTML是有内容的。

SSR的出现,就是为了解决这些CSR的弊端。

1.2.2 权衡使用服务端渲染

并不是所有的WEB应用都必须使用SSR,这需要开发者来权衡,因为服务端渲染会带来以下问题:

代码复杂度增加。为了实现服务端渲染,应用代码中需要兼容服务端和客户端两种运行情况,部分代码只能在客户端运行,需要对其进行特殊处理,才能在服务器渲染应用程序中运行。

需要更多的服务器资源。由于服务器增加了渲染HTML的需求,使得原本只需要输出静态资源文件的nodejs服务,新增了数据获取的IO和渲染HTML的CPU占用,如果流量突然暴增,有可能导致服务器宕机,因此需要使用响应的缓存策略和准备相应的服务器负载。

涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。

因此,在使用服务端渲染SSR之前,需要考虑投入产出比:是否真的需要SEO,是否需要将首屏时间提升到极致。如果都没有,使用SSR反而小题大做了。

1.3 服务端渲染的发展史

其实服务端渲染并不是什么新奇的概念,前后端分层之前很长的一段时间里都是以服务端渲染为主(JSP、PHP),那时后端一把梭哈,在服务端生成完整的 HTML 页面。但那时的服务端渲染和现在还是有本质的区别,存在比较多的弊端,每一个请求都要动态生成HTML,存在大量的重复,服务器机器成本也相对比较高,前后端代码完全掺杂在一起,开发维护难。

随着业务不断发展变化,后端要写的JS逻辑也越发复杂,而且JS有很多潜在的坑使后端越发觉得这是块烫手山芋,于是逐渐出现了前后端分层。伴随AJAX的兴起,浏览器可以做到了不再重现请求页面就可更新局部视图。还可以利用客户端免费的计算资源,后端侧逐渐演变成了提供数据支持。jquery的兴起,良好的客户端兼容性使JS不再受困于各种版本浏览器兼容问题,一统了前端天下。

此后伴随node的兴起,前后端分离越演越烈。前端能摆脱后端的依赖单独起服务,三大框架vue,react,angular也迅势崛起,以操作数据就能更新视图,前端开发人员逐渐摆脱了与烦人的Dom操作打交道,能够专心的关注业务和数据逻辑。前端同时探索出了功能插件,UI库,组件等多种代码复用方案,形成了繁荣的前端生态。

但是三大框架采用客户端渲染模式,随着代码逻辑的加重,首屏时间成了一个很大的问题,同时开发人员也发现SEO也出了问题,大多搜索引擎根本不会去执行JS代码。但是也不可能再回头走老路,于是前端又探索出了一套服务端渲染的框架来解决掉这些问题。此时的服务端渲染是建立在成熟的组组件,模块生态之上,基于Node.js的同构方案成为最佳实践。

2、React服务端渲染的原理

2.1 基本思路

React服务端渲染流程

React服务端渲染的基本思路,简单理解就是将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。因为要考虑React在服务端的运行情况,故相比之前讲的多了在浏览器端绑定状态与事件的过程。

我们可以结合下面的流程图来一览完整的 React服务端渲染的全貌:当浏览器去请求一个页面,前端服务器端接收到请求并执行 React组件代码,此时React代码中可能包含向后端服务器发起请求,待请求完成返回的数据后,前端服务器组装好有内容的HTML里返给浏览器,浏览器解析HTML后已具备展示内容,但页面并不具备交互能力。

下一阶段,在返回的HTMl中还有script链接,浏览器再拉取JS并执行其包含的React 代码,其能在浏览器端执行完整的生命周期,并通过相关API实现复用此前返回 HTML节点并添加事件的绑定,此时页面才就具备完全交互能力。总的来说,react服务端渲染包含有两个过程:服务端渲染 + 客户端 hydrate 渲染。服务端渲染在服务端渲染出了首屏内容;客户端 hydrate 渲染复用服务端返回的节点,进行一次类似于 render 的 hydrate 渲染过程,把交互事件绑上去(此时页面可交互),并接管页面。

服务端处理后返回的

客户端“浸泡”还原后的

核心思想(同构)

从上面的流程中可以看到,客户端和服务端都要执行React代码完成渲染,那是不是就要写两份代码,供双端使用? 当然不需要,也完全不合理。所谓同构,就是让一份React代码,既可以在服务端中执行,也可以在客户端中执行。

SSR技术栈

我们这里简单理了一下服务端渲染涉及到的技术栈:

知道了服务端渲染、同构的大概思路之后,下面从头开始,一步一步完成具体实践,深入理解其原理。

2.2 服务端如何渲染React组件?

按照之前流程的大概思路,我们首先需要将React组件在服务端转换成HTML字符串,那怎么做呢?React 提供的面向服务端的API(react-dom/server),提供了相关方法能够将 React 组件渲染成静态的(HTML)标签。下面我们简单了解下react-dom/server。

react-dom/server

react-dom/server有renderToString、renderToStaticMarkup,renderToNodeStream、renderToStaticNodeStream四个方法能够将 React 组件渲染成静态的(HTML)标签,前两者能在客户端和服务端运行,后两者只能在服务端运行。

renderToStaticMarkup VS renderToString:renderToString 方法会在 React 内部创建的额外 DOM 属性,例如 data-reactroot, 在相邻文本节点之间生成\,这些属性是客户端执行hydrate复用节点的关键所在,data-reactroot属性是服务端渲染的标志之一。如果你希望把 React 当作静态页面生成器来使用,renderToStaticMarkup方法会非常有用,因为去除额外的属性可以节省一些字节。

// Home.jsx
import React from "react";
const Home = () => {
  return (
    <div>
      <h2 onClick={() => console.log("hello")}>This is Home Page</h2>
      <p>Home is the page ..... more discribe</p>
    </div>
  );
};
export default Home;

我们使用React-dom/server下提供的renderToString方法,在服务端将其转换为html字符串:

//  server.js
import Koa from "koa";
import React from "react";
import { renderToString } from "react-dom/server";
import Home from "./containers/Home";

const app = new Koa();
app.use(async (ctx) => {
  // 核心api renderToString 将react组件转化成html字符串
  const content = renderToString(<Home />);
  ctx.body = `
    <html>
      <head>
        <title>ssr</title>
      </head>
      <body>
        <div id="root">${content}</div>
      </body>
    </html>
   `;
});
app.listen(3002, () => {
  console.log("listen:3002");
});

可以看到上面代码里有ES6的import 和jsx语法,不能直接运行在node环境,需要借助webpack打包, 构建目标是commonjs。新建webpack.server.js具体配置如下:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// webpack.server.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const path = require(<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"path"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const nodeExternals = require(<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"webpack-node-externals"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">module.exports<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> = {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  mode: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"development"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  target: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"node"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  entry: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./server.js"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  resolve: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    extensions: [<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">".jsx"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">".js"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">".tsx"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">".ts"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  module: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    rules: [<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">test<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">: /\.jsx?$/,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        loader: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"babel-loader"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        exclude: /node_modules/,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        options: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">          presets: [<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"@babel/preset-react"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"@babel/preset-env"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">          plugins: [<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">            [<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">              <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"@babel/plugin-transform-runtime"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">              {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">                absoluteRuntime: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(86, 182, 194);line-height: 26px;">false<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">                corejs: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(86, 182, 194);line-height: 26px;">false<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">                helpers: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(86, 182, 194);line-height: 26px;">true<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">                regenerator: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(86, 182, 194);line-height: 26px;">true<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">                version: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"7.0.0-beta.0"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">              },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">            ],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">          ],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    ],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  output: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    filename: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"bundle.js"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    path: path.resolve(__dirname, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"build"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">),<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  externals: [nodeExternals()],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">};

在webpack构建完成后,可在Node环境运行build/bundle.js,访问页面后查看网页源代码,可以看到,React组件中的内容已经完整地包含在服务端返回到html里面。我们成功迈出了服务端渲染第一步。此时,我们也有必要再深入了解renderToString 到底做了什么,提前踩坑!

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/3c1e339f9929a4a3a46b4c12c79b74e7)


#### **renderToString**

除了将React组件转换成html字符串外,renderToString还有做了下面这些:

1\. 会执行传入的React组件的代码,但是其只执行到React生命周期初始化过程的render及之前,即下面红框的部分,其余大部分生命周期函数在服务端都不执行;这也是服务端渲染的坑点之一。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/c1e1a6143bee024e45c3ee6d1d5404d3)

2\. **renderToString** 生成的产物中会包含一些额外生成特殊标记,代码体积会有所增大,其中属性data-reactroot是服务端渲染的标志,便于后续客户端通过hydrate复用HTML节点。在React16前后其产物也有差距:在React 16 之前,服务端渲染采用的是基于字符串校验和(string checksum)的 HTML 节点复用方式, 会额外生成生成data-reactid、data-react-checksum等属性;React 16 改用单节点校验来复用(服务端返回的)HTML 节点,不再生成data-reactid、data-react-checksum等体积占用大户,只在空白节点间多了\<!-- --> 这样的标记。
```
renderToString react16前

"" data-reactid="1" data-react-checksum="122239856">

  "2">Welcome to React SSR!  

   Hello There! 





// renderToString react16

"">"here">Welcome to React SSR! Hello There!
``` ```

3.会被故意忽略掉的on开头的的属性,也就忽略掉了react代码中事件处理,这是也是坑点之一。服务端返回的html里没有处理事件点击,需要靠后续客户端js执行绑定事件。

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="color: rgb(198, 120, 221);line-height: 26px;">function shouldIgnoreAttribute(<br></br>  name: string,<br></br>  propertyInfo: PropertyInfo | null,<br></br>  isCustomComponentTag: boolean,<br></br>): boolean {<br></br>  <span style="color: rgb(198, 120, 221);line-height: 26px;">if (propertyInfo !== null) {<br></br>    <span style="color: rgb(230, 192, 123);line-height: 26px;">return propertyInfo.type === RESERVED;<br></br>  }<br></br>  <span style="color: rgb(198, 120, 221);line-height: 26px;">if (isCustomComponentTag) {<br></br>    <span style="color: rgb(230, 192, 123);line-height: 26px;">return <span style="color: rgb(86, 182, 194);line-height: 26px;">false;<br></br>  }<br></br>  <span style="color: rgb(198, 120, 221);line-height: 26px;">if (<br></br>    name.length > 2 &&<br></br>    (name[0] === <span style="color: rgb(152, 195, 121);line-height: 26px;">'o' || name[0] === <span style="color: rgb(152, 195, 121);line-height: 26px;">'O') &&<br></br>    (name[1] === <span style="color: rgb(152, 195, 121);line-height: 26px;">'n' || name[1] === <span style="color: rgb(152, 195, 121);line-height: 26px;">'N')<br></br>  ) {<br></br>    <span style="color: rgb(230, 192, 123);line-height: 26px;">return <span style="color: rgb(86, 182, 194);line-height: 26px;">true;<br></br>  }<br></br>  <span style="color: rgb(230, 192, 123);line-height: 26px;">return <span style="color: rgb(86, 182, 194);line-height: 26px;">false;<br></br>}<br></br>

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/0fd84e3f8b1405ddf51f2cabc99ea80b)
上面的例子我们可以看到React的代码里有点击事件,但点击后没有反应。需要靠后续客户端js执行绑定事件。如何实现?这就需要同构了。


### 2.3 实现基础的同构

前文已经大概讲了同构的概念,那为什么需要同构?之前的服务端代码在处理点击事件时故意忽略掉了这类属性,在服务端执行的生命周期也是不完整的,此时的页面是不具备交互能力的。同构,正是解决这些问题的关键,React代码在服务器上执行一遍之后,浏览器再去加载JS后又运行了一遍React代码,完成事件绑定和完整生命周期的执行,从而才能成为完全可交互页面。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/cb22e7ab5ae29ad66694658e81ed7ed8)
**react-dom:hydrate**

实现同构的另一个核心API是React-dom下的**hydrate,**该方法能在客户端初次渲染的时候去复用服务端返回的原本已经存在的 DOM 节点,于渲染过程中为其附加交互行为(事件监听等),而不是重新创建 DOM 节点。需要注意是,服务端返回的 HTML 与客户端渲染结果不一致时,出于性能考虑,hydrate可以弥补文本内容的差异,但并不能保证修补属性的差异,而是将错就错;只在development模式下对这些不一致的问题报 Warning,因此必须重视 SSR HydrationWarning,要当 Error 逐个解决。

那具体实现同构?

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/3e76085d0f4a305fa310db18d5e4b8ab)
上面这里我们提供了一个基本的架构图,可以看到,服务端运行React生成html代码我们已经基本实现,目前需要做的就是生产出客户端执行的index.js,那么这个index.js我们如何生产出来呢?

**具体实践**

首先新建客户端代码client.js,引入React组件,通过ReactDom.hydrate处理挂载到Dom节点, hydrate是实现复用的关键。
```
// client.js

import React from "react";

import ReactDom from "react-dom";

import Home from "./containers/Home";



const App = () => {

  return ;

};



ReactDom.hydrate(, document.getElementById("root")); ``` ```

客户端代码也需要webpack打包处理,新建webpack.client.js具体配置如下,需要注意打包输出在public目录下,后续的静态资源服务也起在了这个目录下。

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// webpack.client.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const path = require(<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"path"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const resolve = (dir) => path.resolve(__dirname, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./src"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">, dir);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">module.exports = {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  mode: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"development"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  entry: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./client.js"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  output: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    filename: <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"index.js"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    path: path.resolve(__dirname, <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"public"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">),<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  module: {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    rules: [<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      // babel-loader处理js的一些配置<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    ],<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  },<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">};

服务端的静态资源服务使用koaStatic起在public目录,这样就能通过外链访问到打包出来的客户端js,同时我们在html中嵌入这个链接。
```
// server.js

import koaStatic from "koa-static";



const app = new Koa();

app.use(koaStatic("public"));



app.use(async (ctx) => {

  const content = renderToString();

  console.log(content);

  ctx.body = `

    

      

        "root">${content}

            "./index.js">

      

    

   `;

});

app.listen(3003, () => {

  console.log("listen:3003");

});

``` ```

简单看下此时的代码结构是这样:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
├── package.json<br></br>├── webpack.client.js<br></br>├── webpack.server.js<br></br>├── server.js<br></br>├── client.js<br></br>└── containers<br></br>    └── Home.jsx<br></br>

通过上面一番操作,简单的同构基本可以跑起来了,点击对应位置后看到有了反应,查看网页源代码如下。可以看到多了script标签的index.js 这段,这是在客户端执行的js代码。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/a3d9c28228173f15437686944d1e57d6)
![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/fb8af2091be11a6b88a87a7246d51744)
以上我们仅仅是就完成了一个React基础的同构,但这还远不够,一个完整的前端页面还包含路由,状态管理,请求服务端数据等,这些也需要进行同构,且看下面为你一一道来。


### 2.4 路由的同构

我们之前页面只是一个页面, 但实际开发的应用用一般都是多个页面的,这就需要加入路由了。服务端渲染加入路由就涉及到同一份路由在不同端的执行,这就是路由的同构。

下面一步步来:首先加入About页面,并书写路由配置routes.js
```
// routes.js

import Home from "./containers/home";

import About from "./containers/about";

export default [

  { path: "/", component: Home, exact: true },

  {

    path: "/about",

    component: About,

    exact: true,

  },

];

``` ```

在客户端侧加入路由的写法还是熟悉的写法,考虑到页面中可能涉及多级路由的渲染,这里直接引入react-router-config中来处理:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
// client.js<br></br>import { BrowserRouter } from <span style="color: rgb(152, 195, 121);line-height: 26px;">"react-router-dom";<br></br>import { renderRoutes } from <span style="color: rgb(152, 195, 121);line-height: 26px;">"react-router-config";<br></br>import Routes from <span style="color: rgb(152, 195, 121);line-height: 26px;">"./routes";<br></br><br></br>const App = () => {<br></br>  <span style="color: rgb(230, 192, 123);line-height: 26px;">return (<br></br>    <BrowserRouter><br></br>      <div>{renderRoutes(Routes)}</div><br></br>    </BrowserRouter><br></br>  );<br></br>};<br></br>

react-router 为服务端提供了StaticRouter,需显式地向location传path。
```
// server.js

import { StaticRouter } from "react-router-dom";

import { renderToString } from "react-dom/server";

import Routes from "./routes";

import { renderRoutes } from "react-router-config";



const app = new Koa();

app.use(koaStatic("public"));



app.use(async (ctx) => {

  const content = renderToString(

    

      
{renderRoutes(Routes)}


    

  );

}

``` ```

以上就完成了路由的配置,还比较简单吧,此时页面的路由跳转基本没问题了。

2.5 Redux的同构

如何让前端页面的应用状态可控、让协作开发高效也是我们必须考虑的问题。Redux作为React最常见的状态管理方案被很多项目引入来解决这一问题。那引入Redux如何被到同构项目中?这里,我们还是简单回顾一下redux运作流程,不熟悉的可以移步redux熟悉下。接下来开启Redux的同构之旅。

第一步:创建全局STORE

首先我们创建了一个store.js,初始化配置并导出一个函数用来实例化store,以提供给客户端和服务端同时使用。为什么store要导出一个函数?因为这段代码后面服务端使用时,如果下面的store导出的是一个单例,所有的用户用的是同一份store,那将是灾难性的结果。

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">/<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">/ store.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">im<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">port { createStore, applyMiddleware, combineReducers } from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"redux"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">i<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">m<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">port<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> thunk from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"redux-thunk"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// 这里提<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">前引入了About组件下的store<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import { r<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">educer as AboutReducer } from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"../containers/store"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">c<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">onst reducer <span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">= combineReducers({<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  a<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">bout: AboutR<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">educer,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">});<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// 导出成<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">函数的原因<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">export<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> def<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">ault (<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">)<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">return<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> crea<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">te<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">St<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">ore(reducer, applyMiddleware(thunk));<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">};


##### 第二步:连接全局STORE

客户端的写法还是熟悉的样子:
```
//client.js

import { Provider } from "react-redux";

import getStore from "./store";



const App = () => {

  return (

    

      

        
{renderRoutes(Routes)}


      


    

  );

}; ``` ```

服务端server.js的写法也是类似:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
// server.js<br></br>import { Provider } from <span style="color: rgb(152, 195, 121);line-height: 26px;">"react-redux";<br></br>import getStore from <span style="color: rgb(152, 195, 121);line-height: 26px;">"./store";<br></br><br></br>const app = new Koa();<br></br>app.use(koaStatic(<span style="color: rgb(152, 195, 121);line-height: 26px;">"public"));<br></br><br></br>app.use(async (ctx) => {<br></br>  const content = renderToString(<br></br>    <Provider store={getStore()}><br></br>      <StaticRouter location={ctx.request.path}><br></br>        <div>{renderRoutes(Routes)}</div><br></br>      </StaticRouter><br></br>    </Provider><br></br>  );<br></br>}<br></br>


##### 第三步:组件的store

新建About 组件使用的store,其action和reducer的写法如下,注意此时我们在action里发起了一个异步请求。
```
// containers/store/reduccer.js

import { CHANGE_LIST } from "./constants";



const defaultState = { name: "panpan", age: 18, list: [] };

export default (state = defaultState, action) => {

  switch (action.type) {

    case CHANGE_LIST:

      return { ...state, list: action.payload };

    default:

      return state;

  }

}; ``` ```
<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// containers/store/action.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import axios from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"axios"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import { CHANGE_LIST } from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./constants"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const changeAction = (payload) => ({<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">type<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">: CHANGE_LIST,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  payload,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">});<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const getHomeList = () => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">return<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> (dispatch) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">return<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> axios.get(<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"http://localhost:3008/mock/1"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">).<span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(198, 120, 221);line-height: 26px;">then<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">((res) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      dispatch(changeAction(res.data.data || []));<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    });<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  };<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">};<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">export<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> default {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  getHomeList,<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">};

About组件连接Redux:
```
// containers/About.js

import { connect } from "react-redux";

import { action } from "./store";



const About = () => {

  useEffect(() => {

    props.getList();

  }, []);

  // ...

}

const mapStateToProps = (state) => ({

  name: state.about.name,

  age: state.about.age,

  list: state.about.list,

});

const mapDispatchToProps = (dispatch) => ({

  getList() {

    dispatch(action.getHomeList());

  },

});



export default connect(mapStateToProps, mapDispatchToProps)(About); ``` ```

经过一番改造后,项目变成了这样:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
├── package.json<br></br>├── mock.server.js<br></br>├── webpack.client.js<br></br>├── webpack.server.js<br></br>├── routes.js<br></br>├── server.js<br></br>├── client.js<br></br>└── store<br></br>    └── index.js<br></br>└── containers<br></br>    ├── Home.js<br></br>    ├── About.js <br></br>    └── store<br></br>        ├── index.js<br></br>        ├── action.js<br></br>        ├── reducer.js<br></br>        └── constants.js  <br></br>

通过上述操作Redux 基本可以跑起来了,可以发现写法跟熟悉的客户端渲染大体差不多,只多了引入server端的步骤。但是目前的redux还存在一定的问题,我们一起再来看。


##### 服务端没数据问题

上面的redux在同构项目中跑起来咋一看是没什么问题,但当我们把js禁用或直接查看源代码时,就会发现About组件内并不存在异步请求的数据列表,换句话说服务器端的store的list始终是空的,服务端并没有发起相应的数据请求。为什么会这样呢?

分析一下:当浏览器发起页面请求后,服务器接收到请求,此时服务器和客户端的store的list都为空, 接着服务端开始渲染执行React代码,根据此前讲rendertostring坑之一,服务端调用React代码时里面的生命周期的只到初始化时的render及之前,而About组件内发起的异步请求是在useEffect内,相当于是在*ComponentDidMount*阶段发起请求,所以服务器端渲染时根本不会执行里面的异步请求,因此服务器端的store的list始终是空的,所以我们看不到列表数据。之后客户端拉取了JS并执行React代码,React在客户端能够执行完整的生命周期,故可以执行useEffect里的函数获取到数据并渲染到页面。换而言之,目前获取异步数据只是进行了后期的客户端渲染阶段。

如何让服务端将获得数据的操作执行一遍,以达到真正的服务端渲染的效果?这就是接下来要讲的服务端渲染异步数据。


### 2.6 服务端渲染异步数据

上文的同构项目中跑起来后,我们是在组件的useEffect中发起的异步请求,服务端并不能执行到这一块。那能不能在其他生命周期获取异步请求数据?答案是不推荐,因为React16采用了Fiber架构后,render之前的生命周期都可能被中断而执行多次,类似getDerivedStateFromProps(静态方法), ComponentWillMount(废弃), UNSAFE\_componentWillMount的生命周期钩子都有可能执行多次,所以不建议在这些生命周期中做有请求数据之类副作用的操作。而componentDidMount在render之后是确定被执行一次的,所以React开发中建议在componentDidMount生命周期函数进行异步数据的获取。那有没有其他的解决方案呢? React Router 恰好也考虑到了这点,提供了这样一种解决方案,需要我们对路由进行一些改造。

**React Router 解决方案**

React Router 解决方案的基本思路:

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/204a0546b3755c6ab6ae721db9b67ed0)
首先,改造原有的路由,配置了一个loadData参数,这个参数代表要在服务端获取数据的函数:
```
// router.js

import Home from "./containers/home";

import About from "./containers/about";

export default [

  { path: "/", component: Home, exact: true },

  {

    path: "/about",

    component: About,

    exact: true,

    loadData: About.loadData,

  },

];

``` ```

在服务端匹配路径对应的路由,如果这个路由对应的组件有loadData方法,那么就执行一次,并将store传进loadData里面去,注意loadData函数调用后需要返回Promise对象,等待Promise.all都resolve后,此时传过去的store已经完成了更新,便可以在renderToString时放心使用:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// server.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import { renderRoutes, matchRoutes } from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"react-router-config"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import { Provider } from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"react-redux"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import getStore from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./store"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">app.use(async (ctx) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  const store = getStore();<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  // 匹配到路径对应的路由<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  const matchArr = matchRoutes(Routes, ctx.request.path);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">let<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> promiseArr = [];<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  matchArr.forEach((item) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    // 判断有没有 loadData<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(198, 120, 221);line-height: 26px;">if<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> (item.route.loadData) {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      // 要将store传递过去 <br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      // item.route.loadData() 返回的是一个promise<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      promiseArr.push(item.route.loadData(store));<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    }<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  });<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  // 等待异步完成,store已完成更新<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  await Promise.all(promiseArr);<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  const content = renderToString(<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    <Provider store={store}><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      <StaticRouter location={ctx.request.path}><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        <div>{renderRoutes(Routes)}</div><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      </StaticRouter><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    </Provider><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  );<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">}

接下来是组件内loadData函数,发起异步请求,并返回一个Promise
```
// containers/About.js

import { connect } from "react-redux";

import { action } from "./store";



const About = (props) => {

  useEffect(() => {

    props.getList();

  }, []);

  // ...

};

About.loadData = (store) => {

  //可能存在多个数据请求,所以用promise.all包一层 

  return Promise.all([store.dispatch(action.getHomeList())]);

};

const mapStateToProps = (state) => ({

  name: state.about.name,

  age: state.about.age,

  list: state.about.list,

});

const mapDispatchToProps = (dispatch) => ({

  getList() {

    dispatch(action.getHomeList());

  },

});



export default connect(mapStateToProps, mapDispatchToProps)(About); ``` ```

通过以上改造,服务端可以获取到异步数据了。但是眼尖的朋友可能注意到,页面此时还存在问题,页面中还不时存在list闪烁问题,这是什么原因导致的呢?这就涉及到数据的同步问题。

数据的注水和脱水

让我们来分析一下客户端和服务端的运行流程:

可以看到客户端和服务端的store 都经历了初始化置空的问题,导致store不同步, 那如何才能让这两个store的数据同步变化呢? 这就涉及到数据的注水和脱水。“注水”:在服务端获取获取之后,在返回的html代码中加入这样一个script标签,这样就将服务端store数据注入到了客户端全局的window.context对象中。

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
// server.js<br></br>app.use(async (ctx) => {<br></br>  // ...<br></br>  ctx.body = `<br></br>    <html><br></br>      <head><br></br>        <title>ssr</title><br></br>        <br></br>      </head><br></br>      <body><br></br>        <div id=<span style="color: rgb(152, 195, 121);line-height: 26px;">"root"><span style="color: rgb(209, 154, 102);line-height: 26px;">${content}</div><br></br>        <script><br></br>         window.context = {<br></br>          state: <span style="color: rgb(209, 154, 102);line-height: 26px;">${JSON.stringify(store.getState())}<br></br>        }<br></br>        </script><br></br>        <script src=<span style="color: rgb(152, 195, 121);line-height: 26px;">"./index.js"></script><br></br>      </body><br></br>    </html><br></br>   `;<br></br>});<br></br>

“脱水”处理:把window上绑定的数据给到客户端的store,因此在store.js区分了客户端和服务端不同的导出函数。
```
// store.js

// 客户端使用

export const getClientStore = () => {

  const defaultState = window.context ? window.context.state : {};

  return createStore(reducer, defaultState, applyMiddleware(thunk));

};

// 服务端使用

export const getServerSore = () => {

  return createStore(reducer, applyMiddleware(thunk));

}; ``` ```

至此redux 包含异步数据的获取的同构就完成了。

2.7 css 的服务端渲染

为什么需要做css要服务端渲染?主要是解决页面的FOUC 闪屏问题。页面如果没做css的服务服务端渲染,我们一开始拉取到的HTML页面是没有样式的,页面的样式正常显示主要依赖于后面的客户端渲染,我们知道客户端渲染的时间相对要长很多,如果渲染前后存在较大的样式差距,就会引起闪屏。

还是以About组件为例,页面中加入样式:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
.title {<br></br>  color: aqua;<br></br>  background: <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#999;<br></br>  height: 100px;<br></br>  line-height: 100px;<br></br>  font-size: 40px;<br></br>}<br></br>
```
// containers/About.js

import styles from "./about.css";



const About = (props) => {

  // ...

  return (

    List Content

  );

};

``` ```

需要webpack中相应的配置处理css,我们先处理客户端打包

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
{<br></br>    <span style="color: rgb(230, 192, 123);line-height: 26px;">test: /\.css?$/,<br></br>    use: [<br></br>      <span style="color: rgb(152, 195, 121);line-height: 26px;">"style-loader",<br></br>      {<br></br>        loader: <span style="color: rgb(152, 195, 121);line-height: 26px;">"css-loader",<br></br>        options: {<br></br>          modules: <span style="color: rgb(86, 182, 194);line-height: 26px;">true,<br></br>        },<br></br>      },<br></br>    ],<br></br> }<br></br>

上面的代码跑起来,就会发现有明显的闪烁,复盘一下:页面一开始html是没样式的,待到客户端js执行完成后,页面才突然有了样式显示正常。为了避免这种闪烁带来的不愉快体验,服务端也需要进行css的渲染。


##### 在服务端如何处理css?

客户端webpack采用css-loader处理后,style-loader直接将样式通过DOM操作进行插入,这对于浏览器环境很好很方便,但是对于服务端的Node环境,这就没法愉快的玩耍了。Node环境下可将样式插入到生成的html字符串中,而不是进行DOM操作。这时就需要用到另外一个跨平台的loader:isomorphic-style-loader,在服务端的webpack配置是这样:
```
// webpack.server.js

{

    test: /\.css?$/,

    use: [

      "isomorphic-style-loader",

      {

        loader: "css-loader",

        options: {

          modules: true,

        },

      },

    ],

  },

``` ```

通过isomorphic-style-loader处理,我们可以在组件内直接通过styles._getCss即可拿到CSS代码,但这还不够,如何将拿到的css传回到服务端sever.js里从而塞入返回体呢?

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
// containers/About.js<br></br>import styles from <span style="color: rgb(152, 195, 121);line-height: 26px;">"./about.css";<br></br><br></br>const About = (props) => {<br></br>  console.log(styles._getCss && styles._getCss());<br></br>  // ...<br></br>}<br></br>


##### **CSS的服务端渲染**

CSS服务端渲染还需要借助StaticRouter中已经帮我们准备的一个钩子变量context,传入外部对象变量到StaticRouter到context里。路由配置对象routes中的组件都能在服务端渲染的过程中拿到这个context,这个context对于组件来说相当于props.staticContext。将获取到的css推入到staticContext.css里,这样服务端的context对象就完成了改变,我们便可以拼接css到head中。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/977ff03f1b172a802f5203db45effdde)
```
// server.js

app.use(async (ctx) => {

  // 初始化 context

  let context = { css: [] };

  const content = renderToString(

    

      // StaticRouter 传入context,组件接收到的props为staticContext

      

        
{renderRoutes(Routes)}


      

    

  );

  // 将css插入到head里面

  ctx.body = `

    

      

        ssr

        

      

      ...

    

   `;

}); ``` ```
<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// containers/About.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import styles from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"./about.css"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const About = (props) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">   // 只有服务端才传过来了context,组件中props为staticContext<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(198, 120, 221);line-height: 26px;">if<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> (props.staticContext) {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    // 将css推入数组,改变了传入的context<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    props.staticContext.css.push(styles._getCss());<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  }<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  // ...<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">}

通过上面的操作,css的服务端渲染基本能正常工作。需要注意的是React-router传过来的context只有配置对象routes中的组件才能拿到,如果组件里面再嵌入子组件,需要把staticContext透传过去,才能对子组件props.staticContext进行相应操作。当然这里更推荐官方demo里的另一种写法,且看下面。


##### **更推荐写法**

我们可以查看isomorphic-style-loader的demo,更推荐写法是:客户端、服务端都用isomorphic-style-loader,webpack处理客户端css是这样配置的:
```
//  webpack.client.js

{

    test: /\.css?$/,

    use: [

      "isomorphic-style-loader",

      {

        loader: "css-loader",

        options: {

          modules: true,

        },

      },

    ],

},

``` ```

组件内的写法也有相应改变,isomorphic-style-loader 提供了hooks:useStyles

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">// containers/About.js<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">import useStyles from <span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;color: rgb(152, 195, 121);line-height: 26px;">"isomorphic-style-loader/useStyles"<span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">;<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">import styles from <span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;color: rgb(152, 195, 121);line-height: 26px;">"./about.css"<span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">;<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">const About = (props) => {<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">  useStyles(styles);<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">  // ...<br style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;"></br><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;white-space: pre-wrap;float: none;display: inline !important;">}<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">

在服务端的代码里是这样的:
```
// server.js

import StyleContext from "isomorphic-style-loader/StyleContext";

// ...



app.use(async (ctx) => {

  const css = new Set();

  const insertCss = (...styles) =>

    styles.forEach((style) => css.add(style._getCss()));

  const content = renderToString(

    

      

        

          
{renderRoutes(Routes)}


        

      

    

  );

  ctx.body = `

    

      

        ssr

        

      

      

        "root">${content}

        

        "./index.js">

      

    

  `;

}) ``` ```

类似的,客户端也需要做下面的调整:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// client.js<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">import StyleContext from <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(152, 195, 121);line-height: 26px;">"isomorphic-style-loader/StyleContext"<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">;<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">// ...<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">const App = () => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  const insertCss = (...styles) => {<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    const removeCss = styles.map((style) => style._insertCss());<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">return<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> () => removeCss.forEach((dispose) => dispose());<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  };<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  <span style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;color: rgb(230, 192, 123);line-height: 26px;">return<span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"> (<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    <Provider store={getStore()}><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      <StyleContext.Provider value={{ insertCss }}><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        <BrowserRouter><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">          <div>{renderRoutes(Routes)}</div><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">        </BrowserRouter><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">      </StyleContext.Provider><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">    </Provider><br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">  )<br style="white-space:pre-wrap;margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"></br><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(40, 44, 52);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">}


### 



### 2.8 优化title和description

页面中的title,keywords和description在SEO中具有举足轻重的地位。上面的React项目中初始只有一份title和description,虽然不同页面可使用js生成的动态title和descroption,但这类信息搜索引擎是没办法抓取到的。为了更好的SEO,我们需要根据不同的页面组件显示来对应不同的网站标题和描述,这如何实现的呢?我们可以引入react-helmet来解决这个问题。

**引入react-helmet**

组件内:
```
// containers/About.js

 import { Helmet } from "react-helmet";

 // ...

 return (

    


      

        "utf-8" />

        SSR About Page

        "description" content="this is panpan about page" />

      


      


  )

  // .. ``` ```

服务端html部分:

<pre style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_svg/PFPUMLY8F77hIdjvPZicZQzb8TNT8jd4PrlicHRKxa0R7F7wzABRVXaVdhOdHB5gOaA6Pquh7NMg3H1yacxUX1uiaibCyo76cJjF/640?wx_fmt=svg");height: 30px;width: 764px;background-size: 40px;background-color: rgb(40, 44, 52);margin-bottom: -7px;border-radius: 5px;background-position: 10px 10px;background-repeat: no-repeat;">```
// server.js<br></br>const html = `<br></br>    <!doctype html><br></br>    <html ><br></br>        <head><br></br>            <span style="color: rgb(209, 154, 102);line-height: 26px;">${helmet.title.toString()}<br></br>            <span style="color: rgb(209, 154, 102);line-height: 26px;">${helmet.meta.toString()}<br></br>        </head><br></br>    </html><br></br>`;<br></br>





-


3、开箱即用的SSR框架
------------


#### 



#### Next.js

Next.js是一款面向生产使用的 React 框架,提供了好些开箱即用的特性,支持静态渲染/服务端渲染混用、支持 TypeScript、支持打包优化、支持按路由预加载等等:其中,完善的静态渲染/服务端渲染支持让 Next.js 在 React 生态中独树一帜。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/569037b7192a3ec07e44510d1530dd9b)

Next.js 中的预渲染(Pre-rendering),具体的分为两种方式:

SSG(Static Site Generation):也叫 Static Generation,在编译时生成静态 HTML

SSR(Server-Side Rendering):也叫 Server Rendering,用户请求到来时动态生成 HTML

与 SSR 相比,Next.js 更推崇的是 SSG,因为其性能优势更大(静态内容可托管至 CDN,性能提升立竿见影)。因此建议优先考虑 SSG,只在 SSG 无法满足的情况下(比如一些无法在编译时静态生成的个性化内容)才考虑 SSR、CSR。


#### UmiJS

Umi 很多功能是*参考next.js*做的,要说有哪些地方不如Umi,可能是不够贴近业务,不够接地气。Umi 3 结合自身业务场景,在 SSR 上做了大量优化及开发体验的提升,内置 SSR,一键开启,开发调试方便。Umi 不耦合服务端框架,无论是哪种框架或者 Serverless 模式,都可以非常简单进行集成。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/b7eb64e4c4d44f893503a6ae83dcc3dd)


#### icejs

icejs是淘系前端飞冰团队开发的一个基于React 的渐进式框架。支持服务端渲染(即 SSR)能力,开发者可以按需一键开启 SSR 的模式。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/1b87bd86cd9297411d2ecdde750c596d)

4、一些新的API
---------

**新Hook:useId**

服务端、客户端无法简单生成稳定、唯一的id是个由来已久的问题,早在多年前就有人提过issue。直到最近React conf 2021 上再次提出这个问题,推出了官方Hook——useId,可在服务端、客户端生成唯一的id,其背后的原理—— 每个id代表该组件在组件树中的层级结构,具体的就不展开了,有兴趣的可以去了解一下。

**服务端suspense**

React 18 带来了内置支持了 React.lazy 的 全新 SSR 架构, 性能优化的利器。这个架构能很大程度上提升用户体验:对比React18之前对整个应用hydrate,现在可以做到对单个组件hydrate,带来的一个好处,就是可以设置组件的渲染优先级。对比code splitting的优势在于如果同时设置了多个suspense组件,但是用户点击了之中某个组件,会优先hydrate那个被点击的组件。

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2022-09-11/368c219eae8a0d141c79cfad9563be7f)

5、结语
----

以上就是本文关于React服务端渲染 ( SSR )的全部内容, 内容还是比较复杂的 。对于服务端渲染原理的学习可以帮助更好借鉴优秀的程序写法和激发对日常代码编程架构的思考,如果你更倾向箱即用的解决方案,那可以使用现有的 SSR 框架来搭建项目,这些框架的模版抽象和额外的功能扩展可以提供平滑的开箱体验。

附Demo地址:https://github.com/hellopanpan/my-ssr-react

参考:

https://juejin.cn/post/6844904017487724557

https://juejin.cn/post/6844903881390964744

https://zhuanlan.zhihu.com/p/90746589

https://www.jianshu.com/p/3aa991ac3ce7

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8