從 9 月份開始,vuepress 源碼進行了從新設計和拆分。先是開了個 next 分支,後來又合併到 master 分支,爲即將發佈的 1.x 版本作準備。html
最主要的變化是:大部分的全局功能都被拆分紅了插件的形式,以可插拔的方式來支撐 vuepress 的運做,這一點很像 webpack。前端
具體架構以下: vue
從圖中咱們能夠看出,vuepress 被劃分紅了兩個部分:前端部分和服務端(Node.js)部分。node
在這個架構中,主題即插件。也就是說使用(開發)一個主題和使用(開發)一個插件的方式幾乎一致。webpack
根據這個架構,vuepress 的插件即可以作不少事情了。具體用法能夠參考文檔。git
讓咱們先來了解一下 vuepress 的內部插件和官方插件都有些什麼,藉助插件機制作了哪些事情。github
全局加強:默認用來實現全局應用加強的邏輯。 它使用 enhanceAppFiles 指定加強全局應用和主題的文件路徑。憑着這個,vuepress 就能準確地找到你全局加強或是主題的文件所在地。web
佈局組件:默認提供的佈局組件。 它使用 clientDynamicModules 來實現動態引入佈局相關的組件。vue-router
頁面組件:默認提供的頁面組件(佈局組件的子組件)。 它使用 clientDynamicModules 來實現動態引入頁面相關的組件。npm
根組件混入:默認往根組件混入的邏輯。 它使用 clientDynamicModules 來實現動態混入元信息。包括根組件的標題、語言等。
路由:默認的生成路由邏輯。 它使用 clientDynamicModules 來實現動態註冊路由。咱們的 markdown 文件在轉換成 vue 組件後就是經過它自動註冊到 vue-router 的。
站點數據:默認的生成站點數據邏輯。 它使用 clientDynamicModules 來實現生成全局站點數據。咱們在頁面裏拿到的全局計算屬性 $site 就是這樣來的。
模塊化轉化:將 cmd 代碼轉成 esm 代碼的邏輯。 仍是用 clientDynamicModules 來實現將 cmd 代碼轉成 esm 代碼。主要是由於 ClientComputedMixin 這個類先後端代碼都要使用。
樣式加強 全局樣式加強。使用 enhanceAppFiles 和 ready 鉤子來實現(主題樣式+用戶樣式+父主題樣式)。
樣式覆蓋 全局樣式覆蓋,使用 ready 鉤子來實現,覆蓋 config.styl 和父主題的 palette。
dataBlock數據注入 解析 blockType=data 的數據,使用 chainWebpack 和 enhanceAppFiles 來實現,對 blockType=data 類型的數據注入到 markdown 生成的 vue 組件裏去,每一個組件能夠訪問本身的 $dataBlock 屬性拿到。
活動的標題連接 它會在用戶滾動頁面時自動轉變側邊欄的高亮標題。 它使用了 clientRootMixin 和 define 往根組件混入了滾動邏輯:監聽 onScroll 事件,獲取全部錨點元素並根據滾動距離計算出高亮的錨點。
回到頂部 使用了 enhanceAppFiles 和 globalUIComponents 註冊了一個全局組件:點擊後能夠滾動到頁面頂部。
ga 谷歌分析站點的庫。使用了 define 和 enhanceAppFiles 初始化了 ga。
國際化(廢棄) 可讓你的站點擁有切換語言的能力。使用了 enhanceAppFiles 和 additionalPages 註冊了個 I18n 佈局組件。
文檔的最近更新時間 可讓每一個文檔頁下面顯示最近的 git 提交時間。使用 extendPageData 拓展了 $page 的 lastUpdated 屬性。
圖片預覽 集成了 medium-zoom。使用了 define、clientRootMixin 往根組件裏混入了 zoom 的初始化和更新邏輯。
分頁 讓共享側邊菜單欄的文檔擁有分頁切換的能力。使用了 enhanceAppFiles 定義了全部頁面的索引和順序。ready 定義了分頁的規則如排序規則等、clientDynamicModules 生成動態模塊給前端代碼使用。
pwa 集成 service-worker 功能 - 9.1. 使用 ready 開啓 serviceWorker 選項 - 9.2. 使用 alias 實現用 vue 當事件通道 - 9.3. 使用 define、globalUIComponents 註冊更新 PWA 應用按鈕組件 - 9.4. 使用 enhanceAppFiles 注入 register-service-worker 的初始化和更新邏輯 - 9.5. 使用 generated 經過 workbox-build 完成 sw 功能
註冊全局 Vue 組件 使用 enhanceAppFiles 把一個文件夾中的 vue 組件文件都註冊好。
搜索框 使用 alias 和 define 讓搜索框能夠動態引入。
進度條 使用 clientRootMixin 和 enhanceAppFiles 集成 nprogress。
項目管理上,插件機制也使得原來的一個大項目拆成了 1 + N 的形式,package.json
也變得多了起來,爲了管理這種項目,vuepress 引入了 lerna。
關於 lerna 的知識,有興趣的讀者能夠參考:lerna管理前端packages的最佳實踐。
總體流程大體以下:
這裏我劃分紅了兩個階段,用虛線分隔,一個是調用前階段,一個是調用後階段。插件們被調用前,是會被載入以及註冊的,以後化整爲零,映射成若干個 Option 實例。
_initialized
標誌爲 false 才能調用,用於確認哪些插件是能夠被註冊的:
normalizePlugin
方法將之轉成對象
_pluginResolver(ModuleResolver 實例)
來解析模塊
path.resolve
方法解析獲得絕對路徑,而後交給解析絕對路徑模塊的方法處理。
_initialized
標誌位置爲 true,而後註冊全部可用的插件。
Option 類 - 每一個實例初始化 key(選項標識) 和 items(這個選項所對應的函數們) 屬性。
AsyncOption 類
syncApply
,調用函數的時候使用了 await。
我也寫了一個小插件,它能夠將你的 vuepress 站點下載成一個 pdf 文件:vuepress-plugin-export-site
咱們熟悉的 webpack、vue 也有插件系統,它們都有兩個共同的特色:
其實插件機制也能夠看作設計模式的一種體現:抽離出變化的部分,保留不變的部分。這些變化的部分,即可以稱之爲插件。
在咱們造輪子的時候,若是輪子的功能愈來愈多,代碼愈來愈臃腫的話,引入插件機制會讓後續的開發更加靈活。
最後,幫插件機制的開發者真山同窗宣傳一下,屆時會有更加精彩的 vuepress 分享: