热门标签 | 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使用
    关键词: ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了如何按需加载elementui的部分模块,以及如何设置覆盖某些属性。通过import引入Dialog模块,并使用Vue.component进行全局设置。同时使用Vue.use引入ElementUI和VueAxios模块。通过extends进行属性覆盖设置。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • 网址:https:vue.docschina.orgv2guideforms.html表单input绑定基础用法可以通过使用v-model指令,在 ... [详细]
  • uniapp开发H5解决跨域问题的两种代理方法
    本文介绍了uniapp开发H5解决跨域问题的两种代理方法,分别是在manifest.json文件和vue.config.js文件中设置代理。通过设置代理根域名和配置路径别名,可以实现H5页面的跨域访问。同时还介绍了如何开启内网穿透,让外网的人可以访问到本地调试的H5页面。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 在Windows 10中点击“检查更新”按钮可能让你成为微软的测试补丁的“小白鼠”。微软每月的第三、第四周会向稳定通道的用户选择性发放“C”“D”测试补丁,而那些主动点击“检查更新”的用户可能会成为这些补丁的测试对象。这些补丁主要用于测试下一个Patch Tuesday的更新内容的稳定性,也可能用于修复个性化问题。因此,用户需要小心点击“检查更新”,以免遭受不必要的风险。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • Pycharm编辑器取消双击shift弹出搜索框的方法
    在使用Pycharm编辑器时,双击shift会弹出搜索框界面,导致输入失去焦点,给用户带来不便。本文介绍了取消双击shift弹出搜索框的方法:在Pycharm中双击shift,输入registry并回车,找到“ide.suppress.double.click.handler”并勾选后,关闭即可解决该问题。通过这个方法,你再也不会被shift问题困扰了。 ... [详细]
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社区 版权所有