用Publish創(chuàng)建博客(一)—— 入門

Publish是一款專門為Swift開發(fā)者打造的靜態(tài)網(wǎng)站生成器。它使用Swift構(gòu)建整個網(wǎng)站,并支持主題、插件和其他大量的定制選項(xiàng)。
作為Swift開發(fā)者,通過Publish創(chuàng)建站點(diǎn)的開發(fā)過程和體驗(yàn)同開發(fā)其他程序很類似。

開篇

開發(fā)者John Sundell

Publish的開發(fā)者John Sundell這些年一直致力于發(fā)表關(guān)于Swift的高質(zhì)量文章、播客和視頻。他的作品大多都發(fā)布在其獨(dú)立運(yùn)營的 Swift by Sundell上。他開發(fā)了Publish用以創(chuàng)建并管理自己的站點(diǎn)。

在開發(fā)Publish的過程中,他還開源了其他大量的基本庫,比如Ink(高效的Markdown解析器)、Plot(創(chuàng)建HTML、XML、RSS的DSL)、Sweep(高效的字符串掃描庫)、Codextended(Codable增強(qiáng))等。它們不僅一起構(gòu)建了強(qiáng)大的Publish,并且在各自的領(lǐng)域也是極為出色的開源庫。

我為什么使用Publish

我在一年前恢復(fù)自己的個人博客時使用的是Hexo。Hexo在國內(nèi)有非常好的群眾基礎(chǔ),網(wǎng)上有大量優(yōu)秀的教程,也有非常多的開發(fā)者貢獻(xiàn)了自己創(chuàng)作的各種主題和插件。盡管Hexo讓我相當(dāng)滿意,但由于我主要使用的語言是Swift,且對JavaScript非常不熟悉,因此想要對Hexo做更深入的定制或修改很困難。

作為開發(fā)者(即使是業(yè)余的),總希望對自己的項(xiàng)目有更全面的掌控,因此完全由Swift開發(fā)的Publish就成為了我的首選。

隨著使用Publish對肘子的Swift記事本重建過程的深入,我感覺自己做出了正確的選擇。Publish讓我可以用開發(fā)普通app的思路和邏輯來創(chuàng)建站點(diǎn),高效地完成我想要的各種定制和改動。

寫本文的原因

截至落筆時,Publish已經(jīng)在Github上獲得了3.1K的好評。但網(wǎng)絡(luò)上對Publish的介紹并不多,尤其欠缺關(guān)于主題定制、插件開發(fā)方面的資料和交流。在Github上搜索相關(guān)的插件和主題的結(jié)果數(shù)量也非常有限。

造成上述的情況固然有Publish誕生時間較短、使用量不大,Swift圈子較小等原因,但我認(rèn)為下面的情況也加劇了這一局面的形成:由于不同于其他的靜態(tài)網(wǎng)站生成器,在Publish項(xiàng)目中,開發(fā)者可以用短小的代碼實(shí)現(xiàn)各種功能。這種碎片化的代碼其實(shí)是不利于分享且并不容易被搜索;另外,由于Publish中的主題和網(wǎng)站的功能具體實(shí)現(xiàn)綁定的較深,單獨(dú)分享的主題的利用度也較低。

但Publish的這種特質(zhì)也恰恰是其吸引人之處。

有鑒于此,我將用三篇文章(入門、主題開發(fā)插件開發(fā))完成對Publish的簡紹,也希望國內(nèi)的Swift開發(fā)者或愛好者們可以更多的了解和使用這個優(yōu)秀的工具。

為了讓大家能夠快速上手,我已將肘子的Swift記事本站點(diǎn)所用的代碼(包括主題、自定義插件等)放置在Github上,方便大家通過代碼更快的了解和掌握Publish。

快速使用入門

如何安裝Publish

同大量的其他靜態(tài)網(wǎng)站生成器一樣,Publish提供了CLI。你可以通過命令行快速的完成創(chuàng)建模板、內(nèi)容更新、遠(yuǎn)程發(fā)布等一系列操作。Publish目前可以運(yùn)行在Mac和Linux上,由于其代碼對操作系統(tǒng)的依存度極低,估計(jì)其后也出現(xiàn)在Windows平臺上。

Mac下通過brew安裝

$brew install publish

源代碼安裝

$git clone https://github.com/JohnSundell/Publish.git
$cd Publish
$make

創(chuàng)建你的第一個項(xiàng)目

讓我們來創(chuàng)建一個新的Blog項(xiàng)目

$mkdir myblog
$cd myblog
$publish new

Publish將在myblog目錄中創(chuàng)建我們所需的項(xiàng)目模板。它的基本構(gòu)成大概如下:

|-- myblog
|   |-- Content
|           |–– posts
|                 |–– first-post.md
|                 |–– index.md
|           |–– index.md
|   |-- Resources
|   |-- Sources
|           |–– Myblog
|                  |–– main.swift
  • Content

    在此放入你要在網(wǎng)站發(fā)布的文章、頁面等使用markdown編寫的文件。

  • Resources

    項(xiàng)目主題需要的一些資源,比如css,圖片等,目前為空。在你進(jìn)行第一發(fā)布后,可以看到它包含了默認(rèn)的FoundationTheme的styles.css文件。

  • Source

    描述網(wǎng)站的代碼。在main.swift中定義了網(wǎng)站的基本屬性、創(chuàng)建工作流等。

編譯及運(yùn)行

Swift是編譯型語言,因此你的站點(diǎn)的代碼在每次修改之后,都需在本機(jī)編譯并運(yùn)行才能完成內(nèi)容的生成工作,好在這一切都只需要一條命令。

我們讓Publish完成上述工作并啟動內(nèi)置的Web Server供我們?yōu)g覽新創(chuàng)建的項(xiàng)目。

$publish run

第一次運(yùn)行,Publish會自動從Github上獲取所需的其他庫,請稍等幾分鐘。

$publish run
............
Publishing Myblog (6 steps)
[1/6] Copy 'Resources' files
[2/6] Add Markdown files from 'Content' folder
[3/6] Sort items
[4/6] Generate HTML
[5/6] Generate RSS feed
[6/6] Generate site map
? Successfully published Myblog
?? Starting web server at http://localhost:8000

Press ENTER to stop the server and exit

現(xiàn)在你就可以用瀏覽器訪問 http://localhost:8000 來訪問你的新站點(diǎn)了。

網(wǎng)站的全部內(nèi)容都被生成并放置在了Output目錄下。你只需要將其中的內(nèi)容上傳到你的服務(wù)器,或者通過簡單的配置,比如:

.unwrap(.gitHub("fatbobman/fatbobman.github.io", useSSH: true), PublishingStep.deploy)

然后使用

$publish deploy

便可將內(nèi)容發(fā)布到你的github.io上(具體配置后面說明)。

此時你在Content - posts中添加如下文件second-post.md

---
date: 2021-01-29 19:58
description: 我的第二篇文章
tags: publish,swift 
---
# Hello Wolrd

再度執(zhí)行 publish run 便可以看到新文章已經(jīng)出現(xiàn)在頁面上了。

使用我提供的模板快速上手

首先要確保已經(jīng)安裝了Publish CLI

$ git clone https://github.com/fatbobman/PublishThemeForFatbobmanBlog.git
$ cd PublishThemeForFatbobmanBlog
$ publish run

更多關(guān)于Publish的知識

本節(jié)的內(nèi)容將介紹幾個Publish中的概念,對于后面了解主題定制和功能擴(kuò)展十分重要。

Site

當(dāng)你使用Publish來創(chuàng)建項(xiàng)目時,Publish會自動生成一個Swift Package。網(wǎng)站的生成和部署配置都是通過該包完成的,使用的都是原生的且類型安全的Swift代碼。

下面的代碼便是使用publish new myblog生成的main.swift(包的入口文件)中內(nèi)容。

//Site的定義
struct Myblog: Website {
    enum SectionID: String, WebsiteSectionID {
        // 添加你需要的Section
        case posts
    }

    struct ItemMetadata: WebsiteItemMetadata {
        // 在這里添加任何您想使用的特定站點(diǎn)元數(shù)據(jù)
    }

    // 你網(wǎng)站的一些配置xn'xi
    var url = URL(string: "https://your-website-url.com")!
    var name = "Myblog"
    var description = "A description of Myblog"
    var language: Language { .english }
    var imagePath: Path? { nil }
}
//可以在主題或插件等位置訪問Site中的屬性信息

// 執(zhí)行入口。當(dāng)前使用的是默認(rèn)的主題,且使用的是Publish預(yù)置的生成、導(dǎo)出、發(fā)布流程。
// 工作流的定義,更多內(nèi)容見Step
try Myblog().publish(withTheme: .foundation)

Site不僅定義了網(wǎng)站項(xiàng)目的基礎(chǔ)配置信息,而且定義了網(wǎng)站從生成到發(fā)布的工作流程。

Section

每個Section都會在Output下生成的一個子目錄。在main.swift中,通過枚舉的方式對Section進(jìn)行定義。你可以把Section可以作為一組Item(文章)的容器,也可以僅指向某個Page(非Item的自有頁面)。 當(dāng)需要使用Section管理一組文章時,只需要在Content目錄下創(chuàng)建同該Section名字相同的子目錄即可,具體可以查看范例中Content下的postsproject

