服务端渲染 SSR 原理和实现

CSR 优缺点

优点

  • 整个网站打包进 JavaScript 里,当 JavaScript 下载完毕后,相当于网站的页面资源都被下载好了。这样在跳转新页面的时候,不需要向服务器再次请求资源( JavaScript 会直接操作 DOM 进行页面渲染),从而让整个网站的使用体验上更加流畅

缺点

  • 在 JavaScript 体积较大的情况下,会有白屏问题
  • 因为会先下载一个空的 HTML,然后才通过 JavaScript 进行渲染,这个空的 HTML 会导致某些搜索引擎无法通过爬虫正确获取网站信息,从而影响网站的搜索引擎排名

SSR

优点

  • HTML 在服务器端就已经渲染好了,浏览器拿到就可以渲染,减少白屏时间
  • 服务器端渲染拥有良好的首屏性能和 SEO

缺点

  • 每次跳转页面都要向服务器重新请求,意味着用户每次切换页面都要等待一小段时间
  • SSR 相比 CSR 会占用较多的服务器端资源
// 以 vue 为例
import { renderToString } from 'vue/server-renderer'
import { createSSRApp } from 'vue'

// 一个计数的 vue 组件
function createApp() {
  // 通过 createSSRApp 创建一个vue实例
  return createSSRApp({
    data: () => ({ count: 1 }),
    template: `<button @click="count++">{{ count }}</button>`,
  });
}

const app = createApp();

// 通过 renderToString 将 vue 实例渲染成字符串
renderToString(app).then((html) => {
  // 将字符串插入到 html 模板中
  const htmlStr = `
    <!DOCTYPE html>
    <html>
      <head>
        <title>Vue SSR Example</title>
      </head>
      <body>
        <div id="app">${html}</div>
      </body>
    </html>
  `;
  console.log(htmlStr);
});

Server + Client 同构

  • 开始的步骤和 SSR 相同,将生成的 HTML 字符串返回给客户端,同时将 CSR 需要的 JavaScript 也一并发送给客户端
  • 客户端在接收到 SSR 生成的 HTML 后,页面还会再执行一次 CSR 的流程
  • 客户端只有请求的第一个页面是在服务器端渲染的,其它页面则都是在客户端进行的
  • 这样就同时兼顾首屏、SEO和用户体验的网站
    在这里插入图片描述

Hydrate 水合(客户端激活)

  • 服务器执行应用的初始渲染,生成静态 HTML,并将其发送给客户端,这一步其实发送的是静态的模版( Dehydrate 脱水)
  • 客户端加载额外的 JavaScript 代码,并在已有的静态 HTML 上绑定事件监听器等,使页面变得可交互
  • SSR 的瓶颈也就取决于 Hydrate 的过程

数据的获取和初始化

  • 挂载到 window 上或者 Vuex、Pinia 等其它方案
const htmlStr = `
  <!DOCTYPE html>
  <html>
    <head>
      ...
      // 将数据格式化成json字符串,放到script标签中
      <script>window.__INITIAL_DATA__ = ${JSON.stringify(initData)}</script>
    </head>
    ...
  </html>
`;

组件中获取数据

function createApp() {
  return createSSRApp({
    data: () => ({ count: 1 }),
    template: `<button @click="count++">{{ count }}</button>`,
    // 自定义一个名为 asyncData 的函数
    asyncData: async () => { 
        // 在处理远程数据并 return 出去
        const data = await getSomeData()
        return data; 
    },
    async mounted() {
      // 如果已经有数据了,直接从 window 中获取
      if (window.__INITIAL_DATA__) {
        // 有服务端数据时,使用服务端渲染时的数据
        this.count = window.__INITIAL_DATA__;
        window.__INITIAL_DATA__ = undefined;
        return;
      } else {
        // 如果没有数据,就请求数据
        this.count = await getSomeData();
      }
    }
  });
}

预加载资源

  • 在打包过程中生成 manifest
    • 作用是将打包后的模块 ID 与它们关联的 Chunk 和资源文件进行映射
  • 依靠这个 manifest 获取资源的路径,然后创建 Link 标签拼接到 HTML 模板中即可

避免应用单例

  • 服务器端返回给客户端的每个请求都应该是全新的、独立的应用程序实例,避免直接将对象或变量创建在全局作用域,否则它将在所有请求之间共享,在不同请求之间造成状态污染

避免全局副作用代码

  • 比如 vue 服务器端渲染只会执行 beforeCreate 和 created 生命周期,应该避免在这两个生命周期里产生全局副作用的代码
  • 例如使用 setInterval 设置定时器。在纯客户端的代码中,我们可以设置一个定时器,然后在 beforeDestroy 或 destroyed 生命周期中将其销毁。但是在 SSR 期间并不会调用销毁钩子函数,所以定时器将永远保留下来,最终造成服务器内存溢出

相关推荐

  1. 服务渲染SSR

    2024-06-13 15:06:03       76 阅读
  2. vue之服务渲染(SSR)

    2024-06-13 15:06:03       78 阅读
  3. vue什么是服务渲染(SSR)

    2024-06-13 15:06:03       39 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-13 15:06:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-13 15:06:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-13 15:06:03       82 阅读
  4. Python语言-面向对象

    2024-06-13 15:06:03       91 阅读

热门阅读

  1. 互联网摸鱼日报(2024-06-13)

    2024-06-13 15:06:03       33 阅读
  2. mysql相关知识总结

    2024-06-13 15:06:03       34 阅读
  3. Unity3D MMORPG角色的UI血条管理详解

    2024-06-13 15:06:03       34 阅读
  4. Rocketmq的坑又来了

    2024-06-13 15:06:03       23 阅读
  5. 编程课面向和右转的区别:深入解析与实际应用

    2024-06-13 15:06:03       27 阅读
  6. 华为鸿蒙HarmonyOS应用开发者高级认证题库

    2024-06-13 15:06:03       31 阅读