背景
觀察基于 create-react-doc 搭建的文檔站點(diǎn), 發(fā)現(xiàn)網(wǎng)頁代碼光禿禿的一片(見下圖)。這顯然是單頁應(yīng)用 (SPA) 站點(diǎn)的通病 —— 不利于文檔被搜索引擎搜索 (SEO)。
難道 SPA 站點(diǎn)就無法進(jìn)行 SEO 了么, 那么 Gatsby、nuxt 等框架又為何能作為不少博主搭建博客的首選方案呢, 此類框架賦能 SEO 的技術(shù)原理是什么呢? 在好奇心的驅(qū)動(dòng)下, 筆者嘗試對(duì) creat-react-doc 進(jìn)行賦能 SEO 之旅。
搜索引擎優(yōu)化
在實(shí)踐之前, 先從理論上分析為何單頁應(yīng)用不能被搜索引擎搜索到。核心在于 爬蟲蜘蛛在執(zhí)行爬取的過程中, 不會(huì)去執(zhí)行網(wǎng)頁中的 JS 邏輯, 所以隱藏在 JS 中的跳轉(zhuǎn)邏輯也不會(huì)被執(zhí)行。
查看當(dāng)前 SPA 站點(diǎn)打包后的代碼, 除了一個(gè)根目錄 index.html 外, 其它都是注入的 JS 邏輯, 因此瀏覽器自然不會(huì)對(duì)其進(jìn)行 SEO。
此外, 搜索引擎詳優(yōu)化是一門較復(fù)雜的學(xué)問。如果你對(duì) SEO 優(yōu)化比較陌生, 建議閱讀搜索引擎優(yōu)化 (SEO) 新手指南 一文, Google 搜索中心給出了全面的 17 個(gè)最佳做法, 以及 33 個(gè)應(yīng)避免的做法, 這也是筆者近期在實(shí)踐的部分。
SEO 在 SPA 站點(diǎn)中的實(shí)踐案例
在輕文檔站點(diǎn)的背景前提下, 我們暫不考慮 SSR 方案。
對(duì)市面上文檔站點(diǎn)的 SEO 方案調(diào)研后, 筆者總結(jié)為如下四類:
- 靜態(tài)模板渲染方案
- 404 重定向方案
- SSG 方案
- 預(yù)渲染方案
靜態(tài)模板渲染方案
靜態(tài)模板渲染方案以 hexo 最為典型, 此類框架需要指定特定的模板語言(比如 pug)來開發(fā)主題, 從而達(dá)到網(wǎng)頁內(nèi)容直出的目的。
404 重定向方案
404 重定向方案的原理主要是利用 GitHub Pages 的 404 機(jī)制進(jìn)行重定向。比較典型的案例有 spa-github-pages、sghpa。
但是遺憾的是 2019 年 Google 調(diào)整了爬蟲算法, 因此此類重定向方案在當(dāng)下是無利于 SEO 的。spa-github-pages 作者也表示如果需要 SEO 的話, 使用 SSG 方案或者付費(fèi)方案 Netlify。
SSG 方案
SSG 方案全稱為 static site generator, 中文可譯為路由靜態(tài)化方案。社區(qū)上 nuxt、Gatsby 等框架賦能 SEO 的技術(shù)無一例外可以歸類此類 SSG 方案。
以 nuxt 框架為例, 在約定式路由的基礎(chǔ)上, 其通過執(zhí)行 nuxt generate 命令將 vue 文件轉(zhuǎn)化為靜態(tài)網(wǎng)頁。
例子:
-| pages/
---| about.vue/
---| index.vue/
靜態(tài)化后變成:
-| dist/
---| about/
-----| index.html
---| index.html
經(jīng)過路由靜態(tài)化后, 此時(shí)的文檔目錄結(jié)構(gòu)可以托管于任何一個(gè)靜態(tài)站點(diǎn)服務(wù)商。
預(yù)渲染方案
經(jīng)過上文對(duì) SSG 方案的分析, 此時(shí) SPA 站點(diǎn)的優(yōu)化關(guān)鍵已經(jīng)躍然紙上 —— 靜態(tài)化路由。相較于 nuxt、Gatsby 等框架存在約定式路由的限制, create-react-doc 在目錄結(jié)構(gòu)上的組織靈活自由。它的建站理念是文件即站點(diǎn), 同時(shí)它對(duì)存量 markdown 文檔的遷移也十分便捷。
以 blog 項(xiàng)目結(jié)構(gòu)為例, 其文檔結(jié)構(gòu)如下:
-| BasicSkill/
---| basic/
-----| DOM.md
-----| HTML5.md
靜態(tài)化后應(yīng)該變成:
-| BasicSkill/
---| basic/
-----| DOM
-------| index.html
-----| HTML5
-------| index.html
經(jīng)過調(diào)研, 該構(gòu)思與 prerender-spa-plugin 預(yù)渲染方案一拍即合。預(yù)渲染方案的原理可以見如下圖:
至此技術(shù)選型定下為使用預(yù)渲染方案實(shí)現(xiàn) SSG。
預(yù)渲染方案實(shí)踐
create-react-doc 在預(yù)渲染方案實(shí)踐的步驟簡(jiǎn)單概況如下(完整改動(dòng)可見 mr):
- 改造 hash 路由為 history 路由。因?yàn)?history 路由結(jié)構(gòu)與文檔靜態(tài)化目錄結(jié)構(gòu)天然匹配。
export default function RouterRoot() {
return (
- <HashRouter>
+ <BrowserRouter>
<RoutersContainer />
- </HashRouter>
+ </BrowserRouter>
)
}
- 在開發(fā)環(huán)境、生成環(huán)境的基礎(chǔ)上新增
預(yù)渲染環(huán)境, 同時(shí)對(duì)路由進(jìn)行環(huán)境匹配。其主要解決了資源文件與主域名下的子路徑的對(duì)應(yīng)關(guān)系。過程比較曲折, 感興趣的同學(xué)可以見 issue。
const ifProd = env === 'prod'
+ const ifPrerender = window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender
+ const ifAddPrefix = ifProd && !ifPrerender
<Route
key={item.path}
exact
- path={item.path}
+ path={ifAddPrefix ? `/${repo}${item.path}` : item.path}
render={() => { ... }}
/>
- 兼容 prerender-spa-plugin 在 webpack 5 的使用。
官方版本當(dāng)前未支持 webpack 5, 詳見 issue, 同時(shí)筆者存在對(duì)預(yù)渲染后執(zhí)行回調(diào)的需求。因此當(dāng)前 fork 了一份版本 出來, 解決了以上問題。
經(jīng)過上述步驟的實(shí)踐, 終于在 SPA 站點(diǎn)中實(shí)現(xiàn)了靜態(tài)化路由。
SEO 優(yōu)化附加 buff, 站點(diǎn)秒開?
SEO 優(yōu)化至此, 來看下站點(diǎn)優(yōu)化前后 FP、FCP、LCP 等指標(biāo)數(shù)據(jù)的變化。
以 blog 站點(diǎn)為例, 優(yōu)化前后的指標(biāo)數(shù)據(jù)如下(數(shù)據(jù)指標(biāo)統(tǒng)計(jì)來自未使用梯子訪問 gh-pages):
優(yōu)化前: 接入預(yù)渲染方案前, 首次繪制(FP、FCP) 的時(shí)間節(jié)點(diǎn)在 8s 左右, LCP 在 17s 左右。
優(yōu)化后: 接入預(yù)渲染方案后, 首次繪制時(shí)間節(jié)點(diǎn)在 1s 之內(nèi)開始, LCP 在 1.5s 之內(nèi)。
對(duì)比優(yōu)化前后: 首屏繪制速度提升了 8 倍, 最大內(nèi)容繪制速度提升 11 倍。本想優(yōu)化 SEO, 結(jié)果站點(diǎn)性能優(yōu)化的方式又 get 了一個(gè)。
生成站點(diǎn)地圖 Sitemap
在完成預(yù)渲染實(shí)現(xiàn)站點(diǎn)路由靜態(tài)化后, 距離 SEO 的目標(biāo)又近了一步。暫且拋開 SEO 優(yōu)化細(xì)節(jié), 單刀直入 SEO 核心腹地 站點(diǎn)地圖。
站點(diǎn)地圖 Sitemap 格式與各字段含義簡(jiǎn)單說明如下:
<?xml version="1.0" encoding="utf-8"?>
<urlset>
<!-- 必填標(biāo)簽, 這是具體某一個(gè)鏈接的定義入口,每一條數(shù)據(jù)都要用 <url> 和 </url> 包含在里面, 這是必須的 -->
<url>
<!-- 必填, URL 鏈接地址,長(zhǎng)度不得超過 256 字節(jié) -->
<loc>http://www.yoursite.com/yoursite.html</loc>
<!-- 可以不提交該標(biāo)簽, 用來指定該鏈接的最后更新時(shí)間 -->
<lastmod>2021-03-06</lastmod>
<!-- 可以不提交該標(biāo)簽, 用這個(gè)標(biāo)簽告訴此鏈接可能會(huì)出現(xiàn)的更新頻率 -->
<changefreq>daily</changefreq>
<!-- 可以不提交該標(biāo)簽, 用來指定此鏈接相對(duì)于其他鏈接的優(yōu)先權(quán)比值,此值定于 0.0-1.0 之間 -->
<priority>0.8</priority>
</url>
</urlset>
上述 sitemap 中, lastmod、changefreq、priority 字段對(duì) SEO 沒那么重要, 可以見 how-to-create-a-sitemap
根據(jù)上述結(jié)構(gòu), 筆者開發(fā)了 create-react-doc 的站點(diǎn)地圖生成包 crd-generator-sitemap, 其邏輯就是將預(yù)渲染的路由路徑拼接成上述格式。
使用方只需在站點(diǎn)根目錄的 config.yml 添加如下參數(shù)便可以在自動(dòng)化發(fā)版過程中自動(dòng)生成 sitemap。
seo:
google: true
將生成的站點(diǎn)地圖往 Google Search Console 中提交試試吧,
最后驗(yàn)證下 Google 搜索站點(diǎn)優(yōu)化前后效果。
優(yōu)化前: 只搜索到一條數(shù)據(jù)。
優(yōu)化后: 搜索到站點(diǎn)地圖中聲明的位置數(shù)據(jù)。
至此使用 SSG 優(yōu)化 SPA 站點(diǎn)實(shí)現(xiàn) SEO 的完整流程完整實(shí)現(xiàn)了一遍。后續(xù)便剩下參照 搜索引擎優(yōu)化 (SEO) 新手指南 做一些 SEO 細(xì)節(jié)方面的優(yōu)化以及支持更多搜索引擎了。
小結(jié)
本文從 SPA 站點(diǎn)實(shí)現(xiàn) SEO 作為切入點(diǎn), 先后介紹了 SEO 的基本原理, SEO 在 SPA 站點(diǎn)中的 4 種實(shí)踐案例, 并結(jié)合 create-react-doc SPA 框架進(jìn)行完整的 SEO 實(shí)踐。
相關(guān)鏈接
- create-react-doc
- why-is-my-website-not-showing-up-on-google/
- A Technical Guide to SEO With Gatsby.js
- 優(yōu)化向:?jiǎn)雾搼?yīng)用多路由預(yù)渲染指南
- 除了 SSR,就沒有別的辦法了嗎?
- 基于 SSR/SSG 的前端 SEO 優(yōu)化