上篇文章對(duì) VuePress 的源碼進(jìn)行了簡(jiǎn)單分析,了解到:
- vuepress 包負(fù)責(zé) CLI 命令注冊(cè)及處理八拱,@vuepress 包含著主要的邏輯處理部分
- 無論是
vuepress dev docs
還是vuepress build docs
都會(huì)先執(zhí)行 實(shí)例創(chuàng)建createApp 和 解析處理process 兩個(gè)環(huán)節(jié) - processs 環(huán)節(jié)可以分為五個(gè)階段阵赠,分別是:
- 1)配置解析 & 模板加載
- 2)插件入隊(duì) & 初始化
- 3)Markdown 相關(guān)處理
- 4)源文件搜集
- 5)插件執(zhí)行涯塔,最終生成臨時(shí)文件
但是接下來的階段, vuepress dev docs
和 vuepress build docs
就開始走不通的處理路徑了清蚀。先對(duì)兩個(gè)最主要的執(zhí)行流程進(jìn)行分析有助于我們對(duì)整個(gè)項(xiàng)目有一個(gè)大體的了解匕荸,也有助于之后在此基礎(chǔ)上分析實(shí)現(xiàn)細(xì)節(jié).
一、dev 的執(zhí)行過程
使用過 VuePress 制作網(wǎng)站的同學(xué)應(yīng)該都見過運(yùn)行 vuepress dev docs
的結(jié)果:
前面的 [wds]
表明了 VuePress 的 vuepress dev docs
內(nèi)部就是使用了 webpack-dev-server 的能力來開啟本地服務(wù)器并支持調(diào)試的~
為了驗(yàn)證這一點(diǎn)枷邪,我們來看看 vuepress dev docs
的邏輯:
可以看到通過 createApp 創(chuàng)建實(shí)例榛搔,并且等待資源處理過程 process 執(zhí)行完成之后,開始進(jìn)入 dev 的邏輯了:
進(jìn)入 dev 之后執(zhí)行按下面三個(gè)步驟執(zhí)行:
第一步. 通過 DevProcess類構(gòu)建一個(gè)實(shí)例 devProcess
第二步. 接下來用實(shí)例 devProcess 調(diào)用 process (不同于之前 App 類的 process)
第三步. 最后文件變化監(jiān)聽东揣,并在指定端口啟動(dòng)一個(gè)本地服務(wù)器
第一步可以從構(gòu)造器看出只是單純的上下文賦值践惑,而第二步的 process 就包含了比較多的內(nèi)容了:
其中上面三個(gè) watch 負(fù)責(zé)監(jiān)聽變化,中間的 setupDebugTip 是提供給用戶方便調(diào)試的嘶卧,最后在處理完端口和 host 之后就開始生成 Webpack 配置并放入實(shí)例的 webpackConfig 屬性了.
由上可知尔觉,第二步主要做的是設(shè)置監(jiān)聽 & 構(gòu)建Webpack配置,完成之后就可以進(jìn)入第三步開啟本地服務(wù)器了:
對(duì)于上面的寫法可以參考官網(wǎng)的Demo鏈接:https://www.webpackjs.com/guides/hot-module-replacement/#%E9%80%9A%E8%BF%87-node-js-api
當(dāng)在 NodeJS 中使用 webpack-dev-server 時(shí)芥吟,則應(yīng)該通過 addDevServerEntrypoints(config, options 在代碼中為其寫配置侦铜,第一個(gè)參數(shù)config 即 Webpack 配置专甩,第二個(gè)參數(shù) options 則是我們平常在 webpack.config.js 中寫給 devServer 字段的值對(duì)象. 剩余的編譯和服務(wù)器啟動(dòng)就和官網(wǎng)的Demo基本一致無需贅述了.
到這里使用 vuepress dev docs
的執(zhí)行過程已經(jīng)簡(jiǎn)要地過一遍了:在通用的實(shí)例創(chuàng)建 createApp 和 解析處理process 兩個(gè)環(huán)節(jié)之后進(jìn)入 dev流程,設(shè)置觀察文件變化 & 構(gòu)建 Webpack 配置钉稍,最終通過 webpack-dev-server 啟動(dòng)一個(gè)本地服務(wù)器.
二涤躲、build 的執(zhí)行過程
接下來讓我們繼續(xù)來看看 vuepress build docs
的執(zhí)行過程. 首先我們看看執(zhí)行 vuepress build docs
的結(jié)果:
可以看到在插件流程(app.process)之后,分別發(fā)生了Client 和 Server 兩個(gè)編譯記錄贡未,之后還進(jìn)行了"靜態(tài)HTML渲染"种樱,最終生成靜態(tài)資源到目標(biāo)文件夾中.
之所以Client 和 Server 兩個(gè)編譯記錄,按照基于 SSR 的理解應(yīng)該是:
生成了一份服務(wù)端渲染代碼俊卤,用于生成 HTML
生成了一份對(duì)應(yīng)的客戶端代碼缸托,用于激活服務(wù)端生成的 HTML,使其支持響應(yīng)式
而后面進(jìn)行的"靜態(tài)HTML渲染"瘾蛋,基于 VuePress 通過固定的 markdown 文件生成靜態(tài)網(wǎng)頁(yè)這一特性俐镐,可以理解為由于網(wǎng)頁(yè)全靜態(tài)故無需每次都由服務(wù)器生成,直接打包時(shí)生成一份固定的 "服務(wù)端HTML" 即可.
為驗(yàn)證上面的說法故查看生成結(jié)果哺哼,可以看到左側(cè)的 dist 目錄下都是靜態(tài)的 HTML 文件和資源佩抹,index.html 文件中也包含了 data-server-rendered="true" 這個(gè) SSR 頁(yè)面才具備的特殊屬性,故論證了上面的觀點(diǎn)~
OK 上面從執(zhí)行日志和生成結(jié)果倒推出了如下執(zhí)行過程:
第一步. 編譯服務(wù)端渲染程序 & 客戶端激活程序
第二步. 執(zhí)行服務(wù)端渲染程序 -> 生成服務(wù)端渲染的 HTML
那我們來看看相關(guān)的代碼是怎么樣的取董,我們找到 build 方法的位置(@vuepress/core/lib/node/App.js):
可以看到基于 BuildProcess 創(chuàng)建實(shí)例之后確實(shí)是分成 process 和 render 兩步執(zhí)行的. 由于我們之前看過 dev 的源碼棍苹,所以可以猜測(cè) process 方法應(yīng)該只是用于構(gòu)建 Webpack配置和準(zhǔn)備其他配置,具體執(zhí)行的環(huán)節(jié)應(yīng)該都放在 render 里面了茵汰,接下來我們一個(gè)個(gè)看~
1. process 分析
與 dev 相同的是都是用 resolveCacheLoaderOptions 方法做開始 & 用構(gòu)建 Webpack 配置的 prepareWebpackConfig 方法做結(jié)尾枢里,但是 build 這邊中間少去了一堆監(jiān)聽和調(diào)試處理,只做了結(jié)果目錄創(chuàng)建這一件事情.
2. render 分析
接下來進(jìn)入 render 方法蹂午,簡(jiǎn)單瀏覽發(fā)現(xiàn)每個(gè)階段都有簡(jiǎn)答的注釋栏豺,大致和我們倒推出來的執(zhí)行過程是一致的. 所以接下來以 logger.wait('Rendering static HTML...')
這一行打印為分界線,把程序分成兩部分來看.
1)第一步. 編譯服務(wù)端渲染程序 & 客戶端激活程序
這里我們將 render 方法內(nèi)除了 compile() 之外的代碼都屏蔽掉豆胸,重新運(yùn)行 vuepress build docs
之后奥洼,就可以看到生成了一個(gè)臨時(shí)的 mainfest 文件夾,里面存放著
聯(lián)想一下 Vue SSR 官方的流程圖晚胡,沒錯(cuò)這就是最右邊的上下兩個(gè) Bundle:
接下來就是讀取 bundle, 然后準(zhǔn)備好創(chuàng)建一個(gè) Bundle Render灵奖,以供后續(xù)步驟渲染 HTML 頁(yè)面了(這一部分邏輯中重要部分我都給了序號(hào),沒給序號(hào)的部分可以暫時(shí)忽略):
2)第二步. 執(zhí)行服務(wù)端渲染程序 -> 生成服務(wù)端渲染的 HTML
參考 Vue SSR官網(wǎng) -- Bundle Renderer 指引 這一節(jié)估盘,當(dāng) bundle render 已經(jīng)創(chuàng)建完成瓷患,下一步想要生成靜態(tài)頁(yè)面,肯定就是調(diào)動(dòng) renderToString
函數(shù)了(renderToString
函數(shù)的能力是 自動(dòng)執(zhí)行「由 bundle 創(chuàng)建的應(yīng)用程序?qū)嵗顾鶎?dǎo)出的函數(shù)(傳入上下文
作為參數(shù))遣妥,然后渲染它). 現(xiàn)在回到代碼邏輯:
后半部分最終要的其實(shí)就只有 renderPage 的執(zhí)行過程擅编,renderPage 完成渲染后獲得 HTML字符串,最終寫入目標(biāo)文件:
這樣整個(gè) build 的流程就結(jié)束了~
現(xiàn)在我們可以直接進(jìn)入 doc/.vuepress/dist 目錄執(zhí)行 http-server 啟動(dòng)一個(gè)服務(wù)器燥透,查看 build 出來的網(wǎng)頁(yè)喲~
查看請(qǐng)求頁(yè)面沙咏,雖然是用靜態(tài)資源的方式啟動(dòng)的辨图,不過依舊還是 SSR 的形式呢~
小結(jié)
VuePress 的 dev 和 build 兩個(gè)流程前面部分都會(huì)先執(zhí)行 實(shí)例創(chuàng)建createApp 和 解析處理process 兩個(gè)環(huán)節(jié),但是 dev 和 build 后半部分就不太一樣了.
首先他們通過不同的條件判斷獲得不同的 Webpack 配置肢藐,然后:
dev 基于 webpack-dev-server 創(chuàng)建本地調(diào)試服務(wù)器
build 通過 webpack 讀取 Client故河、Server兩份配置打包出兩個(gè) bundle,再用 bundle render 提前完成 HTML 的生成吆豹,實(shí)現(xiàn)直接生成可用的 SSR HTML 的目的(具備 SEO 優(yōu)化和首屏加載優(yōu)化的特點(diǎn)).
歡迎拍磚鱼的,覺得還行也歡迎點(diǎn)贊收藏~
新開公號(hào):「無夢(mèng)的冒險(xiǎn)譚」歡迎關(guān)注(搜索 Nodreame 也可以~)
旅程正在繼續(xù) ??ヽ(°▽°)ノ?