Section的定義

enum SectionID: String, WebsiteSectionID {
     case posts //新創(chuàng)建的項(xiàng)目缺省只有這個,對應(yīng)content-posts目錄
     case links //可以自己添加,將屬于該section的文章放置到對應(yīng)的目錄即可
     case about
}

肘子的Swift記事本中,每個Section同時也對應(yīng)著上方導(dǎo)航區(qū)的一個選項(xiàng)。Section可以有多種用途,在主題定制章節(jié)會做更多探討。

Item

保存在Content--對應(yīng)Sectioin目錄中的文章。每個Item都對應(yīng)一個Section,無需特別設(shè)置,其保存在哪個Section的目錄中,就屬于哪個Section。如果該Section不需要作為文件容器,可以直接在Content中創(chuàng)建和Section同名的md文件。我提供的范例模板中,about就是這種形式。

Page

不歸屬于任何Section的文章。Page不會出現(xiàn)在Sectionitem列表中,通常也不會出現(xiàn)在index(首頁)列表中。在content下的不屬于任何Section的目錄中按如下結(jié)構(gòu)添加文件即可創(chuàng)建Page。注意Page的創(chuàng)建路徑和訪問路徑的關(guān)系。

| content -- 404
|             | -- index.md

你可以通過訪問http://localhost:8000/404/index/查看

Page為我們提供一種構(gòu)建自由頁面的方法。比如你可以用它來創(chuàng)建不需要顯示在列表中的文章,或者像范例主題的演示一樣創(chuàng)建404??。

Content

在此特指Item、Page中的content屬性。作為內(nèi)容集,其范圍包括文本(如標(biāo)題和描述)、所屬標(biāo)簽(tag)、轉(zhuǎn)換后HTML代碼、音頻、視頻等各種元數(shù)據(jù)。元數(shù)據(jù)需要在Markdonw文章的頭部注明。

Section也有Content,它的內(nèi)容對應(yīng)著你在該Section對應(yīng)的Content子目錄中創(chuàng)建的index.md(如果有必要的話)。

在代碼中將來還會碰到另一種Content,確切的說是PublishingContext。里面包含著整個項(xiàng)目的所有信息(Site、Sections、Items、Tags等),通過將它的實(shí)例傳遞給Step或者Plugin來完成修改或配置網(wǎng)站的各種工作。

Metadata

Markdown文件的元數(shù)據(jù),在文章(Markdown)文件的頭部做出標(biāo)識。分為兩類,一種是Publish預(yù)置的。另一種是通過在SiteItemMetadata自定義的。

---
date: 2021-01-29 19:58
description: A description of my first post.
tags: first, article
author: fat 
image: /images/first-post.png
---

預(yù)設(shè)

  • title 文字標(biāo)題

    如果沒有設(shè)置,Publish會直接查找文章正文中第一個Top-level Head#作為標(biāo)題

  • description 文章簡介

  • date 文章創(chuàng)作日期

    如果沒設(shè)置則直接使用文件的modificationDate

  • tags 文章標(biāo)簽,每篇文章可以設(shè)置多個標(biāo)簽,為文章的組織多一個維度

  • image 圖片地址 比如可以用來在item列表中顯示一個文章的主題圖片(需在主題中定義)

  • audio 音頻數(shù)據(jù)

  • video 視頻數(shù)據(jù)

    音視頻的定義過于復(fù)雜,如果確實(shí)需要可以自行定義。

自定義

struct ItemMetadata: WebsiteItemMetadata {
    // Add any site-specific metadata that you want to use here.
    var author:String
}

如果預(yù)置的metadata不足以滿足你的需求,可以在ItemMetadata中自行定義。

兩種metadata的區(qū)別

預(yù)設(shè)的metadata在Publish中是作為的屬性存在的。

for item in content.allItems(sortedBy: \.date){
         print(item.title)
 }

自定義的metadata需通過如下方式獲取

let author = (item.metadata as! Myblog.ItemMetadata).author

在主題中使用更方便

.text(item.metadata.author)

Publish中預(yù)設(shè)的metadata,Item并不要求必須填寫。但是對于自定義的metadata則必須在markdown文檔中添加。index.md、 Page 可以沒有metadata(無論是否為自定義)

Theme

Publish使用Plot作為其HTML主題的描述引擎,開發(fā)者可以用非常Swift的方式來定義頁面。如果使用過DSL類型的開發(fā)方式,會感覺非常親切。比如下面的代碼定義了Section List的布局呈現(xiàn)

