mcpField
C
client-side-rendering

client-side-rendering

github.com/patternsdev开发2571.0k 次安装28 周前更新
代码仓库

关于 client-side-rendering

介绍客户端渲染(CSR)的学习技能,由浏览器中的 JavaScript 负责页面渲染、数据获取和路由。内容涵盖适用场景、权衡因素及加载性能优化方法。

主要功能

  1. 01使用浏览器 JavaScript 渲染页面
  2. 02支持单页应用导航
  3. 03说明 SEO 与加载时间的权衡
  4. 04介绍代码拆分和延迟加载
  5. 05讲解 Service Worker 外壳缓存

适用场景

  1. 01构建交互式内部仪表板
  2. 02创建无需刷新页面的 SPA
  3. 03通过延迟加载减少初始 JavaScript

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill client-side-rendering

安装后在 Claude Code 中输入/client-side-rendering确认已加载。

详细介绍

客户端渲染

在客户端渲染(CSR)中,服务器只渲染页面的基本 HTML 容器。显示页面内容所需的逻辑、数据获取、模板渲染和路由,均由在浏览器/客户端中执行的 JavaScript 代码处理。CSR 作为构建单页应用的方法而广受欢迎,也模糊了网站与已安装应用之间的界限。

适用场景

  • 适用于 SEO 优先级不高的内部工具、仪表板或 SPA
  • 适用于需要完全交互式单页应用体验的场景

不适用场景

  • 对 SEO 至关重要,搜索引擎需要通过服务器渲染的 HTML 为内容建立索引的页面
  • 内容量较大、用户必须等到 JavaScript 加载并执行后才能看到内容的网站
  • 首次内容绘制时间是关键指标的场景——CSR 会将所有渲染工作推迟到客户端

说明

  • 保持初始 JavaScript 包较小(压缩并经过 gzip 后 < 100-170KB),以实现较快的首次内容绘制
  • 使用代码拆分和延迟加载,推迟非关键 JavaScript 的加载
  • 对于 SEO 和初始加载性能很重要的面向公众页面,可考虑 SSR/SSG
  • 使用 Service Worker 和应用 Shell 缓存,提升离线访问和重复访问时的性能

详情

基本结构

考虑这个简单示例:使用 React 在页面上显示并更新当前时间。

import { createRoot } from 'react-dom/client'
import { useEffect, useState } from 'react'

function Clock() {
  const [time, setTime] = useState(new Date())

  useEffect(() => {
    const id = setInterval(() => setTime(new Date()), 1000)
    return () => clearInterval(id)
  }, [])

  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {time.toLocaleTimeString()}.</h2>
    </div>
  )
}

createRoot(document.getElementById('root')!).render(<Clock />)
<div id="root"></div>

HTML 只包含一个根 <div> 标签。另一方面,内容的显示和更新完全由 JavaScript 处理。此过程不会与服务器往返通信,渲染后的 HTML 会直接在原处更新。这里的时间可以替换为其他实时信息,例如从 API 获取的汇率或股价,并在不刷新页面、也不与服务器往返通信的情况下显示。

JavaScript 包与性能

随着页面复杂度增加,例如需要显示图片、展示数据存储中的数据并包含事件处理,渲染页面所需的 JavaScript 代码也会变得更复杂、体积更大。CSR 会产生较大的 JavaScript 包,导致页面的 FCP 和 TTI 延迟。

随着 bundle.js 体积增大,FCP 和 TTI 会推迟。这意味着用户从 FP 到 FCP 期间看到的将一直是空白屏幕。

优缺点

使用 React 时,大部分应用逻辑都在客户端执行,并通过 API 调用与服务端交互,以获取或保存数据。因此,几乎所有 UI 都由客户端生成。整个 Web 应用会在首次请求时加载。用户点击链接进行导航时,服务端不会收到新的页面渲染请求。客户端代码会运行并更改视图或数据。

CSR 让我们能够构建支持无刷新导航的单页应用,并提供出色的用户体验。由于更改视图时处理的数据有限,页面之间的路由通常更快,因此 CSR 应用看起来响应更灵敏。CSR 还使开发者能够清晰地分离客户端和服务端代码。

尽管 CSR 提供了出色的交互体验,但也存在一些弊端:

  1. SEO 方面的考量: 大多数网络爬虫都能以直接明了的方式解析服务端渲染的网站。客户端渲染的情况则稍微复杂一些,因为较大的负载和一连串的网络请求(例如 API 响应)可能导致有意义的内容未能及时渲染,供爬虫索引。爬虫可能能够理解 JavaScript,但也有其局限性。因此,需要采取一些变通方案,让客户端渲染的网站更利于 SEO。

  2. 性能: 使用客户端渲染时,由于无需往返服务端,交互期间的响应时间会大幅缩短。不过,浏览器首次在客户端渲染内容时,必须先等待 JavaScript 加载并开始处理。因此,用户在初次加载页面前会经历一些延迟。随着 JS 包体积增大,和/或客户端处理能力不足,这可能会影响用户体验。

  3. 代码可维护性: 某些代码可能会以不同语言在客户端和服务端(API)重复编写。在其他情况下,可能无法清晰地分离业务逻辑。例如,货币和日期字段的校验与格式化逻辑就可能存在这种情况。

  4. 数据获取: 使用客户端渲染时,数据获取通常由事件触发。页面初次加载时可能没有任何数据。之后可以在页面加载或点击按钮等事件发生时,通过 API 调用获取数据。根据数据量大小,这可能会增加应用的加载或交互耗时。

这些考量对不同应用的重要性可能有所不同。开发者通常希望找到利于 SEO 的解决方案,在不牺牲交互响应时间的前提下更快地提供页面。

提升 CSR 性能

由于 CSR 的性能与 JavaScript 包的大小成反比,因此我们能做的最佳选择就是以优化性能为目标来组织 JavaScript 代码。以下是一些有帮助的建议:

  • 为 JavaScript 制定预算:确保初始页面加载的 JavaScript 预算足够合理且严格。初始包压缩并经 gzip 处理后小于 100-170KB,是一个不错的起点。之后,可以在需要相关功能时按需加载代码。

  • 预加载:这种技术可用于在页面生命周期的早期,预加载页面所需的关键资源。关键资源可能包括 JavaScript,可通过在 HTML 的 <head> 部分加入以下指令来预加载。

<link rel="preload" as="script" href="critical.js" />

这会告知浏览器在页面渲染机制启动前开始加载 critical.js 文件。这样,脚本就能更早准备就绪,且不会阻塞页面渲染机制,从而提升性能。

  • 延迟加载:使用延迟加载时,可以识别非关键资源,并仅在需要时加载。通过这种方式,可以减少初始加载的资源大小,从而缩短初始页面加载时间。例如,聊天组件通常不需要在页面加载时立即显示,因此可以延迟加载。

  • 代码拆分:为避免 JavaScript 代码包过大,可以开始拆分代码包。Webpack 等打包工具支持代码拆分,可用于创建多个能在运行时动态加载的代码包。代码拆分还能让你延迟加载 JavaScript 资源。

  • 使用服务工作线程缓存应用程序外壳:这种技术会缓存应用程序外壳,也就是驱动用户界面的最精简 HTML、CSS 和 JavaScript。服务工作线程可用于离线缓存应用程序外壳。这有助于提供原生单页应用体验,其余内容则会在需要时逐步加载。

通过这些技术,CSR 可以帮助提升单页应用的体验速度,并获得不错的 FCP 和 TTI。

来源

评论

支持 Markdown