我们在创建路由正常的写法是这样的模板
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))。 特别提醒!请认真看文档中的 注意 事项
解决思路:
- 从数据中可以看出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,说明路径中没有/
,函数将返回空字符串作为父路径。
- 上面提及到的嵌套路由中,是否注意到官方文档中这段话注意,以
/
开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。 也就是说,我们可以不用在配置嵌套路由的时候在path的开头加上"/",如上面的模板。那么代码实现是这样的
js
const removeLeadingSlash = (path) => {
return path.startsWith("/") ? path.slice(1) : path;
}
函数解析:
removeLeadingSlash
的函数,它接受一个参数path
。函数的目的是移除path
中开头的一个斜杠(如果存在)。- 函数内部首先使用
startsWith
方法检查path
是否以斜杠开头。如果是,则使用slice
方法从path
中移除第一个字符(即斜杠),然后返回修改后的path
;如果不是,则直接返回path
。
*一个问题,删除斜杠这个函数会将所有节点的path开头的第一个斜杠给删除掉。这导致了访问不到路由路径。这里做了一个取舍,在下面转成树形数据的过程中,为了方便对路径path进行创建节点映射,以及构建树形数据需要对父级节点进行一个判断,所以才将所有开头的斜杠删除掉
- 以上条件处理完成之后,接下来将数据转成树形数据
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
,执行以下操作:
- 从
item
中提取path
属性,并将其转换为不带斜杠的路径(称为cleanPath
)。 - 在
map
对象中创建一个新节点,该节点包含item
的所有属性以及path
和children
属性。
接下来,使用data.forEach
遍历data
数组中的每个元素(称为item
)。对于每个item
,执行以下操作:
- 从
item
中提取path
属性,并将其转换为不带斜杠的路径(称为cleanPath
)。 - 在
map
对象中查找与cleanPath
关联的节点。 - 如果找到父节点,则将当前节点添加到父节点的
children
数组中。 - 如果找不到父节点,则将当前节点添加到
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;