安裝
我先在wsl里面裝了homebrew,然后用brew裝了hugo。
安裝homebrew的時候貌似默認的安裝方式會報錯,用了替代方案
git clone https://github.com/Homebrew/brew ~/.linuxbrew/Homebrew
mkdir ~/.linuxbrew/bin
ln -s ~/.linuxbrew/Homebrew/bin/brew ~/.linuxbrew/bin
eval $(~/.linuxbrew/bin/brew shellenv)
然后用這個添加環(huán)境變量
test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv)
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile
echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile
hugo的構成
hugo new site
后會生成一個新的目錄,里面有幾個文件夾
archetypes
里面是當使用hugo new
生成markdown文件的時候會默認的使用的一個模板。
assets
這個和hugo pip功能相關,暫時還沒看懂。。。大概是說可以比如把css、js這些文件放在這個下面,然后在html里面寫類似這樣的函數(shù)來自動生成引用的html語句,還有些其他的功能,還沒看。。。
{{- $bootcss := resources.Get "css/bootstrap.css" -}}
content
里面存儲著所有的生成網(wǎng)站的內容,比如博客的md文件這些
data
里面存儲著所有的配置文件,hugo支持YAML、JSON、TOML格式
layouts
里面存著html的模板文件
static
里面存著靜態(tài)的文件,比如CSS、JavaScript這些
resources
一些加速文件,不用管
配置
hugo默認先找根目錄下面的config.toml、 config.yaml 或者config.json,new site的時候會自動生成一個config.toml
所有可以配置的可以在這里看到https://gohugo.io/getting-started/configuration/
Hugo Modules
有點復雜沒看懂。。。
內容管理
頁面包
hugo會把一個頁面用到的資源理解成一個page bundle,一個page bundle里面的頁面可以直接引用自己bundle下面的資源,但是不能直接引用其他bundle的。根目錄的bundle只能有一個頁面,其他的地方是可以有多個頁面的。訪問的方式可以這樣寫,例如列出所有自己page bundle下面的圖片
其他引用頁面資源的方式可以看這里
https://gohugo.io/content-management/page-resources/
葉子包是指一個單頁的頁面包,下面不包含其他的內容,索引文件是index.md,而分支包下面包含了其他的內容,索引文件是_index.md
type layout section page
這幾個概念有點復雜,因為要同時搞懂才能分別搞懂。。。反正我看了很久。。。我的理解大概的意思是,content目錄下會有多個文件夾,如果某個文件夾下面有_index.md的話,那么這個文件夾就會被稱為一個section,content也是一個section,這個默認的section類型或者說名字是page,初次以外,其他的md文件都有一個默認的type,這個默認的是就是section的名字,比如有一個文件夾叫blog,下面有一個_index.md,一個first.md,那么生成的時候會生成一個baseURL/blog/這個頁面,會調用layouts/blog/list.html這個模板,同時生成一個baseURL/blog/first/這個頁面,會調用layouts/blog/single.html這個模板。
同時可以在md文件的front matter里面顯式的聲明type和layout,比如在content/blog/second.md的front matter里面寫type: project/layout: one的話,這個頁面的地址還是會在baseURL/blog/second/但是渲染的時候會調用layouts/project/one這個模板。
還有一個情況是比如在content下面有一個contact.md,默認是會找layouts/page/single.html這個模板的,但是因為主頁的頁面,一般都要單獨設置,所以可以在front matter里面聲明layout:contact,然后就會引用layouts/page/contact.html這個模板
所以總結來說,最后生成的頁面的路徑是按照content下文件的結構來生成的,而這些頁面的渲染模板和layouts相關,但是也可以顯示的聲明渲染模板的位置,例如
每個頁面都會有一個尋找模板的順序,這個順序可以看hugo的文檔在這里:https://gohugo.io/templates/lookup-order/
中文summary錯誤問題
調用{{.summary}}的時候,如果頁面是中文的話會有問題,所以需要設置hasCJKLanguage為true,官方文檔是這樣寫的
partials 里面調用變量錯誤問題
如果要在partial里面調用變量的話,需要在調用的時候把參數(shù)傳進去才可以在partials里面引用
在markdown文件里面調用文件(例如加入圖片)
方案1 建立page bundle
在文件同級目錄下新建一個同名文件夾,然后里面加入一個index.md的文件,這樣只要正常使用md語法加入就可以了但是我遇到的問題是這樣的,就是當網(wǎng)頁比較窄,但是圖片比較寬的時候,圖片的寬度會比父級還要寬,所以有了方案2
方案2 shortcodes
在layouts/shortcodes/下建立一個img.html,然后里面填入
<img src="{{ .Get "src" }}" {{ if .Get "alt"}} alt="{{ .Get "alt" }}" {{ end }} {{ if .Get "class"}} class="{{ .Get "class" }}" {{ end }} {{ if .Get "style"}} style="{{ .Get "style" }}" {{ end }} >
然后在md文件里面填入
{{< img src="1.jpg" style="style-text" class="class-text" alt="alt text">}}
圖片放在和剛剛一樣的位置,這樣就可以引用了,并且可以傳入一些參數(shù)例如style和class什么的
2021年5月26日 UPDATE:
用了上面的方法后我發(fā)現(xiàn)一個問題就是每次使用hugo server的時候會默認渲染文件夾下面的index.md文件,例如我在content下面有一個first.md和一個first文件夾,文件夾下面有一個index.md,這時候會優(yōu)先渲染index.md。除非你打開first.md后再保存一下,并且用hugo
生成網(wǎng)站的時候也是有限index.md的,所以我現(xiàn)在的做法是把內容寫在index.md里面,刪除first.md這個文件。。。。目前看著有點奇怪不過能滿足我所有的需求,暫時先這樣吧。。。
分類 taxonomy
分類下面我只用用了tags,但是hugo支持多重分類方式,比如默認有tags和category。
使用分類功能,首先要在config.toml里面配置
然后在寫markdown的時候在front matter里面可以用tags加入標簽
這樣hugo會自動生種兩個頁面,一個是
baseURL/tags/
下面列出了所有的tags,另外是一系列頁面baseURL/tags/xxx
下面列出了每一個tags的頁面。
lookup這個我沒有很理解他的意思,不過我各種嘗試以后的做法是這樣的在/layouts/taxonomy/taxonomuy.html里面寫所有tags的頁面,每個tags的頁面寫到/layouts/taxonomy/term.html
分頁
恩,我遇到了一個奇怪的問題,就是不管怎么樣.paginator都會一直得到nil,后面好像是關了server重新運行了下hugo server好像就好了,同樣的道理之前也遇到過,可能是某個bug吧
恩,正常的做法應該是這樣的,首先你可以在config.toml里面設置兩個參數(shù)第一個是每一頁顯示多少個page,第二個是分頁的那個路徑的名字,比如本來是baseURL/blog,這里是默認的page,那么第一頁就會是baseURL/blog/page/1,第二頁就是baseURL/blog/page/2。
然后就是在list頁面把{{range .Pages}
的前面先設一個變量{{ $paginator := .Paginate .Pages }}
,然后調用{{ range $paginator.Pages }}
。這樣就會調用那一頁應該顯示的頁面了,
比較麻煩的是要做導航欄,因為情況會比較多,比如防止頁面一般顯示的是當前頁面的前后幾頁,比如現(xiàn)在顯示的是第五頁,那一般只有34567這幾個頁面的按鈕。另外還要有上一頁和下一頁,但是第一頁不應該顯示上一頁,最后一頁沒有下一頁。這里我用了dsrkafuu這里寫的教程,我把代碼也復制過來
<!-- 開始 輸出一定數(shù)量的位于 posts 分類下的文章 -->
{{ $paginator := .Paginate (where .Data.Pages "Type" "posts") }}
{{ range $paginator.Pages }}
<div class="post">
<h2 class="post-title">
<a href="{{ .Permalink }}">{{ .Title }}</a>
</h2>
<div class="post-summary">
{{ .Summary }}
</div>
</div>
{{ end }}
<!-- 結束 輸出一定數(shù)量的位于 posts 分類下的文章 -->
<!-- 開始 分頁導航 -->
{{ $paginator := .Paginator }}
<!-- 基礎偏移變量 -->
{{ $offsetLinks := 2 }}
<!-- $maxLinks = ($offsetLinks * 2) + 1 -->
{{ $maxLinks := (add (mul $offsetLinks 2) 1) }}
<!-- $lowerLimit = $offsetLinks + 1 -->
{{ $lowerLimit := (add $offsetLinks 1) }}
<!-- $upperLimit = $paginator.TotalPages - $offsetLinks -->
{{ $upperLimit := (sub $paginator.TotalPages $offsetLinks) }}
<!-- 如果有超過一頁的內容 (即需要導航欄) -->
{{ if gt $paginator.TotalPages 1 }}
<ul class="pagination">
<!-- 上一頁 -->
{{ if $paginator.HasPrev }}
<li class="pag-item pag-previous">
<a href="{{ $paginator.Prev.URL }}" class="pag-link">?</a>
</li>
{{ end }}
<!-- 數(shù)字頁碼部分 -->
{{ range $paginator.Pagers }}
{{ $.Scratch.Set "pageNumFlag" false }}
<!-- 頁碼數(shù)足夠多的情況 -->
{{ if gt $paginator.TotalPages $maxLinks }}
<!-- 如果當前頁面為例子中的 1-3 區(qū)間 -->
{{ if le $paginator.PageNumber $lowerLimit }}
{{ if le .PageNumber $maxLinks }}
{{ $.Scratch.Set "pageNumFlag" true }}
{{ end }}
<!-- 如果當前頁面為例子中的 8-10 區(qū)間 -->
{{ else if ge $paginator.PageNumber $upperLimit }}
{{ if gt .PageNumber (sub $paginator.TotalPages $maxLinks) }}
{{ $.Scratch.Set "pageNumFlag" true }}
{{ end }}
<!-- 如果當前頁面為例子中的 4-7 區(qū)間 -->
{{ else }}
{{ if and ( ge .PageNumber (sub $paginator.PageNumber $offsetLinks) ) ( le .PageNumber (add $paginator.PageNumber $offsetLinks) ) }}
{{ $.Scratch.Set "pageNumFlag" true }}
{{ end }}
{{ end }}
<!-- 頁碼數(shù)不夠多的情況 -->
{{ else }}
{{ $.Scratch.Set "pageNumFlag" true }}
{{ end }}
<!-- 輸出頁碼 -->
{{ if eq ($.Scratch.Get "pageNumFlag") true }}
<li class="pag-item{{ if eq . $paginator }} pag-current{{ end }}">
<a href="{{ .URL }}" class="pag-link">
{{ .PageNumber }}
</a>
</li>
{{ end }}
{{ end }}
<!-- 下一頁 -->
{{ if $paginator.HasNext }}
<li class="pag-item pag-next">
<a href="{{ $paginator.Next.URL }}" class="pag-link">?</a>
</li>
{{ end }}
</ul>
{{ end }}
可以自動實現(xiàn)剛剛的那些邏輯。但是我的網(wǎng)站還沒有那么多頁面。。。所以現(xiàn)在網(wǎng)站是看不到導航條的。。。
404
恩,發(fā)發(fā)現(xiàn)hugo server本地運行的時候是不支持調用layouts/404.html這個頁面的,我查了半天,最近的討論是18年的,大概的意思是不行,不過你要預覽的話可以手動訪問/404.html來看效果
搜索
不知道為什么我看了幾個教程都不能用。。。只有這個里面的代碼是可以用的https://blog.csdn.net/weixin_44903718/article/details/108541002,還有官方的這個鏈接這里面的這個代碼在<script>
里面的代碼會自動的被search.js里面調用,用于排列搜索結果,所以你更改這里的內容,適配頁面。而整個頁面就是顯示搜索結果的,所以可以參考list.htmlli里面的排版,樣子就差不多了。
<section>
<div>
<form action="{{ "search" | absURL }}">
<input id="search-query" name="s"/>
</form>
<div id="search-results">
<h3>Matching pages</h3>
</div>
</div>
</section>
<script id="search-result-template" type="text/x-js-template">
<div id="summary-${key}">
<h4><a href="${link}">${title}</a></h4>
<p>${snippet}</p>
${ isset tags }<p>Tags: ${tags}</p>${ end }
${ isset categories }<p>Categories: ${categories}</p>${ end }
</div>
</script>
<script src="https://cdn.jsdelivr.net/gh/foxscallion11/webp/static/hugo/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.2.0/fuse.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js"></script>
<script src="{{ "js/search.js" | absURL }}"></script>
另外search.js這個里面主要就是jquery的一些函數(shù)。
summaryInclude = 60;
var fuseOptions = {
shouldSort: true,
includeMatches: true,
threshold: 0.0,
tokenize: true,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
{ name: "title", weight: 0.8 },
{ name: "contents", weight: 0.5 },
{ name: "tags", weight: 0.3 },
{ name: "categories", weight: 0.3 }
]
};
var searchQuery = param("s");
if (searchQuery) {
$("#search-query").val(searchQuery);
executeSearch(searchQuery);
} else {
$('#search-results').append("<p>Please enter a word or phrase above</p>");
}
function executeSearch(searchQuery) {
$.getJSON("/index.json", function (data) {
var pages = data;
var fuse = new Fuse(pages, fuseOptions);
var result = fuse.search(searchQuery);
console.log({ "matches": result });
if (result.length > 0) {
populateResults(result);
} else {
$('#search-results').append("<p>No matches found</p>");
}
});
}
function populateResults(result) {
$.each(result, function (key, value) {
var contents = value.item.contents;
var snippet = "";
var snippetHighlights = [];
var tags = [];
if (fuseOptions.tokenize) {
snippetHighlights.push(searchQuery);
} else {
$.each(value.matches, function (matchKey, mvalue) {
if (mvalue.key == "tags" || mvalue.key == "categories") {
snippetHighlights.push(mvalue.value);
} else if (mvalue.key == "contents") {
start = mvalue.indices[0][0] - summaryInclude > 0 ? mvalue.indices[0][0] - summaryInclude : 0;
end = mvalue.indices[0][1] + summaryInclude < contents.length ? mvalue.indices[0][1] + summaryInclude : contents.length;
snippet += contents.substring(start, end);
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
}
});
}
if (snippet.length < 1) {
snippet += contents.substring(0, summaryInclude * 2);
}
//pull template from hugo templarte definition
var templateDefinition = $('#search-result-template').html();
//replace values
var output = render(templateDefinition, { key: key, title: value.item.title, link: value.item.permalink, tags: value.item.tags, categories: value.item.categories, snippet: snippet });
$('#search-results').append(output);
$.each(snippetHighlights, function (snipkey, snipvalue) {
$("#summary-" + key).mark(snipvalue);
});
});
}
function param(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}
function render(templateString, data) {
var conditionalMatches, conditionalPattern, copy;
conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
copy = templateString;
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
if (data[conditionalMatches[1]]) {
//valid key, remove conditionals, leave contents.
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
} else {
//not valid, remove entire section
copy = copy.replace(conditionalMatches[0], '');
}
}
templateString = copy;
//now any conditionals removed we can do simple substitution
var key, find, re;
for (key in data) {
find = '\\$\\{\\s*' + key + '\\s*\\}';
re = new RegExp(find, 'g');
templateString = templateString.replace(re, data[key]);
}
return templateString;
}
也可以根據(jù)需要改一些,比如我不是用一個input來顯示搜索結果的,所以我改成了這個樣
var searchQuery = param("s");
if(searchQuery){
$("#search-query").html(searchQuery);
$("#search-query").append(" 的搜索結果")
executeSearch(searchQuery);
}else {
$('#search-results').append("<p>你總要寫點什么要搜的先</p>");
}
function executeSearch(searchQuery){
$.getJSON( "/index.json", function( data ) {
var pages = data;
var fuse = new Fuse(pages, fuseOptions);
var result = fuse.search(searchQuery);
console.log({"matches":result});
if(result.length > 0){
populateResults(result);
}else{
$('#search-results').append("<p>什么都沒有搜到。。。</p>");
}
});
}
顯示的結果會是這樣
至于改config.toml和search.json就找照著官方教程做就行
字體
直接調用google fonts貌似也是可以用的,大概就是按照下面這樣調用就行。但是不一定什么時候就會要很久才能打開。。。
<link rel='stylesheet'>
但是如果直接在google fonts網(wǎng)站下的話會得到otf格式的,體積非常之大的(大概6-8m每個文件)字體,又沒有辦法使用在網(wǎng)頁上。。。然后我找到了這個https://google-webfonts-helper.herokuapp.com/fonts,他可以讓你選字體,然后自動生成調用方式和對應的文件,每個文件大概1m多
markdown
恩,目前(2021年5月30日)hugo的版本是0.83,剛剛換用了新的markdown渲染引擎為goldmark,然后goldmark是支持渲染的時候增加自定義屬性的(比如增加html的class),然后在0.81這個這版本支持了對于markdown block(不指導是個什么概念。。。)的支持,table啊list啊什么的這些都可以了,另外給標題加屬性也是支持的。
不過目前這個功能默認的配置里是禁用的,所有的默認配置可以看這里
只要把這里的block改成true就可以了,例如這樣給一個table增加.table的屬性,轉成html的時候會自動給這個表格增加一個class='table',就會調用bootstrap的格式美化了,不得不提goldmark默認的表格渲染真的是。。。太簡陋了。。