最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Hugo?Config模塊構(gòu)建實現(xiàn)源碼剖析

 更新時間:2023年02月24日 16:51:27   作者:GitAction  
這篇文章主要為大家介紹了Hugo?Config模塊構(gòu)建實現(xiàn)源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

了然于胸 - collectModules時序圖

經(jīng)過loadConfigapplyConfigDefaults,我們已經(jīng)將用戶自定義信息和默認(rèn)信息都?xì)w置妥當(dāng),并且放在了Config Provider中,方便查用。

Hugo在拿到這些信息后,立馬著手的事情就是collectModules,也就是收集模塊信息了。

正如上圖中loadModulesConfig所示,拿到配置信息后,就進(jìn)行解碼decodeConfig操作。 在我們的示例中,我們的項目用到了名為mytheme的主題,所以在項目配置信息中,我們需要把主題添加到導(dǎo)入項Imports中。

準(zhǔn)備好了模塊的配置信息后,接下來就是要根據(jù)這些配置信息,對模塊進(jìn)行處理了。

需要先準(zhǔn)備好回調(diào)函數(shù)beforeFinalizeHook,為什么要準(zhǔn)備這和個回調(diào)函數(shù)呢? 我們先把這個疑問放一放,一會我們就能發(fā)現(xiàn)實際的觸發(fā)場景。

回調(diào)函數(shù)設(shè)置好后,接著就開始收集模塊了。 如上圖左上角所示,首先需要創(chuàng)建Module Client用來具體處理模塊的收集工作。 為什么要叫Client呢? 這是因為現(xiàn)在Hugo支持Golang的mod模式,意味著可以用go.mod來導(dǎo)入主題,那我們就需要下載依賴包 - 主題工程來管理依賴了。 這樣來看,叫客戶端是不是就不難理解了。 在我們的示例中,主題目錄是用來做流程講解示范的,只有一個文本文件,所以這里的場景并不涉線上go模塊加載。

客戶端設(shè)置好后,開始收集,如上圖中間所示,收集過程總共分四步:

  • 按配置遞歸收集所有模塊 - Collect
  • 設(shè)置處于活躍狀態(tài)的模塊 - setActiveMods
  • 觸發(fā)提前設(shè)置的回調(diào)函數(shù) - HookBeforeFinalize
  • 移除重復(fù)的掛載信息 - Finalize

Collect

先為項目創(chuàng)建工程模塊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就需要導(dǎo)入"mytheme"模塊。 在實際情況中,"mytheme"有可能也是依賴于其它的主題,所以也需要導(dǎo)入其它模塊。

從上面時序圖右下方可以看到,addAndRecurse做了四件事:

  • 為導(dǎo)入的模塊創(chuàng)建模塊文件夾,用來放置模塊所有文件
  • 應(yīng)用主題配置,就像最開始解析項目模塊的配置信息一樣,看是否還需要導(dǎo)入其它模塊
  • 將模塊添加到模塊列表中
  • 為新模塊重復(fù)上述步驟

這樣,我們就能順著項目模塊的配置信息,逐個將所有的模塊信息收集齊全了。

setActiveMods

遞歸收集完所有模塊信息后,需要根據(jù)用戶配置,進(jìn)一步將禁用的模塊給過濾到,留下這一次構(gòu)建所需要的模塊。

HookBeforeFinalize

過濾完模塊后,在Finalize敲定前,是時候回調(diào)我們之前設(shè)置好地回調(diào)函數(shù)了。

除了加載多語言設(shè)置處,回調(diào)函數(shù)所做的操作主要集中在上面時序圖的右下腳。 就是為項目模塊準(zhǔn)備好所有的掛載Mount,包括Content, Static, Layouts, Archetypes, Data, Assets, i18n,共七個組件。 其中Content和其它的組件有點(diǎn)不一樣。 因為Content掛載點(diǎn)和多語言一一對應(yīng),也就是說有幾種語言,就會有幾個內(nèi)容目錄。

Finalize

等有了所有的模塊的信息,掛載點(diǎn)也收集完畢后,我們還要做一件事情。 那就是要保證這些掛載點(diǎn)在全局視野下,沒有重復(fù)。

結(jié)合時序圖,我們進(jìn)一步將其中的關(guān)鍵對象結(jié)構(gòu)體,根據(jù)這些結(jié)構(gòu)體的屬性和行為,按流程處理后所得到的最終結(jié)果放在一起,可視化出來。 方便大家理解:

抽象總結(jié) - 輸入不同類型的值,輸出標(biāo)準(zhǔn)的configProvider

在上圖中,通過下方輸出部分可以看出,一個模塊配置項,對應(yīng)一個模塊。

在左邊的模塊配置信息中,包含了模塊之間的依賴信息。 在上面的示例中項目模塊飽含了主題模塊。

在右邊的模塊實例中,首先要區(qū)分哪一個是項目模塊,因為項目模塊是站點(diǎn)構(gòu)建的起點(diǎn)。 所以在模塊中需要能標(biāo)識身份信息的字段projectMod

如果從掛載Mounts的角度來看模塊,那每個模塊實際上就是一個合并后的根文件系統(tǒng)。 Hugo將這個文件系統(tǒng)用七個組件進(jìn)行了劃分。

