天天看點

CocosCreator 對UI的引用思考UI和代碼結合的弊端

UI和代碼結合的弊端

1.大量通過界面引用UI節點

如下圖所示,通過腳本元件大量直接引用UI節點,會在相應的描述檔案中記錄相應的key和value,在一定程度上會增加相關描述檔案的大小,進而影響這個描述檔案加載所花費的時間。進一步影響資源加載總時間。

CocosCreator 對UI的引用思考UI和代碼結合的弊端
{
   "__type__": "85d24iUBchG45eRtzPrngjE", 
    "_name": "", 
    "_objFlags": 0, 
    "node": {
        "__id__": 1
    }, 
    "_enabled": true, 
    "label": {
        "__id__": 7
    }, 
    "node1": {
        "__id__": 9
    }, 
    "node2": {
        "__id__": 12
    }, 
    "_id": ""}

           

通過腳本元件大量直接引用UI節點這種方式還有一個弊端,當節點上的腳本元件因某種原因丢失或者說被重置了,那麼還需要被重新挂載一次,如果時間長了,可能都忘記了誰對應的誰。

2.代碼中大量查找節點

第一種方式,代碼中通過 getChildByName, getChildByUuid 函數查到相應的節點, 在此以 getChildByName 函數來舉例。通過下面代碼可以看出,正序周遊查找相應節點的名字,傳回第一個查到的節點。

/**
 * !#en Returns a child from the container given its name.
 * !#zh 通過名稱擷取節點的子節點。
 * @method getChildByName
 * @param {String} name - A name to find the child node.
 * @return {Node} a CCNode object whose name equals to the input parameter
 * @example
 * var child = node.getChildByName("Test Node");
 */
getChildByName (name) {
    if (!name) {
        cc.log("Invalid name");
        return null;
    }
    var locChildren = this._children;
    for (var i = 0, len = locChildren.length; i < len; i++) {
        if (locChildren[i]._name === name)
            return locChildren[i];
    }
    return null;
}
           

舉個例子:如下所示,一個根節點下依次有節點A,節點B,節點C,節點D 四個子節點。現在通過 getChildByName 來擷取 節點D。

--根節點
----子節點A
----子節點B
----子節點C
----子節點D

           

如果現在需要查找子節點A呢:

第一次周遊: 查找子節點A,滿足條件,則傳回子節點A

也就是說 getChildByName 查找子節點A的時間複雜度為1。

如果現在需要查找子節點D呢:

第一次周遊: 查找子節點A,不滿足條件,則繼續周遊查找
第二次周遊: 查找子節點B,不滿足條件,則繼續周遊查找
第三次周遊: 查找子節點C,不滿足條件,則繼續周遊查找
第四次周遊: 查找子節點D,不滿足條件,則傳回子節點D
           

也就是說 getChildByName 查找子節點A的時間複雜度為4。

綜上所述: getChildByName 查找相應的節點的時間複雜度為 N。意味得查找的節點廣度大,所需要的時間越長。代碼中大量通過 getChildByName , getChildByUuid 函數查到相應的節點是一個影響性能的因子。

第二種方式,代碼中通過 cc.find 函數查到相應的節點,從下面代碼可以看出,兩層正序周遊查找相應的節點。第一層正序周遊的因子是傳入的路徑分割。第二層正序周遊的因子是節點的children。

/**
 * Finds a node by hierarchy path, the path is case-sensitive.
 * It will traverse the hierarchy by splitting the path using '/' character.
 * This function will still returns the node even if it is inactive.
 * It is recommended to not use this function every frame instead cache the result at startup.
 *
 * @method find
 * @static
 * @param {String} path
 * @param {Node} [referenceNode]
 * @return {Node|null} the node or null if not found
 */
cc.find = module.exports = function (path, referenceNode) {
    if (path == null) {
        cc.errorID(5600);
        return null;
    }
    if (!referenceNode) {
        var scene = cc.director.getScene();
        if (!scene) {
            if (CC_DEV) {
                cc.warnID(5601);
            }
            return null;
        }
        else if (CC_DEV && !scene.isValid) {
            cc.warnID(5602);
            return null;
        }
        referenceNode = scene;
    }
    else if (CC_DEV && !referenceNode.isValid) {
        cc.warnID(5603);
        return null;
    }
    var match = referenceNode;
    var startIndex = (path[0] !== '/') ? 0 : 1; // skip first '/'
    var nameList = path.split('/');
    // parse path
    for (var n = startIndex; n < nameList.length; n++) {
        var name = nameList[n];
        var children = match._children;
        match = null;
        for (var t = 0, len = children.length; t < len; ++t) {
            var subChild = children[t];
            if (subChild.name === name) {
                match = subChild;
                break;
            }
        }
        if (!match) {
            return null;
        }
    }
    return match;
};

           

舉個例子:如下所示,現在一個場景上的節點關系如下。

-- 場景根節點
---- 子節點A
----- 子節點A1
--- 子節點B
--- 子節點C
----- 子節點C1
--- 子節點D
----- 子節點D1
----- 子節點D2
           

如果現在需要查找子節點A1呢(暫不考慮第二個參數的情況下):

cc.find('子節點A/子節點A1') 開始周遊是 names  = [子節點A,子節點A1]

第一層第一次周遊: 找到子節點A,滿足條件,則進行第二層周遊。

第二層第一次周遊: 找到子節點A1,滿足條件,則傳回。
           

也就是說 cc.find 查找子節點A1 的時間複雜度為2。

如果現在需要查找子節點D2呢(暫不考慮第二個參數的情況下):

cc.find(‘子節點D/子節點D2’) 開始周遊是 names = [子節點D,子節點D2]

第一層第一次周遊: 找到子節點A,不滿足條件,則繼續周遊。

第一層第一次周遊: 找到子節點B,不滿足條件,則繼續周遊。

第一層第一次周遊: 找到子節點C,不滿足條件,則繼續周遊。

第一層第一次周遊: 找到子節點D,滿足條件,則進行第二層周遊。

第二層第一次周遊: 找到子節點D1,不滿足條件,則繼續。

第二層第一次周遊: 找到子節點D2,滿足條件,則傳回。
           

也就是說 cc.find 查找子節點D2的時間複雜度為6

綜上所述: cc.find 查找一次節點的時間複雜度為 C(2 n)=n*(n-1)/(2*1)。意味着查找的節點廣度越大和深度越深所需要的時間越長。代碼中大量通過 cc.find 函數查到相應的節點是一個影響性能的因子。

無論是第一種方式還是第二種方式在代碼中引用相關的節點,如果由于某種原因,修改了節點之間的關系,進而查不到相應的節點引發的錯誤。還需要重新修改查找相應節點的路徑。

3. 大量通過界面引用UI節點 和 代碼中大量查找節點

通過這種方式來查找節點,同時具有上面兩種方法的弊端。更重要的是同時通過界面引用UI節點和代碼中查找UI節點,是不是感覺有點亂。