vue 预渲染遇到的坑

前言:

  最近公司项目需要增加seo搜索引擎优化,到网上找了下资料,有预渲染和服务端渲染两种方式,考虑到只需要渲染首页所以我选择了先启用比较简单的预渲染方式来做seo!

步骤:

1、安装 prerender-spa-plugin,使用淘宝镜像安装 cnpm

cnpm install   prerender-spa-plugin -D
  • -D 表示在开发环境下使用
  • cnpm 淘宝镜像安装可以避免安装过程无端报错

2、预渲染一定要把路由模式变成history

const router = new Router({
  mode: 'history', // 预渲染一定要模式改成history
  routes: baseRoute
})

3、最好修改config/index.js 中的build部分的 assetsPublicPath: '/',

build: {
        // Template for index.html
        index: path.resolve(__dirname, '../dist/index.html'),
 
        // Paths
        assetsRoot: path.resolve(__dirname, '../dist'),
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        ....

因为我们是把打包发布后的程序放在了根目录下,所以base 和 assetsPublicPath 都写了 / ,如果网站访问形式是 http://www.XXX.com/web ,则需要把 / 该为 /web/ ,否则访问不到内容

4、webpack.prod.conf.js 添加如下代码:

...
  const PrerenderSPAPlugin = require('prerender-spa-plugin')
  const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
 
  ...
  plugins: [
         // 在vue-cli生成的文件的基础上,只有下面这个才是我们要配置的
    new PrerenderSPAPlugin({
        // 生成文件的路径,也可以与webpakc打包的一致。
        // 下面这句话非常重要!!!
        // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
        staticDir: path.join(__dirname, '../dist'),
 
        // 对应自己的路由文件,如果index有参数,则prerender-spa-plugin不适用(官方文档有说明)
        routes: ['/','/index','/login'],
 
        // Server configuration options.
        server: {
          // Normally a free port is autodetected, but feel free to set this if needed.
          port: 80,
          proxy:{
            '/api': {
              target: 'http://www.xxx.com',
              changeOrigin: true, //是否跨域
              pathRewrite: {
                  '^/api': 'api' //需要rewrite重写的,
              }
            }
          }
        },
        // 这个很重要,如果没有配置这段,也不会进行预编译
        renderer: new Renderer({ 
          // 触发渲染的时间,用于获取数据后再保存渲染结果
          renderAfterTime: 10000,
          // 是否打开浏览器,false 是打开。可用于 debug 检查渲染结果
          headless: false
           // 在项目的main.js入口中使用 `document.dispatchEvent(new Event('render-event'))` 
        renderAfterDocumentEvent: 'render-event', // render-event: 声明的方法名 
        })
    }),
   ...
 
  ]

5、main.js 入口文件中添加 document.dispatchEvent

...
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App),
  /* 这句非常重要,否则预渲染将不会启动 */
  mounted() {
    document.dispatchEvent(new Event('render-event'))
  }
})

6、前面五步都执行完后,我们开始打包了

npm run build

生成dist文件夹

下面我补充一下:

安装一个http server插件,可以直接执行dist文件夹下的index.html类似于直接把打包文件在服务器端部署运行了

1.安装 http server

npm i http-server -g   // 全局安装

2、进入到dist目录

cd dist

3、启动本地服务器

hs -o -p 9999  // 自动启动本地dist目录下的index.html

// 浏览器启动 127.0.0.1:9999

4、至此不出意外我们看到了浏览器端后端返回来了html文件,支持seo爬虫了

但是。。。。

有几个坑我这里必须记录下:

1、history模式下,修改nginx配置,不然页面刷新会报404

#配置Nginx动静分离,定义的静态页面直接从Nginx发布目录读取。
location / {
        alias /data/mystatic/yihao01-iotstatic/;
        try_files $uri $uri/ @router; #需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
        }
#对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
#因此需要rewrite到index.html中,然后交给路由在处理请求资源
location @router {
        rewrite ^.*$ /index.html last;
        }

2、修改webpack打包js的顺序,否则,打包完后会报 vue项目报错webpackJsonp is not defined

在vue单页面应用中,我们大概都会使用CommonsChunkPlugin这个插件。 传送门 CommonsChunkPlugin 

但是在项目经过本地测试没有任何问题,打包上线后却会报错 webpackJsonp is not defined。这是因为公共文件必须在自己引用的js文件之前引用。

可以手动改文件引用,但是推荐以下解决办法: 

找到build→webpack.prod.conf.js→找到HtmlWebpackPlugin插件,添加如下配置即可

chunks: ['manifest', 'vendor', 'app']

 

我是手动修改文件引用的顺序的:

这样配置后真的不会报 webpackJsonp is not defined

3、还有第三个问题目前我还在思考中,就是我的首页做了登录拦截,用户没登录则路由跳转之前被拦截到登录页面,

      加了这个pemmision.js到main.js入口文件后,我发现预渲打包后的dist目录只有 login, static, index.html 三个文件,

   而我预渲染的是三个页面

 routes: ['/','/index','/login'],


按照需求,应该还要生成一个index文件夹,但是没有生成,还有index.html也没有生成有html内容的页面,还是只有一个<div id="app"></div>,什么内容都没有了!!!

但是如果我删掉登录拦截的js后,就可以生成  index, login, static, index.html 四个文件了,而且index.html 是包含了首页内容的文件,
目前我还没搞清楚为什么会这样,这个先做记录后续知道了原因再来更新。。。
原文地址:https://www.cnblogs.com/mmzuo-798/p/10600321.html