重庆建设安全员信息网站,做网站自己有模板要花多少钱,全网营销系统怎么样,有关建筑网站建设方案前端组件库造轮子——Tree组件开发教程
前言
本系列旨在记录前端组件库开发经验#xff0c;我们的组件库项目目前已在Github开源#xff0c;下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。 文章旨在总结经验#xff0c;开源分…前端组件库造轮子——Tree组件开发教程
前言
本系列旨在记录前端组件库开发经验我们的组件库项目目前已在Github开源下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。 文章旨在总结经验开源分享有问题的话也希望路过的大佬指正。
组件开发流程
组件递归
Tree组件算是比较有难度的组件了其核心功能其实就是实现树一样的联级结构。其实实现就是组件递归。
我们来复习一下递归代码
我们的递归代码实现必然是由一个函数和调用函数组成的。同理要实现组件递归也需要做类似的操作。
function dfs() {...
}function Main() {dfs()
}在组件递归中我们就需要类比递归函数的操作我们需要用一个组件node来作为递归组件这个组件起到主要渲染的作用并且需要一个tree组件来调用组件执行。
现在知道了大致思路我们在补充一下如何编写组件。
对于递归函数很重要的一点我们如何让他不断递归同时让他停下来。
我们可以利用props把参数传进去然后在渲染的时候去判断有没有孩子如果没有孩子就不渲染这个可以用v-if来完成。
// node 组件中
div v-ifisRender v-showitems.isOpennodev-for(child, index) in items.children:keyindex:itemschild:labellabel:childrenchildren/node
/div// 判断是否要渲染
const isRender computed(() {return (props.items.children props.items.children.length);
});那这样我们就可以实现node组件的正确递归所以我们只需在tree组件中在调用一次node组件就可以了。
div classtreenodev-for(item, index) in copyData:keyindex:itemsitem:labellabel:childrenchildren/node/div深拷贝和初始化
还没完我们需要对传进来的数据做一些深拷贝和初始化。
为什么要深拷贝应该知道吧vue中props是单向数据流我们是不能直接修改的因此我们需要深拷贝一份来操作。
const deepCopy (target: any, hash_table new WeakMap()) {if (typeof target object) {let clone Array.isArray(target) ? [] : {};if (hash_table.get(target)) return hash_table.get(target);hash_table.set(target, clone);for (const key in target) {clone[key] deepCopy(target[key], hash_table);}return clone;} else {return target;}
};为什么要初始化呢因为在开发tree还需要预设置很多数据例如是否展开那需要实现展开的功能那么每个节点必然需要一个isOpen来控制除此之外还有很多其它的功能比如判断层级等。
interface dataType {label: string;children?: dataType[];isOpen: boolean;
}
const copyData ref([]);
onMounted(() {copyData.value init(deepCopy(props.data));
});const init (data: dataType[]) {if (!data.length || !data) return [];let res [];for (let i 0; i data.length; i) {const child data[i];const children init(child[props.children] || []);const label child[props.label];const isOpen false;res.push({label,children,isOpen,});}return res;
};展开和收缩
接下来我们实现如何渲染节点和展开这个其实很简单我们只要在递归组件上面补充我们的想要插入的数据即可同时绑定好事件利用isOpen属性来实现展开收缩我们只需要在渲染v-if上在添加v-show即可。
ul classtree-nodediv classtree-node-content click.stophandleToggle(items)span{{ items.label }}/span/divdiv v-ifisRender v-showitems.isOpennodev-for(child, index) in items.children:keyindex:itemschild:labellabel:childrenchildren/node/div
/ulconst handleToggle (item: any) {item.isOpen !item.isOpen;
}; 参数设置
接下来我们来设置一些参数因为我们不清楚用户传进来的树结构的属性是什么样子的因此我们可以用参数来标识比如用children来标识子节点这些东西就可以自由发挥了。
const props defineProps({data: {type: Array,default: () [],},label: {type: String,default: label,},children: {type: String,default: children,},
});到此为止我们就把核心功能实现完成其实基础的功能并没有多困难后续会补充源码。 懒加载优化
在这里我补充一个优化吧一个简单的懒加载可以是这样的,只渲染第一层深层的如果没有点击过就不去渲染。
这个实现思路也很容易再增加一个isLazy参数在初始化的时候给每个节点绑定上isLazy在渲染时v-if增加判断isLazy就可以了。在点击的时候再把isLazy取消即可。
在参考了element的源码后他们的懒加载还可以传入一个load函数并用isLeft来标识动态加入新的数据。参考链接
其实实现起来也不难我们只需要多传入一个load函数在点击时调用该函数并且new Promise来回调执行即可。
const init (data: dataType[], level: number) {if (!data.length || !data) return [];let res [];for (let i 0; i data.length; i) {const child data[i];const children init(child[props.children] || [], level 1);const label child[props.label];const isOpen false;const isLazy props.isLazy;const isLeft child[isLeft] || false;res.push({label,children,isOpen,isLazy,isLeft,level,});}return res;
};点击后加载数据
const handleToggle async (item: any) {item.isOpen !item.isOpen;if (item.isLazy) {if (item.isLeft props.load) {await new Promise((resolve) {props.load(item, resolve);}).then((res: any) {for (let i 0; i res.length; i) {res[i].isLazy item.isLazy;res[i].level item.level 1;}item.children res.slice();}).catch((err) {console.log([Tree Component] load Funtion Error, err);});}item.isLazy false;}
};演示demo
完整项目demo
结语
Tree组件的核心开发功能就是上面这些其他更多的详细功能开发可以参考Hview-ui项目源码
如果想要了解更多的组件轮子开发或者组件库开发流程更多详细的组件开发过程更新在GitHub项目源码,最后觉得我们项目or文章不错可以点个star点点小手支持一下也欢迎各路大佬为我们的开源项目添砖加瓦。