大家好,欢迎来到IT知识分享网。
开始切入正题之前,有必要告知大家一下,这篇文章可能有一些深度,初学者可能理解会有些吃力。我会尽量把复杂问题简单化,争取让每个阅读的童鞋们都能看得懂。希望你对element-ui,vue-router,KeepAlive组件有一点了解。现在,我们开始吧。
熟悉element-ui的童鞋们都知道,ElMenuItem和ElSubMenu都需要一个index属性,该属性必须是唯一的。现在,我们想把路由配置直接应用于ElMenu,该如何确保index的唯一性呢?我们需要有一个生成唯一index的函数。如下是genKey函数的定义,是不是很简单?
export const genKey = ((k = 1) => () => k++)()
现在,我们创建addRouteMetaKey函数,该函数对路由树进行递归遍历,为每一个路由配置的meta属性动态添加key字段。这个函数很简单,属于最基础的递归使用例子,我就不做太多解释了。
提示:数组的forEach方法不是纯函数,因为它有副作用,所以forEach方法不能称之为函数式编程工具。
/ @param {import('vue-router').RouteRecordRaw[]} routes */ const addRouteMetaKey = routes => { routes.forEach(route => { if (route.meta) { route.meta.key = genKey() } else { route.meta = { key: genKey() } } route.children && addRouteMetaKey(route.children) }) return routes }
通过addRouteMetaKey函数,我们可以把路由的meta.key作为index的值了。
现在,我们想实现另一个功能,就是基于标签页的路由组件缓存控制。使用过Vuejs KeepAlive组件的童鞋们,应该多多少少都遇到一些坑吧?在我们的项目中,有一个顶部标签页导航,每个tab项对应一个url,以每个url对应路由的title作为tab项标题,当切换tab的时候加载缓存中的url对应的路由组件,关闭tab项,下次打开该路由url,重新挂载对应的路由组件,而不是从缓存中加载。
当路由层级不确定的时候,KeepAlive会变的难以手动控制。所以,我剑走偏锋,尝试了一种简单的解决方案,实践证明:这是非常有效的。我的方案就是把无限层级的路由树转化为一维数组。通过为meta添加必要的字段,进行页面结构个性化定制。
我们需要把每层路由配置的path转化为全路径,所以需要一个函数:getRouteFullPath,该函数定义如下:
/ * 获取路由全路径 * @description 如果path以 / 开头(属于绝对路径),则直接返回,否则拼接路径 * @param {string} path * @param {string} parentPath */ export const getRouteFullPath = (path, parentPath) => { return !parentPath || path.startsWith('/') ? path : joinPath(parentPath, path) }
getRouteFullPath函数中使用到的joinPath函数用于将多个路径字符串拼接为1个完整的路径,定义如下:
/ @param {string[]} paths */ export const joinPath = (...paths) => { const j = '/' const [a, ...b] = paths return (a.endsWith('/') ? a.replace(/(\/+)$/gm, j) : a + j) + b.map(_ => _.replace(/^(\/+)|(\/+)$/gm, '')).join(j) }
现在,我们把路由树转化为一维数组。我们定义toFlatRoutes函数,该函数使用了数组的reduce方法对路由树进行聚合递归,将路由配置中的path属性的值替换为全路径,还顺便给路由配置添加了name属性,返回一个新的一维路由配置数组。这是函数式编程和递归结合的一个例子。
/ * @param {import('vue-router').RouteRecordRaw[]} routes * @returns {import('vue-router').RouteRecordRaw[]} */ const toFlatRoutes = (routes, parentPath) => { return routes.reduce((t, r) => { const path = getRouteFullPath(r.path, parentPath) return [ ...t, ...(r.children ? toFlatRoutes(r.children, path) : [{ ...r, path, name: r.name || `name-${genKey()}` }] ) ] }, []) }
以上,我们解决了KeepAlive的缓存控制问题;现在,我们又有了一个新的用户体验上的需求,就是我们想根据url对应的路由,自动展开该路由meta.key所属的侧边菜单;我们通过查阅element-ui文档得知,ElMenu有一个open方法,接收index作为参数,展开index对应的菜单。
现在的问题是,我们的路由对应的index是ElMenuItem的,而路由树已经被我们转化为了一维数组,通过路由的matched字段是得不到我们想要的菜单index的。所以,我们需要遍历原始路由树。
如下代码,我们定义getMenuKey函数,该函数接收的参数为route对象。如果路由存在,我们进行查找。首先进行简单查找,如果找到一个菜单menu,则返回该菜单的meta.key;如果简单查找无果,则对路由树进行递归查找;这是函数式编程和递归结合的另一个例子。
/ * @param {import('vue-router').RouteRecordRaw} item * @returns {number|undefined} */ export const getMenuKey = item => { if (item) { const menu = state.menus.find(m => item.path.startsWith(m.path)) if (menu) { return menu.meta.key } const itemKey = item.meta.key return (function findFunc (menus) { / @param {import('vue-router').RouteRecordRaw} m */ const fn = m => m.meta.key === itemKey ? true : m.children && m.children.some(fn) const curMenu = menus.find(fn) return curMenu && curMenu.meta.key })(state.menus) // state是响应式对象,定义:const state = reactive({ menus: [] }) } }
现在,我们大功告成了;以上就是本节的知识点,童鞋们理解了吗?只要我们熟悉递归的使用,其实操作树很简单。如果大家还有不懂的,可以评论区问我。感谢阅读!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/96888.html