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;
相关推荐
Martin -Tang7 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发8 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端