笔记 - 搭建 Aimlobo 网站

TOC

1. 构建想法

看到一些有趣的图片thiings ,之前也有做shakagame,我觉得还是可以好好做一些有趣,有意思的小型应用或游戏。

2. 原则

原则是个好东西,可以在发生冲突的时候帮助决策,所以在构建之前,确定这几个原则:

  1. 简单:越简单越好,至少初期,不要不必要的功能,样式,从设计上就要去掉;
  2. 模块化:减少工作量,方便一处修改,全部可用;
  3. 快速构建:拿来主义,不重复造轮子(网上有的,不依赖其他库的,就用)。
  4. 代码清晰,带注释。
  5. 多用Gemini

3. 开始构建

创建网站

xdl@MacBook-Air ~/Documents/github % hugo new site aimlobo-2 --format yaml
Congratulations! Your new Hugo site was created in /Users/xdl/Documents/github/aimlobo-2.

Just a few more steps...

1. Change the current directory to /Users/xdl/Documents/github/aimlobo-2.
2. Create or install a theme:
   - Create a new theme with the command "hugo new theme <THEMENAME>"
   - Or, install a theme from https://themes.gohugo.io/
3. Edit hugo.yaml, setting the "theme" property to the theme name.
4. Create new content with the command "hugo new content <SECTIONNAME>/<FILENAME>.<FORMAT>".
5. Start the embedded web server with the command "hugo server --buildDrafts".

See documentation at https://gohugo.io/.

xdl@MacBook-Air ~/Documents/github % cd aimlobo-2
xdl@MacBook-Air ~/Documents/github/aimlobo-2 % hugo new theme aimlobo
Creating new theme in /Users/xdl/Documents/github/aimlobo-2/themes/aimlobo
xdl@MacBook-Air ~/Documents/github/aimlobo-2 % code .

然后删除~/themes/aimlobo/下的演示内容。

这个时候可以生成Favicon

  • 上传你想要的图标的图片,然后按照说明一步步往下走;

  • 填写要把图标放在哪里

  • 下载文件即可,然后把网站上提供的内容复制到后边的编辑head/meta.html部分。

编辑~/hugo.yaml

baseURL: https://aimlobo.com/
title: Aimlobo
theme: aimlobo

# language
defaultContentLanguage: en
defaultContentLanguageInSubdir: true
languages:
  en:
    contentDir: content/en
    disabled: false
    languageCode: en
    languageDirection: ltr
    languageName: English
    weight: 100
  zh-hant:
    contentDir: content/zh-hant
    disabled: false
    languageCode: zh-Hant
    languageDirection: ltr
    languageName: 繁體中文
    weight: 200
  zh-hans:
    contentDir: content/zh-hans
    disabled: false
    languageCode: zh-Hans
    languageDirection: ltr
    languageName: 简体中文
    weight: 300

params:
  slogan: head_slogan # site slogan
  play_slogan: play_slogan # play slogan, used in app page
  description: head_description


outputs:
  home:
    - HTML
    - JSON

# not wanted
disableHugoGeneratorInject: true
disableRSS: true

disableKinds:
- taxonomy
- term

markup:
  goldmark:
    renderer:
      unsafe: true


# 这是为了hugo可以读取npm安装的库并进行复制
module:
  mounts:
    - source: assets
      target: assets
    - source: node_modules
      target: assets/node_modules

编辑~/themes/aimlobo/layouts/partials/head

这一版不考虑theme,所以:

{{ partial "head/extend_top.html" . }}
{{ partial "head/meta.html" . }}
{{ partial "head/css.html" . }}
{{ partial "head/js.html" . }}
{{ partial "head/extend_bottom.html" . }}
  • partial "文件名":用于调用一个可复用的模板。

  • .:表示当前页面的上下文对象,包含了所有数据。

  • partial "文件名" .:用于将当前页面的数据传递给 partial 模板,让它能够访问和使用这些数据。

所以,在你的 Hugo 项目中,当你需要一个 partial 模板来访问调用它的页面的任何信息(比如文章标题、分类、日期等)时,你就需要使用 {{ partial “search-box” . }} 这种语法。如果你调用的 partial 模板不需要任何页面信息,那么只写 {{ partial “header” }} 也是可以的。

1. 编辑head/meta.html

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="
{{- with .Params.description -}}
    {{- . -}}
{{- else -}}
    {{- with site.Params.description -}}
        {{- T . -}}
    {{- end -}}
{{- end -}}
">
<title>
    {{- if .IsHome -}}
        {{- printf "%s - %s - %s" site.Title (T site.Params.slogan) (T site.Params.play_slogan) -}}
    {{- else -}}
        {{- printf "%s - %s | %s" .Title (T site.Params.play_slogan) site.Title -}}
    {{- end -}}
