对于vue、react、angular这类项目而言,seo真的是一大痛点。
网络爬虫在爬取网页内容的时候,需要分析页面内容,主要有以下几点:
meta
标签中读取 keywords
、 description
的内容。html
的标签爬取和分析内容。一个整体都是用 div
标签的网站和正确使用了 html5
标签的效果是不一样的。a
标签里的链接,通过 a
标签的链接可以跳转到别的网站。(爬虫是先跳转,还是继续爬内容再跳转,就看算法是广度优先还是深度优先了)h1 - h6
标签是具有不同程度的强调意义的。 一般将 h1
视为重要内容。同样有强调内容还有 strong
、 em
标签。爬虫在爬取的过程中,不会去执行js,所以隐藏在js中的跳转也不会获取到。
spa正好就踩到了痛点,只有一个主要的页面(index.html),而且页面里的内容还很少,通常只有 router-view
或者带有 id
的 div
标签。其跳转和业务逻辑的行为都是靠执行js才行的。
据说google已经支持爬取spa。
vue v2.0
+ vue-router
+ vuex
+ webpack v3.6
+ prerender-spa-plugin v3.2.1
+ vue-meta-info
+ vue-cli v2
使用 npm
或 yarn
安装 prerender-spa-plugin
和 vue-meta-info
的过程就不描述了。
在webpack中配置 prerender-spa-plugin
配置先弄懂要配置在哪个文件里,配置是否生效。 vue-cli2
的配置文件很多,对这些文件不了解的话,很容易配置错地方。
这个配置只需要在 build
的时候可以生成预渲染好的html,所以应该配置在 build/webpack.prod.conf.js
这个文件里。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseWebpackConfig = require('./webpack.base.conf')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const webpackConfig = merge(baseWebpackConfig, {
plugins: [
// vue-cli生成的配置中就已有这个了,不要动
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
}),
// 在vue-cli生成的文件的基础上,只有下面这个才是我们要配置的
new PrerenderSPAPlugin({
// 生成文件的路径,也可以与webpakc打包的一致。
// 下面这句话非常重要!!!
// 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
staticDir: path.join(__dirname, '../dist'),
// 对应自己的路由文件,比如index有参数,就需要写成 /index/param1。
routes: ['/', '/index', '/skin', '/slimming', '/exercise', '/alPay', '/wxPay'],
// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: false,
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
renderAfterDocumentEvent: 'render-event'
})
})
]
})
在 webpack.prod.conf.js
配置完成之后,然后再 main.js
里改成如下所示:
new Vue({
el: '#app',
router,
store,
render: h => h(App),
/* 这句非常重要,否则预渲染将不会启动 */
mounted () {
document.dispatchEvent(new Event('render-event'))
}
})
如果将上面两个地方配置好了,离成功也就近了。然后运行一下 npm run build
。看一下生成的 dist
的目录里是不是有每个路由名称对应的文件夹。然后找个 目录里 的 index.html
用IDE打开,看文件内容里是否有该文件应该有的内容。有的话,这步就成功了。
vue-meta-info
vue-meta-info
的使用相对就简单多了,就是vue的一个组件而已。 在 main.js
中写上
import MetaInfo from 'vue-meta-info'
Vue.use(MetaInfo)
在 xxx.vue
文件里写上以下代码。这些代码会转换成网页的 meta
标签里的内容。
export default {
metaInfo: {
title: '我是一个title',
meta: [
{
name: 'keywords',
content: '关键字1,关键字2,关键字3'
},
{
name: 'description',
content: '这是一段网页的描述'
}
]
}
}
这里使用的 prerender-spa-plugin
的版本是 3.2.1
,在 2.x
的版本中有个写法如下
var PrerenderSpaPlugin = require('prerender-spa-plugin')
const webpackConfig = merge(baseWebpackConfig, {
plugins: {
//配置 prerender-spa-plugin
new PrerenderSpaPlugin(
// 生成文件的路径,此处与webpack打包地址一致
path.join(config.build.assetsRoot), //config.build.assetsRoot为vue cli生成的配置,打包后的文件地址
// 配置要做预渲染的路由,只支持h5 history方式
[ '/', '/test']
)
}
})
以上
2.x
的写法,在3.x
的版本会提示一个警告,让你采用对象的形式
网上也有些说法,让预渲染方式下的 route 采用 history
模式,经过试验,没有必要!没有必要!没有必要!(真香)。留言中的大佬说的很好,实验了一下,确实还是得采用history模式,否则每个index.html文件的内容都会是一样的。
route 的 history
模式就是如下代码中配置
export default new Router({
mode: 'history',
routes: [
// ...
]
})
history
模式下的route,将不会以 hash
的形式展示,也就是说,URL里没有 #
了。
配置写错了文件,一开始一直写在了 /config/index.js
这个文件里。一启动,没效果。再启动,还是没效果。直到我冷静下来,喝了一口水,思考了一下人生,想了一想,这里是10楼,每层楼高3.5米,重力加速度是9.8,地面是水泥的,撞击地面的时候会发生轻微的弹性形变,然后享年24岁。
经过一系列的思想斗争,我觉得,肯定是我的配置压根就没有被运行。然后我被我的机智折服了。打开 package.json
文件,看了看 npm run build
指令时执行的js文件的顺序,发现了惊天的秘密。
原来 /config
目录下的文件,只是给webpack的配置项提供值而已。真正配置webpack插件应该在 /build
下配置。
因为是要在 npm run build
的时候生成那些html,所以应该配置在 webpack.prod.conf.js
文件里。
一个习惯引起的坑,在打包的过程中,习惯将 html
和 resources
分开成两个文件夹。一直没想到,多了一层 html
会导致 prerender-spa-plugin
插件卡在预渲染(自动打开一个Chrome,开始编译)。
用了 vue-cli
构建的项目,在配置之后然后开心的 npm run build
时,满心期待,心情愉悦的等待中。发现,诶,不成功!好气,很气,非常气!
而且啊,没有任何错误提示,没有任何错误提示,没有任何错误提示。
然后就这么点东西,花了我三天的时间
除了预渲染,当然还有别的解决方案。实质上方案只有两种,预渲染就是使用的 phantomjs
先运行的。
vue项目提供了ssr的方案,还有集成ssr方案的 nuxt.js
可以使用。不过ssr方案需要在node上调用 renderToString
来渲染。如果不介意在系统结构中增加一层 node
的话,就可以使用ssr的方式,这种方式会改变前后端交互的方式。
总结还是一条一条来吧
不要以为会搬运代码,就可以当程序员了,得会思考。在发现有问题的时候,先停下尝试的手,把已经可以确定的东西分析清。找到产生问题的原因。就比如第一个坑,找到的很多代码都是采用的非对象的写法,在这个写法里, main.js
里也不需要加上那句 mounted() {// ...}
。这个时候首先应该想到版本是不是会影响配置文件的写法。
发生问题的时候,确认一下自己的方向是否正确。从发现配置写在 webpack.prod.conf.js
的时候,已经是第三天了。方向对了之后,花了半天就找到了剩下的问题所在。
代码的心思要多猜,在你看不懂源码,没有那个时间成本去了解深层原理的时候,你需要猜。猜测引起这个问题的原因,但是不要瞎猜。这里就猜测了版本带来的差异,猜测了配置文件没有起作用,猜测了文件夹目录会引起问题。
学会尝试。预渲染这个确实是文档描述的不够仔细,每个属性的说明只有简单的两句描述。网上的例子,大多和自己的环境不匹配。用 vue-cli
的肯定用的 2.x
的 prerender-spa-plugin
。没用 vue-cli
的,配置文件都和自己不一样。只有去尝试更多例子,然后确定哪部分配置才是有效的。
最后,如果这篇文章有说明不仔细或者有纰漏的地方,可以留言告诉我。