【网站技术文档】网站布局与覆盖

【网站技术文档】网站布局与覆盖

本文迁移自信息站仓库的对应 wiki 页面。目前网站的样式已经完全重新设计,不再是覆写的成果;因此,此份文档目前仅可作一份网站设计的入门教程,而不能被视为当前本站技术架构的反映。

本文档将介绍:

  • 为何要覆写由主题所定义的文件;
  • 如何通过 _layouts/default.html 文件修改网页的通用布局;
  • 如何通过 Sass 代码覆写原有主题中的样式;
  • 如何覆写 Javascript 脚本,并实现新的功能。

覆写文件

钱院学辅信息站使用 GitHub Pages 提供的 Leap Day 主题。这是一款很不错的主题,但也有许多问题,包括但不限于:

  • 导航菜单功能有限,样式不够美观,且不支持中文;
  • 在引用块中采用了斜体,而这对中文排版的内容是毁灭性的打击;
  • 标题栏固定在页面顶部,将正文盖住,这使得同页面内的锚记必须向下偏移若干距离,给页内引用造成困扰;
  • 样式不美观……

主题本来是解决「有无」问题的,不应肩负起「自定义」的使命。好在 Jekyll 允许用户「覆盖」主题所定义的布局、样式与脚本,它遵守这样的两条原则:

  1. Jekyll 会优先使用你的仓库中已有的文件。例如,你的仓库中有一份样式表,而主题的仓库中定义了同名、同路径的另一份样式表;这时,Jekyll 便会忽略远端(主题)的那份文件,而应用你的样式表。
  2. 如果 Jekyll 在你的仓库中没有找到所需的文件,则它将使用主题仓库中的文件。

这两条原则决定了你「覆盖」主题的方式:如果你希望继续沿用主题的某个部分,则你什么也不用做;如果你希望改动主题的某个部分——例如,页面生成的 HTML 模板,或者页面采用的 Javascript 脚本,则你应该检查主题仓库的结构,并在你的仓库中对应位置创建同名的文件,将主题的文件覆盖掉。

:warning: 对于 CSS 样式表,则并不需要覆写主题所定义的文件:只需要定义自己的样式,再按照 CSS 的方式「层叠」即可。

以下将分别讨论布局、样式与脚本的覆写方法。

页面布局:HTML

仓库与网站机制这一篇中,你已经知道:Jekyll 能将 Markdown 文档渲染为 HTML 文档。但如果对比文档仓库中的 Markdown 文档,以及由 GitHub Pages 最终发布的网页,你会发现:网页上的许多内容,并非由 Markdown 文档转化而来。例如,网站的标题、导航菜单、许可证信息……它们并未出现在你的 Markdown 文档中,但它们却出现在每一个页面上(如你所愿)。

事实上,这些内容是由仓库中的 HTML 模板所决定的。对于未加指定的 Markdown 文档,Jekyll 总是会以仓库中 _layouts 目录下的 default.html 模板来生成最终的网页;如果你在 Markdown 文档中指定了「分类」,则 Jekyll 还会在 _layouts 搜索与这个分类对应的模板。

:warning: 在阅读以下内容之前,你需要确保自己对 HTML 文档(文件结构、标签)有基本的概念。

:book: 如果你缺少 HTML 的基本知识,可以先快速浏览这篇文章:菜鸟教程 - HTML 简介

页面生成原理

以本站的页面模板为例,_layouts/default.html 的主要内容大致如下:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="/assets/css/style.css">
    <script src="https://code.jquery.com/jquery-3.3.0.min.js"></script>
    <script src="/assets/js/main.js"></script>
  </head>
  <body>
    <header>
      <h1>{{ site.title }}</h1>
      <p>{{ site.description }}</p>
    </header>
    <div id="banner"><!-- 这里写了导航栏的内容 --></div>
    <div class="wrapper">
      <nav><ul></ul></nav>
      <section>
        <!-- 你的 Markdown 文档由此开始 -->
        {{ content }}
        <!-- 你的 Markdown 文档到此结束 -->
        <hr><blockquote>
          <p>本文发布于 {{ page.data }}。</p>
          <p>{{ site.extra_info }}</p>
        </blockquote>
      </section>
      <footer><p>本站由{{ site.user }}出品</p></footer>
    </div>
  </body>
</html>

