vue3+vite路由篇

我们在创建路由正常的写法是这样的模板

js 复制代码
import { createRouter, createWebHistory, type RouteRecordRaw,} from "vue-router";

const routes: RouteRecordRaw[] = [
    {
        path: "/",
        redirect: (to) => {
          return { path: "/MasterPage" };
        }
    },
    {
        path: "/:type",
        component: () => import("@/views/index.vue"),
    },
    {
        name:'home',
        path:'/home',
        component: () => import("@/views/home/index.vue"),
        children:[
            {
                name:'Next',
                path:'Next',
                component: () => import("@/views/home/Next/index.vue"),
            }
        ]
    },
    {
        name:'Next',
        path:'/Next',
        component: () => import("@/views/Next/index.vue")
    }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
});

export default router

有没有发现,每次需要创建路由的时候都需要去写这些路由配置。而且当我们文件的路径变动的时候,同时也要修改路由文件下的配置,这显得比较繁琐

import.meta.glob 能够解决这些繁琐的事情

在vite官方文档中是对import.meta.glob这样描述的:

进入正题~~ 以下暂且创建三层目录结构

js 复制代码
目录结构
src
|- views 
     |- index.vue /** @/views/index.vue */
     |- home
         |--- index.vue /** @/views/home/index.vue */
         |--- Next
               |--- index.vue /** @/views/home/Next/index.vue */

router/index.ts

js 复制代码
import {
  createRouter,
  createWebHistory,
  type RouteRecordRaw,
} from "vue-router";
// 这里做成映射是为了后面方便拓展,假设想对component进行拓展需要写正则,则在map写进行添加,使得方便阅读
const map = {
  views: /views\/(.*?)\/index\.vue/, /** 匹配views下所创建的文件中的index.vue,既是上面目录结构注释 进行匹配*/
};
// 获取views下所有文件,转成route所需字段格式
const module = import.meta.glob("../views/**/*.vue"); // 一个* 表示当前的文件, 两个 * 表示当前的以及嵌套的文件

const pages = Object.entries(module).map(([path, comp]) => {
  const match = path.match(map["views"]) || "";
  const target = (match && match[1]) || "";
  let name = target.split("/").pop() || "MasterPage";
  return {
    name,
    path: `/${target}`,
    component: comp,
  };
});

这里只能获取到'@/views/index.vue'、'@/views/home/index.vue',并不能获取到 home->Next->index.vue,这里涉及到了嵌套路由

嵌套路由

以官方文档的示例为标准,为了更好的理解嵌套路由。请[移步](嵌套路由 | Vue Router (vuejs.org))。 特别提醒!请认真看文档中的 注意 事项

解决思路:

  1. 从数据中可以看出path的层级,我们可以通过层级判断谁是父级节点
js 复制代码
// 获取父级节点
const getParentPath = (path) => {
  const lastSlashIndex = path.lastIndexOf("/");
  if (lastSlashIndex > 0) {
    return path.slice(0, lastSlashIndex);
  } else if (lastSlashIndex === 0) {
    return "/";
  } else {
    return "";
  }
}

函数解析:

  • getParentPath的函数,它接受一个参数path。函数的主要目的是从给定的路径中提取父路径。
  • 函数内部首先使用lastIndexOf方法查找路径中最后一个/的位置。lastIndexOf方法返回指定字符在字符串中最后一次出现的位置,如果没有找到指定字符,则返回-1。
  • 如果lastSlashIndex大于0,说明路径中包含至少一个/,函数将使用slice方法从路径中提取从第一个/到倒数第二个/之间的部分,作为父路径。
  • 如果lastSlashIndex等于0,说明路径中只包含一个/,函数将返回/作为父路径。
  • 如果lastSlashIndex等于-1,说明路径中没有/,函数将返回空字符串作为父路径。
  1. 上面提及到的嵌套路由中,是否注意到官方文档中这段话注意,以 / 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。 也就是说,我们可以不用在配置嵌套路由的时候在path的开头加上"/",如上面的模板。那么代码实现是这样的
js 复制代码
const removeLeadingSlash = (path) => {
  return path.startsWith("/") ? path.slice(1) : path;
}

函数解析:

  • removeLeadingSlash的函数,它接受一个参数path。函数的目的是移除path中开头的一个斜杠(如果存在)。
  • 函数内部首先使用startsWith方法检查path是否以斜杠开头。如果是,则使用slice方法从path中移除第一个字符(即斜杠),然后返回修改后的path;如果不是,则直接返回path

*一个问题,删除斜杠这个函数会将所有节点的path开头的第一个斜杠给删除掉。这导致了访问不到路由路径。这里做了一个取舍,在下面转成树形数据的过程中,为了方便对路径path进行创建节点映射,以及构建树形数据需要对父级节点进行一个判断,所以才将所有开头的斜杠删除掉

  1. 以上条件处理完成之后,接下来将数据转成树形数据
