vue项目seo问题简单解决,并生成sitemap

原文链接https://juejin.cn/post/6844903996793028622

vue项目seo问题简单解决,并生成sitemap

1.场景

随着这几年前端的大变革,对于大型项目,过多的Dom绑定后期的维护已成噩梦。单页面应用由然而生,如今市场上vue,react,angular已成为三驾马车,也是前端人员需要必备的技能了。今天我们主要讲单页面seo的问题,其他的东西就不深入了。

2.单页面为什么不利于seo?

1.搜索引擎爬虫的原理就是抓取你的url,然后获取你的html源代码并解析。单页面应用最终渲染页面都是通过js动态生成的,爬虫获取到的html只是单页面的模型页面不是最终渲染的页面,所以用js来渲染数据对seo并不友好。

2.seo的本质就是一个服务器向另一个服务器发起请求,解析请求内容。通常情况下搜索引擎在追求速度的原因下,并不会去执行请求到的js。这时单页面的问题就来了,html文件在服务端并没有渲染数据,实际渲染数据实在客户端完成的,所以搜索引擎请求到的html只是一个结构,这样就很不利于页面内容被搜索引擎搜索到。所以服务端渲染就是为了解决单页面应用在发送到浏览器之前页面就有内容了。

3.单页面seo的解决方案

1.页面预渲染

2.服务端渲染

服务端渲染已经很成熟了,也有现成的类库,今天我们主要讲的就是页面预渲染。

使用场景:针对于只需改善少数页面,无需与服务端交互实时动态编译html文件的业务场景,比如:门户网站等

使用插件:prerender-spa-plugin,prerender-spa-plugin是webpack的插件,所以用webpack打包的单页面(react,vue,angular等)都能使用,今天的实践是vue的,prerender-spa-plugin原理就是使用浏览器内核预先加载页面渲染数据得到完整页面后生成完整的html文件

4.预渲染实践

4.1 安装prerender-spa-plugin

yarn add prerender-spa-plugin -D 
or 
npm install prerender-spa-plugin -D 

4.2 将vue router mode属性改为history

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: Index
    },
    {
      path: '/about',
      component: About
    }
  ]
})
 

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

4.3 build/webpack.prod.conf.js (vue-cli2生成的结构下都有) build/webpack.prod.conf.js

const path = require('path')
const PreRenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PreRenderSPAPlugin.PuppeteerRenderer;
const routes = [ '/', '/about'];
const resolve = dir => path.join(__dirname, '..', dir);
const webpackConfig = merge(baseWebpackConfig, {
    plugins: [
      new PreRenderSPAPlugin({
          staticDir: resolve('dist'),
          routes,
          renderer: new Renderer({
          inject: {},
          // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
          //render-event的作用就是在render-event事件执行后执行preRender
          renderAfterDocumentEvent: 'render-event',
          //puppeteer参数,标签意思:完全信任在Chrome中打开的内容
          args: ['--no-sandbox', '--disable-setuid-sandbox']
      })
      })
    ]
})
 

本地打包没有问题,linux服务器打包可能会出现ERROR: Fail to lauunch chrome!

原因一:linux docker 容器不能启动chrome,因为docker共享内存不够的原因

其他问题详细答案:github.com/chrisvfritz…

4.4 src/main.js (vue-cli2生成的结构下都有)

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
  mounted() {
    document.dispatchEvent(new Event('render-event'))
  }
});
 

配置完后,npm run build就可以生成routes中路由对应的html了。是不是很简单? 打包后结构:

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

5.自动生成打包后所有页面sitemap

5.1 Sitemap简介

百度百科:Sitemap 可方便网站管理员通知搜索引擎他们网站上有哪些可供抓取的网页。最简单的 Sitemap 形式,就是XML 文件,在其中列出网站中的网址以及关于每个网址的其他元数据(上次更新的时间、更改的频率以及相对于网站上其他网址的重要程度为何等),以便搜索引擎可以更加智能地抓取网站。

