稀土掘金 稀土掘金

如何在vue项目中做SEO优化

什么是SEO

搜索引擎优化(Search engine optimization,简称SEO),指为了提升网页在搜索引擎自然搜索结果中(非商业性推广结果)的收录数量以及排序位置而做的优化行为,是为了从搜索引擎中获得更多的免费流量,以及更好的展现形象。

搜索引擎工作原理

爬虫抓取网页内容

一般爬虫抓取页面内容是先从一个页面出发,从中提取出其他页面的链接,然后当作下一个请求的对象,一直重复这个过程。所以要有良好的SEO,需要你在各大网站上拥有外链,这样会提高你的网站被搜索引擎爬虫的几率。

分析网页内容

爬虫拿到HTML之后,就会对其内容进行分析。一般需要进行去杂、分词、建立索引数据库。什么是索引数据库呢?简单地说就是记录一个词在哪些文档中出现、出现次数、出现的位置等等。为什么要建立索引数据库呢?是为了快速查找。

搜索和排序

搜索会根据你输入的关键词,分别查询其对应的索引数据库,并对结果进行处理和排序。

1. SPA单页面应用

  • 不利于SEO

  • 根据这几个缺点,我们便又引出了接下来讨论的两个主题,SEO和SSR,先谈谈SEO。

单页面为什么不利于seo?

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

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

2. SEO搜索引擎

  • 搜索引擎优化。SEO是一种通过了解搜索引擎的运作规则(如何抓取网站页面,如何索引以及如何根据特定的关键字展现搜索结果排序等)来调整网站,以提高该网站在搜索引擎中某些关键词的搜索结果排名。

如果项目初始展示 loading图,然后通过 Ajax 获取内容,抓取工具并不会等待异步完成后再行抓取页面内容。 此时,SSR便闪亮登场了

SSR服务端渲染

  • 在普通的SPA中,一般是框架及网站页面代码发送给浏览器,然后在浏览器中生成和操作DOM(这也就是为什么第一次SPA网站在同等带宽下比传统的在后端生成HTML发送到浏览器要更慢的主要原因),但其实也可以将SPA应用打包到服务器上,在服务器上渲染出HTML,发送到浏览器,这样的HTML页面还不具备交互能力,所以还需要与SPA框架配合,在浏览器上“混合”成可交互的应用程序。所以,只要能合理地运用SSR技术,不仅能一定程度上解决首屏慢的问题,还能获得更好的SEO。

  • SSR的优点

  • 更快的响应时间,不用等待所有的JS都下载完成,浏览器便能显示比较完整的页面了。

  • 更好的SSR,我们可以将SEO的关键信息直接在后台就渲染成HTML,而保证搜索引擎的爬虫都能爬取到关键数据。

  • SSR的缺点

  • 相对于仅仅需要提供静态文件的服务器,SSR中使用的渲染程序自然会占用更多的CPU和内存资源

  • SSR常用框架

  • React 的 Next

  • Vue.js 的 Nuxt

  • Nust中文官网 www.nuxtjs.cn/

  • 所以要想SEO做的好,建议使用服务端SSR渲染(网页是通过服务端渲染生成后输出给客户端),可采用Nuxt(其本质是Node封装的VUE的SSR框架,所以是在服务端跑的,对首屏渲染友好)

