Vue冷门技巧递归组件实践示例详解
JetTsang 人气:0痛点引出
在平时的开发当中,渲染侧边栏导航菜单有时会遇到过需要侧边栏有多层甚至无限层级的问题。此时更优雅的方式便是使用递归组件
<el-menu> <template slot="title"> <i class="el-icon-location"></i> <span>菜单</span> </template> <el-submenu index="1-1"> <template slot="title">子菜单</template> <el-menu-item index="1-1-1">子菜单选项1</el-menu-item> <el-submenu index="1-1-2"> <template slot="title">子菜单的子菜单</template> <el-menu-item index="1-1-2-1">子菜单的子菜单的选项1</el-menu-item> </el-submenu> </el-submenu> </el-submenu> </el-menu>
可以看到这部分重复的代码可以完全抽离出来做单独的组件。
<el-submenu> <el-menu-item></el-menu-item> ... </el-submenu>
那么问题来了,在一层当中又有重复的submenu怎么办?显然这个层级是需要动态生成的。比如:
<el-submenu> <el-menu-item></el-menu-item> ... <el-submenu> <el-menu-item></el-menu-item> ... <el-submenu> <el-menu-item></el-menu-item> ... //这里省略很多很多层 </el-submenu> </el-submenu> </el-submenu>
很明显,这里需要可以用递归(recursive) 的思想来解决, 那么在template模版当中有办法做这样的组件吗?答案当然是可以,template模版语法也是支持递归。
源码中的体现
先找找源码,我们在src/core/global-api.ts当中找到initExtend函数,这个函数是initGlobalAPI的一个执行步骤,每个组件创建的时候都会去调用。
可以看到如果命中name,则会给自己的components的配置项当中注册自己,使得可以在编译的时候可以识别到自己,从而在template模版语法当中去使用。
组件示例封装
首先定义数据结构能描述这样的菜单
[ { id: '1', title: '父菜单', children:[ {id:'1-1',title:'子选项',children:[]}, {id:'1-1',title:'子菜单',children:[ {id:...,title:...,children:...}, ... ]}, ... ] } ] 简单点描述就是 interface item:{ id: string, title: string, children: item[] | [] } item[]
然后开始封装组件
// RescursiveMenu.vue <template> <el-submenu :index="menuItem.id" v-if="menuItem.children.length"> <template slot="title">{{ menuItem.title }}</template> <template v-for="item in menuItem.children"> <RecursiveMenu :menuItem="item"/> </template> </el-submenu> <el-menu-item v-else>{{ menuItem.title }}</el-menu-item> </template> <script> export default { name:"RecursiveMenu", props: { menuItem: Object } } </script>
当然这只是简单示例demo,后续根据业务需求相信难不倒各位看官。
使用:
<el-submenu> <template v-for="item in menuList.children"> <RecursiveMenu :menuItem="item" :key="item.id"/> </template> </el-submenu>
小扩展
同样的,vue也支持jsx/tsx语法 ,使用jsx则需要抽象需要重复的过程,封装成渲染函数来实现递归,这里采用整个数组渲染过程抽象重复,来实现递归。
//MyMenu.jsx export default { name:"RecursiveMenu", props: { menuList: Array, dafault:()=>([]) }, render(){ const recursiveRender = (menuList)=>{ return menuList.map((menuItem)=>{ return menuItem.children.length > 0 ? ( <elSubmenu index="{menuItem.id}"> <div slot="title">{menuItem.title}</div> {recursiveRender(menuItem.children)} </elSubmenu> ):( <elMenuItem key="{menuItem.id}">{ menuItem.title }</elMenuItem> ) } ) } return (<elMenu> {recursiveRender(this.menuList)} </elMenu> ) } }
当然,如果想用jsx复刻上诉template当中抽象的逻辑,可以写成这样:
// RecursiveMenu.jsx export default { name:"RecursiveMenu", props: { menuItem: Object, dafault:()=>({}) }, render(){ const recursiveRender = (menuItem)=>{ return menuItem.children.length > 0 ? ( <elSubmenu index="{menuItem.id}"> <div slot="title">{menuItem.title}</div> {menuItem.children.map(children=>recursiveRender(children))} </elSubmenu> ):( <elMenuItem key="{menuItem.id}">{ menuItem.title }</elMenuItem> ) } return recursiveRender(this.menuItem) } }
总结
Vue当中实现递归渲染,可以使用模版语法和jsx语法。而实现本质上是抽象出重复的逻辑,以及找到递归退出点。
加载全部内容