粗俗易懂:百度爬虫非常喜欢Sitemap,Sitemap协议使你能够告知搜索引擎网站中可供抓取的网址,sitemap的生成就是让搜索引擎更好的去访问网站从而给网站的收录产生一个好的作用。

5.2 sitemap.js

简介:sitemap.js是高级站点地图生成库,可轻松创建网站地图XML文件。

具体用法可以去github上看,这里不详细深入,就是快速生成sitemap的一个工具(sitemap.js

安装:

yarn add sitemap -D 
or 
npm install sitemap -D 

**5.3 结合prerender-spa-plugin postProcess方法自动生成网站sitemap **

postProcess:prerender-spa-plugin允许您在生成的html写入文件前做个性化的处理,每个生成html的路由都会走进来一次。官方解释

postProcess函数参数:content

  • content.originalRoute: 重定向之前,原始路由
  • content.route:重定向后的预渲染路由,也是文件输出的位置,相对于staticDir,这个属性是可以修改的
  • content.html: 输出的html内容

生成思路:每次生成html之前获取当前html内容中的外链接,将其push进全局地址容器中,以便生成最终的sitemap。

5.4 sitemap需采集内容

需采集链接:

1.单页面路由链接

2.网站html内容中的外链

** 5.5 实践生成sitemap,修改build/webpack.prod.conf.js**

//需生成html的路由
const fs = require('fs');
const path = require('path')
const sm = require('sitemap');
const PreRenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PreRenderSPAPlugin.PuppeteerRenderer;
const routes = [ '/', '/about'];
//sitemap url容器
const siteMapUrls = [];
const resolve = dir => path.join(__dirname, '..', dir);
const webpackConfig = merge(baseWebpackConfig, {
    plugins: [
      new PreRenderSPAPlugin({
      staticDir: resolve('dist'),
      routes,
      postProcess (context) {
        //content 参数
        const {originalRoute, route, html} = context;
        //全局获取href内容正则
        const reg = /(?<=<as*.*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: 0.5, lastmod: new Date().toLocaleDateString()}
          });
          //生成siteMap文件
          const siteMap = sm.createSitemap({
            //路由前缀地址,全地址自动不会添加hostname(https://www.baidu.com不会添加hostname)
            hostname: 'https://www.test.com',
            cacheTime: 600000,  //600 sec (10 min) cache purge period
            urls: currentSiteMapUrls
          });
          //将sitemap文件添加搭配打包文件夹dist中
          fs.writeFileSync(resolve('dist/sitemap.xml'), siteMap.toString());
        }
        //返回当前contet对象
        return context
      },
      renderer: new Renderer({
        inject: {},
        //在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
        //render-event的作用就是在render-event事件执行后执行preRender
        renderAfterDocumentEvent: 'render-event',
        //puppeteer参数,标签意思:完全信任在Chrome中打开的内容
        args: ['--no-sandbox', '--disable-setuid-sandbox']
      })
    }),
  ]
})

生成的sitemap.xml

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://www.test.com</loc>
<priority>0.5</priority>
<lastmod>2019-11-14</lastmod>
<changefreq>weekly</changefreq>
</url>
<url>
<loc>https://www.test.com/about</loc>
<priority>0.5</priority>
<lastmod>2019-11-14</lastmod>
<changefreq>weekly</changefreq>
</url>
<url>
<loc>https://www.baidu.com</loc>
<priority>0.5</priority>
<lastmod>2019-11-14</lastmod>
<changefreq>weekly</changefreq>
</url>
</urlset>
 

总结:

prerender-spa-plugin 预加载适合于门户型静态网站,大型数据交互网站有seo需求的还是使用服务器渲染更好,这也只是一个快速解决seo的一个方案,还有生成sitemap,取页面中的外链上述代码只是简单的取,需要更加完善的外链规则的自己可以定制化,这个方案用于解决门户型小型网站使用单页面构建不利于seo的痛点的简单解决。

原文地址:https://www.cnblogs.com/zyx-blog/p/15471183.html