Nuxt的使用参考 juejin.cn/post/713230…

  • 预编译prerender-spa-plugin插件

  • Nuxt要用他的那套约束,也就是说你的项目要迁移到Nuxt去,解决 SEO 问题是不是只有 SSR 呢?其实预渲染也能做到\

  • 还有的,就是webpack的预编译,可以对首页先加载出来,然后他再慢慢去加载js文件,这样就不用等待首页加载

  • 服务端渲染解决的问题,不仅只是把 HTML 页面给浏览器,更重要的是处理动态逻辑和 JS 代码后,将渲染后完整的 HTML 给浏览器,渲染的过程在服务端。

  • 预渲染,是利用构建工具在 webpack 中生成静态的 HTML,直接给浏览器,渲染的过程在本地。

  • 预渲染插件里面提到两种不能使用:大量路由、动态内容

  • 接下来我们介绍下prerender-spa-plugin插件实现预编译

  • 安装:

  • cnpm install prerender-spa-plugin --save复制代码
    
  • vue-cli3.0的写法

  • 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 。如果不设置history模式,也能运行和生成文件,每个index.html文件的内容都会是一样的。所以必须使用 history模式

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

  • 打包之后如下图:

  • vue-cli2.0的写法

  • 修改 webpack 配置,比较简单就能完成配置\

  • const PrerenderSPAPlugin = require('prerender-spa-plugin')
      const Renderer = PrerenderSPAPlugin.PuppeteerRenderermodule.exports = {
        plugins: [
          //...   
          new PrerenderSPAPlugin({
            staticDir: path.join(__dirname, 'dist'),
            outputDir: path.join(__dirname, 'prerendered'),
            indexPath: path.join(__dirname, 'dist', 'index.html'),
            routes: ['/', '/about', '/some/deep/nested/route'],
            postProcess(renderedRoute) {
              renderedRoute.route = renderedRoute.originalPath
              renderedRoute.html = renderedRoute.html.split(/>[\s]+</gmi).join('><')
              if (renderedRoute.route.endsWith('.html')) {
                renderedRoute.outputPath = path.join(__dirname, 'dist', renderedRoute.route)
              }
              return renderedRoute
            }, minify: {
              collapseBooleanAttributes: true,
              collapseWhitespace: true,
              decodeEntities: true, keepClosingSlash: true,
              sortAttributes: true
            },
            renderer: new Renderer({
              inject: { foo: 'bar' },
              maxConcurrentRoutes: 4
            })
          })
        ]
      }复制代码
    

5. 两者的区别

  • 服务端SSR渲染解决的问题,不仅只是把 HTML 页面给浏览器,更重要的是处理动态逻辑和 JS 代码后,将渲染后完整的 HTML 给浏览器,渲染的过程在服务端。
  • 预渲染,是利用构建工具在 webpack 中生成静态的 HTML,直接给浏览器,渲染的过程在本地。将服务端编译HTML的时机提前到了构建时,因此降低了服务端的压力
  • 共性: 加载速度一样快; 入侵性小;
  • 不同: 服务端渲染在服务器;适用于复杂、较大型、与服务端交互频繁的网站,如电商
    预渲染在客户端上;适用于简单的静态页面
  • 很奇怪的一点是:打开f12,发现刷新很慢,关掉f12,刷新就很快,不明觉厉,

如果你已经采用了前后分离的单页项目,而你的网站内容不需要AJAX去获取内容和展示内容,那么可以试试 prerender-spa-plugin 这个插件,这个插件是一个webpack插件,可以帮助你在打包过程中通过无头浏览器去渲染你的页面,并生成对应的HTML。当然这个方案适合你的路由是静态的,并且路由数量是有限的。

如果你的内容是AJAX动态获取的,那么vue单页项目可以试试 prerender ,这个是一个预渲染服务,可以帮你通过无头浏览器渲染页面,并返回HTML。这个方案和prerender-spa-plugin很相似,都是通过无头浏览器去渲染页面,不同的是渲染的时机,prerender-spa-plugin是在打包过程中渲染,注定了其只能渲染静态路由,而prerender 是在请求时渲染,所以可以渲染动态的路由。下面我重点介绍一下prerender方案。

prerender 的使用

1、安装

$ npm install prerender
复制代码

2、启动服务 server.js

const prerender = require('prerender');
const server = prerender();
server.start();
复制代码

3、测试

http://localhost:3000/render?url=https://www.example.com/
复制代码

经过上面三个步骤,你就已经启动一个预渲染服务,并且会返回" www.example.com/"的内容,整个过程还是…prerender-node (Express)nginx.conf等,那么这个和 prerender 是什么关系呢?是否直接使用中间件就可以呢?下面介绍prerender是如何工作的吧。

