作為一名程式員,寫博客是積累知識、提升水平必不可少的一個方法,我們寫博客主要有三種方法,一種是使用掘金、博客園、CSDN等博客網站,第二種是自己搭建網站,存放自己的博客,第三種就是使用靜態博客生成器,將生成的網頁部署到服務器或者github pages、gitee pages等服務上,
這三種方法中,第一種自由度太低,并且定制樣式很麻煩;第二種每寫一篇博客都要新建個頁面,非常麻煩,因此我選擇了第三種方法,在使用了hexo、vuepress,gridea等多種靜態博客生成器后,我決定自己寫一個來提升自己的能力,
專案地址:https://github.com/Tuzilow/CoinRailgun
明確需求
首先我們要明確需求,確定我們想要的效果
- 初始化博客檔案夾,載入模板
crn init - 根據模板創建markdown檔案,
crn new "Hello CoinRailgun" - 根據markdown檔案生成html檔案,
crn build - 本地運行網站,
crn server
開始撰寫
安裝依賴
根據上面我們分析出來的需求,確定出我們所需要的依賴,并且安裝好他們
art-template撰寫模板所用的模板引擎commander用來撰寫clidayjs處理時間front-matter處理markdown頂部的yml宣告fs-extrafs的擴充模塊glob匹配指定檔案名highlight.js高亮代碼塊koa和koa-static啟動本地服務markdown-it、markdown-it-anchor、markdown-it-toc-done-right決議markdownuslug決議錨點的漢字
"dependencies": {
"art-template": "^4.13.2",
"commander": "^7.0.0",
"dayjs": "^1.10.4",
"front-matter": "^4.0.2",
"fs-extra": "^9.1.0",
"glob": "^7.1.6",
"highlight.js": "^10.5.0",
"koa": "^2.13.1",
"koa-static": "^5.0.0",
"markdown-it": "^12.0.4",
"markdown-it-anchor": "^7.0.1",
"markdown-it-toc-done-right": "^4.2.0",
"uslug": "^1.0.4"
}
搭建專案結構
.
├─ bin
│ └─ crn.js # 執行檔案
├─ lib # crn.js呼叫的各個函式
│ ├─ build.js
│ ├─ clean.js
│ ├─ new.js
│ ├─ preview.js
│ └─ init.js
├─ package.json
└─ template # 模板
├─ site.config.json # 組態檔
└─ theme # 主題
└─ default # 默認主題
├─ assets
└─ layout
crn.js
同樣,根據需求將各個命令、命令的引數和說明先寫出來
關于commander具體如何使用,可以查看commander檔案
#! /usr/bin/env node
const program = require('commander');
const version = require('../package.json').version;
program
.version(version)
.command('init [dir]')
.description('初始化博客')
.action(require('../lib/init'));
program
.command('new <name>')
.description('創建新的文章')
.action(require('../lib/new.js'));
program
.command('server [dir]')
.description('本地預覽網站')
.option('-d, --dir <dir>', 'build時輸出的目錄')
.action(require('../lib/preview.js'));
program
.command('build [dir]')
.description('將文章渲染為html')
.option('-o, --output <dir>', '輸出目錄')
.action(require('../lib/build'));
program
.command('clean')
.description('清空build出來的靜態檔案')
.option('-d, --dir <dir>', 'build時輸出的目錄')
.action(require('../lib/clean.js'));
program.parse(process.argv);
init
初始化的時候可以傳入一個目錄,表示準備初始化的目錄,這里我用了ES2020的新語法dir = dir ?? '.',當dir為null或undefined時,使用問號右邊的值,
在初始化的時候,需要明確好用戶使用的目錄應該是什么樣的
Blog
├─ build
├─ site.config.json
├─ source
│ └─ _posts
│ └─ blog.md
└─ theme
└─ default
├─ assets
└─ layout
將預先準備好的模板根據設計的目錄拷貝到目標目錄下,而不是直接呼叫專案中的,因為拷貝到目標目錄下后,使用者就可以更方便的自定義模板,可以更方便的寫自己的樣式,
關于fs-extra模塊的各種API可以查看fs-extra檔案
關于dayjs可以查看dayjs檔案
const path = require('path');
const fs = require('fs-extra');
const dayjs = require('dayjs');
module.exports = (dir) => {
dir = dir ?? '.';
const templateDir = path.resolve(__dirname, '..', 'template');
fs.copySync(templateDir, path.resolve(dir));
fs.ensureDirSync(path.resolve(dir, 'source'));
newPost(dir);
};
function newPost(dir) {
const firstPost = [
'---',
'title: Hello World',
'date: ' + dayjs().format('YYYY/MM/DD HH:mm:ss'),
'tags: ' + '[blog,CoinRailgunn]',
'category: ' + 'welcome',
'---',
'',
'Welcome to my blog, this is my first post',
'<!-- more -->'
].join('\n');
const file = path.resolve(dir, 'source', '_posts', 'hello.md');
fs.outputFileSync(file, firstPost);
console.log("博客初始化完成,鍵入'crn new <postName>'即可創建新的文章");
}
new
創建新文章的函式和初始化函式有部分的邏輯是相同的,這里我沒有將他們封裝起來,如果感興趣的話你們可以試試,創建文章需要傳入一個name,為創建的文章名,然后將其保存至source/_post下
const fs = require('fs-extra');
const path = require('path');
const dayjs = require('dayjs');
module.exports = (name) => {
const post = [
'---',
`title: ${name}`,
'date: ' + dayjs().format('YYYY/MM/DD HH:mm:ss'),
'tags: ' + '[blog]',
'category: ' + 'code',
'---',
'',
].join('\n');
const file = path.resolve('source', '_posts', `${name}.md`);
fs.outputFileSync(file, post);
console.log(`source/_posts/${name}.md 創建成功!`);
};
build
生成靜態頁是整個專案最關鍵的部分,因為代碼很多這里講一下我的思路,詳細代碼可以查看專案倉庫
首先我們要設計好各個頁面的url,以下為我的設計:
- 首頁:
/index.html和/page/1/index.html - 不同頁碼:
/page/頁碼/index.html - 文章頁:
/categories/分類名/文章名/index.html - 關于我頁面:
/about/index.html - 歸檔頁:
/archives/index.html - 分類頁:
/categories/index.html - 標簽頁:
/tags/index.html - 404頁:
/404/index.html(這個我忘了做了
目前的瀏覽器會自動隱藏index.html,因此使用目錄名/index.html的方式可以美化頁面的地址欄
第一步,根據設計好的url撰寫好各個頁面模板,這里我使用的是art-template
template/theme/default/layout/layout.arttemplate/theme/default/layout/page.art- 其他請查看CoinRailgun默認主題模板
然后,一些網站的基礎資料,比如author、keywords、description等,是不會發生改變的,因此需要將他們寫在統一的組態檔里site.config.json,下面是我的部分組態檔
{
"basic": {
"icon": "",
"avatar": "",
"title": "",
"author": "",
"description": "",
"keywords": []
},
"theme": {
"name": "default",
"highlight": "github-gist",
"pageSize": 7,
"exclude": [
"life"
],
"friends": [],
"about": {
"label": "about me.",
"url": "/about"
},
"nav": [
{
"name": "archives",
"label": "歸檔",
"url": "/archives"
},
{
"name": "categories",
"label": "分類",
"url": "/categories"
},
{
"name": "tags",
"label": "標簽",
"url": "/tags"
}
],
"links": [],
"footer": {
"beian": "",
"copyright": {
"year": "2019-2021"
}
}
},
"dev_server": {
"port": 3000
}
}
在根據markdown和模板生成html時,我們要確定模板上需要的資料,并且將組態檔和markdown的內容轉換為模板上的資料
<!-- layout/post_item.art -->
<div >
<a href="https://www.cnblogs.com/xueyubao/archive/2021/02/08/{{url}}">
{{title}}
</a>
</div>
<div >
<p >
<i aria-hidden="true"></i>
{{date}}
</p>
<p >
<i aria-hidden="true"></i>
<a href="https://www.cnblogs.com/categories">
{{category || ''}}
</a>
</p>
</div>
<div >
<p >{{@ abstracts}}</p>
<p style="display:none;">
<a href="https://www.cnblogs.com/xueyubao/archive/2021/02/08/{{url}}">查看更多</a>
</p>
</div>
<div >
{{each tags}}
<a href="https://www.cnblogs.com/tags">
<i aria-hidden="true"></i>
{{$value}}
</a>
{{/each}}
</div>
以文章串列項為例,這個模板需要title、date、category、url、abstracts和tags,其中url是根據設計好的/categories/分類名/文章名/index.html生成出來的,其他的引數都是從markdown檔案中決議出來的,并且這些引數都寫在檔案頭部的yml配置中,而abstracts一般是使用<!--more-->分割出來,
明確了以上內容后,我們就需要獲取這些引數然后傳遞給模板渲染出來
const template = fs.readFileSync(postTemplate, 'utf-8');
const content = fs.readFileSync(fullPath, 'utf-8');
const fm = require('front-matter');
function renderAbstracts() {
// ....
}
const postItem = art.render(template, {
...fm(content).attributes,
abstracts: renderAbstracts(),
});
這樣我們就得到了渲染后的文章串列項,然后再傳入post_list.art 渲染出來文章串列后傳入page.art中,與其他的資料相組合拿到完整的一個頁面,渲染出頁面后使用fs.outputFileSync將頁面保存到一開始設計好的目錄中build/page/1/index.html
大致思路就是這樣,更多具體實作可以查看專案倉庫
server
生成所有頁面后,就可以開啟本地預覽了,這里我使用的是koa,使用express或者其他的框架都是大差不差的,直接將build目錄設定為靜態資源即可訪問,
const Koa = require('koa');
const staticServe = require('koa-static');
const path = require('path');
module.exports = (dir, options) => {
dir = dir ?? '.';
const app = new Koa();
const siteConfig = require(path.resolve(dir, 'site.config.json'));
const outputDir = path.resolve(dir, options.dir ?? 'build');
app.use(staticServe(outputDir));
app.listen(siteConfig.dev_server.port, () => {
console.log(
`在瀏覽器中打開 http://localhost:${siteConfig.dev_server.port} 以預覽網頁`
);
});
};
這樣我們就了解了制作一個靜態博客生成器的思路和程序,
參考文章
- 手摸手教你擼一個靜態網站生成器
- 自己動手擼一個靜態博客生成器
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/258011.html
標籤:其他
