單頁應用SEO淺談
前言
單頁應用(Single Page Application)越來越受web開發者歡迎,單頁應用的體驗可以模擬原生應用,一次開發,多端相容。單頁應用並不是一個全新發明的技術,而是隨著網際網路的發展,滿足使用者體驗的一種綜合技術。
SEO
一直以來,搜尋引擎優化(SEO)是開發者容易忽略的部分。SEO是針對搜尋(Google、百度、雅虎搜尋等)在技術細節上的優化,例如語義、搜尋關鍵詞與內容相關性、收錄量、搜尋排名等。SEO也是同行、市場競爭常用的的營銷手段。Google、百度的搜尋結果是重要的使用者入口,騰訊雲(www.qcloud.com)有30%左右的流量來自搜尋引擎。因此SEO在品牌、營銷、使用者量的緯度是非常重要的基礎能力。
那麼單頁應用與傳統直出頁面在SEO方面有哪些不同之處呢?
單頁應用的優點
- 更好的使用者體驗,讓使用者在web感受natvie的速度和流暢;
- 經典MVC開發模式,前後端各負其責。
- 一套Server API,多端使用(web、移動APP等)
- 重前端,業務邏輯全部在本地操作,資料都需要通過AJAX同步、提交;
對搜尋引擎不友好
單頁應用實際是把檢視(View)渲染從Server交給瀏覽器,Server只提供JSON格式資料,檢視和內容都是通過本地JavaScript來組織和渲染。而搜尋搜尋引擎抓取的內容,需要有完整的HTML和內容,單頁應用架構的站點,並不能很好的支援搜尋。
如果站點在使用者體驗和搜尋友好權衡時,如果我們做到更好的體驗,也做到友好的搜尋支援,既是一箭雙鵰。
URL中的雜湊(#號)
單頁應用只有一個頁面,檢視的變化通常是通過路由(route)來驅動,首先,我們先來談一談單頁應用的URL中的#號,很多采用單元結構網站的URL都出現了這個符號。
#
號在瀏覽器的URL中是一個錨點,在當前頁改變#
號的引數,頁面會跳轉到錨點所在的位置,通過JavaScript我們可以獲取到#
號後的引數:
1 2 |
location.hash // 獲取URL hash location.hash = "#list" //改變URL hash |
改變#
號後的引數,頁面並不會過載,於是大多數的單頁架構網站,都在URL中採用#
號來作為當前檢視的URL地址,例如:
1 2 3 |
example.com/#index //首頁檢視 example.com/#list //列表頁檢視 example.com/#list/1 //id為1的列表資訊的檢視 |
Backbone.js就是通過改變#
號引數來組織檢視,這裡有一個demo可以很直觀的體驗URL的變化。
看過這個demo,你或許會發現很熟悉的符號#!
,Twitter曾在URL使用這個標識。這個標識是Google提出(AJAX 抓取:網站站長和開發人員指南1):
因為複雜的單頁架構頁面,對Google來說抓取比較困難,於是給開發者制定一個規範:
- 網站提交sitemap給Google;
- Google發現URL裡有
#!
符號,例如example.com/#!/detail/1
,於是Google開始抓取example.com/?_escaped_fragment_=/detail/1
;
_escaped_fragment_
這個引數是Google指定的命名,如果開發者希望把網站內容提交給Google,就必須通過這個引數生成靜態頁面。
根據上面的demo,我簡單示例一下Google要抓取的頁面的樣子:
http://119.28.4.22/?escapedfragment_=/detail/1
如此以來,就需要Server通過生成靜態的內容以便Google抓取。
以下將簡單介紹,單頁架構,爬蟲訪問根目錄時如果配置Server端的路由。
判斷爬蟲
當Google訪問119.28.4.22/#!/detail/1
時,會自動轉化成http://119.28.4.22/?_escaped_fragment_=/detail/1
,以Nginx為例:
1 2 3 |
if ($args ~ _escaped_fragment_) { rewrite ^ /api; } |
/api
為後臺服務的介面,已nodejs為例,代理設定如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
upstream nodejs { server 127.0.0.1:3000; } location /api { proxy_set_header X-Request-URI $request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header Port $server_port; proxy_pass http://nodejs; proxy_redirect off; } |
如此,我們便將Google的訪問重寫到/api
這個介面,然後在Server的/api
處理請求把靜態內容輸出即可。
sitemap
Gogole的這個規範,必須有sitemap支援,因為有可能單頁架構的站點,索引頁面也是JavaScript渲染的。提交sitemap時,不用關注_escaped_fragment_
這個引數名,只提交帶雜湊符號的URL即可,例如:
1 2 3 4 |
<loc>http://119.28.4.22/#!/detail/1</loc> <changefreq>weekly</changefreq> <priority>0.5</priority> </url> |
結語
技術潮流的步伐很快,單頁應用,URL雜湊處理也沒渲染的方式實際上已經流行了很久,在國外很多使用者資料較好的情況下,開發者會選擇HTML5 History API的pushstate特性開發,在URL中拋棄#!
。但是IE6、7等低端瀏覽器使用者情況較多的網站,#
能夠很好的相容。關於採用HTML5 History API來架構單頁應用的方案,也歡迎討論。
參考1: https://support.google.com/webmasters/answer/174992?hl=zh-Hans