本文章寫於 2019-07-05 請注意時效性。html
有關 SPA 項目的 SEO 友好的解決方案其實很少, 常見的解決手段以下:前端
前者很是穩定可是對於已有的 SPA 項目進行改造須要注意的問題有不少並且耗時長與重寫一個沒有太大區別,後者只能對於那些"不管是哪一種用戶訪問返回的結果都同樣"的頁面合適難免十分被動。vue
總的來講都是十分的繁瑣,不過依然有能夠避開修改原有代碼的解決方案, 例以下面的這個:nginx
https://www.cnblogs.com/lipte...
這些方案的基本原理就是,使用代理服務器區分搜索引擎的爬蟲和普通用戶從而實現針對性內容響應,普通用戶響應原有的 SPA 項目也就是「純粹的 index.html 頁面」,而對於爬蟲響應對應路由下的渲染好的HTML頁面。
既然各位智慧無窮的網友開出了藥方,看來咱們須要手動熬製了。不過先慢着看看 github 有什麼現成的藥沒有:
沒錯 github 上已經有了現成的解決方案。git
Rendora 自己是一個代理服務器使用 GO 語言編寫專門被設計與解決 SPA 項目的 SEO 處理,支持配置文件以及對外接口。github
使用 Rendora 能夠相較於其餘方案有以下的優點:redis
它的基本原理就是請求通過 Rendora 的時候它會根據請求頭 user-agent
來判斷請求是屬於爬蟲仍是普通用戶, 普通用戶直接代理到原有的Web服務器, 而爬蟲的請求會通過無頭瀏覽器(head-less browser) 處理生成一張頁面返回給爬蟲,而這個頁面的內容能夠理解爲是運行時的 DOM 快照。chrome
明白了基本原理後咱們不難想到只要是異步加載數據而後再利用數據渲染內容的頁面都適用。並且爬蟲和普通用戶二者最終獲取到的數據能夠高度一致。vue-cli
Rendora 官方文檔中已經給出了安裝方式,我就在這裏直接照搬了,不過 Rendora 自己是由 GO 語言編寫,並且依賴了無頭瀏覽器仍是有許多小坑要踩的。docker
在本文中我使用的系統是 Ubuntu18.04桌面版 ,可是其餘的系統用戶 windows 和 macos 都是能夠安裝以及使用的,安裝 Rendora 方式稍有不一樣可是基本概念都是一致的。
Golang
1.11或者更高的版本chromium
瀏覽器或者 google-chrome
瀏覽器,要確保能夠在環境變量中能夠訪問到他們安裝方式:
git clone https://github.com/rendora/rendora cd rendora make build sudo make install
另外你還可以使用使用 docker:
docker run --net=host -v ./CONFIG_FILE.yaml:/etc/rendora/config.yaml rendora/rendora
注意: make build
過程當中會訪問網絡, 其中有些地址沒法在國內訪問這會致使構建失敗, 國內用戶沒有開啓代理的能夠嘗試在構建執行以下兩條命令進行代理:
# 啓動 go modules 特性 export GO111MODULE=on # 設置 GOPROXY 爲環境變量 export GOPROXY=https://goproxy.io
一樣的其餘平臺的能夠參考 goproxy.io 的官方指導.
Rendora 是基於配置文件運行的, 在運行前咱們須要熟悉一下配置文件.
配置文件支持多種格式, 這裏我就使用 Web 端最多見的 JSON 格式, 須要注意 Rendora 不會檢查拼寫錯誤, 請多多複製.
默認狀況下咱們只須要指定2個參數就能夠了:
{ "backend": { "url": "http://127.0.0.1:8000" }, "target": { "url": "http://127.0.0.1" } }
參數 | 含義 |
---|---|
backend | 原來向用戶提供服務的地址 |
target | 無頭瀏覽器請求的地址 |
請注意: 由於 Rendora 本質上是一個代理服務器也會啓動端口監聽(默認3001端口), 這兩個參數具體填寫的內容取決於後端技術的組合, 例如一個常見的技術組合多是下面這個樣子:
nginx->Rendora->App Server
但也有多是反過來的:
Rendora->nginx->App Server
例如: 我在本地的服務器上監聽了80端口用於託管項目的靜態文件, 那麼實際上這兩個參數的配置是同樣的, 由於原有的地址和瀏覽器請求的地址是一致的.
此外爲了不和本地的端口衝突這裏還有兩個選項是須要注意的:
{ "listen":{ "port":3001 }, "headless":{ "internal":{ "url":"http://localhost:9222" }, }, }
listen.port
指的是 Rendora 監聽的端口號headless.internal.url
指的是無頭瀏覽器的請求的地址此外 Rendora 還能夠配置兩種過濾器:
一個常見的請求過濾器例子以下:
注意:不要複製註釋
--- "filters":{ // 請求過濾器 經過過濾的使用無頭瀏覽器渲染 "userAgent":{ "defaultPolicy":"blacklist", // 匹配策略 黑名單模式(默認全部請求沒法經過過濾器) "exceptions":{ // 只有符合下列規則的請求才會經過過濾 // 只要 user-agent 中含有下列字符之一就符合匹配條件 "keywords":["bot", "bing", "yandex", "slurp", "duckduckgo","baiduspider","googlebot","360spider","Sosospider","sogou spider"] }, // user-agent 徹底匹配下方內容的也能夠經過 "exact":["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36"] } } ---
加上地址過濾器:
--- "paths":{ // 路徑過濾器 符合規則的纔會被無頭瀏覽器渲染 "defaultPolicy": "whitelist", // 白名單模式 全部的請求默認都會經過過濾器 "exceptions":{ // 除了符合下列規則的會被無視 "prefix":["/home"], // 前綴匹配 "exact":["/hello/world"] // 完整匹配 } } ---
headless.waitAfterDOMLoad
:一個 SPA 項目中的 DOMLoad 事件觸發並不意味着頁面渲染完成, 由於網絡請求還未完成內容還未渲染到實際的 DOM 中.
而 Rendora 默認狀況下 DOMLoad 後就輸出 DOM 快照的, 因此咱們須要手動指定一個 DOMLoad 完成後的延時時間, 到了這個時間後纔會獲取快照.
不一樣項目的初次加載完成不一樣, 具體延時多少毫秒可使用 chrome 的 network 面板測量, 這裏我使用了延時2秒也就是2000毫秒, 這個配置能夠在下方的完整例子中找到.
另外 Rendora 還會忽略掉幾乎所有的資源文件的加載(可配置請參考文檔)實際狀況中會比用戶瀏覽器的加載速度快一些.
完整的例子:
{ "listen":{ "port":3001 }, "target":{ "url":"http://localhost:8080" }, "backend":{ "url":"http://localhost:8080" }, "headless":{ "internal":{ "url":"http://localhost:9222" }, "waitAfterDOMLoad":2000 }, "filters":{ "userAgent":{ "defaultPolicy":"blacklist", "exceptions":{ "keywords":["bot", "bing", "yandex", "slurp", "duckduckgo","baiduspider","googlebot","360spider","Sosospider","sogou spider"] }, "exact":["Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36"] }, "paths":{ "defaultPolicy": "whitelist", "exceptions":{ "prefix":["/about"], "exact":["/active/123"] } } } }
咱們使用剛纔的配置進行運行, 首先啓動無頭瀏覽器:
# chromium 版本 chromium-browser --headless --disable-gpu --remote-debugging-port=9222 # google-chrome 版本 google-chrome --headless --disable-gpu --remote-debugging-port=9222
而後啓動咱們的項目, 這裏我使用 vue-cli3
建立了一個默認的項目而且以開發模式啓動,該項目監聽了8080端口:
npm run serve
圖片:瀏覽器中表現
rendora --config ./config.json
先使用 postman 進行無 header 請求一次而後查看 /
路徑下返回的具體內容, 注意咱們這裏請求的是 Rendora 開啓的 3001 端口而不是項目的 8080 端口.
圖片: 沒有使用 header 請求的結果:
從圖片中能夠看到頁面尚未任何渲染結果.
此次請求的時候添加上 user-agent
header 而且查看輸出結果.
圖片: 添加 header 後的輸出:
此時能夠明顯感受到響應有延時, 隨後輸出了頁面的內容, 不過這種延時不是一直存在的 Rendora 會對該地址下的內容進行緩存隨後的訪問中講不會進行任何渲染,而且你能夠指定緩存時間, 另外你還能夠將緩存移動到 redis 中進行管理.
咱們還能夠嘗試被配置中被禁止訪問的 /about
:
此時能夠看到被攔截的 /about
地址返回的是 index.html
中的內容而不是渲染好的 about
頁面。
http://www.runtester.com/deta...
https://github.com/rendora/re...
https://github.com/rendora/re...