上一節討論了VueSSR的構建流程,構建出來的clientManifest和serverBundle最終會被轉換成html,這一節咱們深刻vue-server-renderer的核心內容,看看它們都通過了哪些的處理。這一節的內容包括:javascript
閱讀源碼先總體查看寫下代碼文件結構和入口文件暴露的接口,而後運行一段demo斷點來跟蹤代碼處理數據的細節,下面將以這段demo做爲閱讀代碼的入口:css
vue-server-renderer
提供了兩個API,createRenderer和createBundleRender。它們的用法同樣,若是你閱讀了源碼你會知道createBundleRenderer實際上是在createRenderer上作了擴展,以提供官網上所說的下面幾種特性:html
源碼看到最後,我發現了一段能夠能夠大體的理解它的脈絡的代碼:vue
// templateRenderer.render方法內
if (this.inject) {
return (
template.head(context) +
(context.head || '') +
this.renderResourceHints(context) +
this.renderStyles(context) +
template.neck(context) +
content +
this.renderState(context) +
this.renderScripts(context) +
template.tail(context)
)
} else {
return (
template.head(context) +
template.neck(context) +
content +
template.tail(context)
)
}
複製代碼
轉換成圖來展現:java
原來bundleRenderer以字符串拼接的方式將html的片斷組合成整個html文檔,整個html文檔會劃分爲幾個部分,這裏只列出主要的部分,分別是頭部、資源預加載和預取資源、inlineStyle、正文、state、script。這幾個主要字符片斷跟輔助的片斷結合就結合成了輸出的服務端渲染的html內容。webpack
前面咱們已經知道clientManifest的做用是記錄文檔資源文件的信息,bundleRenderer利用clientManifest的信息,推理出須要預加載和預取的資源和首屏加載的js資源文件,而後拼接成資源預加載和預取片斷和script片斷。而server bundle中記錄中編譯後的源代碼,這正是html文檔中正文的內容來源。web
那麼server bundle是如何被處理成正文的呢?我將bundle的處理過程當中的關鍵步驟畫成了流程圖,以下: 數組
當執行createBundleRunner()時,在內部會執行compileModule(),生成一個處理編譯後源碼的函數evaluate。evaluate函數會將編譯後文件源碼包裝成module對象,而後返回module.exports.defualt,它就是封裝了文件源碼的函數,執行這個函數就就至關於執行文件源碼。當這個文件是入口文件時,返回的就是entry入口文件源碼的封裝函數,也就是runner,那麼執行runner(context)至關於執行entry-server.js導出的函數,以下。服務器
export default context => {
return new Promise((resolve, reject) => {
const { app, router, store } = createApp(context.url)
...
resolve(app)
...
})
}
複製代碼
app就是執行該函數後,Promise狀態爲fulfilled時往下傳遞的單頁應用的vue根實例。以後app會傳入renderToString方法,該方法內會調用render函數,遞歸根實例中的每一個子組件對象,渲染每一個子組件的template而後組裝template,最終生成html中的正文片斷content。app
render函數內實際上是執行了vue內部的render函數,執行組件的生命鉤子函數,生成虛擬dom節點,只不過最後轉化成了template字符串返回。最後templateRenderer.render方法將正文和其餘文檔片斷組件成整個html文檔返回。
bundle Renderer如何推斷出預加載和預取資源的呢?
咱們如今已經知道html是在templateRenderer.render方法中組合的,在它裏面有這麼一句this.renderResourceHints(context)
,預加載和預取片斷就是由它來生成的。如下是renderResourceHints方法的流程圖:
從圖中咱們很清晰得知道renderPreloadLinks方法和renderPrefetchLinks方法都調用了getUsedAsyncFiles方法,來拿到組件實例中所依賴到的代碼chunk文件列表usedAsyncFiles,它是經過context._registeredComponents獲得組件實例依賴的moduleIds,再根據clientManifest中的modules對象(記錄modules之間的依賴關係)和all對象(記錄着全部編譯後的文件),找出moduleIds所對應的文件,這些文件就是初始渲染組件所依賴到的代碼 chunk文件,也就是usedAsyncFiles。
usedAsyncFiles與preloadFiles(clientManifest中的initial文件數組)合併就是須要預加載的資源列表,usedAsyncFiles與prefetchFiles(clientManifest中的async文件數組)的差集就是預取的資源列表。