前言
PWA(Progressive Web App)已经成为了前端开发的一个热门话题,它可以让我们的 Web 应用更像是一个本地应用。而实现 PWA 的一个重要手段是 Service Worker。但是,Service Worker 只能在浏览器端运行,无法直接在服务器端运行。这就导致了一个问题:如何实现 PWA 的 SSR(Server-Side Rendering)?
在本文中,我们将探讨如何实现 PWA 的 SSR,以及一些实现的技巧和注意事项。
SSR 简介
SSR 是指在服务器端渲染页面,将最终渲染好的页面发送给客户端。相对于传统的 SPA(Single-Page Application),SSR 的主要优点是:
- 更好的 SEO(Search Engine Optimization):由于搜索引擎对于 JavaScript 的支持并不完美,对于 SPA 而言,搜索引擎可能无法正确地解析页面内容,从而导致 SEO 效果不佳。而 SSR 可以在服务器端生成静态 HTML,搜索引擎可以直接解析这些 HTML,从而提高了 SEO 的效果。
- 更快的首屏渲染速度:由于 SSR 可以在服务器端生成 HTML,客户端只需要下载静态资源(如 CSS 和 JavaScript)即可,无需等待 JavaScript 加载和执行,从而提高了首屏渲染速度。
- 更好的可访问性:对于一些无法执行 JavaScript 的设备或用户,SSR 可以提供更好的可访问性。
PWA 的 SSR 实现方式
实现 PWA 的 SSR 有很多种方式,这里我们介绍一种比较常用的方式:使用 prerender-spa-plugin。
prerender-spa-plugin 简介
prerender-spa-plugin 是一个 Webpack 插件,它可以在构建时预渲染页面,并将渲染好的 HTML 保存到文件中。当客户端请求这些页面时,服务器端直接返回这些 HTML 文件,从而实现 SSR。
实现步骤
下面我们来看一下具体的实现步骤。
1. 安装依赖
首先,我们需要安装 prerender-spa-plugin 和 vue-server-renderer:
npm install prerender-spa-plugin vue-server-renderer --save
2. 配置 Webpack
在 Webpack 配置文件中,我们需要添加 prerender-spa-plugin 的配置。具体配置如下:
// www.javascriptcn.com code example
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {
// ...
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/', '/about', '/contact'],
renderer: new Renderer({
renderAfterDocumentEvent: 'render-event'
})
})
]
}其中,staticDir 是静态文件目录,routes 是需要预渲染的页面路由,renderer 是渲染器。这里我们使用了 PuppeteerRenderer,它可以使用 Puppeteer 来预渲染页面。
3. 配置服务器端
在服务器端,我们需要使用 vue-server-renderer 来渲染页面。具体实现如下:
// www.javascriptcn.com code example
const express = require('express')
const path = require('path')
const fs = require('fs')
const { createBundleRenderer } = require('vue-server-renderer')
const bundle = require('./dist/vue-ssr-server-bundle.json')
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
const server = express()
const template = fs.readFileSync(path.join(__dirname, './index.html'), 'utf-8')
const renderer = createBundleRenderer(bundle, {
runInNewContext: false,
template,
clientManifest
})
server.use(express.static(path.join(__dirname, './dist')))
server.get('*', (req, res) => {
const context = {
url: req.url
}
renderer.renderToString(context, (err, html) => {
if (err) {
console.error(err)
return res.status(500).send('Internal Server Error')
}
res.end(html)
})
})
server.listen(3000, () => {
console.log('Server started at http://localhost:3000')
})其中,createBundleRenderer 是用来创建渲染器的函数,bundle 是服务器端渲染的 bundle 文件,clientManifest 是客户端渲染的 manifest 文件,template 是 HTML 模板。在 server.get('*') 中,我们使用渲染器来渲染页面,并将生成的 HTML 返回给客户端。
注意事项
实现 PWA 的 SSR 过程中,有一些需要注意的事项:
- Service Worker 只能在浏览器端运行,无法直接在服务器端运行。因此,在 SSR 期间,Service Worker 并不会执行,这意味着我们无法在 SSR 期间缓存一些资源。
- 在 SSR 期间,我们需要将所有的资源(包括 CSS 和 JavaScript)都内联到 HTML 中,这样才能保证页面能够正确地渲染。由于内联的 CSS 和 JavaScript 会增加 HTML 的体积,因此需要注意 HTML 的大小。
- 在 SSR 期间,我们需要将所有的链接替换为绝对路径,否则客户端会尝试从服务器端下载这些资源,从而导致一些错误。
示例代码
完整的示例代码可以在 GitHub 上找到。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/67d8e53da941bf7134007cf1