美文网首页
hugo建站小记

hugo建站小记

作者: 剑山 | 来源:发表于2021-05-13 00:44 被阅读0次

    安装

    我先在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)
    

    然后用这个添加环境变量

    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里面写类似这样的函数来自动生成引用的html语句,还有些其他的功能,还没看。。。

    {{- $bootcss := resources.Get "css/bootstrap.css" -}}
    

    content

    里面存储着所有的生成网站的内容,比如博客的md文件这些

    data

    里面存储着所有的配置文件,hugo支持YAML、JSON、TOML格式

    layouts

    里面存着html的模板文件

    static

    里面存着静态的文件,比如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里面调用变量的话,需要在调用的时候把参数传进去才可以在partials里面引用


    在markdown文件里面调用文件(例如加入图片)

    方案1 建立page bundle

    在文件同级目录下新建一个同名文件夹,然后里面加入一个index.md的文件,这样只要正常使用md语法加入就可以了

    但是我遇到的问题是这样的,就是当网页比较窄,但是图片比较宽的时候,图片的宽度会比父级还要宽,所以有了方案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">}}
    

    图片放在和刚刚一样的位置,这样就可以引用了,并且可以传入一些参数例如style和class什么的

    2021年5月26日 UPDATE:
    用了上面的方法后我发现一个问题就是每次使用hugo server的时候会默认渲染文件夹下面的index.md文件,例如我在content下面有一个first.md和一个first文件夹,文件夹下面有一个index.md,这时候会优先渲染index.md。除非你打开first.md后再保存一下,并且用hugo生成网站的时候也是有限index.md的,所以我现在的做法是把内容写在index.md里面,删除first.md这个文件。。。。目前看着有点奇怪不过能满足我所有的需求,暂时先这样吧。。。

    分类 taxonomy

    分类下面我只用用了tags,但是hugo支持多重分类方式,比如默认有tags和category。

    使用分类功能,首先要在config.toml里面配置


    因为我是用的json文件,所以这里是json格式

    然后在写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里面设置两个参数

    第一个是每一页显示多少个page,第二个是分页的那个路径的名字,比如本来是baseURL/blog,这里是默认的page,那么第一页就会是baseURL/blog/page/1,第二页就是baseURL/blog/page/2。

    然后就是在list页面把{{range .Pages}的前面先设一个变量{{ $paginator := .Paginate .Pages }},然后调用{{ range $paginator.Pages }}。这样就会调用那一页应该显示的页面了,

    比较麻烦的是要做导航栏,因为情况会比较多,比如防止页面一般显示的是当前页面的前后几页,比如现在显示的是第五页,那一般只有34567这几个页面的按钮。另外还要有上一页和下一页,但是第一页不应该显示上一页,最后一页没有下一页。这里我用了dsrkafuu这里写的教程,我把代码也复制过来

    <!-- 开始 输出一定数量的位于 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 }}
    <!-- 结束 输出一定数量的位于 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 }}
    
        <!-- 数字页码部分 -->
        {{ range $paginator.Pagers }}
        {{ $.Scratch.Set "pageNumFlag" false }}
        <!-- 页码数足够多的情况 -->
        {{ if gt $paginator.TotalPages $maxLinks }}
            <!-- 如果当前页面为例子中的 1-3 区间  -->
            {{ if le $paginator.PageNumber $lowerLimit }}
                {{ if le .PageNumber $maxLinks }}
                {{ $.Scratch.Set "pageNumFlag" true }}
                {{ end }}
            <!-- 如果当前页面为例子中的 8-10 区间 -->
            {{ else if ge $paginator.PageNumber $upperLimit }}
                {{ if gt .PageNumber (sub $paginator.TotalPages $maxLinks) }}
                {{ $.Scratch.Set "pageNumFlag" true }}
                {{ end }}
            <!-- 如果当前页面为例子中的 4-7 区间 -->
            {{ else }}
                {{ if and ( ge .PageNumber (sub $paginator.PageNumber $offsetLinks) ) ( le .PageNumber (add $paginator.PageNumber $offsetLinks) ) }}
                {{ $.Scratch.Set "pageNumFlag" true }}
                {{ end }}
            {{ end }}
        <!-- 页码数不够多的情况 -->
        {{ 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 }}
    

    可以自动实现刚刚的那些逻辑。但是我的网站还没有那么多页面。。。所以现在网站是看不到导航条的。。。

    404

    恩,发发现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的一些函数。

    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;
    }
    

    也可以根据需要改一些,比如我不是用一个input来显示搜索结果的,所以我改成了这个样

    var searchQuery = param("s");
    if(searchQuery){
    $("#search-query").html(searchQuery);
    $("#search-query").append("&nbsp;的搜索结果")
    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 href='https://fonts.googleapis.com/css?family=Noto+Sans+SC:100,300,500' rel='stylesheet'>
    

    但是如果直接在google fonts网站下的话会得到otf格式的,体积非常之大的(大概6-8m每个文件)字体,又没有办法使用在网页上。。。然后我找到了这个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默认的表格渲染真的是。。。太简陋了。。

    相关文章

      网友评论

          本文标题:hugo建站小记

          本文链接:https://www.haomeiwen.com/subject/kqttjltx.html