Vue 预渲染实现方案

2021-03-30  本文已影响0人  唯吾听烟雨

前言

Ajax 技术的出现,让我们的 Web 应用能够在不刷新的状态下显示不同页面的内容,这就是单页应用。在一个单页应用中,往往只有一个 html 文件,然后根据访问的 url 来匹配对应的路由脚本,动态地渲染页面内容。单页应用在优化了用户体验的同时,也给我们带来了许多问题,例如 SEO 不友好、首屏可见时间过长等。服务端渲染(SSR)和预渲染(Prerender)技术正是为解决这些问题而生的。

服务端渲染与预渲染区别

下图简单展示了客户端渲染、服务端渲染和预渲染的请求流程。


image.png

服务端渲染与预渲染共同点

针对单页应用,服务端渲染和预渲染共同解决的问题:

什么场景下不适合使用预渲染

Prerender SPA Plugin

prerender-spa-plugin 是一个 webpack 插件用于在单页应用中预渲染静态 html 内容。因此,该插件限定了你的单页应用必须使用 webpack 构建,且它是框架无关的,无论你是使用 React 或 Vue 甚至不使用框架,都能用来进行预渲染。

prerender-spa-plugin 原理

那么 prerender-spa-plugin 是如何做到将运行时的 html 打包到文件中的呢?原理很简单,就是在 webpack 构建阶段的最后,在本地启动一个 phantomjs,访问配置了预渲染的路由,再将 phantomjs 中渲染的页面输出到 html 文件中,并建立路由对应的目录。

image.png
查看 prerender-spa-plugin 源码 prerender-spa-plugin/lib/phantom-page-render.js
// 打开页面
page.open(url, function (status) {
  ...
  // 没有设置捕获钩子时,在脚本执行完捕获
  if (
    !options.captureAfterDocumentEvent &&
    !options.captureAfterElementExists &&
    !options.captureAfterTime
  ) {
    // 拼接 html
    var html = page.evaluate(function () {
      var doctype = new window.XMLSerializer().serializeToString(document.doctype)
      var outerHTML = document.documentElement.outerHTML
      return doctype + outerHTML
    })
    returnResult(html) // 捕获输出
  }
  ...
})

项目实例

该实列基于Vue.js 2.0 + vue-router,使用 vue-cli3.0 生成

安装
npm install prerender-spa-plugin --save
vue.config.js中增加
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
const path = require('path');
module.exports = {
    configureWebpack: config => {
        if (process.env.NODE_ENV !== 'production') return;
        return {
            plugins: [
                new PrerenderSPAPlugin({
                    // 生成文件的路径,也可以与webpakc打包的一致。
                    // 下面这句话非常重要!!!
                    // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
                    staticDir: path.join(__dirname,'dist'),
                    // 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
                    routes: ['/', '/product','/about'],
                    // 这个很重要,如果没有配置这段,也不会进行预编译
                    renderer: new Renderer({
                        inject: {
                            foo: 'bar'
                        },
                        headless: false,
                        // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
                        renderAfterDocumentEvent: 'render-event'
                    })
                }),
            ],
        };
    }
}
在main.js中增加
new Vue({
  router,
  store,
  render: h => h(App),
  mounted () {
    document.dispatchEvent(new Event('render-event'))
  }
}).$mount('#app')
router.js 中设置mode: “history”

预渲染的单页应用路由需要使用 History 模式而不是 Hash 模式。原因很简单,Hash 不会带到服务器,路由信息会丢失。vue-router 启用 History 模式参考这里。

验证是否配置成功

运行npm run build,看一下生成的 dist 的目录里是不是有每个路由名称对应的文件夹。然后找个 目录里 的 index.html 用IDE打开,看文件内容里是否有该文件应该有的内容。有的话,就设置成功了

如果你想修改每个页面的meta 信息,这里推荐使用 vue-meta(https://github.com/nuxt/vue-meta)

首先生成一个项目并安装依赖,组件开发过程我们不关注,具体可以查看示例源代码。

原文链接:https://blog.csdn.net/huangjianfeng21/article/details/92421738

上一篇 下一篇

猜你喜欢

热点阅读