</title>
{{- /* Favicons */}}
<link rel="icon" type="image/png" href="/assets/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/assets/favicon/favicon.svg" />
<link rel="shortcut icon" href="/assets/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="Aimlobo" />
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
<link rel="canonical" href="{{- site.BaseURL -}}">
{{- with site.Languages -}}
{{ range . }}
<link rel="alternate" href="{{- site.BaseURL -}}{{- .LanguageCode -}}" hreflang="{{- .LanguageCode -}}">
{{- end -}}
{{- end -}}

2. 编辑head/css.html

{{- /*
  定义一个可复用的模板,用于生成 <link> 标签。
  该模板会根据 Hugo 的环境(开发或生产)自动添加指纹(fingerprint)和子资源完整性(SRI)属性,
  以确保资源的缓存高效和内容完整性。
*/ -}}
{{- define "cssTag" -}}
  {{- $style := . -}}
  {{- if eq hugo.Environment "development" -}}
    <link rel="stylesheet" href="{{ $style.RelPermalink }}">
  {{- else -}}
    {{- with $style | fingerprint -}}
      <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
    {{- end -}}
  {{- end -}}
{{- end -}}

{{- /*
  为开发和生产环境设置 PostCSS 构建选项。
  在生产环境(非 development)下,启用 CSS 压缩(minify),以减小文件体积。
*/ -}}
{{- $cssBuildOpts := dict "minify" (not (eq hugo.Environment "development")) -}}