:warning: 以上这份 HTML 代码已经被大幅度删改,仅用于展示原理。

文档结构看似繁复,实际上仅是一个常见的单栏网页框架。

首先,在 <head> 标签中,模板引用了仓库中的 style.scss 样式表及 main.js 脚本,由此即可实现样式与功能的自定义。当然,在模板中也不限于引用本地文件,可以引入像 jQuery 这样的外部脚本。样式表使网页变得更加美观,而脚本则可以使「静态」的网页具有一定的「动态」特性。

其次,在 <body> 标签中,贮存着最终显示在网页上的所有内容。静态的标签,自然将在最终的网页上原样呈现;而真正使得这个 HTML 文档成为「模板」的,却是 Jekyll 所支持的 Liquid 标签,由 {{ variable }} 的形式标记出来。在最终生成网页时,Jekyll 会把这些标签「代入」为具体的信息、参数,由此实现模板的功能。在这里的模板中,有三种 Liquid 标签:

  • {{ site.variable }} 对应于站点的元数据信息,如这份模板中的 {{ site.title }}(站点标题)。它们通常由用户在根目录下的 _config.yml 文件定义。
  • {{ page.variable }} 对应于本页面的元数据信息,如这份模板中的 {{ page.data }} (页面的发布或更新日期)。它们通常以头信息的方式定义在 Markdown 文档中。
  • 没有前缀的 {{ variable }},一般是由 Jekyll 预定义、自动处理的数据,例如这份模板中最重要的 {{ content }} 变量就代表着这一页面的具体内容(即由 Markdown 文档转换而来的 HTML 代码)。

由此可以看出,如果说 Markdown 文档经 Jekyll 转换后变成了网页的「内容」,则 default.html 便是容纳这「内容」的「容器」,可以有复杂的结构、「外壳」。至于 Liquid 标签,则是自动生成内容时所必须的工具——你可以将它理解为「时空传送门」或者「叮当猫的神奇口袋」,要什么给什么。

:book: 关于 Liquid 的详细讨论,可参考:Liquid 模板语言中文文档

定义多个模板

那么,如何为不同分类的文档使用不同的模板?事实上,只需要完成以下两步:

  1. _layouts 目录下新建一个模板文件,例如给「文章」专用的 article.html 模板。
  2. 在需要按「文章」呈现的 Markdown 文档中,以如下形式标记其头信息:
---
layout: article
---

# 文章标题
这是一份文档
……

这样就能够实现多种模板了。

样式改进:CSS 或 Sass

HTML 模板仅决定了页面上的「布局」:它定义了页面上应当有标题、正文、底部说明,但没有定义它们的「样式」。这时,就需要 CSS 样式表了。

:book: 如果你对 CSS 还一无所知,可以先快速浏览这两篇文章:CSS 简介CSS 基础语法

新机制:Sass

与传统的站点不同,Jekyll 采用 Sass 来生成最终的 CSS 样式表。Sass 可以被称为是一种「CSS 预处理器」,它使得 CSS 的编写过程变得系统、轻松;更好的是,Sass 最常用的语法被称为 SCSS(不要晕了),而这种语法与 CSS 完全兼容,这使得我们既可以尝试 Sass 的新功能,也可以仍然用传统的 CSS 来「堆叠」样式。

:warning: 注意 Sass 与 CSS 的差异:Sass 的代码并非样式表,不能被直接使用,需要「处理」为 CSS 后才能生效。而上面所说的 SCSS 之「兼容」,本质上说的是:如果你在 Sass 的代码中粘贴了传统的 CSS 代码,则其按 SCSS 语法处理后将得到完全相同的 CSS 样式表,因此可以像使用 CSS 样式表那样编写 Sass 代码。

通常而言,站点的 Sass 代码被存放在 _sass 目录下。对于使用现成主题的站点(如本站)来说,_sass 目录存放在远端,一般不用「取」到自己的仓库之中——HTML 模板会自行从远端加载由主题所定义的样式。

:book: 如果你想了解 Sass 究竟怎样使编写 CSS 变得「系统、轻松」,可以参考这篇文章:SASS 用法指南 - 阮一峰的网络日志

样式覆盖

众所周知,CSS 的最大特点就是「堆叠」:首先允许在不同的位置、以不同的方式多次定义样式,然后按一定的优先级、继承法则决定最终的样式。在这种情况下,采用已有主题定制的 Jekyll 站点就具有特别的优势:既可以保有他人精心定制的样式,也可以根据自己的需要进行样式的「覆盖」。

