讀源碼 | 跟着 iview 的大佬們學編程思想

文 / 景朝霞
來源公號 / 朝霞的光影筆記
ID / zhaoxiajingjing
圖 / 本身畫
❥❥❥❥點個贊,讓我知道你來過~❥❥❥❥

【前情提要】javascript

  1. 題目 | let和var的區別(1、二)
  2. 圖解 | let和var的區別(1、二)
  3. 題目 | 帶VAR和不帶VAR的區別
  4. 圖解 | 帶VAR和不帶VAR的區別
  5. 總結 | LET和VAR區別(3、四)
  6. 圖解 | 做用域和做用域鏈
  7. 練習題 | 做用域和做用域鏈
  8. 圖解 | 理解閉包
  9. 案例 | 閉包做用:保護和保存
  10. 圖解 | 判斷條件中的變量提高、私有變量、全局變量、arguments
  11. 理解 | 堆內存棧內存釋放、null和{}、undefined的區別

【iview】php

  1. iview低版本實現表格拖拽,滾動條列寬計算問題
  2. 案例 | iview中Table:拖拽適配列、自定義固定列、合併列
  3. 讀源碼 | 跟着大佬們學編程思想
  4. 上 | iview的Table組件合併列demo
  5. 下 | iview的Table組件合併列,升級代碼

0 / 讀 iview 源碼

上個月,用iview的Table組件作合併行的效果,認真的讀了下那塊的源碼,收穫頗多。vue

公號ID:zhaoxiajingjing
△14.1iview的Table組件的行/列合併java

https://github.com/view-desig...

公號:朝霞的光影筆記

△14.2在table.vue的props對象裏面定義屬性node

它使用在這:

https://github.com/view-desig...jquery

getChildNode (h, data, nodes) {
    // ...CODE
    if (data.children && data.children.length) {
        data.children.forEach((row, index) => {
            let $tds = [];

            this.columns.forEach((column, colIndex) => {
                // => 在循環每一列時,判斷當前行/列是否須要展現"合併行"的屬性
                // => this.showWithSpan() 方法返回一個布爾值
                // => 是:進入判斷體
                if (this.showWithSpan(row, column, index, colIndex)) {
                    // ...CODE
                    const $td = h('td', {
                        class: this.alignCls(column, row),
                        // => 設置td的attrs屬性,在vue裏面寫入DOM屬性用 atrrs
                        // => this.getSpan() 方法返回rowspan、colspan的值
                        attrs: this.getSpan(row, column, index, colIndex)
                    }, [$tableCell]);
                    // ...CODE
                }
            });
        });
    }
    // ...CODE
}

△table-body.vue設置rowspan/colspangit

/**
 * 獲取span的值
 * @param {object} row 
 * @param {object} column 
 * @param {number} rowIndex 
 * @param {number} columnIndex 
 * 
 * @return {object} 返回一個對象
 */
getSpan (row, column, rowIndex, columnIndex) {
    // => 把父級的spanMethod函數的堆內存16進制地址賦值給fn
    // => fn就是傳進來的那個函數了
    const fn = this.$parent.spanMethod;
    // => 判斷fn是否爲函數
    // => 是函數:一些操做
    // => 不是函數:則返回一個 {}對象
    if (typeof fn === 'function') {
        // => fn是函數,則調用該函數,傳遞實參集合,把函數的返回結果賦值給result
        const result = fn({
            row,
            column,
            rowIndex,
            columnIndex
        });
        // => 初始值rowspan/colspan都默認爲1
        let rowspan = 1;
        let colspan = 1;
        // => 前面截圖:spanMethod能夠返回一個數組/對象
        // => 判斷拿到的result是否爲數組
        // => 分別獲取到用戶設置的rowspan/colspan的值
        if (Array.isArray(result)) {
            rowspan = result[0];
            colspan = result[1];
        } else if (typeof result === 'object') {
            rowspan = result.rowspan;
            colspan = result.colspan;
        }
        // => 把結果返回去
        return {
            rowspan,
            colspan
        };
    } else {
        return {};
    }
},

/**
 *  展現行/列合併
 * @param {object} row 當前行
 * @param {object} column 當前列
 * @param {number} rowIndex 當前行索引
 * @param {number} columnIndex 當前列索引
 * 
 * @return {boolean} 返回一個布爾值:有,true 沒有 false
 */
showWithSpan (row, column, rowIndex, columnIndex) {
    // => 調用 this.getSpan() 方法,把返回結果賦值給常量 result
    const result = this.getSpan(row, column, rowIndex, columnIndex);

    // => 在result裏面rowspan/colspan任意一個爲0,則取反->true
    // => 最後的結果是返回一個boolean值
    return !(('rowspan' in result && result.rowspan === 0) || ('colspan' in result && result.colspan === 0));
};

△table-body.vue處理rowspan/colspangithub

!(('rowspan' in result && result.rowspan === 0) || ('colspan' in result && result.colspan === 0))編程

(1)運算符優先級(數字越大優先級越高):api

()圓括號20

!邏輯非16

in11

===全等號10

&&邏輯與6

||邏輯或5

(2)A||BA爲真,則返回A;A不爲真,則返回B

A&&BA爲真,則返回B;A不爲真,則返回A

(3)prop in objin判斷對象中是否有屬性prop

(4)==等號:只須要判斷值是否相等,若是兩邊數據類型不一致,先轉換數據類型,再進行判斷

===全等號:嚴格判斷,既判斷數據類型也判斷值,都相等才爲TRUE

(5)爲FALSE的5種狀況:''空字符串、nullundefined0NaN

1 / iview的編程思想

(1)行/列合併是在版本4.0.0加上的——保持原有代碼的基礎上升級:使用了spanMethod方法傳參

(2)行/列合併控制table的td屬性rowspan、colspan,須要指定哪行哪列:數組/對象均可以

(3)在收到指定行/列時,就須要判斷是否爲函數,是函數才能夠被調用;函數的返回結果數組/對象拿到對應的值。

在開發中,遇到的問題:

簡單一句:保證原有的代碼沒問題,還要同時能升級保證2+棵樹的展現

囉嗦的解釋,能夠跳過:

基於iview的Form組件的動態增減表單項功能(https://www.iviewui.com/compo...)在實際應用中作了一個動態生成搜索條件的form表單的組件CustomSearch,包含表單項:Input輸入框、DatePicker日期選擇器 、Select 選擇器、Checkbox多選框、Cascader級聯選擇、Tree樹。

其中,Tree樹組件是基於jQuery的zTree樹插件進行封裝。Tree組件所須要的數據是每一個業務場景裏自行傳入的數據進行渲染的,以各個參數傳參過去的,如::treeData=""渲染樹的數組。

在以前的需求中,搜索條件的tree只有一棵樹就知足了。最初設計CustomSearch組件與Tree組件渲染時,就很繁瑣,只按照一棵樹來渲染了。

在最新的需求裏,須要兩棵樹的搜索條件了,動態請求回來的數據是能夠把DOM渲染出來的。可是對於如何傳參進去渲染不一樣的tree又如何對於點選的搜索條件進行獲取,須要在原有的基礎上進行擴展併兼容原有代碼。

公號ID:zhaoxiajingjing
△14.3 需求:在搜索條件展現2+棵樹

2 / 實踐

一、原有支持1棵樹

搜索條件的數據是封裝在CustomSearch這個組件裏面,可是Tree的數據須要單獨請求,再放進去渲染的。
公號:朝霞的光影筆記
△14.4原有的組件只支持一棵樹的展現

二、如今需求是展現2棵樹

如今須要改爲支持2+棵樹,同時此種方法也支持1棵樹的渲染,兼容以前的寫法,這個在其餘地方用的不少,不能影響別人的使用。

公號ID:zhaoxiajingjing
△14.5業務的CustomSearch高級搜索組件裏面

MOCK模擬的接口地址:

form表單動態數據的接口[ https://www.fastmock.site/moc...]

所屬學院組織樹[https://www.fastmock.site/moc...]

學科書籍樹[https://www.fastmock.site/moc...]

/**
 * 設置tree的初始化數據的芳芳
 * @param [Object] treeJSON 展現tree的form項
 * @param [Array] treeKeys 展現tree的form項的key,
 * @returns {Object} 再把處理好的數據還回去
 */
treeMethod(treeJSON, treeKeys) {
    // 給每個Tree添加初始化信息
    treeKeys.forEach((item, index) => {
        // treeParamsCustom 是一個對象,因此拿到它的堆內存地址便可
        let treeParams = treeJSON[item].formItemClass['treeParamsCustom'];
        // 初始化:treeData數據爲空、默認不展現
        this.$set(treeParams, 'treeData', []);
        this.$set(treeParams, 'isShowTree', false);

        // 請求接口
        this.getTreeData([url], treeParams);
    });

    return treeJSON;
},
/**
 * 獲取Tree的數據
 * @param url 請求接口
 * @param treeParams 須要設置的屬性
 */
getTreeData(url, treeParams) {
    treeParams.isShowTree = false;

    _this.$http.get(url).then((response) => {
        let {data: {data: {treeData=[]}}} = response;
        if (response.processStatus) {
            treeParams.treeData = treeData;
            treeParams.isShowTree = true;
        }
    }, () => {
        treeParams.isShowTree = false;
        console.log('error');
    });
},

△應用:treeMethod方法,放在vue文件的methods對象裏面

//獲取isChecked屬性爲true的數據
getShowData(rows){
    let _this = this;
    _this.items=[];
    for(let i=0;i<rows.length;i++){
        let isChecked = rows[i].customClass.isChecked;
        let title = rows[i].title;
        if(isChecked){
            //當title爲空時,通常都是跟前面的搜索內容相關聯的,若是以前的不展現,則沒有title的也不展現在搜索中
            if(!title && rows[i-1] && rows[i-1].customClass.isChecked){
                _this.items.push(rows[i]);
            }
            //若是是第一個則不與前面的搜索內容關聯
            if(!title && !rows[i-1]){
                _this.items.push(rows[i]);
            }
            //正常的搜索內容,都會帶title,
            if(title){
                _this.items.push(rows[i]);
            }
        }
    }
    /*
        以上代碼主要是在把請求回來的數據處理成Form組件須要的格式
        https://www.iviewui.com/components/form#DTZJBDX
        在此時,把異步請求回來的Tree的數據放進來便可
    */
    if (typeof this.treeMethod === 'function') {
        /**
         * by jing_zhaoxia@sina.com
         * 把tree的信息放在form表單裏面渲染
         * 初始化須要$set
         */
        let treeJSON = {};
        let treeKeys = [];
        // => 篩選出 treeJSON和treeKeys
        // => 設置一個對象做爲命名空間,把用戶操做的數據都放進去,這樣在提交保存時候,能夠統一刪除掉便可
        _this.items.forEach((row, index) => {
            let {formItemClass: {formItemType}, key} = row;
            if (formItemType === 'Tree') {
                treeKeys.push(key);
                treeJSON[key] = row;
                // 補一個自定義的tree參數,後面提交時候能夠刪掉
                this.$set(treeJSON[key].formItemClass, 'treeParamsCustom', {});
            }
        });
        // => 把Tree的數據一一到對應的items裏面
        this.getTreeParams(treeJSON, treeKeys)
            .then(result => {
                if (typeof result !== 'undefined' &&
                    ({}).toString.call(result) === '[object Object]') {
                    _this.items = _this.items.map((item, index) => {
                        if (treeKeys.includes(item.key)) {
                            item = result[item.key];
                        }
                        return item;
                    });
                }
            })
            .catch(err => {
                console.log(err);
            });
    }
},
// => 異步請求,這裏使用 Promise來處理
getTreeParams(treeJSON = {}, treeKeys = []) {
    return new Promise((resolve, reject) => {
        const fn = this.treeMethod;
        let result = treeJSON;
        if (typeof fn === 'function') {
            result = fn(treeJSON, treeKeys);
            if (({}).toString.call(result) === '[object Object]') {
                resolve(result);
            }
        }
        resolve(result);
    });
},

△設置form的items項,放在vue文件的methods對象裏面

公號:朝霞的光影筆記
△14.6兼容之前的代碼

/**
 * 設置Tree組件須要的參數,兼容以前的寫法
 * @param item 每一條form-item
 * @param constructor 當前值的數據類型
 * @param key 須要的key
 */
setTreeParams(item, constructor, key){
    if (item.formItemClass['treeParamsCustom'] !== undefined){
        let value = item.formItemClass['treeParamsCustom'][key];
        return ({}).toString.call(value) === `[object ${constructor}]` ? value : this[key];
    }
    return this[key];
},

△兼容之前的代碼,放在vue文件的methods對象裏面

3 / 預告

跟着jquery大佬學編程思想,尚未想好怎麼寫成文字描述的,先貼一個分享地址吧~

[ https://github.com/jingzhaoxi...]

4 / 參考

運算符優先級[ https://developer.mozilla.org...]

iviewui的Table組件[https://www.iviewui.com/compo...]

iviewui的Form組件[https://www.iviewui.com/compo...]

iviewui源碼[https://github.com/view-desig...]

jQuery的zTree[http://www.treejs.cn/v3/api.php]

公號ID:zhaoxiajingjing

相關文章
相關標籤/搜索