前言
- 先了解什麼是seo?
- 再瞭解搜索引擎蜘蛛的工作原理?
- seo爲啥對vue單頁面不友好?
- vue項目怎麼做seo優化?
- prerender-spa-plugin怎麼使用,以及它的工作原理
什麼是seo?
SEO是由英文Search Engine
Optimization縮寫而來, 中文意譯爲“搜索引擎優化”。SEO是指通過對網站進行站內優化和修復(網站Web結構調整、網站內容建設、網站代碼優化和編碼等)和站外優化,從而提高網站的網站關鍵詞排名以及公司產品的曝光度。通過搜索引擎查找信息是當今網民們尋找網上信息和資源的主要手段。
引擎蜘蛛的工作原理?
詳情查看蜘蛛的工作原理;不夠詳細的話自己百度一下
搜索引擎蜘蛛的爬行是被輸入了一定的規則的,它需要遵從一些命令或文件的內容。
網絡爬蟲在爬取網頁內容的時候,需要分析頁面內容,主要有以下幾點:
- 從 meta 標籤中讀取 keywords 、 description 的內容。
- 根據語義化的 html 的標籤爬取和分析內容。一個整體都是用 div 標籤的網站和正確使用了 html5 標籤的效果是不一樣的。
- 讀取 a 標籤裏的鏈接,通過 a 標籤的鏈接可以跳轉到別的網站。(爬蟲是先跳轉,還是繼續爬內容再跳轉,就看算法是廣度優先還是深度優先了)
- 像 h1 - h6 標籤是具有不同程度的強調意義的。
- 一般將 h1 視爲重要內容。同樣有強調內容還有 strong 、 em 標籤。
seo爲啥對vue單頁面不友好?
- 爬蟲在爬取的過程中,不會去執行js,所以隱藏在js中的跳轉也不會獲取到
- vue通過js控制路由然後渲染出對應的頁面,而搜索引擎蜘蛛是不會去執行頁面的js的,導致搜索引擎蜘蛛只能收錄index.html一個頁面,在百度中就搜索不到相關的子頁面的內容。
- 我們加載頁面的時候,瀏覽器的渲染包含:html的解析、dom樹的構建、cssom構建、javascript解析、佈局、繪製,當解析到javascript的時候纔回去觸發vue的渲染,然後元素掛載到id爲app的div上,這個時候我們才能看到我們頁面的內容,所以即使vue渲染機制很快我們仍然能夠看到一段時間的白屏情況,用戶體驗不好
引起的問題
-
收錄的頁面少了->被抓取的頁面就少了->點擊量之類的也就少了;
-
不能對對應的頁面做TDK(title, keywords, description)不同的配置,每個頁面的title和meta標籤都是一樣的,不利於網絡爬蟲的爬取
怎麼解決那?
html就不能通過js生成,我們需要在加載js之前做一下頁面的預渲染,目前瞭解到的有兩種方法,vue的ssr渲染(配置參考)和prerender-spa-plugin插件實現(配置參考)。SSR比較複雜,所以選了prerender-spa-plugin來嘗試。
prerender-spa-plugin的使用
See 配置參考.
本次只介紹vue-cli3的解決方案
使用 webpack + prerender-spa-plugin + vue-meta-info輕鬆地添加預渲染
npm install prerender-spa-plugin --save
vue.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
// eslint-disable-next-line no-unused-vars
const webpack = require('webpack')
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'),
// outputDir: path.join(__dirname, './'),
// 對應自己的路由文件,比如a有參數,就需要寫成 /a/param1。
routes: ['/testData', '/contact'],
// 這個很重要,如果沒有配置這段,也不會進行預編譯
renderer: new Renderer({
inject: { //默認掛在window.__PRERENDER_INJECTED對象上,可以通過window.__PRERENDER_INJECTED.foo在預渲染頁面取值
foo: 'bar'
},
headless: false,
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),兩者的事件名稱要對應上。
renderAfterDocumentEvent: 'render-event'//等到事件觸發去渲染,此處我理解爲是Puppeteer獲取頁面的時機
})
})
]
}
},
}
- staticDir 指的是預渲染輸出的頁面地址,
- routes 指的是需要預渲染的路由地址,
- renderer 則是所採用的渲染引擎是什麼,目前用的是 V3.4.0 版本支持 PuppeteerRenderer。
- inject 則是預渲染過程中都能拿到的值,該值提供給你了機會,讓你覺得是否渲染這部分代碼。例如下面的代碼,是不會被預渲染進 HTML 中的。
showMessage(){
if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.foo =='bar') return;
this.message = '我是測試預加載攔截';
}
- renderAfterDocumentEvent 這個則很關鍵,這個是監聽 document.dispatchEvent 事件,決定什麼時候開始預渲染
main.js
new Vue({
router,
store,
render: h => h(App),
//添加到這裏,這裏的render-event和vue.config.js裏面的renderAfterDocumentEvent配置名稱一致
mounted () {
document.dispatchEvent(new Event('render-event'))
}
}).$mount('#app')
然後就是npm run build打包
1. router.js裏面把mode要爲'history',hash模式會打包的時候生成同樣的頁面,所以一定要history2.打包之後上傳到服務器纔可以看到效果,或者本地自己創建一個服務
這樣dist的文件夾目錄結構就會變成如下的樣子
就是打包之後,每個路由都會打包成一個文件夾,都有一個對應的靜態 HTML;
每一個 HTML 內除了
<div id="app"></div>
這個 Vue 的掛載元素外,還有靜態的標籤內容。
<body>
<div id="app">
<div data-v-fa8a7bc4="" class="app-wrapper openSidebar">
<section data-v-2c0baa24="" data-v-fa8a7bc4="">
<div data-v-2c0baa24="">
<div aria-label="Breadcrumb" role="navigation" class="el-breadcrumb"><span class="el-breadcrumb__item"><span
role="link" class="el-breadcrumb__inner is-link">首頁</span><span role="presentation"
class="el-breadcrumb__separator">/</span></span><span class="el-breadcrumb__item"><span role="link"
class="el-breadcrumb__inner">活動管理</span><span role="presentation"
class="el-breadcrumb__separator">/</span></span><span class="el-breadcrumb__item"><span role="link"
class="el-breadcrumb__inner">活動列表</span><span role="presentation"
class="el-breadcrumb__separator">/</span></span><span class="el-breadcrumb__item"
aria-current="page"><span role="link" class="el-breadcrumb__inner">活動詳情</span><span role="presentation"
class="el-breadcrumb__separator">/</span></span></div>
</div>
</section>
</div>
</div>
<script src="config/config.js"></script>
<script src="/static/js/chunk-vendors.cb183907.js"></script>
<script src="/static/js/app.83e704ba.js"></script>
</body>
遇到的問題
1.本地起服務測試,會發現能正常運行,但是做了優化的頁面刷新之後會沒有樣式
造成問題的原因就是打包的時候vue.config.js的資源路徑配置不對
沒有優化之前的路徑是
// 資源路徑
publicPath: './',
打包之後路由下面的資源路徑是這樣的
解決辦法
優化之後的路徑是
// 資源路徑
publicPath: '/',
打包之後的路由下面的資源路徑是這樣的
就是路徑前面多了一個/,採用了絕對路徑
這個時候需要一個服務來測試
1.全局安裝node.js
2.安裝成功後,執行npm install anywhere -g 安裝anywhere
3.在對應路徑下打開cmd,執行anywhere指令。如圖:
然後就可以在瀏覽器輸入地址訪了,可以看到每個路由對應的 HTML
prerender-spa-plugin 是如何做到將運行時的 html 打包到文件中的呢?
- prerender-spa-plugin插件的工作流程圖:
- prerender-spa-plugin 利用了 Puppeteer[4] 的爬取頁面的功能。 Puppeteer 是一個 Chrome官方出品的 headlessChromenode 庫。它提供了一系列的 API, 可以在無 UI 的情況下調用 Chrome 的功能, 適用於爬蟲、自動化處理等各種場景。它很強大,所以很簡單就能將運行時的 HTML 打包到文件中。
- 原理是在 Webpack 構建階段的最後,在本地啓動一個 Puppeteer 的服務,訪問配置了預渲染的路由,然後將 Puppeteer 中渲染的頁面輸出到 HTML 文件中,並建立路由對應的目錄。
- 每個路由對應的 HTML,然後我們可以更改每個路由文件裏的 title 、 meta keyword等 。
- 另外頁面的內容都已經在 HTML 中直接呈現,也可以解決 js 等資源加載慢導致白屏的問題。
###### prerender-spa-plugin 的確在一定程度上解決了我們對於 SEO 的訴求和頁面加載慢的問題。但是它的缺點還是很明顯的。
- 不同的用戶看到不同的頁面,動態數據頁面(預渲染在獲取用戶權限數據之前就進行渲染了,所有他不能做到這個)
- 動態路由也不可以(webpack編譯的時候 路由還沒掛載那)
- 經常發生變化的頁面,數據實時性展示(比如體育比賽等 我們現在的方式是前端拿到組件後進行組裝數據,然後在進行渲染 像這種實時數據的會不準確)
- 路由過多,構建時間過長
然後就是爲這些打包的靜態頁面分配title和meta標籤,需要用到 vue-meta-info
安裝
npm install vue-meta-info --save
main.js裏面引入vue-meta-info
import MetaInfo from 'vue-meta-info'
Vue.use(MetaInfo)
這樣在組件頁面中就可以使用了
假設你要給contact.vue添加title,meta標籤
contact.vue
<template>
...
</template>
<script>
export default {
metaInfo: {
title: '我是contact頭', // set a title
meta: [{ // set meta
name: 'keyWords',
content: '我是contact關鍵字'
},
{
name: 'description',
content: '我是contact描述'
}],
link: [{ // set link
rel: 'asstes',
href: 'https://assets-cdn.github.com/'
}]
}
}
</script>
這樣再結合prerender-spa-plugin,打包之後,在dist文件夾找到contact文件夾下的index.html
打開你會發現就有title和meta的關鍵字和描述標籤了
本地起服務或者發佈到線上運行項目,在頁面右鍵—查看源代碼—就可以看到頁面的title和meta標籤了。