likes
comments
collection
share

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

作者站长头像
站长
· 阅读数 13

最近我们系统的首屏加载较慢,大概要3-4s白屏,产品一直diss我们,让我们优化。之前就有听说SSR可以优化首屏加载,所以就趁此认真看了看SSR到底是什么,看完之后果断拒绝产品这个“无理要求”😂😂😂。其实要不要用SSR还是要综合考虑,对我们当前这个项目而言使用SSR,有点得不偿失。

SPA单页面应用

1. 什么是单页面应用?

单页面应用,SPA 全称 Single Page Application,一般也称为 CSR(Client Side Render),即客户端渲染。它所需的资源,如 HTMLCSSJS 等,在第一次请求初始化时加载,加载完成后便不会再重新渲染整个页面。对于 SPA 来说,页面的切换就是组件或视图之间的切换。

2. 什么是多页面应用?

多页面应用 ,MPA全称MultiPage Application ,指有多个独立页面的应用(多个html页面),每个页面必须重复加载jscss等相关资源。对于 MPA 来说,页面的切换需要整页资源刷新。

3. SPA 的优缺点

优点

  • 用户体验好
  • 开发效率高
  • 渲染性能好
  • 可维护性高

缺点

  • 首屏渲染时间长
  • 不利于seo

4. 解决方案 - 同构渲染(现代化的服务端渲染)

针对SPA应用的首屏渲染时间长和不利于SEO检索的问题,可利用服务端渲染(SSR)进行优化

传统的服务端渲染

早期的web页面渲染都是在服务端进行的

一. 传统的服务端渲染流程

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

二. 通过Node.js演示传统的服务端渲染模式

1. 新建项目

# 新建一个ssr-study的文件夹,存放此次学习的代码
$ mkdir ssr-study
$ cd ssr-study

新建文件夹node-server-rendering

# 创建 node-server-rendering 文件夹
$ mkdir node-server-rendering
$ cd node-server-rendering

node-server-rendering目录下,新建data.json文件和index.html文件

  • data.json文件用于模拟数据库数据
{
  "title":"前端三剑客",
  "posts":[
    {
      "id":1,
      "title":"HTML",
      "content":"HTML的全称为超文本标记语言,是一种标记语言。\n它包括一系列标签,通过这些标签可以将网络上的文档格式统一,使分散的Internet资源连接为一个逻辑整体。\nHTML文本是由HTML命令组成的描述性文本,HTML命令可以说明文字,图形、动画、声音、表格、链接等。"
    },
    {
      "id":2,
      "title":"CSS",
      "content":"层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。\nCSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。"
    },
    {
      "id":3,
      "title":"JavaScript",
      "content":"JavaScript(简称“JS”)是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。\n虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。"
    }
  ]
}
  • index.html文件,即渲染的模版文件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>体验传统服务端渲染</title>
</head>
<body>
  <h1>体验传统服务端渲染</h1>
</body>
</html>

2. 使用express创建服务端

安装express ,使用express来创建web服务。

# 安装express
$ npm i express

node-server-rendering目录下创建后端服务文件index.js

 const express = require('express')

//  创建一个express实例
 const app = express()

//  添加路由
 app.get('/',(req,res) => {
  res.send('hello world')
 })

 app.listen(3000,() => console.log('server running...'))

3. 启动服务

在控制台使用node index.js 命令启动服务

但是为了方便,我们也可以使用nodemon启动服务,nodemon可以在我们每次写完代码保存之后自动重启服务端。

# 全局安装nodemon 
$ npm i nodemon -g
# 使用nodemon启动服务
$ nodemon index.js

服务启动成功后,打开浏览器,访问localhost:3000,可以收到服务端的hello world

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

4. 把模版和数据结合

现在我们把第1步编写的html模版与data.json获取的数据进行结合,我们使用nodejsfs模块读取文件,可以获取到模板html和数据JSON文件,然后借助第三方插件art-template进行渲染。

# 安装第三方渲染引擎:art-template
$ npm i art-template
// index.js
const express = require('express')   
const fs = require('fs')                 // ++
const template = require('art-template') // ++

