单页面应用(Single Page Application,SPA)
是一种基于前端技术的应用开发模式,它在一个页面中加载所有必要的资源,并通过动态加载和更新内容来实现用户界面的切换和交互。我们使用的Vue 通常情况下用于单页面应用的开发。以下是单页面开发的优缺点:
优点
-
快速响应和交互体验: SPA 初始加载后,通过 AJAX 或 Fetch 请求动态加载数据,实现快速的页面切换和无刷新交互,提供更流畅的用户体验。
-
前后端分离: SPA 通过 API 与后端通信,实现前后端分离。
-
动态加载资源: SPA 只需要加载一次页面,后续的页面切换和内容更新都可以通过动态加载资源来实现,减少了不必要的页面刷新和带宽消耗。
-
减轻服务端压力:减轻服务器压力,服务器只需要提供API接口,不用管页面逻辑和页面的拼接,吞吐能力会提高几倍。
缺点
-
SEO 难度: 由于 SPA 在初始加载时只有一个 HTML 页面,搜索引擎难以获得完整的页面内容,影响搜索引擎优化(SEO)效果。
-
初始加载较慢(白屏问题): SPA 首次加载时需要下载所有必要的资源,导致初始加载时间较长,尤其是在网络条件较差时。
-
不利于弱网络环境: 在弱网络环境下,动态加载资源会导致页面内容出现延迟或加载失败,影响用户体验。
如何用Vue构建大型单页面应用
服务端渲染 (SSR)
什么是 SSR?
Vue.js 是一个用于构建客户端应用的框架。默认情况下,Vue 组件的职责是在浏览器中生成和操作 DOM。然而,Vue 也支持将组件在服务端直接渲染成 HTML 字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用。
为什么要用 SSR?
与客户端的单页应用 (SPA) 相比,SSR 的优势主要在于:
-
更快的首屏加载:这一点在慢网速或者运行缓慢的设备上尤为重要。服务端渲染的 HTML 无需等到所有的 JavaScript 都下载并执行完成之后才显示,所以你的用户将会更快地看到完整渲染的页面。
-
更好的 SEO:搜索引擎爬虫可以直接看到完全渲染的页面。
使用Vue SSR
ssr有两个入口文件,client-entry 和 server-entry.js 也就是客户端和服务端入口文件,都包含了同一套应用代码,webpack 通过两个入口文件分别打包成给服务端用的 server bundle 和给客户端用的 client bundle. 当服务器接收到了来自客户端的请求之后,会创建一个渲染器 bundleRenderer,这个 bundleRenderer 会读取上面生成的 server bundle 文件,并且执行它的代码, 然后发送一个生成好的 html 到浏览器,等到客户端加载了 client bundle 之后,会和服务端生成的DOM 进行对比,也就是判断这个DOM 和自己即将生成的DOM 是否相同,如果相同就将客户端的vue实例挂载到这个DOM上, 否则会提示警告。
- 服务端预渲染
vue2.0引入了虚拟DOM,实现原理就是vue的编译器在编译模板之后, 会将这些模板编译成一个渲染函数,也就是render方法, 函数被调用的时 候就会渲染并且返回一个虚拟DOM的树,在交给一个patch函数,把虚拟 DOM施加到真实的DOM上。这样做的主要原因是因为js的运算是非常快 的,而在浏览器中直接操作DOM会对性能有一定损耗。
- 流式渲染
服务端渲染支持流式渲染,因为http请求也是流式的,在渲染组件时返回一个可读的 stream 流,然后直接写入 到 HTTP 响应中。流式渲染 能够确保服务端的响应度,也能让用户更快地获得渲染内容。
- 对组件进行缓存
- 前后端复用一套代码
前端和服务端可以复用一套代码,提升开发效率和维护性
渲染一个应用
让我们来看一个 Vue SSR 最基础的实战示例。
- 创建一个新的文件夹,
cd
进入 - 执行
npm init -y
- 在
package.json
中添加"type": "module"
- 执行
npm install vue
- 执行
npm install express
- 创建下面的
server.js
文件:
import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'
const server = express()
server.get('/', (req, res) => {
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Vue SSR Example</title>
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`)
})
})
server.listen(3000, () => {
console.log('ready')
})
代码文件结构如上,执行 node server.js
,访问 http://localhost:3000
客户端激活
如果你点击该按钮,你会发现数字并没有改变。这段 HTML 在客户端是完全静态的,因为我们没有在浏览器中加载 Vue。
为了使客户端的应用可交互,Vue 需要执行一个激活步骤。在激活过程中,Vue 会创建一个与服务端完全相同的应用实例,然后将每个组件与它应该控制的 DOM 节点相匹配,并添加 DOM 事件监听器。
把应用的创建逻辑拆分到一个单独的文件 app.js
中:
// app.js (在服务器和客户端之间共享)
import { createSSRApp } from 'vue'
export function createApp() {
return createSSRApp({
data: () => ({ count: 1 }),
template: `<button @click="count++">{{ count }}</button>`
})
}
我们在客户端入口导入通用代码,创建应用并执行挂载:
// client.js
import { createApp } from './app.js'
createApp().mount('#app')
按钮现在可以交互了!