您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Hugo Config模塊構建如何實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Hugo Config模塊構建如何實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
經過loadConfig
和applyConfigDefaults
,我們已經將用戶自定義信息和默認信息都歸置妥當,并且放在了Config Provider
中,方便查用。
Hugo在拿到這些信息后,立馬著手的事情就是collectModules
,也就是收集模塊信息了。
正如上圖中loadModulesConfig
所示,拿到配置信息后,就進行解碼decodeConfig
操作。 在我們的示例中,我們的項目用到了名為mytheme
的主題,所以在項目配置信息中,我們需要把主題添加到導入項Imports
中。
準備好了模塊的配置信息后,接下來就是要根據這些配置信息,對模塊進行處理了。
需要先準備好回調函數beforeFinalizeHook
,為什么要準備這和個回調函數呢? 我們先把這個疑問放一放,一會我們就能發現實際的觸發場景。
回調函數設置好后,接著就開始收集模塊了。 如上圖左上角所示,首先需要創建Module Client
用來具體處理模塊的收集工作。 為什么要叫Client
呢? 這是因為現在Hugo支持Golang的mod模式,意味著可以用go.mod
來導入主題,那我們就需要下載依賴包 - 主題工程來管理依賴了。 這樣來看,叫客戶端是不是就不難理解了。 在我們的示例中,主題目錄是用來做流程講解示范的,只有一個文本文件,所以這里的場景并不涉線上go模塊加載。
客戶端設置好后,開始收集,如上圖中間所示,收集過程總共分四步:
按配置遞歸收集所有模塊 - Collect
設置處于活躍狀態的模塊 - setActiveMods
觸發提前設置的回調函數 - HookBeforeFinalize
移除重復的掛載信息 - Finalize
Collect
先為項目創建工程模塊Project Module
,然后開始遞歸收集模塊:
func (c *collector) collect() { ... // c.gomods is [], GetMain() returns ni projectMod := createProjectModule(c.gomods.GetMain(), c.ccfg.WorkingDir, c.moduleConfig) // module structure, [project, others...] if err := c.addAndRecurse(projectMod, false); err != nil { c.err = err return } ... }
這里為什么會用到遞歸呢? 因為在Hugo中,模塊之間是有相互依賴的。 通過最開始的模塊配置信息也可以看出,我們把依賴的模塊放在了Imports中,Project Module就需要導入"mytheme"模塊。 在實際情況中,"mytheme"有可能也是依賴于其它的主題,所以也需要導入其它模塊。
從上面時序圖右下方可以看到,addAndRecurse
做了四件事:
為導入的模塊創建模塊文件夾,用來放置模塊所有文件
應用主題配置,就像最開始解析項目模塊的配置信息一樣,看是否還需要導入其它模塊
將模塊添加到模塊列表中
為新模塊重復上述步驟
這樣,我們就能順著項目模塊的配置信息,逐個將所有的模塊信息收集齊全了。
setActiveMods
遞歸收集完所有模塊信息后,需要根據用戶配置,進一步將禁用的模塊給過濾到,留下這一次構建所需要的模塊。
HookBeforeFinalize
過濾完模塊后,在Finalize
敲定前,是時候回調我們之前設置好地回調函數了。
除了加載多語言設置處,回調函數所做的操作主要集中在上面時序圖的右下腳。 就是為項目模塊準備好所有的掛載Mount
,包括Content, Static, Layouts, Archetypes, Data, Assets, i18n,共七個組件。 其中Content和其它的組件有點不一樣。 因為Content掛載點和多語言一一對應,也就是說有幾種語言,就會有幾個內容目錄。
Finalize
等有了所有的模塊的信息,掛載點也收集完畢后,我們還要做一件事情。 那就是要保證這些掛載點在全局視野下,沒有重復。
結合時序圖,我們進一步將其中的關鍵對象結構體,根據這些結構體的屬性和行為,按流程處理后所得到的最終結果放在一起,可視化出來。 方便大家理解:
在上圖中,通過下方輸出部分可以看出,一個模塊配置項,對應一個模塊。
在左邊的模塊配置信息中,包含了模塊之間的依賴信息。 在上面的示例中項目模塊飽含了主題模塊。
在右邊的模塊實例中,首先要區分哪一個是項目模塊,因為項目模塊是站點構建的起點。 所以在模塊中需要能標識身份信息的字段projectMod
。
如果從掛載Mounts
的角度來看模塊,那每個模塊實際上就是一個合并后的根文件系統。 Hugo將這個文件系統用七個組件進行了劃分。
項目模塊必需得包含這些信息,但因為依賴于其它模塊,所以需要將項目模塊放在最后處理。 Hugo將項目模塊放在了模塊隊列的第一個,并用一個回調函數幫助在合適的時間點,對項目模的掛載進行了統一的處理。
再用Input -> [?] -> Output
模型來進行分析,可以抽象為以下模型:
主題信息來源于用戶自定義信息,作為輸入傳入收集模塊功能單元。 在處理過程中,Hugo按Name, Module Config, Module, Mounts的對應關系,將模塊相關信息進行處理。 最終生成所有模塊的信息,并通過將這些信息設置在Config Provider中,為后續的操作做好準備。
在知道collectModules
的實現原理后。 按照我們的傳統,讓我們動動小手,用代碼來總結代碼,鞏固一下知識。
可以這里線上嘗試,Show Me the Code, try it yourself
代碼里有注解說明,代碼樣例:
package main import "fmt" type Mount struct { // relative path in source repo, e.g. "scss" Source string // relative target path, e.g. "assets/bootstrap/scss" Target string // any language code associated with this mount. Lang string } type Import struct { // Module path Path string } // Config holds a module config. type Config struct { Mounts []Mount Imports []Import } type Module interface { // Config The decoded module config and mounts. Config() Config // Owner In the dependency tree, this is the first module that defines this module // as a dependency. Owner() Module // Mounts Any directory remappings. Mounts() []Mount } type Modules []Module var modules Modules // moduleAdapter implemented Module interface type moduleAdapter struct { projectMod bool owner Module mounts []Mount config Config } func (m *moduleAdapter) Config() Config { return m.config } func (m *moduleAdapter) Mounts() []Mount { return m.mounts } func (m *moduleAdapter) Owner() Module { return m.owner } // happy path to easily understand func main() { // project module config moduleConfig := Config{} imports := []string{"mytheme"} for _, imp := range imports { moduleConfig.Imports = append(moduleConfig.Imports, Import{ Path: imp, }) } // Need to run these after the modules are loaded, but before // they are finalized. collectHook := func(mods Modules) { // Apply default project mounts. // Default folder structure for hugo project ApplyProjectConfigDefaults(mods[0]) } collectModules(moduleConfig, collectHook) for _, m := range modules { fmt.Printf("%#v\n", m) } } // Module folder structure const ( ComponentFolderArchetypes = "archetypes" ComponentFolderStatic = "static" ComponentFolderLayouts = "layouts" ComponentFolderContent = "content" ComponentFolderData = "data" ComponentFolderAssets = "assets" ComponentFolderI18n = "i18n" ) // ApplyProjectConfigDefaults applies default/missing module configuration for // the main project. func ApplyProjectConfigDefaults(mod Module) { projectMod := mod.(*moduleAdapter) type dirKeyComponent struct { key string component string multilingual bool } dirKeys := []dirKeyComponent{ {"contentDir", ComponentFolderContent, true}, {"dataDir", ComponentFolderData, false}, {"layoutDir", ComponentFolderLayouts, false}, {"i18nDir", ComponentFolderI18n, false}, {"archetypeDir", ComponentFolderArchetypes, false}, {"assetDir", ComponentFolderAssets, false}, {"", ComponentFolderStatic, false}, } var mounts []Mount for _, d := range dirKeys { if d.multilingual { // based on language content configuration // multiple language has multiple source folders if d.component == ComponentFolderContent { mounts = append(mounts, Mount{Lang: "en", Source: "mycontent", Target: d.component}) } } else { mounts = append(mounts, Mount{Source: d.component, Target: d.component}) } } projectMod.mounts = mounts } func collectModules(modConfig Config, hookBeforeFinalize func(m Modules)) { projectMod := &moduleAdapter{ projectMod: true, config: modConfig, } // module structure, [project, others...] addAndRecurse(projectMod) // Add the project mod on top. modules = append(Modules{projectMod}, modules...) if hookBeforeFinalize != nil { hookBeforeFinalize(modules) } } // addAndRecurse Project Imports -> Import imports func addAndRecurse(owner *moduleAdapter) { moduleConfig := owner.Config() // theme may depend on other theme for _, moduleImport := range moduleConfig.Imports { tc := add(owner, moduleImport) if tc == nil { continue } // tc is mytheme with no config file addAndRecurse(tc) } } func add(owner *moduleAdapter, moduleImport Import) *moduleAdapter { fmt.Printf("start to create `%s` module\n", moduleImport.Path) ma := &moduleAdapter{ owner: owner, // in the example, mytheme has no other import config: Config{}, } modules = append(modules, ma) return ma }
輸出結果:
# collect theme as module start to create `mytheme` module # project module has no owner with default mounts &main.moduleAdapter{projectMod:true, owner:main.Module(nil), mounts:[]main.Mount{main.Mount{Source:"mycontent", Target:"content", Lang:"en"}, main.Mount{Source:"data", Target:"data", Lang:""}, main.Mount{Source:"layouts", Target:"layouts", Lang:""}, main.Mount{Source:"i18n", Target:"i18n", Lang:""}, main.Mount{Source:"archetypes", Target:"archetypes", Lang:""}, main.Mount{Source:"assets", Target:"assets", Lang:""}, main.Mount{Source:"static", Target:"static", Lang:""}}, config:main.Config{Mounts:[]main.Mount(nil), Imports:[]main.Import{main.Import{Path:"mytheme"}}}} # theme module owned by project module with no import in the example &main.moduleAdapter{projectMod:false, owner:(*main.moduleAdapter)(0xc000102120), mounts:[]main.Mount(nil), config:main.Config{Mounts:[]main.Mount(nil), Imports:[]main.Import(nil)}} Program exited.
讀到這里,這篇“Hugo Config模塊構建如何實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。