有時大家可能會想為自己的博客增添一些色彩,但這種熱情卻常常因繁雜的配置步驟飽受消磨。CNBlogX是一套項目模板及腳手架的合集,用於快速搭建博客園的頁面定製腳本開發環境。使用方式也非常簡單,專註於自己的代碼即可。讓我們開始吧! ...
有時大家可能會想為自己的博客增添一些色彩,但這種熱情卻常常因繁雜的配置步驟飽受消磨。CNBlogX是一套項目模板及腳手架的合集,用於快速搭建博客園的頁面定製腳本開發環境。使用方式也非常簡單,專註於自己的代碼即可。讓我們開始吧!
起步
- 請確保全裝了Node.js,需要14.0或更新的版本。
通過以下命令基於CNBlogX模板創建一個叫做mytheme的項目:
npm init cnblogx mytheme
這樣項目就建立完成了,讓我們看看src/
下的代碼:
非常簡潔,不必關心CNBlogX在背後做了什麼,我們只要在這三個文件中添加代碼即可。如果有定製化的需要,可以查看README.md中的可配置項,接下來讓我們把代碼部署到博客園。
首次部署
- 若依賴安裝很慢,可以考慮先配置npm淘寶鏡像源。
首先給我們的項目安裝依賴:
npm install
再編譯我們的項目:
npm run build
然後將dist/
下的生成物複製到博客園-管理-設置的對應選項中:
- 將
custom.css
的內容複製到頁面定製CSS代碼中。 - 將
custom.html
的內容複製到頁腳HTML代碼中。
最後保存博客後臺設置即可。
首次部署是必要的步驟,CNBlogX在構建時插入了開發者模式相關代碼,接下來讓我們體驗一下。
開發者模式
- 請確保已經完成首次部署。
首先執行以下命令啟動調試伺服器,它將監視代碼變化並將其應用到博客頁面中:
npm run dev
然後瀏覽器中打開自己的博客,雙擊頁腳的Copyright © 你的名字,進入開發者模式。
此後,若src/
下對應的文件發生了變化,這些變化將立即被應用到博客頁面中:
main.ejs
:支持HTML/EJS(相容,尾碼不可更改)。main.js
:支持Javascript/Typescript(更改尾碼為.ts
即可支持Typescript)。main.scss
:支持CSS/SCSS(相容,尾碼不可更改)。
- 再次雙擊頁腳的Copyright © 你的名字,可退出開發者模式。
實踐:編寫常用組件
讓我們寫幾個常用的博客組件,體驗一下熱模塊替換帶來的效率提升吧!後文中出現的組件可以在這個項目中找到。
評論區頭像
第一版:低解析度頭像
博客園預設不顯示評論區用戶的頭像,但在評論區中提供了每位用戶的頭像鏈接,我們可以通過瀏覽器的開發者工具看到:
所以我們只要通過Javascript新建一個img
標簽,顯示對應鏈接的頭像即可:
嗯... 好像有一點糊,這個頭像解析度太低了。
第二版:回退式高清頭像
經過觀察,我們發現個人主頁的頭像鏈接和評論區的頭像鏈接只存在一個目錄的差異:
那我們可以先顯示這個清晰的頭像,如果獲取失敗了,再回退到低解析度的頭像。效果非常不錯:
第三版:二級回退式頭像
有時會發現,個別用戶根本沒有頭像,我們可以為他們添加一個預設頭像。那麼我們的代碼至多可能有兩次回退,高清頭像->普通頭像->預設頭像,像這樣:
avatar = document.createElement('img');
avatar.src = get_hi_definition_avatar_src(addr); // 設置為高清頭像。
avatar.addEventListener('error', () => {
if (avatar.src != addr) {
avatar.src = addr; // 回退到低解析度頭像。
} else {
avatar.replaceWith(new_default_avatar(nick)); // 回退到預設頭像。
}
});
接下來讓我們給用戶畫一個預設頭像吧!
我們把衣服的位置鏤空,再給頭像元素設置不同的背景色,就可以為不同的用戶顯示不同顏色的頭像了!完整代碼可以看這裡。
隨筆目錄
雖然Markdown允許通過[toc]
創建一個目錄,但每次都要回到頂部查看目錄並不方便,讓我們也寫一個目錄吧。
棧:將數組轉換為一棵樹
目錄通常是多級的,大標題包含小標題。我們可以用.querySelectorAll()
將文章中所有的標題收集到一個數組中,然後通過一個棧將線性的數組轉化為一棵樹,像這樣:
article.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(function (header) {
const node = CreateTocNode(header);
for (; ;) {
if (node_stack.top().level < node.level) {
node_stack.top().add_toc_child(node);
node_stack.push(node);
break;
} else {
node_stack.pop();
}
}
});
下一個功能是讓目錄高亮當前小節的標題。
不妨將「當前小節的標題」定義為「離屏幕頂端最近的一個標題」。那麼思路就清晰起來了:註冊一個滾動事件,於事件發生時遍歷所有的標題,找到getBoundingClientRect().top
的絕對值最小的一個,賦予其一個表示高亮的類名即可。
不過,這朴素的思路存在著一定的效率問題,下麵我們將對它做出一些優化。
節流:防止滾動事件頻繁觸發
頁面滾動時,滾動事件連續觸發的頻率非常高,可以用一個節流函數降低更新高亮目錄的頻率。同時為了避免節流函數導致丟失滾動快結束時的滾動事件,添加一個會被不斷重置的setTimeout
即可。代碼是這樣的:
let timeout = null;
regi_scroll(throttle(() => {
update_current_node();
if (!timeout) {
timeout = setTimeout(() => {
update_current_node();
timeout = null;
}, 400);
}
}, 200));
二分搜索:獲取當前小節的標題
標題們的getBoundingClientRect().top
雖然有正有負,但只要是遞增的,就可以應用二分搜索。通過二分搜索找到top
值在零附近的至多兩個標題,再從中取top
的絕對值最小的一個即可。代碼是這樣的:
let left = 0;
let right = node_list.length - 1;
while (left + 1 < right) {
const mid = Math.floor((left + right) / 2);
if (node_list[mid].top <= 0) {
left = mid;
} else {
right = mid;
}
}
實現目錄大概用了一百行左右的代碼,可以在這裡查看。
常見問題
我的項目能夠與其它代碼共存嗎?
CNBlogX預設會在進入開發者模式時清除用戶的頁面定製CSS代碼,以免用戶混淆部署版本與開發版本的樣式,可以通過PRESERVE_CSS
編譯選項阻止這個預設行為。
有離線的文檔嗎?
有的,看項目根目錄下的README.md
。通過腳手架建立的新項目與Github上的模板只有包名不同。
開發者模式會影響他人閱讀嗎?
不會,開發者模式僅對啟用它的單個瀏覽器有效。
- 要令博客定製代碼對所有讀者生效,需要一次新的部署。
為什麼有時js的更改在刷新後才生效?
因為相關的模塊存在未消除的副作用,參考熱模塊替換的文檔。
可以單獨生成.js
文件嗎?
可以,通過STANDALONE_JS
編譯選項生成單獨的.js
文件。通過PUBLIC_PATH
編譯選項可令.html
文件從指定的路徑載入.js
文件。
可以自定義埠嗎?
可以,通過PORT
編譯選項配置埠,博客園中的代碼與本地測試伺服器的代碼配置的埠應當相同。
沒有頁腳的博客如何進入開發者模式?
打開瀏覽器的控制台,執行以下代碼即可:
cnblogx_development(true);
結語
希望大家喜歡,意見或建議也是很歡迎的,Issue或Pull Request就更歡迎了。