{{- /*
  加载第三方 CSS 库 (cssLibs)。
  这些库通常通过 npm 安装,并从 pages Front Matter 中的 .Params.cssLibs 参数中获取。
  每个库都会被独立处理和加载,以利用浏览器的长期缓存机制。
*/ -}}
{{- with .Params.cssLibs -}}
  {{- range . -}}
    {{- /* 获取 npm 包资源 */ -}}
    {{- $style := resources.Get . -}}
    {{- if $style -}}
      {{- /* 将资源从 node_modules 复制到 public 目录,确保可被浏览器访问 */ -}}
      {{- $style = $style | resources.Copy (printf "css/%s.css" (path.BaseName $style)) -}}
      {{- /* 调用可复用模板,生成带指纹和完整性校验的 <link> 标签 */ -}}
      {{- template "cssTag" $style -}}
    {{- else -}}
      {{- /* 如果资源未找到,则发出警告 */ -}}
      {{- warnf "无法找到 CSS 资源:%s" . -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

{{- /*
  加载全局 CSS (main.css)。
  该文件是网站的基础样式入口,通常包含通过 @import 导入的其他样式。
  使用 PostCSS 管道进行处理,并生成带指纹的 <link> 标签。
*/ -}}
{{- with resources.Get "css/main.css" | postCSS $cssBuildOpts -}}
  {{- template "cssTag" . -}}
{{- end -}}

{{- /*
  加载页面自定义 CSS (css)。
  这些文件通过页面的 Front Matter 参数 .Params.css 声明。
  为了减少 HTTP 请求,所有自定义 CSS 文件都会被合并成一个,并进行构建和压缩。
*/ -}}
{{- with .Params.css -}}
  {{- $cssFiles := slice -}}
  {{- range . -}}
    {{- $cssFiles = $cssFiles | append (resources.Get .) -}}
  {{- end -}}

  {{- /* 生成一个基于页面路径和标题的唯一合并文件名,避免文件名冲突 */ -}}
  {{- $pageCssPath := printf "%s-%s" .Path .Title | base64Encode -}}
  {{- $mergedCss := $cssFiles | resources.Concat (printf "css/%s-merged.css" $pageCssPath) | postCSS $cssBuildOpts -}}

  {{- /* 调用可复用模板,生成最终的 <link> 标签 */ -}}
  {{- template "cssTag" $mergedCss -}}
{{- end -}}

说下逻辑:

  • 如果有外部CSS库,那么在页面的front matter中cssLibs下写明文件路径,目前的代码是按照所有外部内容都使用npm安装来写的,注意文件要使用minify过了的版本
  • 加载全站通用的main.css,和其他文件区分开,虽说加载次数增加了,但是好处在于可以利用浏览器缓存;
  • 加载页面需要的自定义的section.css等,说明下,合并的文件名称采用页面所在的路径+名称(Path + Title) 然后使用base64Encode编码以生成唯一的名称。

3. 编辑head/js.html

{{- /*
  定义一个可复用的模板来生成 <script> 标签。
  它会根据环境自动添加指纹和完整性属性,确保资源的安全加载和缓存。
*/ -}}
{{- define "jsTag" -}}
  {{- $script := . -}}
  {{- if eq hugo.Environment "development" -}}
    <script type="module" src="{{ $script.RelPermalink }}" defer></script>
  {{- else -}}
    {{- with $script | fingerprint -}}
      <script type="module" src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous" defer></script>
    {{- end -}}
  {{- end -}}
{{- end -}}

{{- /*
  为开发和生产环境设置 JavaScript 构建选项。
  在生产环境(非 development)下,启用压缩(minify)。
  "target" 设置为 ES2018,确保代码兼容主流浏览器。
*/ -}}
{{- $jsBuildOpts := dict "minify" (not (eq hugo.Environment "development")) "target" "es2018" -}}

{{- /*
  加载第三方 JavaScript 库 (jsLibs)。
  这些库通常通过 npm 安装,并通过页面的 Front Matter 参数 .Params.jsLibs 声明。
  遍历列表,Hugo 会处理每个文件,并独立加载以最大化浏览器缓存效果。
*/ -}}
{{- with .Params.jsLibs -}}
  {{- range . -}}
    {{- /* 获取 npm 包资源 */ -}}
    {{- $script := resources.Get . -}}
    {{- if $script -}}
      {{- /* 将资源从 node_modules 复制到 public 目录,确保可被浏览器访问 */ -}}
      {{- $script = $script | resources.Copy (printf "js/%s.js" (path.BaseName $script)) -}}
      {{- /* 调用可复用模板,生成带指纹和完整性校验的 <script> 标签 */ -}}
      {{- template "jsTag" $script -}}
    {{- else -}}
      {{- /* 如果资源未找到,发出警告 */ -}}
      {{- warnf "无法找到 JS 资源:%s" . -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

{{- /*
  加载全局 JS (main.js)。
  该文件通常包含全站通用的功能,如导航、基础交互等。
  使用 js.Build 管道处理,并生成带指纹的 <script> 标签。
*/ -}}
{{- with resources.Get "js/main.js" | js.Build $jsBuildOpts -}}
  {{- template "jsTag" . -}}
{{- end -}}

{{- /*
  加载页面自定义 JS (js)。
  这些文件通过页面的 Front Matter 参数 .Params.js 声明。
  为了减少 HTTP 请求,将所有自定义 JS 文件合并成一个,并进行构建和压缩。
*/ -}}
{{- with .Params.js -}}
  {{- $jsFiles := slice -}}
  {{- range . -}}
    {{- $jsFiles = $jsFiles | append (resources.Get .) -}}
  {{- end -}}

  {{- /* 生成一个基于页面路径和标题的唯一合并文件名,避免冲突 */ -}}
  {{- $pageJsPath := printf "%s-%s" .Path .Title | base64Encode -}}
  {{- $mergedJs := $jsFiles | resources.Concat (printf "js/%s-merged.js" $pageJsPath) | js.Build $jsBuildOpts -}}

  {{- /* 调用可复用模板,生成最终的 <script> 标签 */ -}}
  {{- template "jsTag" $mergedJs -}}
{{- end -}}

说下逻辑:

  • 如果有外部JS库,那么在页面的front matter中jsLibs下写明文件路径,目前的代码是按照所有外部内容都使用npm安装来写的,注意文件要使用minify过了的版本
  • 加载全站通用的main.js,和其他文件区分开,虽说加载次数增加了,但是好处在于可以利用浏览器缓存;
  • 加载页面需要的自定义的section.js等,说明下,合并的文件名称采用页面所在的路径+名称(Path + Title) 然后使用base64Encode编码以生成唯一的名称。

编辑~/assets/css/

根据hugo中模块化css的说明,安装插件:

xdl@MacBook-Air ~/Documents/github/aimlobo-2 % npm install postcss postcss-cli postcss-import autoprefixer --save-dev

added 68 packages in 4s

22 packages are looking for funding
  run `npm fund` for details

在项目根目录创建 postcss.config.js 文件:

module.exports = {
  plugins: [
      require('postcss-import')({
          path: ['assets/css'] // 指定 CSS 模块的基准路径
      }),
      require('autoprefixer') // 可选:添加浏览器前缀
  ]
}

安装一个animate.css 第三方库,用来实现一些CSS相关的动画效果:

xdl@MacBook-Air ~/Documents/github/aimlobo-2 % npm install animate.css --save-dev

added 2 packages, and audited 71 packages in 642ms

22 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

说明:

  • --save-dev: 将包添加到 devDependencies。这通常用于项目开发时所必需的工具和库,例如打包工具(Webpack, Gulp)、测试框架、CSS 预处理器(PostCSS, Sass)以及你所使用的 animate.css。

  • 对于 animate.css 这样的 CSS 库,它是在你的 Hugo 网站构建时被处理,而不是在网站运行时被用户浏览器下载和执行的。因此,它更适合被归类为开发依赖。

  • 为什么使用npm安装库

安装音频库:

xdl@MacBook-Air ~/Documents/github/aimlobo-2 % npm install howler  

added 1 package, and audited 72 packages in 680ms

22 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

这里没有-save-dev是因为howler.js是一个音频库,它需要在用户的浏览器中运行,以便播放音频。因此,它是你项目功能的一部分,属于运行时依赖。

文件基础结构:

xdl@MacBook-Air ~/Documents/github/aimlobo-2/assets/css % tree
.
├── _basic.css
├── _reset.css
├── _typography.css
├── _var.css
└── main.css

1 directory, 5 files

后期每个页面有自己的需求,就创建相应的文件夹然后section.css等就好。

逐一说明下:

1. _reset.css

重置浏览器的所有默认CSS样式。

/*
  Modern Reset
  See https://hankchizljaw.com/wrote/a-modern-css-reset/

  Note: This file uses some CSS variables and thus cannot be wholly updated via copy+paste.
*/

/* Box sizing rules */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* Remove default margin */
* {
    margin: 0;
}

ul,
ol {
    list-style: none;
    padding: 0;
}

/* Set core root defaults */
html {
    scroll-behavior: smooth;
}

/* body {
    -webkit-font-smoothing: antialiased;
} */

/* A elements that don't have a class get default styles */
a:not([class]) {
    text-decoration-skip-ink: auto;
}

a {
    text-decoration: none;
}

/* Make images easier to work with */
img,
picture,
video,
canvas,
svg {
    display: block;
    max-width: 100%;
}

img {
    height: auto;
}

/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
    font: inherit;
}


/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        scroll-behavior: auto !important;
        transition-duration: 0.01ms !important;
    }
}


@media (prefers-reduced-motion: no-preference) {
    html {
        interpolate-size: allow-keywords;
    }
}


p,
h1,
h2,
h3,
h4,
h5,
h6 {
    overflow-wrap: break-word;
    hyphens: auto;
}

p {
    text-wrap: pretty;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    text-wrap: balance;
}

2. _typography.css

网站会用到的字体:

@font-face {
  font-family: 'Mija';
  src: url('/assets/fonts/Mija_Bold-webfont-subset-v2.woff2') format('woff2');
  font-weight: bold;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-007F; /* 覆盖 ASCII 的 0-127 号字符 */
}

@font-face {
  font-family: 'Torus';
  src: url('/assets/fonts/torus-bold-latin.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-007F; /* 覆盖 ASCII 的 0-127 号字符 */
}

@font-face {
  font-family: 'Proxima Nova';
  src: url('/assets/fonts/proxima-nova-bold-latin.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-007F; /* 覆盖 ASCII 的 0-127 号字符 */
}

@font-face {
  font-family: 'Proxima Nova';
  src: url('/assets/fonts/proxima-nova-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-007F; /* 覆盖 ASCII 的 0-127 号字符 */
}

我这里因为这个字体只需要英文的部分,所以有个unicode-range: U+0000-007F;,浏览器可以只加载这128个字符,有利于减少不必要的流量消耗;

当然要在~/statics/assets/fonts/下边放置这些字体文件。

3. _var.css

存储CSS变量,好处不用多说:一处更改,处处适用,但是相应的也需要在各个地方都尽可能使用变量以便统一。另外有个不方便的地方:目前是hugo v0.147.7,如果后期编辑_var.css这个文件,每次编辑后都需要重新hugo server才能看到变化的地方,这是因为这个文件是被import_basic.css(该文件又被importmain.css),我不知道原因,但我猜测是import的缓存问题。反正只要是被import的CSS文件发生变化,就重新启动服务就好。


4. _basic.css

这个文件可以用来放针对要搭建的网站而独立于其他项目的内容。比如前三个_reset.css, _typography.css, _var.css 这些内容可以在不同项目间通用,那么这个文件就可以放个性化的东西,当然也可以在main.css中来操作。