為部落格園開發了一套腳手架及模板——實時預覽頁面定製效果
有時大家可能會想為自己的部落格增添一些色彩,但這種熱情卻常常因繁雜的配置步驟飽受消磨。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.parentNode.replaceChild(new_default_avatar(nick), avatar);
}
});
接下來讓我們給使用者畫一個預設頭像吧!
我們把衣服的位置鏤空,再給頭像元素設定不同的背景色,就可以為不同的使用者顯示不同顏色的頭像了!完整程式碼可以看這裡。
隨筆目錄
雖然Markdown允許通過[toc]
建立一個目錄,但每次都要回到頂部檢視目錄並不方便,讓我們也寫一個目錄吧。
棧:將陣列轉換為一棵樹
目錄通常是多級的,大標題包含小標題。我們可以用.querySelectorAll()
將文章中所有的標題收集到一個數組中,然後通過一個棧將線性的陣列轉化為一棵樹,像這樣:
article.querySelectorAll(SELECTOR_HEADERS).forEach(function (header) {
const node = CreateTocNode(header);
for (; ;) {
if (level(node_stack_top().refel) < level(header)) {
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 (distance(mid) <= 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就更歡迎了。