項目模塊必需得包含這些信息,但因為依賴于其它模塊,所以需要將項目模塊放在最后處理。 Hugo將項目模塊放在了模塊隊列的第一個,并用一個回調(diào)函數(shù)幫助在合適的時間點(diǎn),對項目模的掛載進(jìn)行了統(tǒng)一的處理。

再用Input -> [?] -> Output模型來進(jìn)行分析,可以抽象為以下模型:

主題信息來源于用戶自定義信息,作為輸入傳入收集模塊功能單元。 在處理過程中,Hugo按Name, Module Config, Module, Mounts的對應(yīng)關(guān)系,將模塊相關(guān)信息進(jìn)行處理。 最終生成所有模塊的信息,并通過將這些信息設(shè)置在Config Provider中,為后續(xù)的操作做好準(zhǔn)備。

動手實踐 - Show Me the Code of collectModules

在知道collectModules的實現(xiàn)原理后。 按照我們的傳統(tǒng),讓我們動動小手,用代碼來總結(jié)代碼,鞏固一下知識。

可以這里線上嘗試,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
}

輸出結(jié)果:

# 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模塊構(gòu)建實現(xiàn)源碼剖析的詳細(xì)內(nèi)容,更多關(guān)于Hugo Config模塊構(gòu)建的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang空結(jié)構(gòu)體struct{}用途,你知道嗎

    Golang空結(jié)構(gòu)體struct{}用途,你知道嗎

    這篇文章主要介紹了Golang空結(jié)構(gòu)體struct{}用途,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Golang調(diào)用FFmpeg轉(zhuǎn)換視頻流的實現(xiàn)

    Golang調(diào)用FFmpeg轉(zhuǎn)換視頻流的實現(xiàn)

    本文主要介紹了Golang調(diào)用FFmpeg轉(zhuǎn)換視頻流,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • go時間/時間戳操作大全(小結(jié))

    go時間/時間戳操作大全(小結(jié))

    這篇文章主要介紹了go時間/時間戳操作大全,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 教你利用Golang可選參數(shù)實現(xiàn)可選模式

    教你利用Golang可選參數(shù)實現(xiàn)可選模式

    本文討論Golang函數(shù)可選參數(shù)及函數(shù)類型,以及如何利用可選函數(shù)類型實現(xiàn)可選模式。同時通過構(gòu)造函數(shù)作為示例,實現(xiàn)強(qiáng)大帶可選參數(shù)的構(gòu)造函數(shù),讓代碼更直觀、靈活、支持?jǐn)U展
    2023-01-01
  • Golang函數(shù)重試機(jī)制實現(xiàn)代碼

    Golang函數(shù)重試機(jī)制實現(xiàn)代碼

    在編寫應(yīng)用程序時,有時候會遇到一些短暫的錯誤,例如網(wǎng)絡(luò)請求、服務(wù)鏈接終端失敗等,這些錯誤可能導(dǎo)致函數(shù)執(zhí)行失敗,這篇文章主要介紹了Golang函數(shù)重試機(jī)制實現(xiàn)代碼,需要的朋友可以參考下
    2024-04-04
  • Golang 內(nèi)存模型詳解(一)

    Golang 內(nèi)存模型詳解(一)

    這篇文章主要介紹了Golang 內(nèi)存模型詳解(一),本文講解了Go內(nèi)存模型interface、,需要的朋友可以參考下
    2014-10-10
  • Go語言題解LeetCode455分發(fā)餅干示例詳解

    Go語言題解LeetCode455分發(fā)餅干示例詳解

    這篇文章主要為大家介紹了Go語言題解LeetCode455分發(fā)餅干示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例

    golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例

    這篇文章主要介紹了golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • golang實現(xiàn)并發(fā)控制的方法和技巧

    golang實現(xiàn)并發(fā)控制的方法和技巧

    golang 是一門支持并發(fā)的編程語言,它提供了 goroutine 和 channel 等強(qiáng)大的特性,讓我們可以輕松地創(chuàng)建和管理多個執(zhí)行單元,實現(xiàn)高效的任務(wù)處理,在本文中,我們將介紹一些 golang 的并發(fā)控制的方法和技巧,希望對你有所幫助
    2024-03-03
  • go?doudou應(yīng)用中使用枚舉類型教程示例

    go?doudou應(yīng)用中使用枚舉類型教程示例

    這篇文章主要為大家介紹了go?doudou應(yīng)用中使用枚舉類型教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評論

柳州市| 迁西县| 新巴尔虎右旗| 旅游| 阿鲁科尔沁旗| 凤阳县| 牟定县| 徐州市| 台州市| 尼木县| 墨竹工卡县| 麦盖提县| 城步| 灵寿县| 和静县| 和田市| 同仁县| 呈贡县| 长寿区| 封丘县| 河北省| 普洱| 宁乡县| 南宁市| 峡江县| 通州市| 剑河县| 亚东县| 乳山市| 柯坪县| 大田县| 阜康市| 南京市| 贵港市| 巴楚县| 裕民县| 大余县| 伊通| 兴安盟| 来凤县| 广平县|