js 复制代码
const buildTree = (data) => {
  let tree = [];
  const map = {};

   // 创建节点映射
   data.forEach(item => {
    const { path } = item;
    const cleanPath = removeLeadingSlash(path);
    map[cleanPath] = { ...item, path: cleanPath, children: [] };
  });

  // 构建树形结构
  data.forEach(item => {
    const { path } = item;
    const cleanPath = removeLeadingSlash(path);
    const node = map[cleanPath];
    const parentPath = getParentPath(cleanPath);
    if (parentPath) {
      const parent = map[parentPath];
      if (parent) {
        const childPath = parentPath === "/" ? cleanPath : cleanPath.substring(parentPath.length + 1);
        parent.children.push({ ...node, path: childPath });
      }
    } else {
      tree.push({ ...node });
    }
  });
  tree = tree.map(item => {
    return {
      ...item,
      path: `/${item.path}` // 处理将开头所有删除斜杠问题,让父级节点path带上斜杠
      
    }
  })
  return tree;
}

函数解析:

定义了一个名为buildTree的函数,它接收一个名为data的参数。data是一个包含节点信息的数组。函数的目的是根据给定的数据构建一棵树形结构。

函数内部首先创建了一个名为tree的空数组和一个名为map的空对象。然后,使用data.forEach遍历data数组中的每个元素(称为item)。对于每个item,执行以下操作:

  1. item中提取path属性,并将其转换为不带斜杠的路径(称为cleanPath)。
  2. map对象中创建一个新节点,该节点包含item的所有属性以及pathchildren属性。

接下来,使用data.forEach遍历data数组中的每个元素(称为item)。对于每个item,执行以下操作:

  1. item中提取path属性,并将其转换为不带斜杠的路径(称为cleanPath)。
  2. map对象中查找与cleanPath关联的节点。
  3. 如果找到父节点,则将当前节点添加到父节点的children数组中。
  4. 如果找不到父节点,则将当前节点添加到tree数组中。

最后,对tree数组进行处理,将每个节点的path属性开头添加斜杠/,并将处理后的节点返回。

请看完整代码

js 复制代码
import {
  createRouter,
  createWebHistory,
  type RouteRecordRaw,
} from "vue-router";

const map = {
  views: /views\/(.*?)\/index\.vue/,
};
// 获取views下所有文件,转成route所需字段格式
const module = import.meta.glob("../views/**/*.vue"); // 一个* 表示当前的文件, 两个 * 表示当前的以及嵌套的文件

const pages = Object.entries(module).map(([path, comp]) => {
  const match = path.match(map["views"]) || "";
  const target = (match && match[1]) || "";
  let name = target.split("/").pop() || "MasterPage";
  return {
    name,
    path: `/${target}`,
    component: comp,
  };
});

 // 获取父级路径
 const getParentPath = (path) => {
  const lastSlashIndex = path.lastIndexOf("/");
  if (lastSlashIndex > 0) {
    return path.slice(0, lastSlashIndex);
  } else if (lastSlashIndex === 0) {
    return "/";
  } else {
    return "";
  }
}
// 删除斜杠/
const removeLeadingSlash = (path) => {
  return path.startsWith("/") ? path.slice(1) : path;
}
// 转成树形数据 包含children
const buildTree = (data) => {
  let tree = [];
  const map = {};

   // 创建节点映射
   data.forEach(item => {
    const { path } = item;
    const cleanPath = removeLeadingSlash(path);
    map[cleanPath] = { ...item, path: cleanPath, children: [] };
  });

  // 构建树形结构
  data.forEach(item => {
    const { path } = item;
    const cleanPath = removeLeadingSlash(path);
    const node = map[cleanPath];
    const parentPath = getParentPath(cleanPath);
    if (parentPath) {
      const parent = map[parentPath];
      if (parent) {
        const childPath = parentPath === "/" ? cleanPath : cleanPath.substring(parentPath.length + 1);
        parent.children.push({ ...node, path: childPath });
      }
    } else {
      tree.push({ ...node });
    }
  });
  tree = tree.map(item => {
    return {
      ...item,
      path: `/${item.path}` // 处理将开头所有删除斜杠,到时父级节点path没有斜杠/
      
    }
  })
  return tree;
}

const menuRouter = buildTree(pages)
const routes: RouteRecordRaw[] = [
  {
    path: "/",
    redirect: (to) => {
      return { path: "/MasterPage" };
    },
  },
  {
    path: "/:type",
    component: () => import("@/views/index.vue"),
  },
  ...menuRouter,
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
});

export default router;
相关推荐
庸俗今天不摸鱼2 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下9 分钟前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox19 分钟前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞22 分钟前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行22 分钟前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_5937581023 分钟前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周27 分钟前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei1 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring