server components--提高页面性能的新利器

1,193 阅读4分钟

最近react团队宣布了一个新特性: server components

目前这个feature仍然是实验性的,目前没有还没有正式的文档。简单来说server components就是在服务端渲染的组件,但是状态并没有丢失。

这个有点类似SSR,但是工作原理有一些差异

SSR 的限制

目前,ssr方案被普遍用于提高首屏渲染速度。在不使用ssr的情况下,html文件内容很简单只有一个祖父节点来挂载整个应用

具体如下: 当使用SSR的时候,客户端的JS代码会在服务端运行,从而生成需要首屏渲染的DOM节点,客户端在请求html文件的时候,由于所有节点都已经生成,所以就提高了首屏渲染的速度

但是这种方式并没有提高可交互的时间,我们需要等待所有的js都加载完了。所以ssr通常用来提高首屏渲染速度

0 打包体积

很多情况,JS包是非常大的。例如在某个场景需要使用moment.js,而且这个包只会使用一次。
这个时候对应用的性能会造成一定的影响,因为该库只会使用一次,但是浏览器会下载整个包的内容。

基于这个问题,可以用tree-shaking来过滤一些我们不需要的代码,但是tree-shaking无法解决某个库只使用了一次的情况,例如上面的情况需要使用moment.js进行时间格式化。

server components可用于解决上面提到的问题:

// NoteWithMarkdown.js
import marked from 'marked' // 35.9k
import sanitizeHtml from 'sanitize-html'; // 206k

function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text))
  return (/* render */)
}

上面代码中,主要做的就是引入两个库用于渲染Markdown。但是可以看到这个库sanitize-html非常大。 此外这里只是提供了渲染的markdown,这个markdown并不能进行交互。这个时候引入这个库是否是必要的,因为需要消耗大量时间去拉取资源

使用server component解决上面问题

// NoteWithMarkdown.server.js
import marked from 'marked' // 35.9k
import sanitizeHtml from 'sanitize-html'; // 206k

function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text))
  return (/* render */)
}

内容其实没有差异,文件名从NoteWithMarkdown.js变成了NoteWithMarkdown.server.js。相比与正常的组件渲染,客户端不会去请求整个markdown库,客户端只会请求渲染后的jsx内容

因此,这种方式对于客户端来说减少了请求的包体积,把这部分的内容放在了后端来实现

自动代码分割

在以往我们需要使用React.lazy这串代码来实现code spliting

// PhotoRenderer.js (before Server Components)
import React from "react";

const OldPhotoRenderer = React.lazy(() => import("./OldPhotoRenderer.js"));
const NewPhotoRenderer = React.lazy(() => import("./NewPhotoRenderer.js"));

function Photo(props) {
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <PhotoRenderer {...props} />;
  }
}

上面这段代码是只有加载对应的组件的时候才会去加载对应的 js 文件从而达到让客户端请求更少的包资源。 server components引入了自动代码拆分,把客户端所有的正常导入都视为可能的动态组件。同时他也允许开发者选择需要更早加载的组件

// PhotoRenderer.server.js - Server Component
import React from "react";

import OldPhotoRenderer from "./OldPhotoRenderer.client.js";
import NewPhotoRenderer from "./NewPhotoRenderer.client.js";

function Photo(props) {
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <PhotoRenderer {...props} />;
  }
}

具有完整的后端权限

本质上server component就是运行在后端环境中的组件,所有该组件拥有后端的所有权限,例如能够操作数据,文件系统等等

server component让组件具有上述功能变得更加简单,react 团队针对实现上面功能提供了几个库: react-pgreact-fetchreact-fs

例如在下面组件中,我们调用了操作文件的能力

import fs from 'react-fs';

function Note({id}) {
  const note = JSON.parse(fs.readFile(`${id}.json`))
  return <NoteWithMarkdown note={note}>
}

此外还可以使用react提供的其他库来操作数据库

总结

要想了解更多,可以参考react团队搞的demo 这个新feature提供了对于提高页面性能的一些新思路,不得不佩服facebook的工程师们。 但是看完这个提案以后以后一些小疑问:

  1. 目前看来这个server component适用于状态不频繁的组件
  2. 社区工程化需要跟进
  3. 是不是会带来一些安全问题
  4. 如何拆分客户端组件和server component(因为产品功能是不断叠加的,当初设计是否会一直满足后续的需求)