IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    高性能分组列表设计(1)

    Ashes Born\'s Blog发表于 2021-07-28 10:27:27
    love 0
    • 高性能分组列表设计
      • 整体目标
      • 分析
      • 数据结构设计
      • 算法选择
        • 一维对象数组转换成嵌套结构设计:
      • 具体实现
        • 一维对象数组转换成嵌套结构实现:

    高性能分组列表设计#

    整体目标#

    1. 分组存在嵌套关系,且深度无理论上限
    2. 可以通过拖拽,将已分组元素拖出,接触分组关系
    3. 可以通过拖拽,将未分组元素拖入分组内,建立新的分组关系
    4. 未分组列表项移动时,会自动越过分组及其子组件
    5. 未分组列表项进行分组时,应保持分组前的相对顺序
    6. 已分组列表项,在解除分组时,应保持分组前的相对顺序
    7. 以上操作对直接操作分组时,也应有效(这里将分组也作为一个列表项进行操作) ## 分析
    • 由于目标1&5,数据结构应保持一维结构,即对象数组的形式。这样的数据结构,提供了列表项的基础顺序,方便在创建分组时保持列表项的相对顺序。
    • 对于目标2&3&5&6,在计算拖拽项是否建立/更新/删除分组关系时,应记录已分组列表项在组内的相对位置,方便在分组关系变化时,对列表的位置进行排序
    • 对于目标7,应把分组也作为列表项之一。提供“type”字段作为分组列表项和其他列表的区别,为后续可能拓展分组的展开/收起功能
    • 渲染列表时会使用一个多维结构的数据,方便递归的对列表进行渲染,对于jsx语法友好。 ## 数据结构设计

    列表项数据结构

    1
    2
    3
    4
    interface ListItem {
    code: string;
    groupCode: string;
    }

    列表数据结构

    1
    type List = ListImte[]

    更新分组时的辅助数据结构

    1
    2
    3
    4
    5
    type GroupStack = {
    groupCode: string;
    index: number; // 分组真实的下标
    offsetNumber: number // 分组的长度,方便记录分组内列表项的相对位置
    }[]

    用于react渲染的数据结构

    1
    2
    3
    4
    5
    interface AssistStruct {
    code: string;
    children?: AssistStruct[];
    parentGroupCode?: string; //pop stack flag
    }

    算法选择#

    一维对象数组转换成嵌套结构设计:#

    检测分组闭合,算法属于括号闭合算法的变种。

    使用栈记录,未闭合的分组code。当前列表项中的group-code字段与栈顶的code不相等时,表示分组闭合,并且弹出当前栈顶元素。

    具体实现#

    一维对象数组转换成嵌套结构实现:#

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    /**
    * 将一维数组转成多层结构
    * @param compCodes 所有组件的code
    * @param compDatas 所有组件的数据
    * @returns 返回和code相关的嵌套结构、
    */
    const subList = (compCodes: string[], compDatas: JDV.State['compDatas']): AssistStruct[] => {
    let groupStack: GroupStack[] = [];
    const resultData: AssistStruct[] = [];

    const stackPop = (groupCode?: string) => {
    let len = groupStack.length - 1;
    while (len >= 0) {
    if (groupStack[len].groupCode !== groupCode) {
    groupStack.pop();
    } else {
    break;
    }
    len--;
    }
    };

    const setResult = (result: AssistStruct[], groupStack: GroupStack[], groupCode: string, value: AssistStruct) => {
    groupStack.forEach((item, index) => {
    if (!result) {
    return null;
    }
    if (!result[item.index]) {
    return;
    }
    if (result[item.index].code !== groupCode) {
    // 如果当前组件的分组不等于结果中的key,向下搜索
    return setResult(result[item.index].children as AssistStruct[], groupStack.slice(index + 1), groupCode, value);
    } else {
    if (result[item.index].children) {
    (result[item.index].children as AssistStruct[]).push(value);
    item.offsetNumber += 1;
    } else {
    result[item.index].children = [value];
    }
    }
    });
    };

    compCodes.forEach((item, index) => {
    const hasGroup = compDatas[item] ? compDatas[item].config.groupCode : undefined;
    stackPop(hasGroup);
    if (compDatas[item].compCode === 'group') {
    if (hasGroup) {
    // 如果当前组件的父组件在栈顶,更新结果树
    setResult(resultData, groupStack.slice(0), hasGroup, {
    code: item,
    children: [],
    });

    //如果当前分组有父分组,此时分组栈一定不为空,分组索引为父分组长度-1
    // debugger;
    groupStack.push({
    groupCode: item,
    index: groupStack.length ? groupStack[groupStack.length - 1].offsetNumber - 1 : index,
    offsetNumber: 0,
    });
    } else {
    groupStack = []; //没有分组,清空栈
    resultData.push({
    code: item,
    children: [],
    });
    //如果当前分组没有父分组,此时分组栈一定为空,分组索引为结果长度
    groupStack.push({
    groupCode: item,
    index: resultData.length - 1,
    offsetNumber: 0,
    });
    }
    } else {
    if (hasGroup) {
    // 如果当前组件的父组件在栈顶,更新结果树
    setResult(resultData, groupStack.slice(0), hasGroup, {
    code: item,
    });
    } else {
    groupStack = []; //没有分组,清空栈
    resultData.push({
    code: item,
    });
    }
    }
    });
    return resultData;


沪ICP备19023445号-2号
友情链接