什么是SEO
搜索引擎優化(Search engine optimization,簡稱SEO),指為了提升網頁在搜索引擎自然搜索結果中(非商業性推廣結果)的收錄數量以及排序位置而做的優化行為,是為了從搜索引擎中獲得更多的免費流量,以及更好的展現形象。
SEM(Search engine marketing,搜索引擎營銷),則既包括了SEO,也包括了付費的商業推廣優化。
本文主要介紹的是前端如何在代碼上做SEO以及單頁項目如何實現SEO。
搜索引擎工作原理
要了解SEO,首先得了解搜索引擎的工作原理,其原理是比較復雜,流程簡化如下:
爬蟲抓取網頁內容
一般爬蟲抓取頁面內容是先從一個頁面出發,從中提取出其他頁面的鏈接,然后當作下一個請求的對象,一直重復這個過程。所以要有良好的SEO,需要你在各大網站上擁有外鏈,這樣會提高你的網站被搜索引擎爬蟲的幾率。
分析網頁內容
爬蟲拿到HTML之后,就會對其內容進行分析。一般需要進行去雜、分詞、簡歷索引數據庫。什么是索引數據庫呢?簡單地說就是記錄一個詞在哪些文檔中出現、出現次數、出現的位置等等。為什么要簡歷索引數據庫呢?是為了快速查找。
搜索和排序
搜索會根據你輸入的關鍵詞,分別查詢其對應的索引數據庫,並對結果進行處理和排序。
前端編碼的SEO
網站結構
網站結構要清晰。一般網站的結構是樹形的,一般分為三個層次:首頁 → 頻道頁(列表頁) → 文章頁(詳情頁)。
網站的結構要扁平。結構層數越少越好,一般不要超過三層,搜索引擎一般到了第三層就不想繼續深入地爬取了。多數的網站,例如掘金、雪球等,他們的網站結構是兩層,他們的首頁和頻道頁是同一個頁面。
導航
頁面應該要有簡明的導航。導航可以讓搜索引擎知道網站的結構,也可以讓搜索引擎知道當前頁面在網站結構所在的層次。 建議:
每一個頁面都包含導航。
對於內容較多的網站可以采用面包屑導航。
鏈接使用文字鏈接,如果是圖片,則通過alt屬性告知搜索引擎鏈接的指向。
規范的URL
規范、簡單、易理解的URL能讓搜索引擎更好地抓取內容。建議:
同一個頁面,只對應一個url 。多個url可以采用301進行重定向。
url可以反應網頁內容以及網站結構信息。例如www.a.com/blog、www.a.com/blog/123、www.a.com/article。
url盡量簡短。
盡量減少動態url中包含的變量參數。
提交Sitemap
Sitemap 可通知搜索引擎他們網站上有哪些可供抓取的網頁,以便搜索引擎可以更加智能地抓取網站。
robot.txt
搜索引擎爬行網站第一個訪問的文件就是robots.txt。在這個文件中聲明該網站中不想被蜘蛛訪問的部分,這樣,該網站的部分或全部內容就可以不被搜索引擎訪問和收錄了,或者可以通過robots.txt指定使搜索引擎只收錄指定的內容。
合理的HTTP返回碼
不同的返回碼,搜索引擎的處理邏輯是不一樣的。
如果站點臨時關閉,當網頁不能打開時,建議使用503狀態。503可以告知百度spider該頁面臨時不可訪問,請過段時間再重試。
如果百度spider對您的站點抓取壓力過大,請盡量不要使用404,同樣建議返回503。這樣百度spider會過段時間再來嘗試抓取這個鏈接,如果那個時間站點空閑,那它就會被成功抓取了。
有一些網站希望百度只收錄部分內容,例如審核后的內容,累積一段時間的新用戶頁等等。在這種情況,建議新發內容暫時返回403,等審核或做好處理之后,再返回正常狀態的返回碼。
站點遷移,或域名更換時,請使用301返回。
合適的title
title是告訴搜索引擎網頁的主要內容。
每個網頁應該有一個獨一無二的標題,切忌所有的頁面都使用默認標題
標題要主題明確和精練,包含這個網頁中最重要的內容,且不羅列與網頁內容不相關的信息
用戶瀏覽通常是從左到右的,重要的內容應該放到title的靠前的位置
百度建議描述:
首頁:網站名稱 或者 網站名稱_服務介紹/產品介紹
頻道頁:頻道名稱_網站名稱
文章頁:文章標題_頻道名稱_網站名稱
合適的description
description是對網頁內容的精練概括。這個標簽存在與否不影響網頁權值,只會用做搜索結果摘要的一個選擇目標。 百度推薦做法:
為每個網頁創建不同的description,避免所有網頁都使用同樣的描述
網站首頁、頻道頁、產品參數頁等沒有摘要的網頁最適合使用description
准確的描述網頁,不要堆砌關鍵詞,長度合理
HTML語義化
HTML語義化是用標簽和屬性來描述內容。所以HTML語義化是SEO的基石。一般建議:
HTML結構要清晰和簡潔
跳轉使用<a>標簽,不要使用js跳轉
圖片加alt說明
文章用<article>標簽承載
...
Vue單頁項目的SEO
目前,對於SEO支持比較好的項目方案是采用服務端渲染。所以如果項目有SEO需求,那么比較好的方案是服務端渲染。
如果你已經采用了前后分離的單頁項目,而你的網站內容不需要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/"的內容,整個過程還是比較簡單的。其github官網上面還介紹了它的許多中間件(Middleware),例如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通過識別在<head>中的<meta>標簽來設置頁面返回的HTTP狀態碼。
復制代碼
如果你需要設置301重定向,可以這樣做
復制代碼
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.prerenderReady為false之后,會等待你設置為true再返回結果。
復制代碼
當你渲染完成之后,一般在接口請求完成並渲染完成之后
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
// 這里是統計代碼,可以保存請求和返回的相關信息
})
復制代碼
小結
通過以上的優化方法(除了自定義渲染結束時間和開啟緩存),我已經將HTML的請求時間穩定在2.5s左右。
總結
以上就是我想講的關於前端編碼SEO的全部內容,總而言之,就是
合適的HTML標簽和屬性
合理的HTTP狀態碼
Sitemap & robot.txt
合適的渲染方案
參考文章:
https://juejin.im/post/5d2d64f36fb9a07eba2c6f65