小杨博客
March 23, 2023

Vue 项目做SEO优化

 •  3   • 478 
Table of contents

基于Vue2 + Router 的项目做SEO搜索引擎优化

vue 页面预渲染

  1. 安装prerender-spa-plugin
yarn add prerender-spa-plugin --save
or 
npm install prerender-spa-plugin --save
  1. 将vue router mode属性改为history

    const routes = [
      {
        path: '/',
        name: 'Home',
        component: Home
      }
    ]
    const router = new VueRouter({
      mode: 'history',
      base: process.env.BASE_URL,
      routes
    })
    

    mode必须为history,如果不改为history,在hash模式下,prerender-spa-plugin打包出来的静态html只有首页的html是完整的,其他页面还是用的首页骨架,然后动态生成生成html替换,就达不到seo的效果。

  2. 页面中生成meta 元信息

    • 安装vue-meta-info

      npm install vue-meta-info --save
      or 
      yarn add vue-meta-info --save
      
    • 在需要的预渲染的页面中加入meta元信息

      export default {
        metaInfo: {
          title: '★广州APP开发,广州APP开发公司_广州思久信息科技有限公司',
          meta: [
            {
              name: 'keywords',
              content: '广州APP开发公司,广州网站建设公司,企业管理系统开发'
            },
            {
              name: 'description',
              content:
                '思久科技专业提供广州app开发,微信开发,h5开发,管理系统开发,网站建设等服务'
            }
          ]
        },
        name: 'Home',
        data(){
           return {}
        }
      }
      
  3. 在vue.config.js 配置项或者webpack.config.js 中使用prerender-spa-plugin插件

    // 预渲染模式插件,将SPA单页面应用打包成多页面
    const PrerenderSpaPlugin = require('prerender-spa-plugin')
    const Renderer = PrerenderSpaPlugin.PuppeteerRenderer
    var routes = ['/', '/about', '/enterprise']
    module.exports = {
      configureWebpack: {
         publicPath: './',
      	 outputDir: 'dist',
      	 assetsDir: 'static',
      	 parallel: false,
         plugins:[
          	new PrerenderSpaPlugin({
            // 生成文件的路径,也可以与webpakc打包的一致。
            // 下面这句话非常重要!!!
            // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
            staticDir: path.join(__dirname, 'dist'),
            // 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
            routes,
            // 预渲染代理接口
            // server: {
            //   proxy: {
            //     '/api': {
            //       target: 'http://localhost:9001',
            //       secure: false
            //     }
            //   }
            // },
            // 这个很重要,如果没有配置这段,也不会进行预编译
            renderer: new Renderer({
              // 可选 - 您希望您的应用程序可以通过 `window.injectProperty` 访问的任何值。
              inject: {
                foo: 'bar'
              },
              headless: false, // 渲染时显示浏览器窗口。用于调试
              // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
              renderAfterDocumentEvent: 'render-event',
              // puppeteer参数,标签意思:完全信任在Chrome中打开的内容
              args: ['--no-sandbox', '--disable-setuid-sandbox']
            })
          })
          ]
    
     } 
    }
    
  4. 在main.js 中加入预编译执行事件

    new Vue({
      router,
      store,
      render: h => h(App),
      mounted () {
        // 预编译执行事件
        document.dispatchEvent(new Event('render-event'))
      }
    }).$mount('#app')
    
  5. 在App.vue 中加入data-server-rendered="true",防止预渲染时页面空白

    <template>
      <div id="app" data-server-rendered="true">
        <router-view/>
      </div>
    </template>
    
  6. 注意

    • 生成预渲染页面最好是在生产环境中打包生成
    • 打包生成的预渲染页面需要在配置nginx中访问,否则资源加载不出来,导致页面空白
    • 对项目中的某几个页面网站进行 SEO 优化,可以使用预渲染,如果网站页面有太多的话就不适用预渲染

生成sitemap 网站地图

  1. 安装sitemap

    yarn add sitemap@4 -D 
    or 
    npm install sitemap@4 -D 
    
  2. 在vue.config.js 中配置生成sitemap网站地图

    const fs = require('fs')
    const path = require('path')
    const sm = require('sitemap')
    const PrerenderSpaPlugin = require('prerender-spa-plugin')
    const Renderer = PrerenderSpaPlugin.PuppeteerRenderer
    var routes = ['/', '/about', '/enterprise']
    module.exports = {
      configureWebpack: {
         plugins:[
          	new PrerenderSpaPlugin({
            // 生成文件的路径,也可以与webpakc打包的一致。
            // 下面这句话非常重要!!!
            // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
            staticDir: path.join(__dirname, 'dist'),
            // 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
            routes,
            // 生成sitemap 网站地图
            postProcess (context) {
              // // content 参数
              const { originalRoute, route, html } = context
              // 全局获取href内容正则
              const reg = /(?<=<a\s*.*href=")[^"]*(?=")/g
              // 过滤不包含http或https开头的url
              const urlList = html.match(reg).filter(url => url.startsWith('http'))
              // 将路由添加到全局sitemap容器
              siteMapUrls.push(originalRoute)
              // 将html中的外链添加到全局sitemap容器
              if (urlList.length) {
                urlList.forEach(url => siteMapUrls.push(url))
              }
              // 当当前路由为最后一个生成路由时
              if (route === routes[routes.length - 1]) {
              // 去除重复的链接
                let currentSiteMapUrls = Array.from(new Set(siteMapUrls))
                // 过滤掉链接中的锚点后内容
                currentSiteMapUrls = currentSiteMapUrls.map(url => {
                  const isMao = url.indexOf('#') > -1
                  // 生成sitemap所需数据,具体参数参详sitemap.js官网
                  return { url: isMao ? url.split('#')[0] : url, changefreq: 'weekly', priority: 1.0, lastmod: new Date().toLocaleDateString() }
                })
                // 生成siteMap文件
                const siteMap = sm.createSitemap({
                // 路由前缀地址,全地址自动不会添加hostname(https://www.baidu.com不会添加hostname)
                  hostname: 'http://www.seejoys.com',
                  cacheTime: 600000, // 600 sec (10 min) cache purge period
                  urls: currentSiteMapUrls
                })
                // 将sitemap文件添加搭配打包文件夹dist中
                fs.writeFileSync(path.join(__dirname, 'dist/sitemap.xml'), siteMap.toString())
              }
              // 返回当前contet对象
              return context
            },
            // 预渲染代理接口
            // server: {
            //   proxy: {
            //     '/api': {
            //       target: 'http://localhost:9001',
            //       secure: false
            //     }
            //   }
            // },
            // 这个很重要,如果没有配置这段,也不会进行预编译
            renderer: new Renderer({
              // 可选 - 您希望您的应用程序可以通过 `window.injectProperty` 访问的任何值。
              inject: {
                foo: 'bar'
              },
              headless: false, // 渲染时显示浏览器窗口。用于调试
              // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
              renderAfterDocumentEvent: 'render-event',
              // puppeteer参数,标签意思:完全信任在Chrome中打开的内容
              args: ['--no-sandbox', '--disable-setuid-sandbox']
            })
          })
          ]
    
     } 
    }
    
Follow me

I work on front-end development work