//  创建一个express实例
const app = express()

//  添加路由
app.get('/', (req, res) => {

  // 1. 获取页面模版 - 使用nodejs的fs模块读取文件,
  // 为了简单起见,此处使用readFileSync同步方法读取文件
  const templateStr = fs.readFileSync('./index.html','utf-8')
  // fs.readFileSync默认是二进制文件流,所以要传入第二个参数utf-8,转换成字符串

  // 2. 获取数据
  const data = JSON.parse(fs.readFileSync('./data.json','utf-8'))
  
  // 3. 渲染:数据 + 模版 = 渲染结果
  // render方法接收两个参数,第一个是要渲染的模版字符串,第二个是数据对象;返回一个html
  const html = template.render(templateStr, data)

  // 4. 把渲染结果发送给客户端
  res.send(html)
})

app.listen(3000, () => console.log('server running...'))

使用art-template模版语法改写模版文件index.html, 具体的art-template用法可参考☞aui.github.io/art-templat…

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>体验传统服务端渲染</title>
</head>
<body>
  <h1>体验传统服务端渲染</h1>
  <!-- 将数据里的title渲染到这里 -->
  <h2>{{ title }}</h2>
  <ul>
    <!-- each 循环  -->
    {{ each posts }}
      <!-- $value 获取循环的每一项 -->
    <li>{{ $value.title }}</li>
    <!-- /each 循环结束 -->
    {{ /each }}
  </ul>
</body>
</html>

刷新浏览器,可以看到渲染的结果

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

5. 传统服务端渲染存在的问题

通过这个模拟过程,可以发现传统服务端渲染存在的一些问题:

  • 前后端代码完全耦合在一起,不利于开发和维护
  • 前端没有足够发挥空间
  • 服务端压力大
  • 用户体验一般

客户端渲染

客户端渲染得益于AJax技术的发展,Ajax使得客户端动态获取数据成为可能

客户端渲染实现了前后端分离

  • 后端:负责处理数据接口
  • 前端:负责将接口数据渲染到页面中

一、客户端渲染流程

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

二、使用Vue项目进行客户端渲染

1. 新建一个Vue项目

此处是用Vue-Cli创建的Vue2项目,也可以用Vue3来体验

# 在ssr-study目录下
# 新建vue2项目 
$ vue create vue-client-rendering
# 安装router (可以在创建项目时,勾选安装,若未勾选可以使用以下命令安装)
$ vue add router

2. 修改Vue项目

  • 修改App.vue文件
<template>
    <div id="app">
        <nav>
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </nav>
        <router-view/>
     </div>
</template>
- 复制之前的`data.json`文件放在`public`目录下
- 安装`axios`并修改`views/HomeView.vue`
# 安装axios
$ npm i axios
    <template>
      <div class="home">
        <h2>{{ title }}</h2>
        <ul>
          <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
        </ul>
      </div>
    </template>

    <script>
    import axios from 'axios'

    export default {
      name:'App',
      data(){
        return {
          title:'',
          posts:[]
        }
      },
      async created(){
        const {data} = await axios({
          method:'GET',
          url:'../data.json'
        })
        this.title = data.title
        this.posts = data.posts
      }
    }
    </script>

3. 启动项目

使用命令npm run serve启动项目, 打开浏览器:localhost:8080

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

4. 为什么首屏渲染慢?

在快速网络的情况下,针对服务端渲染(≈8ms)和客户端渲染(≈12ms)的首屏加载时间差距并不是很大。

