一个demo,带你了解SSR服务器渲染和CSR客户端渲染

799 阅读6分钟

VUE项目是SPA的项目,既可以做SSR服务器渲染也可以CSR客户端渲染

SPA (Single Page Application)

SPA是一种单页面应用的Web应用的设计模式。

在这种模式下,用户在浏览网站时,页面不会重新加载或只会部分重载

  1. 用户体验

    • SPA提供了一种接近原生应用的体验,页面切换快速且流畅。
    • 用户交互更加直接,减少了等待新页面加载的时间。
  2. 页面加载方式

    • 初始加载时,会一次性加载大量的HTML、CSS和JavaScript代码。
    • 随后的用户交互通过AJAX等技术从服务器获取数据,并动态更新DOM(文档对象模型),而不需要重新请求整个页面。
  3. URL处理

    • 使用浏览器的历史API或者hashbang(#!)来改变URL路径,使得用户可以在不同的“页面”状态之间导航,并且可以使用后退按钮。
    • URL的变化不会导致页面刷新。
  4. 前后端分离

    • 前端负责展示逻辑和用户界面,而后端则主要处理数据处理和业务逻辑。
    • 这种分离有助于开发团队并行工作,并且可以更容易地扩展应用。
  5. SEO(搜索引擎优化)挑战

    • 初始的页面内容是通过JavaScript动态生成的,这可能会影响搜索引擎对页面内容的理解,进而影响SEO排名。
    • 不过,现代搜索引擎如Google已经能够执行JavaScript来索引SPA中的内容,但这依然可能不如传统的静态页面那样高效。
  6. 性能

    • SPA可能会在首次加载时下载较多的数据,这可能会导致较长的加载时间。
    • 但是,由于后续的页面转换不需要重新加载整个页面,所以可以提高响应速度。
  7. 开发和维护

    • SPA通常依赖于现代前端框架如React、Vue.js或Angular,这些框架提供了丰富的工具和库来帮助构建复杂的用户界面。
    • 单页应用的复杂性可能会增加维护成本,特别是在没有良好架构设计的情况下。
  8. 资源消耗

    • SPA通常比多页面应用消耗更多的客户端资源,因为它需要在客户端解析和渲染所有内容。

CSR客户端渲染

在我们平常写VUE项目的时候大部分时候使用的都是CSR客户端渲染的方式

其中页面的主要内容和动态数据都是在用户的浏览器中通过JavaScript生成的。

这就导致了SEO的排名会靠后,搜索引擎爬虫可能无法有效地执行JavaScript代码,它们可能无法完全理解和索引通过JavaScript动态生成的内容。

我们直接创建一个vite项目,运行后可以看到

image.png

在浏览器中只有一个app的挂载点,并且其他的页面都是通过JavaScript去生成的,也就意味着,像响应式之类的操作,以及页面的交互都是在浏览器端完成的,但是如果是搜索引擎爬虫的话就没办法高效的读取,就意味着对其不太友好,搜索引擎爬虫希望的是访问得到的就是有完整结构的html

这时候就需要通过服务端将页面处理好发给前端

SSR服务端渲染

服务端渲染(Server-Side Rendering, SSR)是一种Web开发技术,它允许在服务器端生成完整的HTML页面,并将其发送到客户端浏览器。这种方式与传统的客户端渲染(CSR)不同,在CSR中,HTML页面的初始加载仅包含基本结构,而动态内容则通过JavaScript在客户端加载后动态生成。

接下来我们写一个例子

我们先添加对应的依赖

npm install express vue @vue/server-renderer @vue/compiler-sfc @vue/compiler-ssr

分别是以下依赖

"express": "^4.17.2",
"vue":"^3.2.26",
"@vue/server-renderer":"^3.2.26"
const express = require("express"); // 类koa 简单backend框架 commonjs
const app = express();
const Vue = require("vue"); // vue走向后端
const readerer3 = require("@vue/server-renderer"); // ssr
const vue3Compile = require("@vue/compiler-ssr"); // 编译模板

我们创建一个vue的页面

const vueapp = {
  template: `
        <div>
            <h1 @click="add">{{num}}</h1>
            <ul>
                <li v-for="(todo, n) in todos">{{n+1}}-- {{todo}}</li>
            </ul>
        </div>
    `,
  data() {
    return {
      num: 1,
      todos: ["吃饭", "睡觉", "打豆豆"],
    };
  },
  methods: {
    add() {
      this.num++;
      console.log("11");
    },
  },
};

  • template: 定义了组件的 HTML 结构,包含一个可点击的标题和一个列表。
  • data: 返回组件的状态,包括 num 和 todos
  • methods: 包含一个 add 方法,每次点击标题时增加 num 的值并打印 "11" 到控制台。
  • :key: 在 v-for 中使用 :key 来确保列表渲染时性能更佳。- template: 定义了组件的 HTML 结构,包含一个可点击的标题和一个列表。
  • data: 返回组件的状态,包括 num 和 todos
  • methods: 包含一个 add 方法,每次点击标题时增加 num 的值并打印 "11" 到控制台。
  • :key: 在 v-for 中使用 :key 来确保列表渲染时性能更佳。

接下来我们为vueapp添加ssrRender属性

vueapp.ssrRender = new Function(
  "require",
  vue3Compile.compile(vueapp.template).code
)(require);

添加一个get请求

// http 协议基于请求响应的简单协议
app.get("/", async (req, res) => {
  // 爬虫 拿到 组件的html  在vue之前
  // res.end('hello world');
  // 创建服务器端的渲染app
  let vapp = Vue.createSSRApp(vueapp);
  // 渲染
  let html = await readerer3.renderToString(vapp);
  const title = "vue3 ssr";
  let ret = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>${title}</title>
        </head>
        <body>
            <div id="app">${html}</div>
        </body>
        </html>
    `;

  res.end(ret);
});

创建 Vue 应用

  // 创建服务器端的渲染app
  let vapp = Vue.createSSRApp(vueapp);
  • let vapp = Vue.createSSRApp(vueapp); : 创建一个 Vue 3 服务器端渲染的应用实例 vapp,其中 vueapp 是之前定义的 Vue 组件。这个实例将用于生成 HTML。

渲染组件

  // 渲染
  let html = await readerer3.renderToString(vapp);
  • let html = await readerer3.renderToString(vapp); : 使用 @vue/server-renderer 提供的 renderToString 方法,将 Vue 应用 vapp 渲染成 HTML 字符串。这个过程是异步的,所以使用 await 关键字。### 渲染组件
  // 渲染
  let html = await readerer3.renderToString(vapp);
  • let html = await readerer3.renderToString(vapp); : 使用 @vue/server-renderer 提供的 renderToString 方法,将 Vue 应用 vapp 渲染成 HTML 字符串。这个过程是异步的,所以使用 await 关键字。

生成完整的 HTML 响应

  const title = "vue3 ssr";
  let ret = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>${title}</title>
        </head>
        <body>
            <div id="app">${html}</div>
        </body>
        </html>
    `;
  • const title = "vue3 ssr"; : 定义 HTML 页面的标题。
  • let ret = ...`` : 使用模板字符串生成完整的 HTML 文档。${html} 会被替换为渲染后的 Vue 组件的 HTML 内容。### 生成完整的 HTML 响应
  const title = "vue3 ssr";
  let ret = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>${title}</title>
        </head>
        <body>
            <div id="app">${html}</div>
        </body>
        </html>
    `;
  • const title = "vue3 ssr"; : 定义 HTML 页面的标题。
  • let ret = ...`` : 使用模板字符串生成完整的 HTML 文档。${html} 会被替换为渲染后的 Vue 组件的 HTML 内容。

启动项目

app.listen(9093, () => {
  console.log("server is running at port 9093");
});

最后执行后端并访问前端即可看到页面显示

image.png

并且该页面是由后端处理好,前端直接显示的,这也就是SSR服务器渲染

image.png