高級前端進階(一)

希望疫情早日結束!!!

一、首先來一道開胃菜,css方面問題

在前端方面,渲染列表數據的時候,可能會更改第一個或者最後一個的樣式,又或者其中第幾個的樣式,怎麼做呢?
其實很簡單,便是使用last-child選擇器相關知識。
問題是,有時候,我們使用last-child會出現失效的問題。
先來個實際場景:
目的:給最後一個class=”invalid”的元素添加背景色。

<h1>這樣是無效的,也就是last-child失效了</h1>
<div class="question">
    <div class="invalid">111111</div>
    <div class="invalid">222222</div>
    <div class="invalid">333333</div>
    <div class="invalid">444444</div>
    <div class="invalid">555555</div>
    <div class="invalid">666666</div>
    <div class="disturb">777777</div>
</div>

<style lang="scss" scoped>
.question {
  .invalid:last-child {
    background-color: cornflowerblue;
  }
}
</style>

為啥會失效?主要問題就是最後有個class=”disturb”的元素。
使用last-child選擇器必須要有個容器包裹,否則就會失效,把上面class=”invalid”元素再用一個div包裹起來便可以了。
改成以下便可以了

<div class="question">
    <div>
      <div class="invalid">111111</div>
      <div class="invalid">222222</div>
      <div class="invalid">333333</div>
      <div class="invalid">444444</div>
      <div class="invalid">555555</div>
      <div class="invalid">666666</div>
    </div>
    <div>
      <div class="invalid">111111</div>
      <div class="invalid">222222</div>
      <div class="invalid">333333</div>
      <div class="invalid">444444</div>
      <div class="invalid">555555</div>
      <div class="invalid test">666666</div>   <!-- 這樣也是可以的 -->
    </div>
    <div class="disturb">777777</div>
</div>

二、重點來了

list轉樹型數據

關於這一點,個人覺得前端一般不需要這麼做,畢竟前端主要負責進行數據渲染,以及用戶交互。
但將list轉為樹型數據,裡面涉及到的演算法,我們還是要了解一下的。
將list轉為樹型數據,一般用到的是遞歸跟多次遍歷方法。
其他的方法便不多做介紹了。在此介紹掌握最高效的方法,一次遍歷即可。

list數據

[
    {
        id: 1, pid: 0, name: "一級數據1"
    },
    {
        id: 2, pid: 0, name: "一級數據2"
    },
    {
        id: 3, pid: 0, name: "一級數據3"
    },
    {
        id: 4, pid: 1, name: "二級數據2-1"
    },
    {
        id: 5, pid: 1, name: "二級數據2-2"
    },
    {
        id: 6, pid: 1, name: "二級數據2-3"
    },
    {
        id: 7, pid: 3, name: "三級數據3-1"
    },
    {
        id: 8, pid: 3, name: "二級數據3-2"
    },
    {
        id: 9, pid: 8, name: "三級數據3-1"
    }
]

樹型數據

[{
        id: 1,
        pid: 0,
        name: "一級數據1",
        children: [{
                id: 4,
                pid: 1,
                name: "二級數據2-1"
            },
            {
                id: 5,
                pid: 1,
                name: "二級數據2-2"
            },
            {
                id: 6,
                pid: 1,
                name: "二級數據2-3"
            }
        ]
    },
    {
        id: 2,
        pid: 0,
        name: "一級數據2"
    },
    {
        id: 3,
        pid: 0,
        name: "一級數據3",
        children: [{
                id: 7,
                pid: 3,
                name: "三級數據3-1"
            },
            {
                id: 8,
                pid: 3,
                name: "二級數據3-2",
                children: [{
                    id: 9,
                    pid: 8,
                    name: "三級數據3-1"
                }]
            },
        ]
    }
]

為了實現這個目的,我們需要了解很多的知識點。
1、基本的原理
每條數據都要有相應的id跟pid
id指的是這條數據的主鍵(就這麼理解吧),一定要是唯一的。
pid指的是這條數據的上級數據的主鍵id,(相當於組織機構中,上級組織機構的主鍵id)。
當每條數據滿足具有id跟pid的時候,那便可以將數據轉為樹型結構。
2、JavaScript是弱類型語言。舉個例子:

var o={};// 申明一個o空對象變數(裡面沒有任何的屬性,不是null)
o.children=[];// 給o變數添加一個數組類型的變數children。這便是與強類型的區別。強類型語言必須要在一個class裡面將變數都定義好。

3、引用類型(這個便是重中之重了,也是為啥一次遍歷便能夠實現想要的結果的原因)
引用類型,變數存儲的是地址,而地址對應的是值,我們需要做的是改變值,但這個變數名稱是不變的,也就是地址是不變的,值是改變的。
通過定義一個變數itemMap,動態地給itemMap添加id屬性值,而itemMap屬性的值為list中的一個數組對象,並且循環遍歷的時候,不斷給itemMap屬性值添加相應的children。

詳細程式碼如下:

// list轉樹型數據
function listToTree(data, mapping) {
    let idMap = 'id';
    let pidMap = 'pid';
    if (mapping) {
        idMap = mapping.id;
        pidMap = mapping.pid;
    }
    // 以上部分主要是針對list的id跟parentid欄位,萬一不是id跟pid的。
    let result = [];// 存儲最終的結果
    let itemMap = {};// 這個對象變數便是最核心的地方
    for (let item of data) { // for-of遍歷
        let id = item[idMap]; // 獲取id
        let pid = item[pidMap]; // 獲取pid
        itemMap[id] = item;// 開始給itemMap變數賦值
        let parentData = itemMap[pid];// 淺拷貝    父親節點
        if (!parentData) {
            result.push(itemMap[id]);
            continue; // 結束當前循環,繼續下一個循環
        }
        parentData.children = []; //這樣做的話,給有子集的項添加children屬性,沒子集的話,便沒有children屬性
        parentData.children.push(item);
    }
    return result;
}

樹型數據轉list(倒過來)

這個,暫時沒想到什麼更好的方法,便用遞歸實現了。
遞歸,有個很形象的說法,一層一層地撥開你的心!!!
嘻嘻

// 樹型數據轉list
function treeToList(tree, mapping) {
    var list = [];
    let childrenName = 'children';
    if (mapping) {
        childrenName = mapping.children;
    }
    handleTreeToList(tree, list, childrenName);
    return list;
}
// 遞歸方法
function handleTreeToList(tree, list, childrenName) {
    if (!tree || !tree.length) {
        return;
    }
    for (let item of tree) {// 遍歷
        let temp = JSON.parse(JSON.stringify(item));// 深拷貝
        if (item[childrenName]) {
            delete temp[childrenName];// 刪除子項
        }
        list.push(temp);
        if (item[childrenName]) {
            handleTreeToList(item[childrenName], list, childrenName);// 遞歸調用
        }
    }
}

三、擴展(遞歸的使用,鞏固遞歸演算法)

給樹型數據每項添加額外的數據

// 給樹型數據每項添加flag屬性
function addDataTree(tree, childrenName) {
    if (childrenName == undefined)
        childrenName = 'children';
    for (let item of tree) {
        item.flag = false;// 添加flag屬性
        if (item[childrenName])
            addDataTree(item[childrenName], childrenName);// 遞歸調用
    }
}

四、總結

遞歸演算法,還是很有必要掌握的,在樹型結構方面經常會用到。

希望疫情早點結束!!!