热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

改造@vue/cli项目为服务端渲染ServerSideRender

VUESEO方案二-SSR服务端渲染在上一章中,我们分享了预渲染的方案来解决SEO问题,个人还是很中意此方案的,既简单又能解决大部分问题。但是也有着一定的缺陷,所以我们继续来看下一

VUE SEO方案二 - SSR服务端渲染


在上一章中,我们分享了预渲染的方案来解决SEO问题,个人还是很中意此方案的,既简单又能解决大部分问题。但是也有着一定的缺陷,所以我们继续来看下一个方案--服务端渲染。



1.概述

官方文档

服务端渲染的配置相比预渲染就复杂多了,要做到同构,还要保证服务端和客服端的组件状态一致,我们需要对整个项目进行改造。大部分的内容官方文档中都说明的比较清楚,这里就不重复讲述了,需要各位花费一些时间照着文档一步步改造项目。

本人一开始也是这样照着文档做的,但是改造到最后,发现文档一点不走心啊,本人用官方的@vue/cli创建的项目,一步步走到最后根本跑不起来,要么服务端各种报错,要么客服端各种渲染失败。

所以这边主要讲述在按照文档改造之后还要做的一些配置和其他问题。若有不实之处请大家指正。


2.简单原理

原理很简单,同一套代码,根据服务端和客户端的需求不同,分为两个入口,分别打包出两套逻辑一样的代码包。分别在服务器与客户端运行。

本例中,服务端使用node的express框架搭建。


3.开始配置

首先,需要保证服务端同样安装和开发环境同样的node_modules,因为服务端打包,并没有将所有的第三方包与组件打包到一起,也没有那个必要。在服务器端安装好就行了,服务端会自行调用,否则服务端会报找不到第三方插件的错误,如:

1Error: Cannot find module 'vuex'

服务端渲染的核心插件vue-server-renderer,安装:

1npm i -D vue-server-renderer 
2npm i -g cross-env

全局安装cross-env使得我们可以在package.json中添加服务端入口打包命令

1"scripts": {
2    "build:server""cross-env WEBPACK_TARGET=node vue-cli-service build"
3}


这样我们可以将参数WEBPACK_TARGET=node代入打包进程中,用以识别打包目标,也可以将客户端打包与此命令使用&&连接,一起执行。


打包配置大致如下

1// vue.config.js
2const path = require('path')
3const resolve = dir => path.join(__dirname, dir)
4const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
5const target = TARGET_NODE ? 'server' : 'client'
6const nodeExternals = require('webpack-node-externals')
7const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
8const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
9
10module.exports = {
11    // 将server-bundle.json与模板一起放在服务器,静态资源则打包到cdn中
12    outputDir: TARGET_NODE? resolve('dist') : resolve('../../www/statics/m/first-aid/'),
13    ...
14    chainWebpack: config => {
15        ...
16        // 两个入口
17        config.entry('app')
18            .clear()
19            .add(`./src/entry-${target}.js`)
20        config.target(TARGET_NODE ? 'node' : 'web')
21        config.output
22            .libraryTarget(TARGET_NODE ? 'commonjs2' : undefined)
23        if(TARGET_NODE){
24            config.externals(nodeExternals({
25                allowlist: [/\.css$/]
26            }))
27            config.optimization
28                .splitChunks(false)
29            config.module
30                .rule('vue')
31                .use('vue-loader')
32                .tap(options => {
33                    options.optimizeSSR = false
34                    return options
35                })
36        }
37        config.plugin('vue-server-renderer')
38            .use(TARGET_NODE ? VueSSRServerPlugin : VueSSRClientPlugin)
39        ...
40    },
41    css: {
42        sourceMaptrue
43    }
44    ...
45}

其中与官方文档不同的几处:



  1. nodeExternals配置的whitelist属性是旧版的,新版要用allowlist

  2. 我们需要关闭vue-loader的optimizeSSR属性,否则你将遇到下面这样的错误,具体原因可查询 vue-loader文档 的相关部分



  3. 服务端打包的时候,你可能也会遇到这样的错误:

1(node:18696) UnhandledPromiseRejectionWarning: Error: Server-side bundle should have one single entry file.
2Avoid using CommonsChunkPlugin in the server config.

这时还需要关闭config.optimization.splitChunks,看来服务端渲染的时候对分包不是很友好,不过这也没有必要,所有的包都在服务器本地,当然不需要分包来优化下载。



  1. 关于vue-server-renderer/client-plugin插件还有一个bug, 查看gitHub issues,需要设置css: {sourceMap: true},否则服务端将会报以下错误导致不能渲染成功。



4. 服务器配置

1// server.js
2const express = require("express")
3const path = require("path")
4const resolve = dir => path.join(__dirname, dir)
5const fs = require("fs")
6const jsdom = require('jsdom')
7const { JSDOM } = jsdom
8
9const { createBundleRenderer } = require('vue-server-renderer')
10const serverBundle = require(resolve('dist/vue-ssr-server-bundle.json'))
11const clientManifest = require('../../www/statics/m/first-aid/vue-ssr-client-manifest.json')
12const renderer = createBundleRenderer(serverBundle, {
13    runInNewContextfalse,
14    template: fs.readFileSync(resolve('template.html'), 'utf-8'),
15    clientManifest
16})
17
18const app = express()
19// 静态资源
20app.use(express.static(resolve('../../www')))
21
22app.use((req, res) => {
23    const context = { url: req.url }
24    const resourceLoader = new jsdom.ResourceLoader({
25        userAgent: req.headers['user-agent'],
26    });
27    const dom = new JSDOM('', {
28        url: req.protocol+'://'+req.hostname,
29        resources: resourceLoader
30    });
31    global.window = dom.window
32    global.document = window.document
33    global.navigator = window.navigator
34    window.nodeis = true
35    window.scrollTo = (x, y) => {
36        document.documentElement.scrollTop = y;
37    }
38    renderer.renderToString(context, (err, html) => {
39        if (err) {
40            console.log(err)
41            if (err.code === 404) res.status(404).end('Page not found')
42            else res.status(500).end('Internal Server Error')
43        } else res.end(html)
44    })
45})
46
47app.listen(80)