所以我们利用浏览器调试工具来模拟慢速网络下,看二者首屏加载的时间。 认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

  • 客户端渲染SPA首屏加载耗时大约17s,有三次http请求周期
  • 首先加载空的html文件使用了2.02s
  • 加载js文件使用了13.30s(这步加载结束时,页面已经渲染出了Home|About
  • ajax请求数据使用了2.05s(请求到数据后才渲染出前端三剑客的内容)

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

  • 服务端渲染首屏加载耗时大约2s
  • 服务端渲染只需要有一次http请求周期,即请求渲染好的html文件

5. SPA为什么不利于SEO?

  • 搜索引擎说怎么获取网页内容的?

    使用nodejs简单模拟搜索引擎的工作

    // 新建seo/index.js文件 
    // 搜索引擎说怎么获取网页内容的?
    const http = require('http')
    
    // 搜索引擎 - 获取网页内容、分析、收录
    // 通过程序获取指定的网页内容
    
    // http.get('http://localhost:8080/',res => {
    http.get('http://localhost:3000/',res => {
      let data = ''
      res.on('data', chunk => {
        data += chunk
      })
      res.on('end',() => {
        console.log(data);
      })
    })
    
  • 获取localhost:3000的服务端渲染的网页

    认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

  • 获取localhost:8080的客户端渲染的网页

    认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同? 客户端的内容,是必须要解析执行chunk-vendors.jsapp.js才能渲染出网页的内容

现代化的服务端渲染 (同构渲染)

同构渲染 = 后端渲染 + 前端渲染

  • 基于ReactVue等框架,客户端渲染和服务端渲染的结合

    • 在服务器端执行一次,用于实现服务端渲染(首屏直出)
    • 在客户端再执行一次,用于接管页面交互
  • 核心解决SEO和首屏渲染慢的问题

  • 拥有传统服务端渲染的优点,也有客户端渲染的优点

一、现代服务端渲染的流程

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

二、通过Nuxt体验同构渲染

  • React生态Next.js
  • Vue生态Nuxt.js

1. 新建Nuxt项目

   # 新建目录
   $ mkdir ssr
   $ cd ssr
   # 生成package.json文件
   $ npm init -y
   # 安装 nuxt2
   $ npm i nuxt@2  
   # 修改package.json文件

添加启动脚本命令

    ...
      "scripts": {
        "dev":"nuxt"
      }
    ...

2. 创建pages目录,并在该目录下创建index.vue

Nuxt.js 会依据 pages 目录中的所有 *.vue 文件生成应用的路由配置。

    <template>
      <div class="home">
        <h2>{{ title }}</h2>
        <ul>
          <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
        </ul>
      </div>
    </template>

    <script>
    import axios from 'axios'

    export default {
      name:'App',
      // Nuxt中特殊提供的一个钩子函数,专门用于服务端渲染获取数据
      async asyncData({isDev, route, store, env, params, query, req, res, redirect, error}) {
        const { data } = await axios({
          method:'GET',
          url:'http://localhost:3000/data.json'
        })
        return data
      }
    }
    </script>

我们用到了axios,还是需要安装一下axiosnpm i axios

3. 启动服务 npm run dev

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同? 可以看到内容是直接有服务端渲染的,在返回的HTML文件里有这些元素内容。

4. 它还是单页面应用吗?

新建layouts文件夹,在里面新建default.vue文件

    <template>
      <div>
        <ul>
          <li>
            <!-- 类似于router-link -->
            <nuxt-link to="/">Home</nuxt-link>
          </li>
          <li>
            <nuxt-link to="/about">About</nuxt-link>
          </li>
        </ul>
        
        <!-- 子页面出口 -->
        <nuxt/>
      </div>

    </template>

    <script>
    export default {}
    </script>

pages文件夹里,新建一个about.vue文件

<template>
      <div>
        <h1>About</h1>
      </div>
    </template>

    <script>
    export default {}
    </script>

重启项目,此时我们通过HomeAbout切换,可以观察到页面没有刷新,所以它依旧是单页面应用程序。

认识SSR,从《传统的服务端渲染》到《现代化的服务端渲染》有何不同?

三、同构渲染的问题

  • 开发条件所限

    • 浏览器特定的代码只能在某些生命周期钩子函数中使用
    • 一些外部扩展库可能需要特殊处理才能在服务端渲染应用中运行
    • 不能在服务端渲染期间操作DOM
  • 涉及构建设置和部署的更多要求

    • 只能部署在Node.js Server
  • 更多的服务器负载

所以是否真的需要服务端渲染,要取决于“首屏渲染速度是否对我们的项目很重要”,或者是“有SEO的需求”。