zoukankan      html  css  js  c++  java
  • vue预渲染及其cdn配置

    VUE SEO方案一 - 预渲染及其cdn配置

    项目接入VUE这样的框架后,看起来真是太漂亮了,奈何与MCV框架比起来,单页应用程序却满足不了SEO的业务需求,首屏渲染时间也是个问题。总不能白学VUE,预渲染和SSR还是要搞起来。

    1.原理

    为什么做服务端渲染之前先去了解了预渲染呢?因为预渲染方案相比服务端渲染简单太多了,而且并不是所有项目都需要服务端渲染的。

    预渲染是怎么实现的呢?原理很简单,在项目开发完成之后,将有限的需要SEO的页面挑选出来,借助prerender-spa-plugin插件实行一次浏览器渲染,再将渲染好的源代码(.html)打包到项目包中,为这些页面在服务端额外配置路由(不再返回首页模板index.html)。这样这些页面就有单独的静态页面,从而解决了SEO和首屏问题。

    2.适用场景及其优劣

    预渲染适合那些SEO需求页面有限的应用,比如一个功能型的app。当只有一些推广页面需要SEO的时候,针对这些页面预渲染就非常方便了。相反,如果是一个资讯型的app,有成千上万的文章页面,这时再去做预渲染就比较坑了。

    另外,如果预渲染的页面中含有一些动态的数据,那么预渲染的页面首屏数据不会自动更新(因为那是我们已经事先渲染好的,当然不会和用户需要的一致)。这种情况就需要事先布置好骨架屏,等等页面vue接管后重新渲染正确的数据,以免出现不对的数据。当然如果这些数据是SEO相关的,那预渲染就解决不了了。

    所以个人认为,预渲染已经可以解决多数SEO问题已经首屏速度问题了,实在不行再上SSR。

    3.开始配置

    首先我们需要安装预渲染插件prerender-spa-plugin

    prerender-spa-plugin文档

    1npm i -D prerender-spa-plugin

    对于整个项目都部署在静态服务根目录下的只要简单的配置就好了

     1// vue.config.js
    2const path = require('path')
    3const resolve = dir => path.join(__dirname, dir)
    4const PrerenderSPAPlugin = require('prerender-spa-plugin')
    5const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
    6
    7module.exports = {
    8    outputDir: resolve('dist'),
    9    publicPath'/',
    10    indexPath: resolve('dist/index.html'),
    11    ...
    12    chainWebpack: config => {
    13        if(process.env.NODE_ENV === 'production'){
    14            config.plugin('prerender-spa-plugin')
    15                .use(PrerenderSPAPlugin, [{
    16                    // 必须 - app出口,和outputDir一致就好.
    17                    staticDir: resolve('dist'),
    18                    // 必须 - 需要预渲染的路由.
    19                    routes: ['/''/about/'],
    20                    // 必须 - 模拟浏览器渲染
    21                    renderer: new Renderer({
    22                        // 渲染时打开浏览器debug
    23                        headless: false,
    24                        // 渲染时机,正确的时机保证渲染页面的完整性
    25                        renderAfterDocumentEvent: 'render-event'
    26                    })
    27                }])
    28        }
    29    }
    30}
    31
    32//main.js
    33new Vue({
    34    router,
    35    store,
    36    renderh => h(App)
    37    mounted () {
    38        // 这里的触发事件就是renderAfterDocumentEvent中的事件
    39         document.dispatchEvent(new Event('render-event'))
    40    }
    41})

    4.cdn的坑

    大部分情况下,上面的配置是无法满足项目需求的,因为一般我们会把模板和静态资源分开部署,使用cdn加速。这时候配置就有一点繁琐了。

     1// vue.config.js
    2const path = require('path')
    3const resolve = dir => path.join(__dirname, dir)
    4const PrerenderSPAPlugin = require('prerender-spa-plugin')
    5const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
    6
    7module.exports = {
    8    outputDir: process.env.NODE_ENV === 'production' ? resolve('dist') : resolve('/www/statics/'),
    9    publicPath: process.env.NODE_ENV === 'production' ? 'http://localhost/' : '/',
    10    indexPath: resolve('dist/index.html'),
    11    ...
    12    if(process.env.NODE_ENV === 'production'){
    13        config.plugin('prerender-spa-plugin')
    14            .use(PrerenderSPAPlugin, [{
    15                staticDir: resolve('dist'),
    16                indexPath: resolve('/www/statics/index.html'),
    17                routes: ['/''/about/'],
    18                postProcess (renderedRoute) {
    19                    renderedRoute.html = renderedRoute.html.replace(/http://localhost/g'http://cdn.path/')
    20                    return renderedRoute
    21                },
    22                renderernew Renderer({
    23                    injectProperty'__PRERENDER_INJECTED__',
    24                    inject'prerender',
    25                    headlessfalse,
    26                    renderAfterDocumentEvent'render-event'
    27                })
    28            }])
    29    }
    30}
    • 首先我们将输出模板配置到indexPath: resolve('dist/index.html')

    • 但是我们分离了静态资源,outputDir: resolve('/www/statics/')

    • 分离了静态资源后,预渲染入口index.html也会打包到resolve('/www/statics/')中,因此要为PrerenderSPAPlugin配置indexPath: resolve('/www/statics/index.html')

    • 这时,按理我们应该将静态资源配置为publicPath:http://cdn.path/。但是这样预渲染的时候因为线上cdn中并没有对应的资源,因此会渲染失败。我们需要预先配置一个能够访问到本地资源的地址,比如publicPath:http://localhost/(这边的http://localhost/是我项目中自己开的本地服务),这样预渲染服务器就能渲染成功了,但是静态html中的静态资源地址都变成了http://localhost/,因此我们在PrerenderSPAPlugin中配置postProcess,在其中将http://localhost/替换为http://cdn.path/,再返回出的html就是拥有正确cdn地址的文件了,如果其中有多个cdn或者其他需求也可以自行匹配替换。

    • 看似成功了,但是我们发现,页面中vue接管的时候,webpack中的baseUrl也成了http://localhost/,因此从chunk-vendors.js中异步加载的组件.js的下载域名也是http://localhost/,因此加载错误,所以这边我们需要额外配置webpack。

    • 首先配置PrerenderSPAPlugin的renderer属性injectProperty: 'PRERENDER_INJECTED__'`与`inject: 'prerender'`,这样,在预渲染的时候会配置参数`window.__PRERENDER_INJECTED = prerender

    • 在入口页面的最上方我们引入配置文件@/common/prerender.js

       1import '@/common/prerender'
      2import Vue from 'vue'
      3import App from './App.vue'
      4import { createRouter } from './router'
      5import { createStore } from './store'
      6...
      7
      8// prerender.js
      9/* eslint camelcase: "off" */
      10if (process.env.NODE_ENV === 'production') {
      11    let isPrerender = window.__PRERENDER_INJECTED__ === 'prerender'
      12    __webpack_public_path__ = isPrerender ? 'http://localhost/' : 'http://cdn.path/'
      13}
      14// 此配置在预渲染模式下继续使用'http://localhost/'这样的本地域名,而正常打包时,这替换为'http://cdn.path/',这样项目就可以正常运行了
      15// 因为'__webpack_public_path__'命名不符合eslint,但是我们又需要此规则,所以'prerender.js'第一行加注释'/* eslint camelcase: "off" */'
      16//同时因为'__webpack_public_path__'变量是webpack的全局属性,所以配置:
      17globals: {
      18    __webpack_public_path__'writable'
      19},

    5.最后

    预渲染的页面被服务器返回之后,浏览器就会自动抓取页面信息,首屏渲染之后,vue引擎启动并接管页面,这时vue会根据自己加载的组件.js重新渲染页面,这显然是不必要的,因此我们需要在layout-App.vue的根元素(<div>)中添加data-server-rendered="true"属性,这样vue就知道dom是服务端渲染的,它会自动比较并更新需要更新的dom,不会全部重新渲染


  • 相关阅读:
    完美兼容的纯CSS下拉菜单
    ASP.Net分页控件发布(转)
    ASP.NET(C#)FileUpload实现上传限定类型和大小的文件到服务器
    完美的ASP.NET页面分页控件
    Asp.net上传图片同时生成缩略图和水印图
    狗狗约瑟夫环(链表)
    丹叔链表
    囧囧出的题……他自己都没过(一元多项式之和)
    More is better
    最短路
  • 原文地址:https://www.cnblogs.com/qinyuandong/p/13649984.html
Copyright © 2011-2022 走看看