Vue项目预渲染机制引入实践

简介: 周末想顺便把已经做好静态页面的webApp项目做一下SEO优化,由于不想写蹩脚的SSR代码,所以准备采用预渲染,本来想着网上有这么多预渲染的文章,随便找个来跟着做不就完了嘛,结果年轻的我付出了整个周末..... 这篇文章就记录一下最后是怎么配置的 T.T声明:以下配置只保留有必要的生成目录这里使用base代替,请自行修改vue-cli模板使用webpack,其他模板类推webApp - 在线预览Github - 配置了预渲染的demo感兴趣的同学可以加文末的微信群,一起讨论吧~

周末想顺便把已经做好静态页面的webApp项目做一下SEO优化,由于不想写蹩脚的SSR代码,所以准备采用预渲染,本来想着网上有这么多预渲染的文章,随便找个来跟着做不就完了嘛,结果年轻的我付出了整个周末..... 这篇文章就记录一下最后是怎么配置的 T.T


声明:


  1. 以下配置只保留有必要的
  2. 生成目录这里使用base代替,请自行修改
  3. vue-cli模板使用webpack,其他模板类推
  4. webApp - 在线预览
  5. Github - 配置了预渲染的demo


感兴趣的同学可以加文末的微信群,一起讨论吧~


1. 简介与使用场景


我们知道SPA有很多优点,不过一个缺点就是对(不是Google的)愚蠢的搜索引擎的SEO不友好,为了照顾这些引擎,目前主要有两个方案:服务端渲染(Server Side Rendering)、预渲染(Prerending)。


如果你只需要改善少数页面(例如 /, /about, /contact 等)的 SEO,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 HTML (服务端渲染, SSR),而是使用预渲染方式,在构建时(build time)简单地生成针对特定路由的静态 HTML 文件。它主要使用 prerender-spa-plugin 插件,其与SSR一样都可以加快页面的加载速度,并且侵入性更小,在已上线的项目稍加改动也可以轻松引入预渲染机制,而SSR方案则需要将整个项目结构推翻;


访问预渲染出来的页面在访问时与SSR一样快,并且它将服务端编译HTML的时机提前到了构建时,因此也降低了服务端的压力,如果你的服务器跟我的一样买的 1M1G1核 的小水管服务器 ( 穷 ),那么预渲染可能更适合你。不过SSR和预渲染的使用场景还是有较明显的区别的。预渲染的使用场景更多是简单的静态页面。服务端渲染适用于复杂、较大型、与服务端交互频繁的功能型网站,比如电商网站。


2. 安装配置


首先来看看相关技术栈:vue^2.5.2、vue-router^3.0.1、vue-cli^2.9.6、webpack^3.6.0、prerender-spa-plugin^3.3.0


2.1 安装


安装跟其他库一样


# Yarn
$ yarn add prerender-spa-plugin -D
# or NPM
$ npm install prerender-spa-plugin --save-dev
复制代码


2.2 前端配置


首先看看文件结构,用的是vue-cli2的webpack模板生成的文件结构


│  .babelrc
│  index.html
│  package.json
│  README.md
├─build
│      build.js
│      check-versions.js
│      utils.js
│      vue-loader.conf.js
│      webpack.base.conf.js
│      webpack.dev.conf.js
│      webpack.prod.conf.js
├─config
│      dev.env.js
│      index.js
│      prod.env.js
├─src
│  │  App.vue
│  │  main.js
│  │ 
│  ├─assets
│  ├─components
│  ├─router
│  │      index.js
│  ├─styles
│  ├─utils
│  └─views
│          BigData.vue
│          CompanyHonor.vue
复制代码


然后是router/index.js的配置,预渲染要求是histroy模式,有的文章说不需要history模式,这是错的,否则生成的页面都是同一个html。另外注意加上base否则如果你希望跳转到二级页面的localhost/base/home时候,在页面中点击<router-link to="/home">home</router-link>的时候会跳转localhost/home


// src/router/index.js


import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
  mode: 'history',
  base: '/base/',
  routes: [...]
})
复制代码


然后是config,这里注意assetsPublicPath不是./,


// config/index.js


const path = require("path")
module.exports = {
  build: {
    index: path.resolve(__dirname, "../base/index.html"),
    assetsRoot: path.resolve(__dirname, ".."),
    assetsSubDirectory: "base/static",
    assetsPublicPath: "/",
  }
}
复制代码


然后是插件的配置,是放在prod中的,因为只有build的时候会用


// build/webpack.prod.conf.js


const path = require('path')
const config = require('../config')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const webpackConfig = merge(baseWebpackConfig, {
  new PrerenderSPAPlugin({
   staticDir: config.build.assetsRoot,
   outputDir: path.join(config.build.assetsRoot, 'base'),
   indexPath: config.build.index,
   // 对应路由文件的path
   routes: [
     '/',
     '/BigData',
     '/CompanyHonor'
   ],
   renderer: new Renderer({
     headless: false,            // 无桌面系统去掉
     renderAfterDocumentEvent: 'render-event'
   })
  })
})
复制代码


注意了,如果你的项目是部署在linux/centOS之类没有桌面的系统,需要把headless: false去掉,如果centOS报没有找到lib的错,请参考 issue-200 的解决办法。


另外注意上面一个renderAfterDocumentEvent: 'render-event'了么,这个意思是在render-event事件触发之后执行prerender,这个事件我们在main.js中mounted钩子触发


// src/main.js


import Vue from 'vue'
import App from './App'
new Vue({
  el: '#app',
  render: h => h(App),
  mounted() {
    document.dispatchEvent(new Event('render-event'))
  }
})
复制代码


还有个配置要注意下在 build/utils.js 中的 ExtractTextPlugin.extractpublicPath ,否则一些vue中引用的资源会找不到


// build/utils.js


ExtractTextPlugin.extract({
  use: loaders,
  fallback: 'vue-style-loader',
  // publicPath: '../../'
})
复制代码


这时候执行npm run build就可以生成刚刚配置在PrerenderSPAPlugin插件中routes中的页面html了,这过程中会一闪而过的短暂打开chromium浏览器,不用管。

最后生成的目录树:


│  index.html
├─BigData
│      index.html
├─CompanyHonor
│      index.html
└─static
    ├─css
    ├─fonts
    ├─img
    └─js
复制代码


最后如果希望进一步优化生成出来页面的SEO,可以配合 vue-meta-info 这个网上有很多文章,就不赘述了


2.3 nginx配置


顺便贴一下nginx配置


server {
        listen 80;
        server_name localhost;
        root /nginx-1.14.0/html;
        error_page 500 502 503 504 /50x.html;
        location ~ ^/base/ {
          try_files $uri $uri/ /base/index.html;
        }
        location = /50x.html {
            root html;
        }
}
复制代码


网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~


参考:

  1. Vue.js 服务器端渲染指南
  2. Vue 服务端渲染 & 预渲染
  3. vue-cli v3, can't get it to work.. issue #215
  4. unable to start Puppeteer. Failed to launch ... issue #200
  5. When assetsPublicPath is set, the... issue #176
  6. vue.js vue-router history模式路由,域名二级目录子目录nginx配置
  7. 处理 Vue 单页面 Meta SEO的另一种思路



相关文章
|
7天前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
91 2
|
3月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
527 0
|
2月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
100 0
|
2月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
231 1
|
3月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
5月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
741 4
|
4月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
531 77
|
5月前
|
缓存 JavaScript 前端开发
Vue 基础语法介绍
Vue 基础语法介绍
|
3月前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
362 17