从react server components聊聊前端渲染的前生今世

2021-01-29 10:40:14 浏览数 (1)

最近React又出了新的特性,react server components,目前还属于开发期,并不稳定。但是,从React的这些动作可以看出,前端在如何渲染页面的道路上,一直在探索,在改变,也在朝着更快,更优雅,体验更好的方向努力。

  • 官网资料:https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html
  • 知乎文章

青铜时代 - PHP/ASP/JSP

这是最早的服务器渲染。服务器接到请求后,查询数据库然后把数据“塞”到页面里面,最后把生成好的 html 发送给客户端。当用户点击某个链接后,继续重复上面的步骤,展示新的页面。

这个时期,有各种各样的后端模板出现,最常见的应该是jsp。前后端未分离,每个程序员基本是全栈开发,纯前端岗位很少。

代码可能是这样的:

image.png

代码语言:javascript复制
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="beandemo.jsp" method="post">
        用户名:<input type="text" name="username">
        密码:<input type="password" name="password">
        <input type="submit" value="Submit">
    </form>
</body>
</html>
痛点

用户体验自然比较糟糕,每个操作几乎都要刷新页面,服务器处理完之后再返回新的页面。 而且,前端技术发展被后端牢牢制约住,举步维艰。

白银时代 - SPA

Ajax兴起之后,程序终于可以将JavaScript从HTML页面里分离出来(感谢谷歌),利用Ajax动态获取云端数据的能力,从而实现HTML的动态渲染。

这一时期,出现了很多优秀的SPA框架,Top 3 的自然为Angular/Vue/React三驾马车。Angular提供了整套解决方案,而React和Vue更专注在View层。

单页应用(SPA)主要为客户端渲染。服务器接到请求后,把 index.html 以及 js/css/img 等发送给浏览器,浏览器负责渲染整个页面。后续用户操作和前面的 php/jquery 一样,通过 ajax 和后端交互。

SPA一个典型的特征是,服务器返回的HTML body体,除了一个根DOM节点再无其他内容。

HTML可能是这样的:

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="description" content="React with Server Components demo">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="style.css" />
    <title>React Notes</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

所以,痛点也就显而易见了。

痛点
  • 第一次访问时只返回了什么内容都没有的 index.html 空页面,没法做 SEO。
  • 页面需要等到 js/css 和接口都返回之后才能显示出来,首次访问会有白屏。

PS:后期我们通过骨架屏方式可以缓解白屏的糟糕体验。

黄金时代 - SSR

为了解决SEO和首屏优化,且基于Node.js大行其道,前端开辟了SSR新的渲染方式。

第一次访问时由服务器(通常是 Node.js)来渲染页面,然后把已经渲染好的 html 发送给浏览器。后续的用户操作依然通过 ajax 获取数据,然后在浏览器端渲染组件和页面。

这种模式看起来很像早期的JSP(核心思想都是服务端完成页面渲染工作),最大的不同在于,其建立在前端成熟的生态模式上,是基于Node.js同构方案的最佳实践。React有next.js框架,Vue有nuxt.js框架,都可以同样的组件开发方式支持客户端和服务端不同渲染。

以next.js为例,关键的入口文件_app.js可能是这样的:

代码语言:javascript复制
import React from 'react'
class MyApp extends App<any> {
  static async getInitialProps(options) {
    const { ctx } = options
    const {
      req,
      query: { code },
    } = ctx
  ...
    return { pageProps, }
  }

  render() {
    return (
        <Container>
          <Head>
            <title>
              {title || __.common.title}-{__.common.title_suffix}
            </title>
            <meta name="description" content={description || __.common.meta_description_default} />
            <meta name="keywords" content={keywords || __.common.meta_keywords} />
          </Head>
          <Provider store={reduxStore}>
            <Layout>{HocComponent}</Layout>
          </Provider>
        </Container>
    )
  }
}
痛点

SSR 首屏还是太慢。 原因:服务端渲染时请求接口太多,导致响应时间太长。

新时代的尝试:react server components

用官网视频里面的两张图来对比下react server components和普通的react components区别。

现在的模式是,客户端从服务端获取数据,然后基于数据渲染组件。

image.png

react server components模式,直接在服务端获取组件。

image.png

官网给了Demo 例子:https://github.com/reactjs/server-components-demo,有人将其部署,在线体验:https://react-server-components.musicfe.dev/

我们跟着Demo,看下整个流程是怎样的。

1. 启动本地服务器

image.png

2. webpack打包

image.png

ReactServerWebpackPlugin插件配置可以看出,server components是不会被打包到build包里面的。

3. 页面初始化

image.png

没有.client.js.server.js后缀的js文件,既可以作为服务端组件使用,也可作为客户端组件使用。

4. 首屏渲染

image.png

首屏根据location对象(会序列化为缓存KEY)从缓存中获取server组件,通过response.readRoot()取到组件对象,渲染组件。

5. Cache.client

createFromFetch如何响应呢?

6. server端处理

server-handler.png

看一眼../build/react-client-manifest.json的本地mock数据是怎样的:

代码语言:javascript复制
{
  "file:///Users/yelan/git-study/server-components-demo/src/Cache.client.js": {
    "": {
      "id": "./src/Cache.client.js",
      "chunks": [
        "main"
      ],
      "name": ""
    },
    "*": {
      "id": "./src/Cache.client.js",
      "chunks": [
        "main"
      ],
      "name": "*"
    },
    "useRefresh": {
      "id": "./src/Cache.client.js",
      "chunks": [
        "main"
      ],
      "name": "useRefresh"
    },
    "useServerResponse": {
      "id": "./src/Cache.client.js",
      "chunks": [
        "main"
      ],
      "name": "useServerResponse"
    }
  },
...
}

schema的含义,笔者还不明,等待更官网的解释吧。

7. 浏览器请求情况

Request:

request.png

Response:

reponse.png

如此一来,大致的流程就比较清晰了。

那么,Server Components和SSR的区别在哪里呢?

  • SSR是在服务端把js转成HTML,返回给客户端(基于不同SSR框架,客户端会视情况重新做全量或者部分重复渲染);而Server Components在服务端输出chunks,客户端渲染组件。

Server Components和SPA的区别呢?

  • 如果组件依赖云端数据,那么,SPA是在客户端同时做数据获取和组件创建,而Server Components下客户端获取到的组件已经是经过数据处理过的纯组件。

Server Components目前有什么局限性呢?

  • 不能使用stateeffects、以及浏览器的一些API,目前只适合用在纯展示的组件。比如商品列表等。

0 人点赞