func makeSectionHTML(
        for section: Section<Site>,
        context: PublishingContext<Site>
    ) throws -> HTML {
        HTML(
            .lang(context.site.language), //網(wǎng)頁語言
            .head(for: section, on: context.site), //頭文件,metadata、css等
            .body(
                .header(for: context, selectedSection: section.id), //網(wǎng)站的上部區(qū)域,范例中包括了Logo,以及導(dǎo)航條
                .wrapper(
                    .itemList(for: section.items, on: context.site) // 文章列表
                ),
                .footer(for: context.site) //最下方的版權(quán)區(qū)域
            )
        )
    }

在Theme中定義的布局細(xì)節(jié)仍需要在css中進(jìn)行進(jìn)一步設(shè)置。

上面代碼中 header、wrapper等在Plot中都被稱作Node,除了使用Publish中預(yù)置的大量Node外,我們可以使用自己編寫的Node。

更多關(guān)于Theme的內(nèi)容,將在用Publish創(chuàng)建博客(二)中做詳細(xì)介紹。

Step

Publish采用工作流(Pipeline)的方式來明確定義各個環(huán)節(jié)的操作過程。從文件讀取、markdown解析、HTML生成、RSS導(dǎo)出等等。通過publish new生成的main.swift中,盡管只使用了一條語句

try Myblog().publish(withTheme: .foundation)

但其背后對應(yīng)著下面一系列操作步驟:

using: [
      .group(plugins.map(PublishingStep.installPlugin)),
      .optional(.copyResources()),
      .addMarkdownFiles(),
      .sortItems(by: \.date, order: .descending),
      .group(additionalSteps),
      .generateHTML(withTheme: theme, indentation: indentation),
      .unwrap(rssFeedConfig) { config in
      .generateRSSFeed(
               including: rssFeedSections,
                      config: config
                    )
          },
       .generateSiteMap(indentedBy: indentation),
       .unwrap(deploymentMethod, PublishingStep.deploy)
         ]

在多數(shù)的情況下,我們都會顯式的將每一個操作步驟標(biāo)明出來。每個步驟在Publish中被稱為Step。Publish已經(jīng)預(yù)置了不少Step供開發(fā)者使用。我么也可以將自己創(chuàng)建的Step注入到工作流中合適的位置以實(shí)現(xiàn)更多功能。

每個Step都會被傳遞一個PublishContent實(shí)例,該實(shí)例可用于更改網(wǎng)站中的各種元素(包括文件、文件夾、Item、Page等)。關(guān)于PublishContentContent的不同,請見上文。

Plugin

.installPlugin(.highlightJS()), //語法高亮

上面的代碼在Publish工作流中通過名為installPluginStep來完成插件highlightJS的安裝。

Plugin的開發(fā)和Step非常類似,都會獲得一個PublishContent實(shí)例,并通過其完成相關(guān)工作。

比如說,你可以用Step來完成某些具有副作用的操作;用Plugin來完成類如Modifier(markdown的定制化解析)注入的工作。

對于自定義代碼,從功能角度講,兩者都能實(shí)現(xiàn)對方的工作。因此在創(chuàng)建功能擴(kuò)展時,采用Step還是Plugin取決于個人的偏好。

關(guān)于如何定制StepPlugin將在用Publish創(chuàng)建博客(三)中做詳細(xì)說明。

Publish適合什么人

Publish同當(dāng)前主流的靜態(tài)網(wǎng)站生成器相比還略有不足,如社區(qū)活躍度較低、開發(fā)時間較短、Swift語言用戶量較小等。當(dāng)前Publish較適合符合如下狀況的朋友:

  • 使用Swift語言的開發(fā)者或Swift的愛好者
  • 欠缺Javascripte的經(jīng)驗(yàn),或者喜歡Javascripte free的風(fēng)格
  • 追求高效、簡潔的網(wǎng)頁呈現(xiàn)方式
  • 希望能夠完整掌握網(wǎng)站的各個環(huán)節(jié)并通過自己的雙手逐步實(shí)現(xiàn)各項(xiàng)功能
  • 善于嘗鮮者

Next

我將在用Publish創(chuàng)建博客(二)——主題開發(fā)中探討Theme的開發(fā),在用Publish創(chuàng)建博客(三)——插件開發(fā)中了解如何通過多種手段擴(kuò)展Publish的功能。

如果你已經(jīng)開始感興趣,馬上在Github上開通你的github.io站點(diǎn),用Publish一鍵deploy屬于自己的博客吧。

我的個人博客肘子的Swift記事本中會有更多關(guān)于Swift、SwiftUI、CoreData的內(nèi)容。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容