前言
hello world欢迎来到前端的新世界
😜当前文章系列专栏:vue.js
🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹
💖感谢大家支持!您的观看就是作者创作的动力
什么是 SSR?
Vue.js 是一个用于构建客户端应用的框架。默认情况下,Vue 组件的职责是在浏览器中生成和操作 DOM。然而,Vue 也支持将组件在服务端直接渲染成 HTML 字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用。
一个由服务端渲染的 Vue.js 应用也可以被认为是“同构的”(Isomorphic) 或“通用的”(Universal),因为应用的大部分代码同时运行在服务端和客户端。
基础教程
渲染一个应用
让我们来看一个 Vue SSR 最基础的实战示例。
- 创建一个新的文件夹,cd 进入
- 执行 npm init -y
- 在 package.json 中添加 “type”: “module” 使 Node.js 以 ES modules mode 运行
- 执行 npm install vue
- 创建一个 example.js 文件:
// 此文件运行在 Node.js 服务器上 import { createSSRApp } from 'vue' // Vue 的服务端渲染 API 位于 `vue/server-renderer` 路径下 import { renderToString } from 'vue/server-renderer' const app = createSSRApp({ data: () => ({ count: 1 }), template: `<button @click="count++">{{ count }}</button>` }) renderToString(app).then((html) => { console.log(html) })
接着运行:
> node example.js
它应该会在命令行中打印出如下内容:
<button>1</button>
renderToString()
接收一个 Vue 应用实例作为参数,返回一个 Promise
,当 Promise resolve
时得到应用渲染的 HTML。当然你也可以使用Node.js ``Stream API
或者 Web Streams API
来执行流式渲染。查看 SSR API
参考获取完整的相关细节。
然后我们可以把 Vue SSR
的代码移动到一个服务器请求处理函数里,它将应用的 HTML
片段包装为完整的页面 HTML。接下来的几步我们将会使用 express
:
- 执行
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 事件监听器。
为了在激活模式下挂载应用,我们应该使用 createSSRApp() 而不是 createApp():
// 该文件运行在浏览器中 import { createSSRApp } from 'vue' const app = createSSRApp({ // ...和服务端完全一致的应用实例 }) // 在客户端挂载一个 SSR 应用时会假定 // HTML 是预渲染的,然后执行激活过程, // 而不是挂载新的 DOM 节点 app.mount('#app')
代码结构
想想我们该如何在客户端复用服务端的应用实现。这时我们就需要开始考虑 SSR 应用中的代码结构了——我们如何在服务器和客户端之间共享相同的应用代码呢?
这里我们将演示最基础的设置。首先,让我们将应用的创建逻辑拆分到一个单独的文件 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')
服务器在请求处理函数中使用相同的应用创建逻辑:
// server.js (不相关的代码省略) import { createApp } from './app.js' server.get('/', (req, res) => { const app = createApp() renderToString(app).then(html => { // ... }) })
此外,为了在浏览器中加载客户端文件,我们还需要:
- 在 server.js 中添加 server.use(express.static(‘.’)) 来托管客户端文件。
- 将 添加到 HTML 外壳以加载客户端入口文件。
- 通过在 HTML 外壳中添加 Import Map 以支持在浏览器中使用 import * from ‘vue’。
更通用的解决方案
从上面的例子到一个生产就绪的 SSR 应用还需要很多工作。我们将需要:
- 支持 Vue SFC 且满足其他构建步骤要求。事实上,我们需要为同一个应用执行两次构建过程:一次用于客户端,一次用于服务器。
- 在服务器请求处理函数中,确保返回的 HTML 包含正确的客户端资源链接和最优的资源加载提示 (如 prefetch 和 preload)。我们可能还需要在 SSR 和 SSG 模式之间切换,甚至在同一个应用中混合使用这两种模式。
- 以一种通用的方式管理路由、数据获取和状态存储。
完整的实现会非常复杂,并且取决于你选择使用的构建工具链。因此,我们强烈建议你使用一种更通用的、更集成化的解决方案,帮你抽象掉那些复杂的东西。下面推荐几个 Vue 生态中的 SSR 解决方案。
后言
创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力