不只懂 Vue 语法:以 Vue 和 Nuxt 为例,说明 SPA 和 SSR 的概念?

问题回答

Vue 是 SPA 框架,而 Nuxt 是 Vue 生态系里的一个能同时实现 SPA 和 SSR 的框架。SPA 的主要技术是不用重刷页面来更新页面内容,而是透过切换元件来达成切换页面的效果,提升浏览时的流畅度,但最大缺点是不利於 SEO。

SSR 模式是较传统的方式,在 SPA 还没流行时主要是用 SSR,意思就是所有内容都在服务器准备好,再把完整的 HTML 档案传到前端,前端只需显示它就行了。但最大缺点是,每次跳转页面都要重新向服务器发出请求,并重刷页面,浏览流畅度明显不及 SPA。但好处是有利 SEO,因为搜寻引擎能直接爬到在 HTML 上的内容。

相反,Nuxt 是一个同时能结合 SSR 和 SPA 的框架,目的是解决以上提到的 SEO 问题。在首次载入页面时,用户端向服务器发出请求。服务器会回传两种档案,一是完整的 HTML 档案,二是跑 SPA 模式时会用到的档案。因此,在刚载入第一页时,我们是使用 SSR 模式,即是从服务器端把所有内容都处理好,直接把 HTML 塞回前端。之後,当用户与网页互动时,就会跑 SPA 模式。

以下会再详细解说这些概念。

以 Vue 为例,到底什麽是 SPA?

  • 切换页面其实就是切换元件
  • 使用 AJAX 非同步远端取资料的方式,更新部分画面
  • 在客户端把内容从 JavaScript 档案渲染在画面上

切换页面其实就是切换元件

用 Vue 为例子,即使我们打开不同网页,但由始至终我们仍然是在 index.html 这档案里。当我们切换网页时,其实是在 index.html 此档案里切换不同元件。

假设我由首页,跳到 about 页,再跳到 products 页。每次跳转都不用重刷页面,而是切换元件即可。由始至终我都是在 index.html 此档案里。

比起一般传统网页需时重新刷新和载入页面,感觉更为顺畅和快速,体验就如使用 App 一样顺畅,也因此我们会称为 Single Page "Application"。

使用 AJAX 非同步远端取资料的方式,更新部分画面

SPA 依赖 AJAX 技术来实现局部更新画面。

以最广为人知的 Gmail 为例,打开 Gmail 看看,例如由收件匣页面跳到草稿时,网页 URL 尾部由 #inbox 转至 #drafts,跳转时并没有刷新页面。如果再仔细一点看,会发现在跳转时,只有内容会更新,最顶部的 Navbar ,以及左边的选单都没有太大变化。所以,其实 Gmail 主要只需信件内容,其余部分不需要重新载入。

因此,以上面由收件匣页面跳到草稿的情况为例,我们只需要透过 AJAX 向後端服务器发出请求来取得资料,即是打 API 取得此 Gmail 用户的草稿资料。在收到 response 後,把资料掉回去前端,再利用 JavaScript,只针对右边的内容做重新渲染,把从 response 抓回来的资料渲染上去。

在客户端把 JavaScript 的内容渲染出来

SPA 另一个特点是在客户端读取 JavaScript 来渲染画面内容,而非等後端把整个网页内容都渲染好并掉回前端。

当用户载入网页时,这时候不用等後端把整个网页内容回传并显示,浏览器只需等待服务器回传 HTML、CSS 和 JavaScript 这三种档案。接着浏览器会把打包在 JavaScript 里的资料内容渲染成画面。

以 Vue CLI 所开发的网站为例,使用 npm run build 来产生 dist 资料夹打包档案,再用 live server 插件来预览 dist 里的 index.html。当你用 page source 来看网页的原始码时,会发现此页面并没有资料内容,除了只有基本的 HTML,就只有一堆 CSS 和 JavaScript 档案:

但如果是用检查元素的方式去看,就会发现内容,例如是 nav 的内容:

以上差别明显可见,网页的内容都是由浏览器透过读取 JavaScript 来渲染。

补充一点,Vue、React、Angular 三大前端框架都是 SPA 框架(Single Page Application)。SPA 是一个开发模式,正如常见後端会采用 MVC 模式一样,所以即使不使用 Vue、React 这些框架,用原生 JavaScript 或 jQuery 等都可以完成一个 SPA 的网页。

什麽是 SSR?

SSR(server side render)就是 SPA 还没出现时所采用的传统做法。重点如下:

  • 服务器回传完整 HTML 档案给前端作显示
  • 每次跳转到新页面,浏览器都需要刷新页面

服务器回传完整 HTML 档案给前端作显示

常见做法透过 MVC(Model、View、Controller)的架构,在後端完成:

  • 接收前端请求
  • 按请求操控资料库来取得所需资料
  • 把取回来的资料,塞进模版里,产生完整 HTML 档案,把它传回前端

前端的职责相对少,只需直接把收到的 HTML 档案显示出来就完成。

每次跳转到新页面,浏览器都需要刷新页面