这边与官方文档差别不大,但是使用jsdom插件解决了不存在document全局变量的问题。当项目中用到像window这样的顶层对象时,服务器因为不存在此变量报错document is not defined错误,导致渲染失败

安装jsdom插件,使用请求来源客服端的UA创建虚拟DOM,从而模拟出顶级对象下的各属性与方法



5.最后

经过官方文档的改造,再通过这些重新配置后,一个服务端渲染的@vue/cli项目总算是运行正常了,再通过与vue-meta的配合,设置返回页面的title与description等,就达到我们解决SEO的目的。边角细节的配置就需要各位再慢慢摸索了。






推荐阅读
  • Vue应用预渲染技术详解与实践 ... [详细]
  • 在多模块项目中,项目A作为一个独立的工具包,不依赖于任何第三方库。其目录结构如下:`--src--main--java--resources`。当将项目A打包成JAR文件后,发现无法正确访问`resources`目录下的文件资源。这一问题可能源于JAR文件的构建配置或类路径设置不当,需要仔细检查Maven或Gradle的构建脚本,确保资源文件被正确包含并加载。 ... [详细]
  • 阿里云MySQL与Oracle数据库的主从复制技术详解 ... [详细]
  • 资源管理器的基础架构包括三个核心组件:1)资源池,用于将CPU和内存等资源分配给不同的容器;2)负载组,负责承载任务并将其分配到相应的资源池;3)分类函数,用于将不同的会话映射到合适的负载组。该系统提供了两种主要的资源管理策略。 ... [详细]
  • 本文详细探讨了 jQuery 中 `ajaxSubmit` 方法的使用技巧及其应用场景。首先,介绍了如何正确引入必要的脚本文件,如 `jquery.form.js` 和 `jquery-1.8.0.min.js`。接着,通过具体示例展示了如何利用 `ajaxSubmit` 方法实现表单的异步提交,包括数据的发送、接收和处理。此外,还讨论了该方法在不同场景下的应用,如文件上传、表单验证和动态更新页面内容等,提供了丰富的代码示例和最佳实践建议。 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 本文介绍了 Vue 开发的入门指南,重点讲解了开发环境的配置与项目的基本搭建。推荐使用 WebStorm 作为 IDE,其下载地址为 。安装时请选择适合您操作系统的版本,并通过 获取激活码。WebStorm 是前端开发者的理想选择,提供了丰富的功能和强大的代码编辑能力。 ... [详细]
  • 微信小程序实现类似微博的无限回复功能,内置云开发数据库支持
    本文详细介绍了如何利用微信小程序实现类似于微博的无限回复功能,并充分利用了微信云开发的数据库支持。文中不仅提供了关键代码片段,还包含了完整的页面代码,方便开发者按需使用。此外,HTML页面中包含了一些示例图片,开发者可以根据个人喜好进行替换。文章还将展示详细的数据库结构设计,帮助读者更好地理解和实现这一功能。 ... [详细]
  • 在使用 SQL Server 时,连接故障是用户最常见的问题之一。通常,连接 SQL Server 的方法有两种:一种是通过 SQL Server 自带的客户端工具,例如 SQL Server Management Studio;另一种是通过第三方应用程序或开发工具进行连接。本文将详细分析导致连接故障的常见原因,并提供相应的解决策略,帮助用户有效排除连接问题。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 本文探讨了 Kafka 集群的高效部署与优化策略。首先介绍了 Kafka 的下载与安装步骤,包括从官方网站获取最新版本的压缩包并进行解压。随后详细讨论了集群配置的最佳实践,涵盖节点选择、网络优化和性能调优等方面,旨在提升系统的稳定性和处理能力。此外,还提供了常见的故障排查方法和监控方案,帮助运维人员更好地管理和维护 Kafka 集群。 ... [详细]
  • 在 Windows 10 环境中,通过配置 Visual Studio Code (VSCode) 实现基于 Windows Subsystem for Linux (WSL) 的 C++ 开发,并启用智能代码提示功能。具体步骤包括安装 VSCode 及其相关插件,如 CCIntelliSense、TabNine 和 BracketPairColorizer,确保在 WSL 中顺利进行开发工作。此外,还详细介绍了如何在 Windows 10 中启用和配置 WSL,以实现无缝的跨平台开发体验。 ... [详细]
  • 清华大学出版社 | 杨丹:基于MATLAB机器视觉的黑色素瘤皮肤癌检测技术及源代码分析(第1689期)
    清华大学出版社 | 杨丹:基于MATLAB机器视觉的黑色素瘤皮肤癌检测技术及源代码分析(第1689期) ... [详细]
  • 本文详细解析了如何使用 jQuery 实现一个在浏览器地址栏运行的射击游戏。通过源代码分析,展示了关键的 JavaScript 技术和实现方法,并提供了在线演示链接供读者参考。此外,还介绍了如何在 Visual Studio Code 中进行开发和调试,为开发者提供了实用的技巧和建议。 ... [详细]
author-avatar
手机用户2502926901
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有