庫(kù):從零構(gòu)建可檢索的Markdown復(fù)習(xí)手冊(cè))
說個(gè)挺實(shí)在的體驗(yàn)準(zhǔn)備面試那陣子我手里的 Markdown 筆記散落在好幾個(gè)文件夾里今天記一道 JS 閉包明天寫一段 Vue 響應(yīng)式原理時(shí)間一長(zhǎng)連自己存過啥都忘了。后來我把整套內(nèi)容整合進(jìn)一個(gè) VuePress 站點(diǎn)用下來最大的感受是它把我的“八股文”筆記變成了一本能檢索、能導(dǎo)航、能自動(dòng)部署成網(wǎng)站的復(fù)習(xí)手冊(cè)。這篇東西就是記錄我當(dāng)時(shí)怎么一步步從零搭出來的從目錄設(shè)計(jì)、側(cè)邊欄配置到部署上線和搜索優(yōu)化全部走一遍。適合正在準(zhǔn)備筆試面試、手里攢了不少零散筆記、又不想用現(xiàn)成博客框架遷數(shù)據(jù)的人。1. 為什么我把“面試八股文”做成靜態(tài)站點(diǎn)而不是在線問答庫(kù)1.1 面試知識(shí)積累的真實(shí)痛點(diǎn)面試復(fù)習(xí)這件事痛點(diǎn)不只是“不會(huì)”更多是“學(xué)過就忘”和“找起來費(fèi)勁”。我試過各類云端筆記也見過不少人用 Notion、語(yǔ)雀建題庫(kù)最后發(fā)現(xiàn)最大的問題不是記錄能力而是組織方式。筆記一旦多起來層級(jí)深、散落亂復(fù)習(xí)時(shí)根本沒法做到“按模塊刷題”“按難度回顧”更別說形成一個(gè)穩(wěn)定的、能對(duì)外展示的知識(shí)庫(kù)。我當(dāng)時(shí)給自己定了一個(gè)目標(biāo)所有面試相關(guān)的內(nèi)容必須在一個(gè)地方沉淀而且要有清晰的模塊劃分比如“JavaScript 基礎(chǔ)”“瀏覽器原理”“前端工程化”“手寫題合集”等。還有一個(gè)額外需求不管是電腦還是手機(jī)打開瀏覽器就能看不用先裝軟件不用去解壓 zip。這就天然傾向于靜態(tài)站點(diǎn)方案——把 Markdown 文件變成網(wǎng)頁(yè)部署一次之后改內(nèi)容只要重新生成即可。1.2 和 GitBook、Docsify、Hexo 橫向?qū)Ρ仍谶x型的時(shí)候我心里有幾個(gè)候選GitBook、Docsify、Hexo以及最后選擇的 VuePress。每個(gè)方案都有自己的適用場(chǎng)景但放到“面試手冊(cè)”這個(gè)具體需求里經(jīng)過實(shí)測(cè)比較差別其實(shí)非常大。下面是我當(dāng)時(shí)整理的真實(shí)感受不是簡(jiǎn)單的官網(wǎng)特性復(fù)述而是上手過一輪后的結(jié)論。方案上手成本側(cè)邊欄 / 多級(jí)導(dǎo)航搜索整合內(nèi)容形態(tài)我的評(píng)價(jià)GitBook中低比較方便有內(nèi)置搜索Markdown商業(yè)化程度高但本地化部署麻煩新版收費(fèi)門檻明顯Docsify低可配置需要另外做Markdown運(yùn)行時(shí)渲染SEO 差一點(diǎn)內(nèi)容大時(shí)首屏不算快Hexo中偏博客插件生態(tài)強(qiáng)大Markdown/MDX擅長(zhǎng)寫博客但不擅長(zhǎng)“文檔式”知識(shí)導(dǎo)航VuePress中默認(rèn)主題非常完善內(nèi)置 插件Markdown Vue文檔站體驗(yàn)貼合我這種“手冊(cè)”需求2.x 后性能也好了很多我自己的結(jié)論是Hexo 那套更偏“流水賬式博客”按時(shí)間線組織對(duì)知識(shí)分類不友好Docsify 雖然啟動(dòng)快但動(dòng)態(tài)渲染的頁(yè)面在瀏覽器里加載全部?jī)?nèi)容頁(yè)數(shù)多了以后首屏和檢索都受拖累GitBook 對(duì)新用戶不直觀、自托管有額外坑。VuePress 最大的優(yōu)勢(shì)在于它把“導(dǎo)航、側(cè)邊欄、搜索、打包部署”這些做知識(shí)庫(kù)最需要的東西都做成了默認(rèn)能力而且支持在 Markdown 里寫 Vue 組件。這意味著我可以做出很多個(gè)性化的小工具而不只是純靜態(tài)頁(yè)面。1.3 內(nèi)容管理和自動(dòng)化的邊界這里有必要跟沒接觸過的人說清楚VuePress 本身不負(fù)責(zé)“管理內(nèi)容數(shù)據(jù)庫(kù)”它本質(zhì)上是把 Markdown 文件編譯成 HTML 的靜態(tài)站點(diǎn)生成器。你在本地有一個(gè)docs文件夾里面全是.md文件寫完以后執(zhí)行vuepress build docs它會(huì)生成一堆 HTML、CSS、JS丟到任意靜態(tài)托管平臺(tái)就能跑。內(nèi)容管理靠文件系統(tǒng)你只需要維護(hù)好文件夾和命名規(guī)范剩下的交給約定和組織。說實(shí)話這種“文件即內(nèi)容”的模式對(duì)單個(gè)開發(fā)者來說體驗(yàn)比數(shù)據(jù)庫(kù)后臺(tái)更好用。因?yàn)槟憧梢灾苯佑镁庉嬈髋恳苿?dòng)、重命名、搜索替換Markdown 本身就是一種長(zhǎng)期可讀的格式未來就算換技術(shù)棧內(nèi)容也不會(huì)被鎖定在某家平臺(tái)里。而且支持 Git 管理每一次修改都有記錄復(fù)習(xí)稿迭代了多少版、哪些題目更新過一目了然。2. 初始化項(xiàng)目與目錄設(shè)計(jì)2.1 環(huán)境準(zhǔn)備與腳手架命令2022 年那會(huì)兒 VuePress 2.x 已經(jīng)比較成熟我也果斷從 1.x 遷到了 2.x。如果你想復(fù)現(xiàn)這個(gè)過程建議先確認(rèn) Node 版本最好在 16 以上因?yàn)?VuePress 2.x 對(duì) Node 版本有要求太老的環(huán)境跑vuepress dev很容易碰到模塊兼容報(bào)錯(cuò)。準(zhǔn)備好環(huán)境后創(chuàng)建項(xiàng)目只需要幾步mkdir interview-manual cd interview-manual npm init -y npm install -D vuepressnext vuepress2.0.0-beta.53裝完后在package.json里加上常用腳本{ scripts: { docs:dev: vuepress dev docs, docs:build: vuepress build docs } }接著建一個(gè)docs目錄并在docs下創(chuàng)建README.md里面隨便寫點(diǎn)內(nèi)容。然后執(zhí)行npm run docs:dev瀏覽器打開http://localhost:8080看到頁(yè)面就算初始化成功。這里有個(gè)小細(xì)節(jié)VuePress 默認(rèn)把項(xiàng)目約定為“源碼目錄”你項(xiàng)目里的docs文件夾就是所有文檔內(nèi)容的根目錄最好不要把別的工程文件塞進(jìn)docs否則構(gòu)建時(shí)會(huì)多出不必要的文件掃描。2.2 目錄結(jié)構(gòu)到底怎么設(shè)計(jì)才合理一個(gè)面試手冊(cè)如果不提前劃分好目錄后期整理成本會(huì)越來越高。我的建議是頂層目錄按照“知識(shí)域”劃分而不是按照來源或時(shí)間劃分。比如我最初的結(jié)構(gòu)是docs/ ├── README.md ├── .vuepress/ │ ├── config.js │ └── public/ ├── javascript/ │ ├── README.md │ ├── 01-closure.md │ ├── 02-this.md │ └── 03-promise.md ├── css/ │ ├── README.md │ ├── 01-box-model.md │ └── 02-flex-layout.md ├── vue/ │ ├── README.md │ ├── 01-reactive.md │ └── 02-lifecycle.md ├── network/ │ ├── README.md │ ├── 01-tcp-handshake.md │ └── 02-http-cache.md └── algorithms/ ├── README.md └── 01-array.md這里的命名規(guī)范是“數(shù)字序號(hào) 主題名”。為什么這么設(shè)計(jì)因?yàn)?VuePress 默認(rèn)側(cè)邊欄如果沒有單獨(dú)配置會(huì)按照文件名的字典序排序我如果用01-closure.md這樣的前綴就可以人為控制每篇文檔的展示順序。類似的每個(gè)知識(shí)域下的README.md會(huì)作為該目錄的入口頁(yè)打開javascript/時(shí)先看到的是這個(gè)入口的簡(jiǎn)介而不是一片空白。另外我強(qiáng)烈建議在docs根目錄放一個(gè)README.md里面寫清楚這份手冊(cè)的使用方式比如“每天按模塊刷一遍標(biāo)綠的表示已掌握”。這樣部署之后首頁(yè)就是一個(gè)引導(dǎo)頁(yè)自己訪問時(shí)心理負(fù)擔(dān)小別人看到也不至于一頭霧水。2.3 文檔目錄與側(cè)邊欄的映射關(guān)系很多人在 VuePress 里踩的第一個(gè)坑就是“為什么我建立了文件夾側(cè)邊欄沒有自動(dòng)生成”VuePress 1.x 默認(rèn)會(huì)做一些自動(dòng)側(cè)邊欄處理但 2.x 里如果想穩(wěn)定控制側(cè)邊欄更推薦的還是顯式配置。表面上看好像多了一步手寫配置其實(shí)對(duì)長(zhǎng)期維護(hù)是好事目錄層級(jí)一旦亂起來自動(dòng)生成的側(cè)邊欄往往會(huì)給你驚喜顯式配置則能保證每次渲染結(jié)果都可預(yù)期。我的經(jīng)驗(yàn)是先定目錄、再寫側(cè)邊欄配置。具體來說.vuepress/config.js里通過themeConfig.sidebar來設(shè)置側(cè)邊欄。針對(duì)上面那個(gè)目錄我當(dāng)時(shí)的側(cè)邊欄配置大致長(zhǎng)這樣module.exports { lang: zh-CN, title: 面試手冊(cè), description: 前端面試知識(shí)點(diǎn)與手寫題匯總, themeConfig: { logo: /logo.png, nav: [ { text: 首頁(yè), link: / }, { text: JavaScript, link: /javascript/ }, { text: Vue, link: /vue/ }, ], sidebar: { /javascript/: [ { text: JavaScript 基礎(chǔ), collapsible: true, children: [ /javascript/01-closure.md, /javascript/02-this.md, /javascript/03-promise.md, ], }, ], /vue/: [ { text: Vue 原理, collapsible: true, children: [ /vue/01-reactive.md, /vue/02-lifecycle.md, ], }, ], }, }, };這里我用了對(duì)象形式的sidebar按路徑分區(qū)塊配置這比全局?jǐn)?shù)組形式的側(cè)邊欄靈活得多。要注意路徑必須以/開頭并且指向的是docs目錄下的相對(duì)路徑不要寫成從項(xiàng)目根目錄出發(fā)的完整路徑。此外路徑結(jié)尾的小寫目錄名要和實(shí)際文件夾保持一致。部署到 Linux 服務(wù)器時(shí)大小寫敏感的問題會(huì)直接導(dǎo)致頁(yè)面 404這是我實(shí)測(cè)踩過的坑。3. 核心配置與內(nèi)容寫作技巧3.1 config.js 里那些值得多看一眼的配置項(xiàng)除了導(dǎo)航和側(cè)邊欄config.js里還有幾個(gè)配置項(xiàng)對(duì)面試手冊(cè)場(chǎng)景特別有用我逐一說明。首先是lang我會(huì)設(shè)置為zh-CN這會(huì)影響站點(diǎn)語(yǔ)言和瀏覽器、搜索引擎對(duì)頁(yè)面語(yǔ)言的理解對(duì)中文內(nèi)容 SEO 也有輔助作用。其次是head可以往 HTML 的head里注入自定義標(biāo)簽比如引入字體、添加 meta 描述。我當(dāng)時(shí)順手加了主題顏色和關(guān)鍵詞 meta生成后的頁(yè)面在搜索分享時(shí)會(huì)更漂亮。module.exports { head: [ [meta, { name: theme-color, content: #3eaf7c }], [meta, { name: keywords, content: 前端面試, VuePress, 八股文, 面試題 }], ], };還有一個(gè)容易被忽略的是markdown配置。VuePress 2.x 默認(rèn)支持代碼高亮但如果你需要行號(hào)、特定語(yǔ)言的代碼塊渲染可以在markdown選項(xiàng)里做更細(xì)的調(diào)整。比如把code的行號(hào)顯示打開對(duì)“手寫題”尤其有用——讀者能看到每一行代碼的順序和縮進(jìn)而不是一大片看不清的字符串。module.exports { markdown: { lineNumbers: true, }, };3.2 側(cè)邊欄的三種玩法手寫、半自動(dòng)、全自動(dòng)側(cè)邊欄這塊我花了不少時(shí)間研究因?yàn)槲壹认胍刂屏τ植幌朊看渭游恼露几呐渲谩W罱K我總結(jié)出三種玩法按需求程度不同可以選。第一種是純手寫。就是上面給出的方式路徑寫死在配置里。好處是結(jié)構(gòu)完全可控壞處是新增一篇文章時(shí)必須同步改配置否則不會(huì)出現(xiàn)在側(cè)邊欄里。第二種是半自動(dòng)。也就是只配置側(cè)邊欄分組但每個(gè)分組下的文件列表通過讀取目錄文件生成或者在README.md里用相對(duì)鏈接的方式自己維護(hù)一個(gè)“目錄頁(yè)”。這樣既能看到頁(yè)面又不會(huì)完全依賴 VuePress 的默認(rèn)行為。第三種是全自動(dòng)??梢詫懸粋€(gè) Node 腳本構(gòu)建前掃描目錄自動(dòng)生成側(cè)邊欄配置對(duì)象再注入到config.js或者單獨(dú)導(dǎo)出的sidebar.js。這種方式適合內(nèi)容特別多、更新特別頻繁的手冊(cè)。我當(dāng)時(shí)因?yàn)閮?nèi)容已經(jīng)開始膨脹就寫了一個(gè)很粗的腳本通過fs讀取目錄結(jié)構(gòu)把二級(jí)目錄下的*.md文件轉(zhuǎn)為側(cè)邊欄 children。優(yōu)點(diǎn)是省事缺點(diǎn)是一旦目錄文件命名不規(guī)范腳本生成的側(cè)邊欄會(huì)亂掉。如果你只是個(gè)人用我建議從手寫開始等真的感覺每次加文件太繁瑣了再切換到腳本生成。不要一上手就自動(dòng)化否則排查渲染問題時(shí)你會(huì)多一層的變量。3.3 用 Markdown 增強(qiáng)語(yǔ)法把答題模板寫活VuePress 的 Markdown 不只是普通 Markdown。它內(nèi)置了container自定義容器可以寫提示框、警告框、危險(xiǎn)提示等。這在面試手冊(cè)里非常實(shí)用每個(gè)問題的標(biāo)準(zhǔn)答案和面試官追問部分我可以用不同容器區(qū)分開一眼就能看出哪些是基礎(chǔ)結(jié)論哪些是擴(kuò)展考點(diǎn)。舉個(gè)例子我在 JS 閉包那篇文檔里是這樣寫的::: tip 結(jié)論一句話 閉包是指函數(shù)能夠訪問其詞法作用域之外的變量。在 JavaScript 中每次創(chuàng)建函數(shù)時(shí)都會(huì)在函數(shù)內(nèi)部保存對(duì)詞法環(huán)境的引用這就形成了閉包。 ::: ::: warning 常見追問 如果閉包引用的變量在外部被修改閉包內(nèi)部看到的是最新值還是舊值答最新值因?yàn)殚]包保存的是變量對(duì)象引用而不是當(dāng)時(shí)的快照。 ::: ::: details 手寫例子 function createCounter() { let count 0; return function () { count 1; return count; }; } const counter createCounter(); console.log(counter()); // 1 :::這個(gè)寫法最大的好處是復(fù)習(xí)時(shí)我不需要讀完整段長(zhǎng)篇大論只掃一眼提示框里的結(jié)論就能快速回憶起核心點(diǎn)。需要深入時(shí)再點(diǎn)開 details 折疊塊看代碼。比紙質(zhì)筆記好用得多也比靜態(tài)圖片分享方便得多。如果你愿意甚至可以約定一套顏色規(guī)范比如“tip 表示記憶口訣、warning 表示易錯(cuò)點(diǎn)、danger 表示高頻面試坑”把整本手冊(cè)做成一個(gè)可掃讀的復(fù)習(xí)卡。3.4 在八股文頁(yè)面里嵌入 Vue 組件做打卡這是 VuePress 相對(duì)其他靜態(tài)站生成器最讓我驚喜的一點(diǎn)。Markdown 文件里可以直接寫 Vue 組件。意味著我不只能寫文檔還能把“復(fù)習(xí)進(jìn)度打卡”“隨機(jī)抽題”“答案展開折疊”之類的小工具做成組件插進(jìn)文檔里用。我當(dāng)時(shí)做了一個(gè)簡(jiǎn)單的“每日打卡”組件放在首頁(yè)。代碼不復(fù)雜隨便貼一下核心思路template div p今天已復(fù)習(xí) {{ checkedCount }} / {{ total }} 個(gè)模塊/p button clickcheckIn打卡/button /div /template script setup import { ref } from vue const checkedCount ref(0) const total ref(12) function checkIn() { checkedCount.value 1 } /script這個(gè)組件編譯后會(huì)打包進(jìn)頁(yè)面加載方式比單純靜態(tài)頁(yè)面多了一點(diǎn)交互。當(dāng)然這里有個(gè)限制由于是靜態(tài)站點(diǎn)沒有后端數(shù)據(jù)沒法持久化保存。我的做法是把打卡結(jié)果通過localStorage存在瀏覽器本地這樣在同一臺(tái)電腦上復(fù)習(xí)進(jìn)度不會(huì)丟。如果你需要跨設(shè)備同步那就是另一個(gè)話題了得自己接存儲(chǔ)服務(wù)才行。在面試復(fù)習(xí)這個(gè)場(chǎng)景里我用這個(gè)方式實(shí)現(xiàn)了“隨機(jī)抽題按鈕”——每次刷新從數(shù)組里隨機(jī)取一道題顯示。這比固定順序刷題更能檢測(cè)真實(shí)掌握程度因?yàn)槊嬖嚂r(shí)你并不知道下一個(gè)問題是什么。4. 搜索、插件與閱讀體驗(yàn)優(yōu)化4.1 本地搜索和第三方搜索怎么選面試手冊(cè)內(nèi)容多了以后光靠側(cè)邊欄點(diǎn)來點(diǎn)去是不夠的。想象一下你在復(fù)習(xí)“協(xié)商緩存”如果每道題都要先從側(cè)邊欄找到 HTTP 緩存那篇再往下翻到對(duì)應(yīng)標(biāo)題效率很低。這時(shí)候就要靠全文搜索。VuePress 2.x 默認(rèn)是帶本地搜索能力的但需要做一些配置。如果你用的是默認(rèn)主題可以安裝vuepress/plugin-search插件。這個(gè)插件基于 minisearch 實(shí)現(xiàn)對(duì)個(gè)人站點(diǎn)來說非常輕量支持全文搜索不需要后端服務(wù)。配置方式也很簡(jiǎn)單npm install -D vuepress/plugin-searchnext然后在 config 里引入const { searchPlugin } require(vuepress/plugin-search); module.exports { plugins: [ searchPlugin({ locales: { /: { placeholder: 搜索面試題, }, }, maxSuggestions: 10, hotKeys: [s, /], }), ], };這里hotKeys可以設(shè)置快捷鍵按s或/就能喚起搜索框復(fù)習(xí)時(shí)手不離鍵盤效率提升明顯。如果你的內(nèi)容量很大、且對(duì)搜索精確度有更高要求可以考慮付費(fèi)接入 Algolia DocSearch但那個(gè)需要網(wǎng)站有公開域名并且要去申請(qǐng)。對(duì)個(gè)人手冊(cè)來說本地搜索完全夠用。4.2 我常用的幾個(gè)插件組合除了搜索插件還有幾個(gè)插件我用了之后覺得不錯(cuò)這里列出來供你參考。第一個(gè)是vuepress/plugin-pwa可以把站點(diǎn)變成 PWA支持離線訪問和更加接近 App 的體驗(yàn)。對(duì)復(fù)習(xí)類工具來說離線能力簡(jiǎn)直是加分項(xiàng)地鐵里沒信號(hào)也能看題。npm install -D vuepress/plugin-pwanext用的時(shí)候注意PWA 插件要求站點(diǎn)是 HTTPS 部署并且需要你提供一個(gè) 512x512 的圖標(biāo)。配置時(shí)如果沒有圖標(biāo)構(gòu)建不會(huì)失敗但實(shí)際運(yùn)行時(shí)安裝提示會(huì)消失。第二個(gè)是vuepress/plugin-git可以統(tǒng)計(jì)每篇文檔的最后更新時(shí)間和貢獻(xiàn)者信息。對(duì)單人手冊(cè)來說“最后更新時(shí)間”字段非常有用能提醒我這篇筆記是不是太久沒更新了。老舊的答案在技術(shù)迭代后可能已經(jīng)過時(shí)沒這個(gè)字段我根本想不起來去核對(duì)。第三個(gè)是vuepress/plugin-seo和vuepress/plugin-sitemap這倆是社區(qū)插件用來生成 SEO 元數(shù)據(jù)和 sitemap 文件。如果你的手冊(cè)打算公開分享、想讓搜索引擎收錄可以考慮加上。不過面試手冊(cè)這種內(nèi)容搜索引擎收錄的價(jià)值優(yōu)先級(jí)不高我當(dāng)時(shí)的排序是搜索、PWA、Git 信息最后才是 SEO。4.3 閱讀體驗(yàn)調(diào)整深淺色、字體、高亮閱讀體驗(yàn)這件事初期會(huì)覺得無所謂但當(dāng)你連續(xù)刷兩個(gè)小時(shí)題眼睛開始有感覺的時(shí)候才知道深淺色切換和合理字體有多重要。VuePress 默認(rèn)主題自帶主題切換按鈕但我當(dāng)時(shí)做了一些細(xì)調(diào)比如把正文的max-width調(diào)到一個(gè)舒服的寬度避免文字過長(zhǎng)影響閱讀又比如調(diào)整了代碼高亮主題讓代碼塊和正文的對(duì)比更清晰。如果你不想自己寫樣式可以在.vuepress/styles/index.scss里覆蓋默認(rèn)變量。常用的變量包括主色、鏈接顏色、代碼背景色等。例如$accentColor: #2c6fbb; $textColor: #2c3e50; $codeBgColor: #282c34;這種方式的好處是不用去動(dòng)源碼改完構(gòu)建后全局生效。需要注意的是VuePress 2.x 的默認(rèn)主題樣式變量名可能跟 1.x 有區(qū)別直接照搬網(wǎng)上舊文章容易失效建議先在項(xiàng)目里查一下實(shí)際的變量定義文件再改。5. 部署上線從本地到公網(wǎng)5.1 GitHub Pages 部署流程手冊(cè)本地能用只是第一步真正讓它變成“隨時(shí)隨地都能訪問”的復(fù)習(xí)工具還得部署到公網(wǎng)。我的首選是 GitHub Pages因?yàn)樗鼘?duì)靜態(tài)站點(diǎn)免費(fèi)、支持自定義域名而且和 Git 配合很順滑。但這里有個(gè)容易踩的坑如果你的項(xiàng)目倉(cāng)庫(kù)名不是user.github.io這個(gè)格式而是隨便取的名字比如interview-manual那么構(gòu)建產(chǎn)物的資源路徑需要特殊處理。VuePress 默認(rèn)生成的資源引用路徑是以/開頭的部署到子路徑下會(huì)全部 404。解決辦法是給 config 設(shè)置basemodule.exports { base: /interview-manual/, };如果部署在根域名下base就是默認(rèn)的/。如果你不確定自己部署在什么路徑可以先統(tǒng)一設(shè)置成倉(cāng)庫(kù)名后面再按實(shí)際情況調(diào)整。我的經(jīng)驗(yàn)是不要等部署完發(fā)現(xiàn) 404 才處理在一開始搭項(xiàng)目時(shí)就在config.js里把base寫好。5.2 服務(wù)器部署與 Nginx 配置如果你有自己的云服務(wù)器也可以用 Nginx 托管靜態(tài)文件。這種方式的好處是不受 GitHub 訪問限制的影響國(guó)內(nèi)訪問速度也好一些。部署過程聽起來簡(jiǎn)單把docs/.vuepress/dist目錄下的文件丟到服務(wù)器某個(gè)目錄再配置 Nginx 指向這個(gè)目錄就完成了。但實(shí)際有幾個(gè)細(xì)節(jié)值得注意。我在自己的服務(wù)器上配置大概是這樣的server { listen 80; server_name your-domain.com; root /var/www/interview-manual; index index.html; location / { try_files $uri $uri/ /index.html; } }這里有個(gè)重要的點(diǎn)VuePress 生成的是純靜態(tài)頁(yè)面但 SPA 路由部分在刷新時(shí)會(huì)導(dǎo)致 Nginx 去找實(shí)際不存在的路徑。加try_files可以回退到index.html保證路由不出現(xiàn) 404。如果你只在 GitHub Pages 部署那不太會(huì)遇到這個(gè)問題因?yàn)?GitHub Pages 對(duì)靜態(tài)文件的處理方式不完全一樣。但自己服務(wù)器上一定要記得加。另外我強(qiáng)烈建議部署時(shí)給站點(diǎn)加 HTTPS?,F(xiàn)在申請(qǐng)證書已經(jīng)非常簡(jiǎn)單免費(fèi)的 Let’s Encrypt 就夠用。沒有 HTTPS 的話PWA 插件和瀏覽器的一些新 API 都會(huì)失效而且對(duì)用戶也不友好。5.3 用 GitHub Actions 自動(dòng)發(fā)布手動(dòng)部署一兩次還好頻繁更新筆記、每次都重新 build 上傳很快就會(huì)煩。所以我后來加了 GitHub Actions只要git push自動(dòng)完成構(gòu)建和發(fā)布。這樣我在本地只做一件事寫完 Markdown提交推到遠(yuǎn)程剩下的不用管。一個(gè)簡(jiǎn)單的 workflow 大致長(zhǎng)這樣name: Deploy on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkoutv3 - name: Setup Node uses: actions/setup-nodev3 with: node-version: 16 - name: Install dependencies run: npm install - name: Build run: npm run docs:build - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pagesv3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/.vuepress/dist第一次配置的時(shí)候很多人會(huì)卡在gh-pages分支的創(chuàng)建或 token 權(quán)限上。其實(shí) GitHub Actions 默認(rèn)提供了GITHUB_TOKEN只要在倉(cāng)庫(kù) Settings 里允許 workflow 寫入權(quán)限就可以直接部署。不需要額外生成 token。這里注意publish_dir一定要指向正確的產(chǎn)物目錄如果路徑寫錯(cuò)了部署出來的頁(yè)面會(huì)是舊版或者空白。6. 常見問題與踩坑記錄6.1 典型問題排查速查表我自己在搭 VuePress 面試手冊(cè)的過程中被幾個(gè)問題卡過不少時(shí)間這里整理成一張速查表希望你能跳過這些坑?,F(xiàn)象可能原因解決辦法部署后頁(yè)面 404base配置不對(duì)根據(jù)倉(cāng)庫(kù)名或子路徑設(shè)置正確的base側(cè)邊欄不顯示沒有配置sidebar在themeConfig里顯式配置側(cè)邊欄代碼塊沒有高亮語(yǔ)言標(biāo)識(shí)不對(duì)或缺少插件檢查代碼塊的 fence 語(yǔ)言名如js、bash本地開發(fā)頁(yè)面錯(cuò)亂Node 版本過低升級(jí)到 Node 16刪除node_modules重裝修改 config 后沒生效dev server 緩存先停掉npm run docs:dev重新啟動(dòng)搜索結(jié)果不準(zhǔn)搜索插件范圍配置不當(dāng)檢查searchPlugin的 locales 與maxSuggestions圖片資源 404資源路徑寫錯(cuò)圖片放到.vuepress/public路徑不要帶中文這張表未必覆蓋所有場(chǎng)景但大多數(shù)剛接觸 VuePress 的人遇到的問題都集中在這幾類。遇到問題時(shí)我習(xí)慣先去檢查config.js里有沒有語(yǔ)法錯(cuò)誤再看瀏覽器控制臺(tái)有沒有資源加載失敗這樣能快速定位到底是配置問題還是文件路徑問題。6.2 幾個(gè)容易被忽視的細(xì)節(jié)如果要說我最想提醒的細(xì)節(jié)第一個(gè)是文件路徑的大小寫。在本地 macOS 或 Windows 上文件名大小寫不敏感但部署到 Linux 服務(wù)器后大小寫不匹配就會(huì) 404。我吃過這個(gè)虧最終解決方式是每次寫完文檔后用腳本批量檢查文件名和內(nèi)部引用是否一致。VuePress 的鏈接解析比較嚴(yán)格如果內(nèi)部鏈接大小寫不一致本地可能沒問題線上就完了。第二個(gè)細(xì)節(jié)是不要過于依賴自動(dòng)側(cè)邊欄。VuePress 2.x 的默認(rèn)主題在“自動(dòng)側(cè)邊欄”上其實(shí)沒有做到很多人期待的那種全自動(dòng)如果你發(fā)現(xiàn)側(cè)邊欄沒有你剛建的文檔正?,F(xiàn)象不要懷疑自己裝錯(cuò)了包。老老實(shí)實(shí)寫配置或是用腳本生成配置反而更快。第三個(gè)細(xì)節(jié)是關(guān)于圖片路徑。如果你是像我一樣把圖片放在.vuepress/public下那么引用圖片時(shí)路徑要寫成/img/xxx.png而不是相對(duì)路徑。相對(duì)路徑在某個(gè)二級(jí)頁(yè)面里可能指向了錯(cuò)誤的位置。我當(dāng)時(shí)因?yàn)檫@個(gè)反復(fù)確認(rèn)了好幾次最終統(tǒng)一改成絕對(duì)路徑再?zèng)]出過問題。第四個(gè)細(xì)節(jié)是代碼塊里嵌套的內(nèi)容。面試手冊(cè)里經(jīng)常要展示“源碼 輸出結(jié)果”如果你在 Markdown 代碼塊里繼續(xù)寫 Markdown 語(yǔ)法會(huì)渲染錯(cuò)亂。我后來養(yǎng)成了一個(gè)習(xí)慣所有示例代碼一律只寫純代碼遇到需要同時(shí)展示解釋的就把解釋放到提示容器里。這樣構(gòu)建時(shí)不會(huì)出現(xiàn)瘋狂的嵌套報(bào)錯(cuò)。6.3 內(nèi)容維護(hù)的長(zhǎng)線建議最后聊一點(diǎn)內(nèi)容上的經(jīng)驗(yàn)。一開始建手冊(cè)時(shí)我總想寫得“全面”每個(gè)知識(shí)點(diǎn)都想覆蓋得滴水不漏。后來發(fā)現(xiàn)這種全量思維導(dǎo)致更新頻率很低因?yàn)槊科臋n都寫得特別長(zhǎng)。后來我改了策略每篇文檔只要求能回答“這道題在面試?yán)镌趺创稹边@一個(gè)問題答完就收尾。以這樣的思路推進(jìn)手冊(cè)的完成度反而提高得很快。在此基礎(chǔ)上我還給自己定了一個(gè)“每周修訂日”每周抽 30 分鐘把臨時(shí)的碎片筆記整理進(jìn)手冊(cè)。這個(gè)習(xí)慣一直堅(jiān)持下來手冊(cè)才從一個(gè)啟動(dòng)項(xiàng)目變成真正每天都會(huì)打開的工具。工具的價(jià)值從來不在于裝了什么強(qiáng)功能而在于你用它的頻率。我在實(shí)際使用中最大的體會(huì)是VuePress 本身只是一個(gè)“渲染 Markdown 的引擎”真正讓這本手冊(cè)好用的是我不斷根據(jù)復(fù)習(xí)習(xí)慣調(diào)整它的結(jié)構(gòu)。比如后來我加了更多折疊塊、更多打卡組件甚至在文章頁(yè)底部嵌入了一個(gè)“是否掌握”的選擇按鈕雖然數(shù)據(jù)只存在瀏覽器里但每次復(fù)習(xí)后的即時(shí)反饋?zhàn)屓烁菀讏?jiān)持下來。如果你也準(zhǔn)備做一本屬于自己的面試手冊(cè)不要著急一上來就把所有功能都配齊先寫內(nèi)容、再優(yōu)化結(jié)構(gòu)最后再補(bǔ)工具鏈。內(nèi)容到位了工具自然會(huì)有用武之地。