前言
這兩天在嘗試用語雀+ vuepress + github 搭建個人部落格。
小破站地址 :王天的 web 進階之路
語雀作為編輯器,釋出文件推送 github,再自動打包部署,大概流程如下。
問題
我使用的elog
外掛批次匯出語雀文件。elog
採用的配置是所有文章平鋪匯出,沒有按照語雀知識庫目錄生成markdown
,這導致 vuepress 側邊欄無法和語雀一致,如下圖。
上圖,左側是語雀知識庫,右側是匯出到 vuepress 展示的效果,很明顯沒有目錄這很影響閱讀體驗呀
解決
在查閱 vuepress 文件後,發現配置silderbar.ts
可以自定義側邊欄目錄,配置引數如下:
export default {
theme: defaultTheme({
// 可摺疊的側邊欄
sidebar: {
"/web/": [
{
text: "王天的web進階手冊",
collapsible: true, // 目錄是否摺疊
children: ["/reference/cli.md", "/reference/config.md"], // 文件目錄
},
{
text: "王天的魔法工具箱",
collapsible: true,
children: [
"/reference/bundler/vite.md",
"/reference/bundler/webpack.md",
],
},
],
},
}),
};
遞迴生成選單
配置sidebar.ts
可以修改左側選單,但是一個個手動修改這忒麻煩了啊啊啊啊。那如何批次生產選單配置項呢?
遞迴函式呀呀呀呀呀呀 ????
elog 在同步語雀文件時,會自動建立
elog.cache.json
快取檔案,在 vueprss 專案根目錄中檢視。
開啟elog.cache.json
檔案,我們能看到語雀文件知識庫的資料結構
"catalog": [
{
"type": "DOC",
"title": "前言",
"uuid": "17Os-_V_hcS37KOD",
"url": "wqbpyf5083qc7ho8",
"prev_uuid": "",
"sibling_uuid": "dmQSRn6AXUBSg96x",
"child_uuid": "",
"parent_uuid": "",
"doc_id": 141216125,
"level": 0,
"id": 141216125,
"open_window": 1,
"visible": 1
}
]
catlog 屬性是文件快取資料,關鍵欄位:
- type:值為'DOC' 是文章、值為 TITLE 則為目錄
- uuid:文章 id
- prent_uuid:父節點的 uuid
我們們根據以上引數,編寫遞迴函式, 將elog.cache.json
的一維陣列,遞迴生成 vuepress 側邊欄配置資料
程式碼如下:
function genYuqueRoute() {
// 引數1:遍歷陣列
// 引數2:父選單id
const deep = (arrlist, parantId) => {
let forList: any[] = [];
arrlist.forEach((element) => {
// 選單id不一致,跳出迴圈呼叫
if (element.parent_uuid !== parantId) return;
// 如果是TITLE型別新增配置項
if (element.type === "TITLE") {
forList.push({
text: element.title,
collapsible: true,
children: deep(arrlist, element.uuid),
});
// 如果是DOC 型別追加檔案地址
} else {
forList.push(element.url + ".md");
}
});
return forList;
};
return deep(catalog, "");
}
效果
敲重點啦!
遞迴函式本質上是一個在回撥自身的函式,用於改造資料結構,重點在於跳出迴圈的機制,否則陷入死迴圈啦
DFS vs BFS ?
什麼是 DFS 、BFS ?
- DFS 深度優先搜尋:可以用於找到一條路徑、判斷圖中是否存在迴圈、拓撲排序、生成連通分量等。
- BFS 廣度優先搜尋:可以用於找到最短路徑、生成最小生成樹、進行網路分析等。
:::danger
??♀️ 簡單理解為,橫向 、豎向 遍歷據狀結構
- 深度優先搜尋,對資料結構的橫向執行,從第一行遍歷子節點、葉子節點,依次直到最後一行。
- 廣度優先搜尋,對資料結構的豎向執行,把樹結構平面鋪開、以層級數為列數,從第一列依次執行。
:::
將深度搜尋、廣度搜尋代入到生活場景更容易理解。
我們們先看一個家庭關係樹狀圖,爺爺奶奶是一級屬性、父母叔伯二級、孫子孫女三級屬性、重孫們是四級屬性,以此類推。形成一個家庭關係樹狀圖。
假如奶奶過八十大壽,按輩分來,首先是父母叔伯這一輩祝壽,其次是孫子孫女輩分,最後重孫們,以此類推,這個豎向執行的祝壽過程就是廣度優先搜尋
那過年走親戚的話,我們們沒有俺輩分,去分批的吧?至少我們老家不是的,都是一去一家子呢。那這個橫線執行的過程,就是深度優先搜尋。
深度優先搜尋(DFS)示例程式碼:
從 A 節點依次取出資料
// 圖的鄰接表表示
const graph = {
A: ["B", "C"],
B: ["D", "E"],
C: ["F", "G"],
D: [],
E: [],
F: [],
G: [],
};
// 使用深度優先搜尋遍歷圖
function dfs(graph, start) {
const visited = new Set(); // 儲存已訪問節點的集合
function traverse(node) {
visited.add(node); // 將當前節點標記為已訪問
console.log(node); // 列印遍歷的節點
const neighbors = graph[node]; // 獲取當前節點的鄰居節點
for (const neighbor of neighbors) {
// 遍歷當前節點的鄰居節點
if (!visited.has(neighbor)) {
// 如果鄰居節點未被訪問過
traverse(neighbor); // 遞迴遍歷鄰居節點
}
}
}
traverse(start); // 從起始節點開始進行深度優先搜尋
return visited; // 返回所有已訪問的節點
}
輸出結果:
dfs(graph, "A"); // 對圖進行深度優先搜尋,從起始節點 'A' 開始,並列印遍歷結果
// A
// B
// D
// E
// C
// F
// G
在上述程式碼中,圖使用鄰接表表示,dfs
函式使用遞迴方式實現了深度優先搜尋。從起始節點 'A'
開始,遞迴訪問其鄰居節點,並在訪問時輸出節點的值。
廣度優先搜尋(BFS)示例程式碼:
// 廣度搜尋 BFS
let graph = {
A: ["B", "C"],
B: ["A", "C", "D"],
C: ["A", "D", "E"],
D: ["B", "C", "E"],
E: ["C", "D", "F"],
F: ["E", "W"],
W: ["C"],
};
function bfs(graph, startPoint) {
let queue = []; // 用於儲存待訪問節點的佇列
let result = []; // 儲存遍歷結果的陣列
queue.push(startPoint); // 將起始節點新增到佇列
result.push(startPoint); // 將起始節點新增到遍歷結果
while (queue.length > 0) {
// 當佇列不為空時進行迴圈
let point = queue.shift(); // 取出佇列中的第一個節點作為當前節點
let nodes = graph[point]; // 獲取當前節點的所有鄰居節點
for (let node of nodes) {
// 遍歷當前節點的鄰居節點
if (result.includes(node)) continue; // 如果鄰居節點已經在遍歷結果中,則跳過
result.push(node); // 將鄰居節點新增到遍歷結果中
queue.push(node); // 將鄰居節點新增到佇列中,以便後續訪問其鄰居節點
}
}
return result; // 返回遍歷結果
}
console.log(bfs(graph, "B")); // 執行廣度優先搜尋,從起始節點 'B' 開始,並輸出遍歷結果
在上述程式碼中,圖使用鄰接表表示,bfs
函式使用佇列實現了廣度優先搜尋。從起始節點 'A'
開始,將其加入佇列並標記為已訪問,然後依次從佇列中取出節點,並訪問其鄰居節點,同時將鄰居節點加入佇列中,直到佇列為空。
案例
深度優先搜尋(DFS)和廣度優先搜尋(BFS)在前端專案中有許多實際的應用場景。下面有兩個常見的前端開發專案案例
1、元件樹遍歷
在前端開發中,經常會有需要對元件樹進行遍歷的場景,例如渲染元件、查詢元件等。下面是一個使用 DFS 進行元件樹遍歷的示例:
function dfs_component_traversal(component) {
console.log(component); // 處理當前元件
if (component.children) {
for (const child of component.children) {
dfs_component_traversal(child); // 遞迴遍歷子元件
}
}
}
以上的程式碼展示了一個使用深度優先搜尋進行元件樹遍歷的函式。我們可以根據元件的層級關係,從根元件開始遞迴地遍歷每個元件及其子元件,以實現對整個元件樹的遍歷和操作。
這個演算法可以幫助我們在前端專案中處理元件之間的關係,例如渲染元件、查詢相關元件等。透過對元件樹的深度遍歷,我們可以有序地處理元件及其子元件,並執行相應的操作。
2、頁面導航
在前端開發中,頁面導航是一個常見的需求。我們可以使用廣度優先搜尋來實現頁面導航功能,以確保按照層級關係有序地展示頁面。
function bfs_page_navigation(page) {
const queue = [page]; // 使用佇列作為輔助資料結構來進行廣度優先搜尋
while (queue.length > 0) {
const current = queue.shift(); // 移除佇列頭部元素作為當前頁面
console.log(current); // 處理當前頁面
for (const child of current.children) {
queue.push(child); // 將子頁面加入佇列
}
}
}
以上程式碼展示了一個使用廣度優先搜尋進行頁面導航的函式。在這個函式中,我們使用佇列作為輔助資料結構來進行廣度優先搜尋。透過不斷將子頁面加入佇列,並按照佇列中的順序處理每個頁面,可以實現按照層級關係有序地導航頁面。
3、DFS + BFS 綜合案例
const root = {
value: 1,
children: [
{
value: 2,
children: [],
},
{
value: 3,
children: [
{
value: 7,
children: [
{
value: 8,
children: [],
},
],
},
],
},
{
value: 4,
children: [
{
value: 6,
children: [],
},
],
},
],
};
// 在深度優先搜尋 - 堆
// 我們首先處理當前節點,然後遞迴地處理每個子節點、直到葉子節點(沒有子節點的節點),最後依次遍歷完成
const digui = (node) => {
console.log(node.value);
if (node.children) {
for (const children of node.children) {
digui(children);
}
}
};
// 廣度優先搜尋-棧,把多維樹結構,取出來平鋪,依次訪問。
// 在廣度優先搜尋中,我們使用佇列來儲存待訪問的節點,確保按照層級順序進行遍歷。
// 每次從佇列中取出隊頭節點,處理該節點後,將其鄰居節點(子節點)入隊,以便後續遍歷。這樣,就可以依次訪問所有節點,並保持層級順序。
function breadthFirstSearch(root) {
if (!root) {
return;
}
const queue = []; // 建立一個空佇列,用於存放待訪問的節點
queue.push(root); // 將根節點入隊
while (queue.length !== 0) {
// 當佇列不為空時迴圈執行以下步驟
const current = queue.shift(); // 出隊隊頭節點作為當前節點
console.log(current.value); // 進行二次加工或其他操作,這裡簡單地輸出節點的值
for (const child of current.children) {
// 遍歷當前節點的鄰居節點(子節點)
queue.push(child); // 將未訪問過的鄰居節點入隊
}
}
}
console.log(digui(root));
console.log(breadthFirstSearch(root));
總結
遞迴函式本質上是一個在回撥自身的函式,用於改造資料結構,重點在於跳出迴圈的機制,否則陷入死迴圈啦
深度優先搜尋(DFS)的原理很簡單:我們從起始節點開始,沿著一條路徑不斷向下探索,直到達到終點或者無法繼續為止。如果遇到終點,就找到了一條路徑;如果無法繼續,則回溯到上一個節點,然後嘗試探索其他路徑。這個過程會遞迴地進行,或者使用棧來儲存節點的順序。
相比之下,廣度優先搜尋(BFS)的原理稍微有些不同:我們從起始節點開始,逐層地訪問其鄰居節點。也就是說,我們首先訪問起始節點的鄰居節點,然後是鄰居節點的鄰居節點,依此類推,直到遍歷完所有節點或者找到目標節點為止。為了遍歷節點的順序,我們使用佇列資料結構。
讀者朋友好呀,我是王天~
嘗試做過很多事情,汽修專業肄業生,半路出道的野生程式設計師、前端講師、新手作者,最終還是喜歡寫程式碼、樂於用文字記錄熱衷分享~
如文章有錯誤或者不嚴謹的地方,期待給於指正,萬分感謝。
如果喜歡或者 有所啟發,歡迎 star,對作者也是一種鼓勵。
微信:「wangtian3111
」,加我進王天唯一的讀者群。
個人部落格:https://itwangtian.com