自定义样式的方法是:在仓库中创建名为 assets/css 的目录,新建名为 style.scss 的 Sass 代码;打开该文件后,先输入这样几行:

---
---

@import "jekyll-theme-primer";

其中,前面的两行「分割线」实际上是一个空的「头信息」,它告诉 Jekyll 这份文件需要「处理」,不能省略;否则,这份 Sass 代码不会被处理为 CSS 样式表。接下来是一个 Sass 命令,它将由主题所定义的那些 Sass 样式代码全部引入到现有的这份 style.scss 文件中,以供覆盖。

接下来,读者就可以在以上的这几行代码后,写上自己的 CSS 样式,例如让 <body> 标签内的所有段落(<p>)用亮灰色显示:

body p {
    color: lightgray;
}

以此类推。当然,如果你已经精通了 Sass 的 SCSS 语法,甚至可以写的更为简洁。

自定义样式表的位置,取决于 HTML 模板中的定义。这里所确定的目录,是由 GitHub Pages 的主题中所定义的;读者可以修改仓库中 _layouts/default.html 模板引用样式表的方式,进而自定义样式表的存放位置。

检查样式效果

定义 CSS 样式,最值得担心的问题是:在不同的浏览器、不同大小的屏幕上,显示效果是否始终如一?为了测试不同平台上的显示效果,你可以使用浏览器自带的「开发者工具」,检查样式的定义情况,切换窗口大小,甚至直接修改样式。

:book: 不同浏览器的「开发者工具」不尽相同,因此这里并不详细介绍。读者可参考这篇文章:MDN - 什么是浏览器开发者工具 。其是以 Firefox 为例介绍的,但同时也给出了其他浏览器的对应链接。

开发 Javascript 脚本

许多人可能对仅由 HTML 与 CSS 组织的「静态」页面不甚满意。例如,我们希望学辅信息站上能支持这些功能:

  • 根据文章的各级标题,自动生成一个导航菜单/目录,点击其中的条目就能「平滑」地滚动到文章对应位置;
  • 搜索页面上的一些特定对象,改变它们的样式(CSS 自有的选择器往往无能为力);
  • 光标在页面上移动时,会伴随炫丽的幻影。

除了第三个功能没有意义外,剩余的几项功能都非常有必要实现。这时,可以用 Javascript 或相关的库(如 JQuery)写一些简单的脚本,实现这些带有「动态」属性的功能。Javascript 并非网页设计的必选项,你可以充分的依赖别人已有的成果;不过,即使是做一些简单的个性化调整,也需要你至少具有一定的 Javascript 基础。

:book: 如果你希望快速了解 Javascript 的语法与特性,可浏览这篇教程:菜鸟教程 - Javascript 简介。此外,我们的网站中常常使用到 jQuery,它是一个 Javascript 库,你可以浏览菜鸟教程 - jQuery 教程中的有关条目——它的语法非常简单,且功能强大,很容易上手。

脚本在哪里?

在我们所用的主题中,其仅使用了一份脚本:位于 assets/js 目录下的 main.js。在原本的主题中,其定义了两项功能:

  • sectionHeight() 函数,用于计算网页的高度;
  • 导航栏的生成与运行脚本,定义了导航栏的生成方式(提取正文标题)、运行方式(点击链接后,会以动画的形式跳转到对应位置)。

其中,第二项功能的实现不甚完备,特别是对中文支持不佳。为了完善这些问题,首先在本地仓库的对应位置克隆了原有的 main.js 文件,然后再根据实际需求进行改动。

覆写实例:导航栏

:warning: 导航栏的改动尚未完成,个别样式还需进一步优化。因此,这里暂不讨论改动的具体内容。

新建实例:shieldsManu.js

除了优化已有的功能,你也可以在自己的站点中尝试新的脚本。例如,在本站中,有一份名为 shieldsManu.js 的脚本,用来优化网站中 shields 标牌的样式与使用。它用 jQuery 写成,便于理解,也便于修改。

:scroll: 关于 shieldsManu 插件的功能与使用方法,请先跳转至这份技术文档:Markdown 文档规范

这里对 shiledsManu.js 的实现方式作简单讨论。……

参与讨论