每当用户访问一个网页,後端就要回传一次新的 HTML 档案给前端。因此每次跳转网页时都会重新刷新页面,导致使用流畅度明显比 SPA 差。

SPA 与 SSR 的优劣

虽然听来 SPA 比较优胜,但是两者都有限制。

SPA

SPA 优点:

  • 使用者体验更顺畅,跳转页面时不用等重新刷新页面。
  • 明确的前後端分离,後端只需专注资料回传和资料库操作。前端只需专注画面。

SPA 限制:

  • 不利於搜寻引擎爬虫运作,不利SEO。因为内容是透过浏览器自己再渲染出来,在原始码只会见到此网站载入了一些 JavaScript 档案,因此在爬虫爬内容时会有影响。

SSR

SSR 优点:

  • 有利於 SEO

SSR 限制:

  • 使用者体验相对差。每次跳转页面都要重新刷新画面
  • 等待时间有机会较长,只要有一个请求卡住,後端都要等它完成再一次过把完整的 HTML 档案回传到前端。但 SPA 因为是依靠非同步 AJAX 技术来取资料,因此可以先回传一部分已成功抓取的资料,不至於完全没画面。
  • 没有前後端分离。後端要一并处理画面、前端请求和资料库。
  • 如果网站流量较大,对服务器的负担会比较重。因为每个请求都要求後端回传一个完整的 HTML 档案。相比 SPA 依靠 AJAX 来取资料,负担明显更大。

二合一的解决方案:前端 SSR 框架

为了解决 SPA 的 SEO 问题,最常见方法之一是使用前端的 SSR 框架。在 Vue 的生态圈就是使用 Nuxt.js 框架。核心概念就是在首次载入画面时用 SSR 模式,跑完一次 SSR 後,往後所有请求都是 SPA 进行,运作流程如下:

  1. 浏览器发出请求,向 Nuxt.js 所包含的 Node.js 服务器请求取得资料
  2. 如果有需要打 API 取资料,Node.js 服务器会在这候打 API 取资料
  3. 把取得的资料产生一个完整 HTML 档案,再回传到前端
  4. 完成一次以上步骤後,之後就会转换为 SPA 模式,所有请求都会用 AJAX 来处理

这二合一的解决方法就能同时兼顾 SEO 和使用者体验。较明显的代价是:

  • 前端要再顾及一个後端服务器,以致有两个後端的情况,这也影响到前端的部署方式。
  • Nuxt.js 不是由 Vue 官方开发,因此会出现像是 Vue 升级成为 Vue 3,但 Nuxt 还未能支援 Vue 3 的尴尬情况。
  • 前端的工作变得更复杂,要考虑某些内容和套件,到底是适合在 SSR 还是 SPA 处理和使用。

虽说 Nuxt 是 SPA + SSR 框架,但在 Nuxt 里,只不过它预设是 SPA + SSR 模式,如果你只想要 SPA,也可以在 Nuxt 的 nuxt.config.js 档案里做设定。

预设模式是 universal,即是 SSR 加 SPA。以下示范用 Nuxt 建立一个专案,并在 Nuxt 载入第一页面时检查原始码,会发现有网页内容。有找到在网页中 Welcome to your Nuxt Application 这一句:

把设定转为 SPA 模式,就没有网页内容,只是载入 JavaScript 档案。没找到在网页中 Welcome to your Nuxt Application 这一句:

原因就如以上提过,因为 SPA 是以读取 JS 档案来渲染内容,并不像 SSR ,直接在 HTML 里就找到内容。

总结

总结一下重点:

  • Vue 是 SPA 框架。
  • SSR 是较传统做法,後端把整个 HTML 回传到前端显示。
  • SPA 依赖 AJAX 技术,後端只需回传资料到浏览器,前端会负责做渲染。
  • Nuxt 是 SSR+SPA 的框架,为了解决 Vue 的 SEO 问题而出现。

参考资料

Server-side rendering with Vue and Nuxt.js
Server side render (SSR) 是什麽吗? 为何要用 SSR ? 跟 SPA 和 SEO 又有什麽关联?
前後端分离与 SPA
Vue 服务端渲染原理及入门


<<:  Day 12. Hashicorp Vault: HA with Consul

>>:  33岁转职者的前端笔记-DAY 12 一些网页切版技巧的小笔记-Part 3

Javascript档案中使用Django template 变数

在template中我们可以定义javascript变数为djagno变数,如下: <scri...

[Day31] 第三十一课 Azure资讯安全中心-1

疑,怎麽又出现了,因为对於之前挖的坑,总是想要把他填上,所以又出现了 今天想要谈一下Azure资讯安...

硬体的讯号怎麽丢给软件?

预设 先要有一个开发板,可以接各种sensor。 可以先跟电脑有实体连接,这样就有指定的port可以...

Day11:调度器(Dispatchers),我跳进来了,又跳出去了

Coroutine 一个重要的特性就是可以轻易的切换执行绪,不过 Coroutine 是使用 Cor...

跨职能流程图指南

基本流程图与跨职能流程图 流程可以用流程图来表示,但它们有一个缺点——标准流程图无法表明谁负责这些活...