<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
經過loadConfig
和applyConfigDefaults
,我們已經將使用者自定義資訊和預設資訊都歸置妥當,並且放在了Config Provider
中,方便查用。
Hugo在拿到這些資訊後,立馬著手的事情就是collectModules
,也就是收集模組資訊了。
正如上圖中loadModulesConfig
所示,拿到設定資訊後,就進行解碼decodeConfig
操作。 在我們的範例中,我們的專案用到了名為mytheme
的主題,所以在專案設定資訊中,我們需要把主題新增到匯入項Imports
中。
準備好了模組的設定資訊後,接下來就是要根據這些設定資訊,對模組進行處理了。
需要先準備好回撥函數beforeFinalizeHook
,為什麼要準備這和個回撥函數呢? 我們先把這個疑問放一放,一會我們就能發現實際的觸發場景。
回撥函數設定好後,接著就開始收集模組了。 如上圖左上角所示,首先需要建立Module Client
用來具體處理模組的收集工作。 為什麼要叫Client
呢? 這是因為現在Hugo支援Golang的mod模式,意味著可以用go.mod
來匯入主題,那我們就需要下載依賴包 - 主題工程來管理依賴了。 這樣來看,叫使用者端是不是就不難理解了。 在我們的範例中,主題目錄是用來做流程講解示範的,只有一個文字檔案,所以這裡的場景並不涉線上go模組載入。
使用者端設定好後,開始收集,如上圖中間所示,收集過程總共分四步:
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("%#vn", 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` modulen", 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模組構建實現原始碼剖析的詳細內容,更多關於Hugo Config模組構建的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45