gnosis23 / hello-world-blog

还是 issues 里面写文章方便
https://bohao.work
0 stars 0 forks source link

ssr #100

Open gnosis23 opened 2 years ago

gnosis23 commented 2 years ago

React 现在鼓励 ssr 了,得了解下原理,找了几篇文章看看:

SSR流程:

截屏2021-12-11 上午10 20 41

关于前面文章里提到的视频教程,可以到B站上搜搜

2022 更新

gnosis23 commented 2 years ago

node识别JSX

直接用 webpack 打包 node 代码

// webpack.config.js
const path = require("path")
const webpackNodeExternals = require("webpack-node-externals")

module.exports = {
  target: "node",
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "build"),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
    ],
  },
  externals: [
    // 不要把 node_modules 里的代码打包进去
    webpackNodeExternals()
  ]
}

server端的代码如下:

import express from "express"
import React from "react"
import { renderToString } from "react-dom/server"
import Home from "./client/components/Home"

const app = express()

app.use(express.static("public"))
app.get("/", (req, res) => {
  const content = renderToString(<Home />)

  const html = `
    <html>
      <head></head>
      <body>
        <div id="root">${content}</div>      
        <script src="bundle.js"></script>
      </body>
    </html>
  `

  res.send(html)
})

app.listen(3000, () => {
  console.log('listening on port 3000')
})
gnosis23 commented 2 years ago

支持SSR

比如 React-Router 和 Redux 都号称支持 SSR,那么它们是怎么支持的呢...

React Router

Server端使用 StaticRouter 来得到路径(因为Server没有history这些)

app.get("*", (req, res) => {
  const content = renderToString(
    <StaticRouter location={req.path}>
      <Routes />
    </StaticRouter>
  )
 // ...
});

数据加载

需要满足以下几点:

例1: getServerSideProps

看看 Next.js 里面的 getServerSideProps 方法:

export async function getServerSideProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

  return {
    props: {}, // will be passed to the page component as props
  }
}

只要组件里包含了 getServerSideProps ,那么就可以把数据作为 props 传到组件里

例2: useLoaderData

可以导出一个 loader 函数,在组件里可以使用 useLoaderData 来获取数据

export function loader() {
  let res = fetch("https://api.gitub.com/gists");
  return res.json();
}

export default function GistsRoute() {
  let gists = useLoaderData();
  return (
    <ul>
      {gists.map(gist => (
        <li>
          <a href={gist.html_url}>{gist.id}</a>
        </li>
      ))}
    </ul>
  );
}
gnosis23 commented 1 year ago

React 18 新SSR架构

一些关键信息摘录

老的 SSR 会按 fetch - server render - client - hydration 4个步骤执行,只有前一步完成才能继续下一步。 而新的架构可按 <Suspense> 划分应用,不用等到所有部分好了才能看到。

Streaming HTML 技术可以先把部分页面发送到客户端,然后等到其他部分好了再重新发生过去,客户端负责插入到页面上。 举个例子,假设文章里有些评论,但是评论加载比较慢,可以先把评论用 Suspense 包裹,那么 React 就能先发送占位符,等到评论加载好了再发送过去。

Selective Hydration 技术可以让页面先部分 hydration。如之前的例子,假设评论没有加载好,页面也能交互~

结论

1 多用用 Suspense 2 fetch 放在相关组件里