prerender方案的原理

首先服务端接收到一个页面的请求,然后判断这个请求是否来自搜索引擎的爬虫,如果不是,则直接返回单页项目的HTML,按照普通单页项目的工作模式(客户端渲染),如果是,则把请求转发给prerender服务,prerender服务会通过无头浏览器进行预渲染,渲染完成把内容返回,这样爬虫就可以拿到有内容的HTML了。prerender中间件就是用来判断请求是否来自搜索引擎爬虫和转发请求的。

值得注意的是,prerender服务是不包含无头浏览器的,所以需要自行安装chrome浏览器。因此,整个方案运行需要三部分:

  • chrome浏览器
  • prerender服务
  • prerender中间件

那么prerender服务是怎么知道页面渲染已经完成的呢? Prerender服务通过计算未完成的请求数量,来确定页面何时完成加载。一旦未完成的请求数达到零,服务会等待一段时间(默认500ms),然后保存HTML。

prerender的最佳实践

经过实践,请求一个经过prerender渲染的页面是时间,快的时候约2s,慢的时候会长达8s。一般来说,请求时间在3s以内是最好的。所以我从以下几个方面入手,探索prerender的优化方法。

减少资源请求的时间

影响prerender渲染时间的资源主要有js请求资源和api请求资源,api请求时间一般由后端决定,所以我考虑的是如何减少js资源请求时间。一般prerender服务渲染的资源请求地址是由页面请求URL决定的,所以一般是线上的地址,如果我们把prerender服务部署在网站的服务器上,让prerender服务请求资源走本地,那么就可以缩短资源的请求时间了。

如果你的线上服务是开启了CDN的话,那么资源走本地还有一个好处,就是可以节省CDN流量。

优化prerender选项

prerender提供了一些自定义的选项

pageDoneCheckInterval:这个参数是prerender检查页面请求是否完成的定时器时间,默认是500ms,即每500ms检查未完成的请求数量是否为零,我将其修改为100ms,提高其检查的频率。

waitAfterLastRequest:这个参数是最后一个请求完成之后等待的时间,默认是500ms,主要是请求完成之后,页面更新渲染需要时间,立即返回的话,可能请求的数据来不及渲染,我将时间修改为200ms。

prerender插件

httpHeaders —— 返回合理的HTTP状态码

添加httpHeaders这个插件,可以更改返回的HTML的HTTP状态码,添加方式如下

var prerender = require('prerender');
var server = prerender()
server.use(prerender.httpHeaders());
server.start();
复制代码

prerender通过识别在中的标签来设置页面返回的HTTP状态码。

<meta name="prerender-status-code" content="404">
复制代码

如果你需要设置301重定向,可以这样做

<meta name="prerender-status-code" content="301">
<meta name="prerender-header" content="Location: http://www.xxx.com">
复制代码

blockResources —— 无需等待图片资源

prerender是根据未完成的请求数来判断是否渲染结束的。但是我们给搜索引擎返回的HTML只需要渲染通过js动态增加的DOM,其实不需要渲染css或者渲染接口返回的图片的,我们来看下prerender在渲染中是否会请求这些资源。 prerender可以开启是否打印请求,开启方式如下:

var server = prerender({
    logRequests: true
});
复制代码

开启之后就可以在控制台看到请求了,请求里面是包含css和图片资源的。

2019-07-17T04:34:03.180Z - 47 http://xxx.com/css/chunk-f4a02584.da8dca38.css
2019-07-17T04:34:03.180Z {
  source: 'network',
  level: 'error',
  text: 'Failed to load resource: net::ERR_INVALID_ARGUMENT',
  timestamp: 1563338043130.37,
  url: 'http://xxx.com/wefid/css/chunk-f4a02584.da8dca38.css',
  networkRequestId: '1000039068.65' 
}

2019-07-17T04:34:03.924Z + 3 http://xxx.com/img/erweima_wx.a84d82ef.jpg
2019-07-17T04:34:03.924Z + 4 http://xxx.com/img/erweima_wb.06971584.png
复制代码

为什么prerender要等待这些资源呢?因为prerender服务还有一个强大的功能,那就是Prerender.com,其可以通过一个接口给你返回如下的东西:

  • 网页的HTML文件
  • 网页的屏幕截图(视口或全屏)
  • 网页的PDF文件
  • 网页的HAR文件
  • 执行您自己的javascript并返回json和HTML

很明显,这些功能是需要加载你所需的CSS或图片资源的,不然网页显示有问题。这个时候,如果你只需要满足SEO需求而不需要Prerender.com的功能的话,那么blockResources插件就可以派上用场了。插件添加方式如下:

var prerender = require('prerender');
var server = prerender()
server.use(prerender.blockResources());
server.start();
复制代码

使用blockResources插件之后,图片资源和字体资源会被abort(舍弃)。

自定义渲染结束时间

如果你想更细粒化地控制prerender的返回时机,提前结束或者延后结束,那么可以使用这个标志window.prerenderReady

首先需要设置window.prerenderReady为false,prerender在检测到window.prerenderReadyfalse之后,会等待你设置为true再返回结果。

<script> window.prerenderReady = false; </script>
复制代码

当你渲染完成之后,一般在接口请求完成并渲染完成之后

window.prerenderReady = true;
复制代码

这样你就可以更加自由地控制渲染结束的时机。

开启缓存

缓存这里有两个方面,一方面是HTTP缓存(浏览器缓存),另一方面是渲染结果缓存。

首先HTTP缓存可以让prerender服务不用频繁地发起资源请求,节省传输时间。这个我就不展开将,我想讲的是渲染结果缓存。prerender中间件提供了两种缓存方式, redis 或者 memcached ,以redis为例:

$ npm install redis
复制代码
var redis = require("redis"),
client = redis.createClient();

prerender.set('beforeRender', function(req, done) {
	client.get(req.url, done);
}).set('afterRender', function(err, req, prerender_res) {
	client.set(req.url, prerender_res.body)
});
复制代码

你可以通过 beforeRender 和 afterRender 这两个钩子进行细粒化地控制,对于内容变化频繁的不缓存或缓存时间短,对于内容变化不频繁的设置长时间缓存。开启缓存不仅可以加速返回时间,还可以减轻服务器的压力。

统计和监控

统计和监控可以放在中间件的 afterRender 中进行。

prerender.set('afterRender', function(err, req, prerender_res) {
  if(err){
    // 这里是错误监控代码
    // ...
    // return
  }
  let {headers: req_headers, originalUrl} = req
  let {headers: res_headers, body} = prerender_res
  // 这里是统计代码,可以保存请求和返回的相关信息
})

小结

要想seo好,使用ssr服务端渲染,即服务端生成静态页面给客户端,交互的请结合Nuxt;或者不在服务端渲染,使用webpack的prerender-spa-plugin插件实现预编译,生成静态的 HTML,直接给浏览器,渲染的过程在本地。使用webpack的code splitting按需加载压缩优化首页渲染或者vue分片

最后总结

  • 如果构建大型网站,如商城类 => SSR服务器渲染
  • 如果只是正常公司官网, 博客网站等 => 预渲染/静态化/Phantomjs 都比较方便

代做工资流水公司常州代开贷款流水常州流水单样本九江银行流水修改代开大连做工资流水app截图温州代做背调工资流水太原个人银行流水图片杭州个人流水价格嘉兴工资证明模板杭州背调工资流水打印西宁做车贷流水惠州转账流水多少钱德阳贷款流水代做舟山贷款银行流水制作中山制作银行流水修改沈阳开银行流水合肥办理个人银行流水潍坊开在职证明许昌车贷银行流水 制作镇江代办入职流水厦门自存流水模板成都代做工资证明沈阳开收入证明深圳企业贷流水制作曲靖打签证工资流水杭州制作企业银行流水无锡对公账户流水价格咸阳代办企业对公流水德阳工资流水app截图办理合肥个人银行流水制作沈阳查询入职银行流水香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化