[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frouter":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frouter":3555,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frouter":3581},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":3551,"stem":3552,"id":3553,"extension":3554},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frouter","路由与权限控制","Vue Router 4 是 Vue 官方路由库，与 Vue 3 配套使用。见 Vue 基础。","技能小册",[12,13],"vue","router","2021-09-12",false,{"type":17,"value":18,"toc":3539},"minimark",[19,22,38,41,46,49,54,175,200,203,382,386,389,406,564,575,700,704,707,711,740,855,859,866,991,995,1004,1020,1119,1127,1141,1170,1330,1333,1337,1343,1478,1485,1495,1646,1649,1656,1727,1730,1741,1963,1967,1971,1977,2037,2041,2048,2116,2127,2131,2189,2193,2208,2432,2438,2442,2448,2650,2656,2660,2715,2728,2732,2741,3326,3330,3466,3469,3472,3475,3536],[20,21,8],"h1",{"id":8},[23,24,25,32,33,37],"p",{},[26,27,31],"a",{"href":28,"rel":29},"https:\u002F\u002Frouter.vuejs.org\u002F",[30],"nofollow","Vue Router 4"," 是 Vue 官方路由库，与 Vue 3 配套使用。见 ",[26,34,36],{"href":35},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fbasics","Vue 基础","。",[23,39,40],{},"Vue Router 是 Vue.js 官方的路由管理器，与 Vue 3 深度集成。合理运用动态路由、导航守卫、元信息、懒加载等特性，可以构建具有权限控制、优雅过渡、良好性能的单页应用。",[42,43,45],"h3",{"id":44},"一动态路由","一、动态路由",[23,47,48],{},"动态路由允许将路径中的一部分作为参数传递给组件，实现同一组件对应不同内容。",[50,51,53],"h4",{"id":52},"_1-路由参数的匹配模式","1. 路由参数的匹配模式",[55,56,57,80],"table",{},[58,59,60],"thead",{},[61,62,63,67,70,73],"tr",{},[64,65,66],"th",{},"模式",[64,68,69],{},"定义方式",[64,71,72],{},"匹配示例",[64,74,75,79],{},[76,77,78],"code",{},"route.params","（组合式 API）",[81,82,83,104,131,156],"tbody",{},[61,84,85,89,94,99],{},[86,87,88],"td",{},"必选参数",[86,90,91],{},[76,92,93],{},":id",[86,95,96],{},[76,97,98],{},"\u002Fuser\u002F123",[86,100,101],{},[76,102,103],{},"{ id: '123' }",[61,105,106,109,114,123],{},[86,107,108],{},"可选参数",[86,110,111],{},[76,112,113],{},":id?",[86,115,116,119,120],{},[76,117,118],{},"\u002Fuser"," 或 ",[76,121,122],{},"\u002Fuser\u002F456",[86,124,125,119,128],{},[76,126,127],{},"{}",[76,129,130],{},"{ id: '456' }",[61,132,133,136,141,146],{},[86,134,135],{},"通配（catch-all）",[86,137,138],{},[76,139,140],{},"\u002F:pathMatch(.*)*",[86,142,143],{},[76,144,145],{},"\u002Ffiles\u002Fa\u002Fb\u002Fc",[86,147,148,151,152,155],{},[76,149,150],{},"{ pathMatch: ['a','b','c'] }","（数组，需 ",[76,153,154],{},"props"," 转换时可用字符串）",[61,157,158,161,166,170],{},[86,159,160],{},"自定义正则",[86,162,163],{},[76,164,165],{},":id(\\\\d+)",[86,167,168],{},[76,169,98],{},[86,171,172,174],{},[76,173,103],{},"（非数字不匹配）",[176,177,178],"blockquote",{},[23,179,180,181,185,186,189,190,193,194,199],{},"Vue Router 4 ",[182,183,184],"strong",{},"已移除"," Vue Router 3 的 ",[76,187,188],{},"*","、",[76,191,192],{},"+"," 重复参数语法，通配须使用带自定义正则的参数名（见 ",[26,195,198],{"href":196,"rel":197},"https:\u002F\u002Frouter.vuejs.org\u002Fzh\u002Fguide\u002Fmigration\u002F",[30],"官方迁移说明","）。",[23,201,202],{},"示例路由配置：",[204,205,210],"pre",{"className":206,"code":207,"language":208,"meta":209,"style":209},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","const routes = [\n  { path: \"\u002Fuser\u002F:id\", component: User },\n  { path: \"\u002Fuser\u002F:id?\", component: UserOptional },\n  { path: \"\u002Ffiles\u002F:pathMatch(.*)*\", name: \"files\", component: Files },\n  { path: \"\u002Fpost\u002F:id(\\\\d+)\", component: Post },\n];\n","js","",[76,211,212,233,272,299,340,373],{"__ignoreMap":209},[213,214,217,221,225,229],"span",{"class":215,"line":216},"line",1,[213,218,220],{"class":219},"sbsja","const",[213,222,224],{"class":223},"s_hVV"," routes",[213,226,228],{"class":227},"smGrS"," =",[213,230,232],{"class":231},"su5hD"," [\n",[213,234,236,240,244,247,251,255,258,261,264,266,269],{"class":215,"line":235},2,[213,237,239],{"class":238},"sP7_E","  {",[213,241,243],{"class":242},"skxfh"," path",[213,245,246],{"class":238},":",[213,248,250],{"class":249},"sjJ54"," \"",[213,252,254],{"class":253},"s_sjI","\u002Fuser\u002F:id",[213,256,257],{"class":249},"\"",[213,259,260],{"class":238},",",[213,262,263],{"class":242}," component",[213,265,246],{"class":238},[213,267,268],{"class":231}," User ",[213,270,271],{"class":238},"},\n",[213,273,275,277,279,281,283,286,288,290,292,294,297],{"class":215,"line":274},3,[213,276,239],{"class":238},[213,278,243],{"class":242},[213,280,246],{"class":238},[213,282,250],{"class":249},[213,284,285],{"class":253},"\u002Fuser\u002F:id?",[213,287,257],{"class":249},[213,289,260],{"class":238},[213,291,263],{"class":242},[213,293,246],{"class":238},[213,295,296],{"class":231}," UserOptional ",[213,298,271],{"class":238},[213,300,302,304,306,308,310,313,315,317,320,322,324,327,329,331,333,335,338],{"class":215,"line":301},4,[213,303,239],{"class":238},[213,305,243],{"class":242},[213,307,246],{"class":238},[213,309,250],{"class":249},[213,311,312],{"class":253},"\u002Ffiles\u002F:pathMatch(.*)*",[213,314,257],{"class":249},[213,316,260],{"class":238},[213,318,319],{"class":242}," name",[213,321,246],{"class":238},[213,323,250],{"class":249},[213,325,326],{"class":253},"files",[213,328,257],{"class":249},[213,330,260],{"class":238},[213,332,263],{"class":242},[213,334,246],{"class":238},[213,336,337],{"class":231}," Files ",[213,339,271],{"class":238},[213,341,343,345,347,349,351,354,357,360,362,364,366,368,371],{"class":215,"line":342},5,[213,344,239],{"class":238},[213,346,243],{"class":242},[213,348,246],{"class":238},[213,350,250],{"class":249},[213,352,353],{"class":253},"\u002Fpost\u002F:id(",[213,355,356],{"class":223},"\\\\",[213,358,359],{"class":253},"d+)",[213,361,257],{"class":249},[213,363,260],{"class":238},[213,365,263],{"class":242},[213,367,246],{"class":238},[213,369,370],{"class":231}," Post ",[213,372,271],{"class":238},[213,374,376,379],{"class":215,"line":375},6,[213,377,378],{"class":231},"]",[213,380,381],{"class":238},";\n",[50,383,385],{"id":384},"_2-路由参数的响应式监听","2. 路由参数的响应式监听",[23,387,388],{},"在组件内部，需要监听路由参数变化以重新获取数据。组合式 API 提供两种方式：",[390,391,392],"ul",{},[393,394,395,405],"li",{},[182,396,397,398,401,402],{},"使用 ",[76,399,400],{},"watch"," 监听 ",[76,403,404],{},"$route","：",[204,407,409],{"className":206,"code":408,"language":208,"meta":209,"style":209},"import { useRoute } from \"vue-router\";\nimport { watch } from \"vue\";\nconst route = useRoute();\nwatch(\n  () => route.params.id,\n  (newId, oldId) => {\n    \u002F\u002F 当 id 变化时重新获取数据\n    fetchData(newId);\n  },\n);\n",[76,410,411,438,459,476,483,507,529,536,551,557],{"__ignoreMap":209},[213,412,413,417,420,423,426,429,431,434,436],{"class":215,"line":216},[213,414,416],{"class":415},"sVHd0","import",[213,418,419],{"class":238}," {",[213,421,422],{"class":231}," useRoute",[213,424,425],{"class":238}," }",[213,427,428],{"class":415}," from",[213,430,250],{"class":249},[213,432,433],{"class":253},"vue-router",[213,435,257],{"class":249},[213,437,381],{"class":238},[213,439,440,442,444,447,449,451,453,455,457],{"class":215,"line":235},[213,441,416],{"class":415},[213,443,419],{"class":238},[213,445,446],{"class":231}," watch",[213,448,425],{"class":238},[213,450,428],{"class":415},[213,452,250],{"class":249},[213,454,12],{"class":253},[213,456,257],{"class":249},[213,458,381],{"class":238},[213,460,461,463,466,468,471,474],{"class":215,"line":274},[213,462,220],{"class":219},[213,464,465],{"class":223}," route",[213,467,228],{"class":227},[213,469,422],{"class":470},"sGLFI",[213,472,473],{"class":231},"()",[213,475,381],{"class":238},[213,477,478,480],{"class":215,"line":301},[213,479,400],{"class":470},[213,481,482],{"class":231},"(\n",[213,484,485,488,491,493,496,499,501,504],{"class":215,"line":342},[213,486,487],{"class":238},"  ()",[213,489,490],{"class":219}," =>",[213,492,465],{"class":231},[213,494,495],{"class":238},".",[213,497,498],{"class":231},"params",[213,500,495],{"class":238},[213,502,503],{"class":231},"id",[213,505,506],{"class":238},",\n",[213,508,509,512,516,518,521,524,526],{"class":215,"line":375},[213,510,511],{"class":238},"  (",[213,513,515],{"class":514},"s99_P","newId",[213,517,260],{"class":238},[213,519,520],{"class":514}," oldId",[213,522,523],{"class":238},")",[213,525,490],{"class":219},[213,527,528],{"class":238}," {\n",[213,530,532],{"class":215,"line":531},7,[213,533,535],{"class":534},"sutJx","    \u002F\u002F 当 id 变化时重新获取数据\n",[213,537,539,542,545,547,549],{"class":215,"line":538},8,[213,540,541],{"class":470},"    fetchData",[213,543,544],{"class":242},"(",[213,546,515],{"class":231},[213,548,523],{"class":242},[213,550,381],{"class":238},[213,552,554],{"class":215,"line":553},9,[213,555,556],{"class":238},"  },\n",[213,558,560,562],{"class":215,"line":559},10,[213,561,523],{"class":231},[213,563,381],{"class":238},[390,565,566],{},[393,567,568,574],{},[182,569,570,571],{},"使用导航守卫 ",[76,572,573],{},"onBeforeRouteUpdate","（推荐，避免重复获取数据）：",[204,576,578],{"className":206,"code":577,"language":208,"meta":209,"style":209},"import { onBeforeRouteUpdate } from \"vue-router\";\nonBeforeRouteUpdate(async (to, from) => {\n  if (to.params.id !== from.params.id) {\n    await fetchData(to.params.id);\n  }\n});\n",[76,579,580,601,626,662,686,691],{"__ignoreMap":209},[213,581,582,584,586,589,591,593,595,597,599],{"class":215,"line":216},[213,583,416],{"class":415},[213,585,419],{"class":238},[213,587,588],{"class":231}," onBeforeRouteUpdate",[213,590,425],{"class":238},[213,592,428],{"class":415},[213,594,250],{"class":249},[213,596,433],{"class":253},[213,598,257],{"class":249},[213,600,381],{"class":238},[213,602,603,605,607,610,613,616,618,620,622,624],{"class":215,"line":235},[213,604,573],{"class":470},[213,606,544],{"class":231},[213,608,609],{"class":219},"async",[213,611,612],{"class":238}," (",[213,614,615],{"class":514},"to",[213,617,260],{"class":238},[213,619,428],{"class":514},[213,621,523],{"class":238},[213,623,490],{"class":219},[213,625,528],{"class":238},[213,627,628,631,633,635,637,639,641,643,646,648,650,652,654,656,659],{"class":215,"line":274},[213,629,630],{"class":415},"  if",[213,632,612],{"class":242},[213,634,615],{"class":231},[213,636,495],{"class":238},[213,638,498],{"class":231},[213,640,495],{"class":238},[213,642,503],{"class":231},[213,644,645],{"class":227}," !==",[213,647,428],{"class":231},[213,649,495],{"class":238},[213,651,498],{"class":231},[213,653,495],{"class":238},[213,655,503],{"class":231},[213,657,658],{"class":242},") ",[213,660,661],{"class":238},"{\n",[213,663,664,667,670,672,674,676,678,680,682,684],{"class":215,"line":301},[213,665,666],{"class":415},"    await",[213,668,669],{"class":470}," fetchData",[213,671,544],{"class":242},[213,673,615],{"class":231},[213,675,495],{"class":238},[213,677,498],{"class":231},[213,679,495],{"class":238},[213,681,503],{"class":231},[213,683,523],{"class":242},[213,685,381],{"class":238},[213,687,688],{"class":215,"line":342},[213,689,690],{"class":238},"  }\n",[213,692,693,696,698],{"class":215,"line":375},[213,694,695],{"class":238},"}",[213,697,523],{"class":231},[213,699,381],{"class":238},[42,701,703],{"id":702},"二路由导航守卫体系","二、路由导航守卫体系",[23,705,706],{},"Vue Router 4 提供了三种类型的守卫，用于在跳转前后执行逻辑，如权限验证、页面标题修改、数据预加载等。",[50,708,710],{"id":709},"_1-全局守卫","1. 全局守卫",[390,712,713,724,732],{},[393,714,715,720,721,37],{},[182,716,717],{},[76,718,719],{},"beforeEach","：在导航触发时调用，",[182,722,723],{},"适合进行全局权限检查",[393,725,726,731],{},[182,727,728],{},[76,729,730],{},"beforeResolve","：在所有组件内守卫和异步路由组件被解析之后、导航被确认之前调用。",[393,733,734,739],{},[182,735,736],{},[76,737,738],{},"afterEach","：导航成功完成后调用，不可改变导航，适合打点统计、页面标题更新。",[204,741,743],{"className":206,"code":742,"language":208,"meta":209,"style":209},"router.beforeEach((to, from) => {\n  \u002F\u002F 返回 false 取消导航\n  \u002F\u002F 返回 { name: 'Login' } 重定向到登录页\n  if (!isAuthenticated && to.meta.requiresAuth) {\n    return { name: \"Login\" };\n  }\n  return true;\n});\n",[76,744,745,769,774,779,811,832,836,847],{"__ignoreMap":209},[213,746,747,749,751,753,755,757,759,761,763,765,767],{"class":215,"line":216},[213,748,13],{"class":231},[213,750,495],{"class":238},[213,752,719],{"class":470},[213,754,544],{"class":231},[213,756,544],{"class":238},[213,758,615],{"class":514},[213,760,260],{"class":238},[213,762,428],{"class":514},[213,764,523],{"class":238},[213,766,490],{"class":219},[213,768,528],{"class":238},[213,770,771],{"class":215,"line":235},[213,772,773],{"class":534},"  \u002F\u002F 返回 false 取消导航\n",[213,775,776],{"class":215,"line":274},[213,777,778],{"class":534},"  \u002F\u002F 返回 { name: 'Login' } 重定向到登录页\n",[213,780,781,783,785,788,791,794,797,799,802,804,807,809],{"class":215,"line":301},[213,782,630],{"class":415},[213,784,612],{"class":242},[213,786,787],{"class":227},"!",[213,789,790],{"class":231},"isAuthenticated",[213,792,793],{"class":227}," &&",[213,795,796],{"class":231}," to",[213,798,495],{"class":238},[213,800,801],{"class":231},"meta",[213,803,495],{"class":238},[213,805,806],{"class":231},"requiresAuth",[213,808,658],{"class":242},[213,810,661],{"class":238},[213,812,813,816,818,820,822,824,827,829],{"class":215,"line":342},[213,814,815],{"class":415},"    return",[213,817,419],{"class":238},[213,819,319],{"class":242},[213,821,246],{"class":238},[213,823,250],{"class":249},[213,825,826],{"class":253},"Login",[213,828,257],{"class":249},[213,830,831],{"class":238}," };\n",[213,833,834],{"class":215,"line":375},[213,835,690],{"class":238},[213,837,838,841,845],{"class":215,"line":531},[213,839,840],{"class":415},"  return",[213,842,844],{"class":843},"syTEX"," true",[213,846,381],{"class":238},[213,848,849,851,853],{"class":215,"line":538},[213,850,695],{"class":238},[213,852,523],{"class":231},[213,854,381],{"class":238},[50,856,858],{"id":857},"_2-路由独享守卫","2. 路由独享守卫",[23,860,861,862,865],{},"在路由配置中直接定义 ",[76,863,864],{},"beforeEnter","，仅对该路由生效。",[204,867,869],{"className":206,"code":868,"language":208,"meta":209,"style":209},"const routes = [\n  {\n    path: \"\u002Fadmin\",\n    component: Admin,\n    beforeEnter: (to, from) => {\n      if (!isAdmin) return { name: \"Home\" };\n      return true;\n    },\n  },\n];\n",[76,870,871,881,886,902,914,935,967,976,981,985],{"__ignoreMap":209},[213,872,873,875,877,879],{"class":215,"line":216},[213,874,220],{"class":219},[213,876,224],{"class":223},[213,878,228],{"class":227},[213,880,232],{"class":231},[213,882,883],{"class":215,"line":235},[213,884,885],{"class":238},"  {\n",[213,887,888,891,893,895,898,900],{"class":215,"line":274},[213,889,890],{"class":242},"    path",[213,892,246],{"class":238},[213,894,250],{"class":249},[213,896,897],{"class":253},"\u002Fadmin",[213,899,257],{"class":249},[213,901,506],{"class":238},[213,903,904,907,909,912],{"class":215,"line":301},[213,905,906],{"class":242},"    component",[213,908,246],{"class":238},[213,910,911],{"class":231}," Admin",[213,913,506],{"class":238},[213,915,916,919,921,923,925,927,929,931,933],{"class":215,"line":342},[213,917,918],{"class":470},"    beforeEnter",[213,920,246],{"class":238},[213,922,612],{"class":238},[213,924,615],{"class":514},[213,926,260],{"class":238},[213,928,428],{"class":514},[213,930,523],{"class":238},[213,932,490],{"class":219},[213,934,528],{"class":238},[213,936,937,940,942,944,947,949,952,954,956,958,960,963,965],{"class":215,"line":375},[213,938,939],{"class":415},"      if",[213,941,612],{"class":242},[213,943,787],{"class":227},[213,945,946],{"class":231},"isAdmin",[213,948,658],{"class":242},[213,950,951],{"class":415},"return",[213,953,419],{"class":238},[213,955,319],{"class":242},[213,957,246],{"class":238},[213,959,250],{"class":249},[213,961,962],{"class":253},"Home",[213,964,257],{"class":249},[213,966,831],{"class":238},[213,968,969,972,974],{"class":215,"line":531},[213,970,971],{"class":415},"      return",[213,973,844],{"class":843},[213,975,381],{"class":238},[213,977,978],{"class":215,"line":538},[213,979,980],{"class":238},"    },\n",[213,982,983],{"class":215,"line":553},[213,984,556],{"class":238},[213,986,987,989],{"class":215,"line":559},[213,988,378],{"class":231},[213,990,381],{"class":238},[50,992,994],{"id":993},"_3-组件内守卫","3. 组件内守卫",[23,996,997,998,1001,1002,37],{},"使用组合式 API 中的 ",[76,999,1000],{},"onBeforeRouteLeave"," 和 ",[76,1003,573],{},[390,1005,1006,1013],{},[393,1007,1008,1012],{},[182,1009,1010],{},[76,1011,1000],{},"：离开当前组件时触发（如阻止未保存的编辑）。",[393,1014,1015,1019],{},[182,1016,1017],{},[76,1018,573],{},"：同一组件因路由参数变化而复用时触发。",[204,1021,1023],{"className":206,"code":1022,"language":208,"meta":209,"style":209},"import { onBeforeRouteLeave } from \"vue-router\";\nonBeforeRouteLeave((to, from) => {\n  if (hasUnsavedChanges) {\n    return confirm(\"您有未保存的更改，确定要离开吗？\");\n  }\n  return true;\n});\n",[76,1024,1025,1046,1066,1079,1099,1103,1111],{"__ignoreMap":209},[213,1026,1027,1029,1031,1034,1036,1038,1040,1042,1044],{"class":215,"line":216},[213,1028,416],{"class":415},[213,1030,419],{"class":238},[213,1032,1033],{"class":231}," onBeforeRouteLeave",[213,1035,425],{"class":238},[213,1037,428],{"class":415},[213,1039,250],{"class":249},[213,1041,433],{"class":253},[213,1043,257],{"class":249},[213,1045,381],{"class":238},[213,1047,1048,1050,1052,1054,1056,1058,1060,1062,1064],{"class":215,"line":235},[213,1049,1000],{"class":470},[213,1051,544],{"class":231},[213,1053,544],{"class":238},[213,1055,615],{"class":514},[213,1057,260],{"class":238},[213,1059,428],{"class":514},[213,1061,523],{"class":238},[213,1063,490],{"class":219},[213,1065,528],{"class":238},[213,1067,1068,1070,1072,1075,1077],{"class":215,"line":274},[213,1069,630],{"class":415},[213,1071,612],{"class":242},[213,1073,1074],{"class":231},"hasUnsavedChanges",[213,1076,658],{"class":242},[213,1078,661],{"class":238},[213,1080,1081,1083,1086,1088,1090,1093,1095,1097],{"class":215,"line":301},[213,1082,815],{"class":415},[213,1084,1085],{"class":470}," confirm",[213,1087,544],{"class":242},[213,1089,257],{"class":249},[213,1091,1092],{"class":253},"您有未保存的更改，确定要离开吗？",[213,1094,257],{"class":249},[213,1096,523],{"class":242},[213,1098,381],{"class":238},[213,1100,1101],{"class":215,"line":342},[213,1102,690],{"class":238},[213,1104,1105,1107,1109],{"class":215,"line":375},[213,1106,840],{"class":415},[213,1108,844],{"class":843},[213,1110,381],{"class":238},[213,1112,1113,1115,1117],{"class":215,"line":531},[213,1114,695],{"class":238},[213,1116,523],{"class":231},[213,1118,381],{"class":238},[50,1120,1122,1123,1126],{"id":1121},"_4-vue-router-4-中-next-的演进","4. Vue Router 4 中 ",[76,1124,1125],{},"next"," 的演进",[23,1128,1129,1130,1133,1134,1140],{},"在 Vue Router 3 中，守卫必须调用 ",[76,1131,1132],{},"next()"," 来决定导航行为，这容易导致错误（如多次调用）。Vue Router 4 ",[182,1135,1136,1137,1139],{},"废弃了 ",[76,1138,1125],{}," 参数","，推荐直接返回：",[390,1142,1143,1154,1160,1167],{},[393,1144,1145,1146,1149,1150,1153],{},"返回 ",[76,1147,1148],{},"true","（或 ",[76,1151,1152],{},"undefined","）→ 继续导航。",[393,1155,1145,1156,1159],{},[76,1157,1158],{},"false"," → 取消导航。",[393,1161,1162,1163,1166],{},"返回一个路由地址对象（如 ",[76,1164,1165],{},"{ name: 'Login' }","）→ 重定向。",[393,1168,1169],{},"抛出错误 → 导航被取消。",[204,1171,1173],{"className":206,"code":1172,"language":208,"meta":209,"style":209},"\u002F\u002F Vue Router 3 风格（不推荐）\nrouter.beforeEach((to, from, next) => {\n  if (!isAuth) next(\"\u002Flogin\");\n  else next();\n});\n\n\u002F\u002F Vue Router 4 推荐风格\nrouter.beforeEach((to, from) => {\n  if (!isAuth) return \"\u002Flogin\";\n  return true;\n});\n",[76,1174,1175,1180,1209,1237,1248,1256,1262,1267,1291,1313,1321],{"__ignoreMap":209},[213,1176,1177],{"class":215,"line":216},[213,1178,1179],{"class":534},"\u002F\u002F Vue Router 3 风格（不推荐）\n",[213,1181,1182,1184,1186,1188,1190,1192,1194,1196,1198,1200,1203,1205,1207],{"class":215,"line":235},[213,1183,13],{"class":231},[213,1185,495],{"class":238},[213,1187,719],{"class":470},[213,1189,544],{"class":231},[213,1191,544],{"class":238},[213,1193,615],{"class":514},[213,1195,260],{"class":238},[213,1197,428],{"class":514},[213,1199,260],{"class":238},[213,1201,1202],{"class":514}," next",[213,1204,523],{"class":238},[213,1206,490],{"class":219},[213,1208,528],{"class":238},[213,1210,1211,1213,1215,1217,1220,1222,1224,1226,1228,1231,1233,1235],{"class":215,"line":274},[213,1212,630],{"class":415},[213,1214,612],{"class":242},[213,1216,787],{"class":227},[213,1218,1219],{"class":231},"isAuth",[213,1221,658],{"class":242},[213,1223,1125],{"class":470},[213,1225,544],{"class":242},[213,1227,257],{"class":249},[213,1229,1230],{"class":253},"\u002Flogin",[213,1232,257],{"class":249},[213,1234,523],{"class":242},[213,1236,381],{"class":238},[213,1238,1239,1242,1244,1246],{"class":215,"line":301},[213,1240,1241],{"class":415},"  else",[213,1243,1202],{"class":470},[213,1245,473],{"class":242},[213,1247,381],{"class":238},[213,1249,1250,1252,1254],{"class":215,"line":342},[213,1251,695],{"class":238},[213,1253,523],{"class":231},[213,1255,381],{"class":238},[213,1257,1258],{"class":215,"line":375},[213,1259,1261],{"emptyLinePlaceholder":1260},true,"\n",[213,1263,1264],{"class":215,"line":531},[213,1265,1266],{"class":534},"\u002F\u002F Vue Router 4 推荐风格\n",[213,1268,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289],{"class":215,"line":538},[213,1270,13],{"class":231},[213,1272,495],{"class":238},[213,1274,719],{"class":470},[213,1276,544],{"class":231},[213,1278,544],{"class":238},[213,1280,615],{"class":514},[213,1282,260],{"class":238},[213,1284,428],{"class":514},[213,1286,523],{"class":238},[213,1288,490],{"class":219},[213,1290,528],{"class":238},[213,1292,1293,1295,1297,1299,1301,1303,1305,1307,1309,1311],{"class":215,"line":553},[213,1294,630],{"class":415},[213,1296,612],{"class":242},[213,1298,787],{"class":227},[213,1300,1219],{"class":231},[213,1302,658],{"class":242},[213,1304,951],{"class":415},[213,1306,250],{"class":249},[213,1308,1230],{"class":253},[213,1310,257],{"class":249},[213,1312,381],{"class":238},[213,1314,1315,1317,1319],{"class":215,"line":559},[213,1316,840],{"class":415},[213,1318,844],{"class":843},[213,1320,381],{"class":238},[213,1322,1324,1326,1328],{"class":215,"line":1323},11,[213,1325,695],{"class":238},[213,1327,523],{"class":231},[213,1329,381],{"class":238},[23,1331,1332],{},"这种写法更简洁、不易出错，且天然支持异步（返回 Promise）。",[42,1334,1336],{"id":1335},"三路由元信息meta","三、路由元信息（meta）",[23,1338,1339,1340,1342],{},"在路由配置中添加 ",[76,1341,801],{}," 字段，用于附加任意数据，如权限标识、布局组件、页面缓存等。",[204,1344,1346],{"className":206,"code":1345,"language":208,"meta":209,"style":209},"const routes = [\n  {\n    path: \"\u002Fadmin\",\n    component: Admin,\n    meta: {\n      requiresAuth: true,\n      roles: [\"admin\", \"super_admin\"],\n      layout: \"AdminLayout\",\n      keepAlive: true,\n    },\n  },\n];\n",[76,1347,1348,1358,1362,1376,1386,1395,1406,1436,1452,1463,1467,1471],{"__ignoreMap":209},[213,1349,1350,1352,1354,1356],{"class":215,"line":216},[213,1351,220],{"class":219},[213,1353,224],{"class":223},[213,1355,228],{"class":227},[213,1357,232],{"class":231},[213,1359,1360],{"class":215,"line":235},[213,1361,885],{"class":238},[213,1363,1364,1366,1368,1370,1372,1374],{"class":215,"line":274},[213,1365,890],{"class":242},[213,1367,246],{"class":238},[213,1369,250],{"class":249},[213,1371,897],{"class":253},[213,1373,257],{"class":249},[213,1375,506],{"class":238},[213,1377,1378,1380,1382,1384],{"class":215,"line":301},[213,1379,906],{"class":242},[213,1381,246],{"class":238},[213,1383,911],{"class":231},[213,1385,506],{"class":238},[213,1387,1388,1391,1393],{"class":215,"line":342},[213,1389,1390],{"class":242},"    meta",[213,1392,246],{"class":238},[213,1394,528],{"class":238},[213,1396,1397,1400,1402,1404],{"class":215,"line":375},[213,1398,1399],{"class":242},"      requiresAuth",[213,1401,246],{"class":238},[213,1403,844],{"class":843},[213,1405,506],{"class":238},[213,1407,1408,1411,1413,1416,1418,1421,1423,1425,1427,1430,1432,1434],{"class":215,"line":531},[213,1409,1410],{"class":242},"      roles",[213,1412,246],{"class":238},[213,1414,1415],{"class":231}," [",[213,1417,257],{"class":249},[213,1419,1420],{"class":253},"admin",[213,1422,257],{"class":249},[213,1424,260],{"class":238},[213,1426,250],{"class":249},[213,1428,1429],{"class":253},"super_admin",[213,1431,257],{"class":249},[213,1433,378],{"class":231},[213,1435,506],{"class":238},[213,1437,1438,1441,1443,1445,1448,1450],{"class":215,"line":538},[213,1439,1440],{"class":242},"      layout",[213,1442,246],{"class":238},[213,1444,250],{"class":249},[213,1446,1447],{"class":253},"AdminLayout",[213,1449,257],{"class":249},[213,1451,506],{"class":238},[213,1453,1454,1457,1459,1461],{"class":215,"line":553},[213,1455,1456],{"class":242},"      keepAlive",[213,1458,246],{"class":238},[213,1460,844],{"class":843},[213,1462,506],{"class":238},[213,1464,1465],{"class":215,"line":559},[213,1466,980],{"class":238},[213,1468,1469],{"class":215,"line":1323},[213,1470,556],{"class":238},[213,1472,1474,1476],{"class":215,"line":1473},12,[213,1475,378],{"class":231},[213,1477,381],{"class":238},[50,1479,1481,1482,1484],{"id":1480},"基于-meta-的权限控制","基于 ",[76,1483,801],{}," 的权限控制",[23,1486,1487,1488,1490,1491,1494],{},"在全局 ",[76,1489,719],{}," 中读取 ",[76,1492,1493],{},"to.meta","，判断用户权限。",[204,1496,1498],{"className":206,"code":1497,"language":208,"meta":209,"style":209},"router.beforeEach((to, from) => {\n  if (to.meta.requiresAuth && !isAuthenticated) {\n    return \"\u002Flogin\";\n  }\n  if (to.meta.roles && !to.meta.roles.includes(userRole)) {\n    return \"\u002Fforbidden\";\n  }\n  return true;\n});\n",[76,1499,1500,1524,1551,1563,1567,1613,1626,1630,1638],{"__ignoreMap":209},[213,1501,1502,1504,1506,1508,1510,1512,1514,1516,1518,1520,1522],{"class":215,"line":216},[213,1503,13],{"class":231},[213,1505,495],{"class":238},[213,1507,719],{"class":470},[213,1509,544],{"class":231},[213,1511,544],{"class":238},[213,1513,615],{"class":514},[213,1515,260],{"class":238},[213,1517,428],{"class":514},[213,1519,523],{"class":238},[213,1521,490],{"class":219},[213,1523,528],{"class":238},[213,1525,1526,1528,1530,1532,1534,1536,1538,1540,1542,1545,1547,1549],{"class":215,"line":235},[213,1527,630],{"class":415},[213,1529,612],{"class":242},[213,1531,615],{"class":231},[213,1533,495],{"class":238},[213,1535,801],{"class":231},[213,1537,495],{"class":238},[213,1539,806],{"class":231},[213,1541,793],{"class":227},[213,1543,1544],{"class":227}," !",[213,1546,790],{"class":231},[213,1548,658],{"class":242},[213,1550,661],{"class":238},[213,1552,1553,1555,1557,1559,1561],{"class":215,"line":274},[213,1554,815],{"class":415},[213,1556,250],{"class":249},[213,1558,1230],{"class":253},[213,1560,257],{"class":249},[213,1562,381],{"class":238},[213,1564,1565],{"class":215,"line":301},[213,1566,690],{"class":238},[213,1568,1569,1571,1573,1575,1577,1579,1581,1584,1586,1588,1590,1592,1594,1596,1598,1600,1603,1605,1608,1611],{"class":215,"line":342},[213,1570,630],{"class":415},[213,1572,612],{"class":242},[213,1574,615],{"class":231},[213,1576,495],{"class":238},[213,1578,801],{"class":231},[213,1580,495],{"class":238},[213,1582,1583],{"class":231},"roles",[213,1585,793],{"class":227},[213,1587,1544],{"class":227},[213,1589,615],{"class":231},[213,1591,495],{"class":238},[213,1593,801],{"class":231},[213,1595,495],{"class":238},[213,1597,1583],{"class":231},[213,1599,495],{"class":238},[213,1601,1602],{"class":470},"includes",[213,1604,544],{"class":242},[213,1606,1607],{"class":231},"userRole",[213,1609,1610],{"class":242},")) ",[213,1612,661],{"class":238},[213,1614,1615,1617,1619,1622,1624],{"class":215,"line":375},[213,1616,815],{"class":415},[213,1618,250],{"class":249},[213,1620,1621],{"class":253},"\u002Fforbidden",[213,1623,257],{"class":249},[213,1625,381],{"class":238},[213,1627,1628],{"class":215,"line":531},[213,1629,690],{"class":238},[213,1631,1632,1634,1636],{"class":215,"line":538},[213,1633,840],{"class":415},[213,1635,844],{"class":843},[213,1637,381],{"class":238},[213,1639,1640,1642,1644],{"class":215,"line":553},[213,1641,695],{"class":238},[213,1643,523],{"class":231},[213,1645,381],{"class":238},[50,1647,1648],{"id":1648},"布局切换",[23,1650,1651,1652,1655],{},"在根组件中根据路由 ",[76,1653,1654],{},"meta.layout"," 动态渲染不同的布局组件。",[204,1657,1660],{"className":1658,"code":1659,"language":12,"meta":209,"style":209},"language-vue shiki shiki-themes material-theme-lighter github-light github-dark","\u003Ctemplate>\n  \u003Ccomponent :is=\"$route.meta.layout || 'DefaultLayout'\">\n    \u003CRouterView \u002F>\n  \u003C\u002Fcomponent>\n\u003C\u002Ftemplate>\n",[76,1661,1662,1674,1698,1709,1718],{"__ignoreMap":209},[213,1663,1664,1667,1671],{"class":215,"line":216},[213,1665,1666],{"class":238},"\u003C",[213,1668,1670],{"class":1669},"sQzsp","template",[213,1672,1673],{"class":238},">\n",[213,1675,1676,1679,1682,1686,1689,1691,1694,1696],{"class":215,"line":235},[213,1677,1678],{"class":238},"  \u003C",[213,1680,1681],{"class":1669},"component",[213,1683,1685],{"class":1684},"s9AJx"," :is",[213,1687,1688],{"class":238},"=",[213,1690,257],{"class":249},[213,1692,1693],{"class":253},"$route.meta.layout || 'DefaultLayout'",[213,1695,257],{"class":249},[213,1697,1673],{"class":238},[213,1699,1700,1703,1706],{"class":215,"line":274},[213,1701,1702],{"class":238},"    \u003C",[213,1704,1705],{"class":1669},"RouterView",[213,1707,1708],{"class":238}," \u002F>\n",[213,1710,1711,1714,1716],{"class":215,"line":301},[213,1712,1713],{"class":238},"  \u003C\u002F",[213,1715,1681],{"class":1669},[213,1717,1673],{"class":238},[213,1719,1720,1723,1725],{"class":215,"line":342},[213,1721,1722],{"class":238},"\u003C\u002F",[213,1724,1670],{"class":1669},[213,1726,1673],{"class":238},[50,1728,1729],{"id":1729},"页面缓存策略",[23,1731,1732,1733,1736,1737,1740],{},"结合 ",[76,1734,1735],{},"\u003CKeepAlive>","，通过 ",[76,1738,1739],{},"meta.keepAlive"," 控制是否缓存组件。",[204,1742,1744],{"className":1658,"code":1743,"language":12,"meta":209,"style":209},"\u003Ctemplate>\n  \u003CRouterView v-slot=\"{ Component }\">\n    \u003CKeepAlive :include=\"cachedViews\">\n      \u003Ccomponent :is=\"Component\" \u002F>\n    \u003C\u002FKeepAlive>\n  \u003C\u002FRouterView>\n\u003C\u002Ftemplate>\n\u003Cscript setup>\nimport { computed } from \"vue\";\nimport { useRoute } from \"vue-router\";\nconst route = useRoute();\nconst cachedViews = computed(() => (route.meta.keepAlive ? [route.name] : []));\n\u003C\u002Fscript>\n",[76,1745,1746,1754,1774,1795,1815,1824,1832,1840,1852,1873,1893,1907,1954],{"__ignoreMap":209},[213,1747,1748,1750,1752],{"class":215,"line":216},[213,1749,1666],{"class":238},[213,1751,1670],{"class":1669},[213,1753,1673],{"class":238},[213,1755,1756,1758,1760,1763,1765,1767,1770,1772],{"class":215,"line":235},[213,1757,1678],{"class":238},[213,1759,1705],{"class":1669},[213,1761,1762],{"class":1684}," v-slot",[213,1764,1688],{"class":238},[213,1766,257],{"class":249},[213,1768,1769],{"class":253},"{ Component }",[213,1771,257],{"class":249},[213,1773,1673],{"class":238},[213,1775,1776,1778,1781,1784,1786,1788,1791,1793],{"class":215,"line":274},[213,1777,1702],{"class":238},[213,1779,1780],{"class":1669},"KeepAlive",[213,1782,1783],{"class":1684}," :include",[213,1785,1688],{"class":238},[213,1787,257],{"class":249},[213,1789,1790],{"class":253},"cachedViews",[213,1792,257],{"class":249},[213,1794,1673],{"class":238},[213,1796,1797,1800,1802,1804,1806,1808,1811,1813],{"class":215,"line":301},[213,1798,1799],{"class":238},"      \u003C",[213,1801,1681],{"class":1669},[213,1803,1685],{"class":1684},[213,1805,1688],{"class":238},[213,1807,257],{"class":249},[213,1809,1810],{"class":253},"Component",[213,1812,257],{"class":249},[213,1814,1708],{"class":238},[213,1816,1817,1820,1822],{"class":215,"line":342},[213,1818,1819],{"class":238},"    \u003C\u002F",[213,1821,1780],{"class":1669},[213,1823,1673],{"class":238},[213,1825,1826,1828,1830],{"class":215,"line":375},[213,1827,1713],{"class":238},[213,1829,1705],{"class":1669},[213,1831,1673],{"class":238},[213,1833,1834,1836,1838],{"class":215,"line":531},[213,1835,1722],{"class":238},[213,1837,1670],{"class":1669},[213,1839,1673],{"class":238},[213,1841,1842,1844,1847,1850],{"class":215,"line":538},[213,1843,1666],{"class":238},[213,1845,1846],{"class":1669},"script",[213,1848,1849],{"class":1684}," setup",[213,1851,1673],{"class":238},[213,1853,1854,1856,1858,1861,1863,1865,1867,1869,1871],{"class":215,"line":553},[213,1855,416],{"class":415},[213,1857,419],{"class":238},[213,1859,1860],{"class":231}," computed",[213,1862,425],{"class":238},[213,1864,428],{"class":415},[213,1866,250],{"class":249},[213,1868,12],{"class":253},[213,1870,257],{"class":249},[213,1872,381],{"class":238},[213,1874,1875,1877,1879,1881,1883,1885,1887,1889,1891],{"class":215,"line":559},[213,1876,416],{"class":415},[213,1878,419],{"class":238},[213,1880,422],{"class":231},[213,1882,425],{"class":238},[213,1884,428],{"class":415},[213,1886,250],{"class":249},[213,1888,433],{"class":253},[213,1890,257],{"class":249},[213,1892,381],{"class":238},[213,1894,1895,1897,1899,1901,1903,1905],{"class":215,"line":1323},[213,1896,220],{"class":219},[213,1898,465],{"class":223},[213,1900,228],{"class":227},[213,1902,422],{"class":470},[213,1904,473],{"class":231},[213,1906,381],{"class":238},[213,1908,1909,1911,1914,1916,1918,1920,1922,1924,1927,1929,1931,1933,1936,1939,1942,1944,1947,1949,1952],{"class":215,"line":1473},[213,1910,220],{"class":219},[213,1912,1913],{"class":223}," cachedViews",[213,1915,228],{"class":227},[213,1917,1860],{"class":470},[213,1919,544],{"class":231},[213,1921,473],{"class":238},[213,1923,490],{"class":219},[213,1925,1926],{"class":231}," (route",[213,1928,495],{"class":238},[213,1930,801],{"class":231},[213,1932,495],{"class":238},[213,1934,1935],{"class":231},"keepAlive ",[213,1937,1938],{"class":227},"?",[213,1940,1941],{"class":231}," [route",[213,1943,495],{"class":238},[213,1945,1946],{"class":231},"name] ",[213,1948,246],{"class":227},[213,1950,1951],{"class":231}," []))",[213,1953,381],{"class":238},[213,1955,1957,1959,1961],{"class":215,"line":1956},13,[213,1958,1722],{"class":238},[213,1960,1846],{"class":1669},[213,1962,1673],{"class":238},[42,1964,1966],{"id":1965},"四路由懒加载的深度实践","四、路由懒加载的深度实践",[50,1968,1970],{"id":1969},"_1-动态导入与代码分割原理","1. 动态导入与代码分割原理",[23,1972,397,1973,1976],{},[76,1974,1975],{},"() => import('.\u002FFoo.vue')"," 定义路由组件，Webpack \u002F Vite 会自动将每个动态导入的组件打包成独立的 chunk，仅在访问该路由时加载。",[204,1978,1980],{"className":206,"code":1979,"language":208,"meta":209,"style":209},"const routes = [{ path: \"\u002Fabout\", component: () => import(\".\u002Fviews\u002FAbout.vue\") }];\n",[76,1981,1982],{"__ignoreMap":209},[213,1983,1984,1986,1988,1990,1992,1995,1997,1999,2001,2004,2006,2008,2010,2012,2015,2017,2020,2022,2024,2027,2029,2031,2033,2035],{"class":215,"line":216},[213,1985,220],{"class":219},[213,1987,224],{"class":223},[213,1989,228],{"class":227},[213,1991,1415],{"class":231},[213,1993,1994],{"class":238},"{",[213,1996,243],{"class":242},[213,1998,246],{"class":238},[213,2000,250],{"class":249},[213,2002,2003],{"class":253},"\u002Fabout",[213,2005,257],{"class":249},[213,2007,260],{"class":238},[213,2009,263],{"class":470},[213,2011,246],{"class":238},[213,2013,2014],{"class":238}," ()",[213,2016,490],{"class":219},[213,2018,2019],{"class":227}," import",[213,2021,544],{"class":231},[213,2023,257],{"class":249},[213,2025,2026],{"class":253},".\u002Fviews\u002FAbout.vue",[213,2028,257],{"class":249},[213,2030,658],{"class":231},[213,2032,695],{"class":238},[213,2034,378],{"class":231},[213,2036,381],{"class":238},[50,2038,2040],{"id":2039},"_2-配合-webpack-magic-comments-控制-chunk-命名","2. 配合 Webpack Magic Comments 控制 chunk 命名",[23,2042,2043,2044,2047],{},"在 ",[76,2045,2046],{},"import()"," 中添加注释，可以指定 chunk 名称，便于调试和缓存管理。",[204,2049,2051],{"className":206,"code":2050,"language":208,"meta":209,"style":209},"const UserProfile = () => import(\u002F* webpackChunkName: \"user-profile\" *\u002F \".\u002Fviews\u002FUserProfile.vue\");\nconst AdminPanel = () => import(\u002F* webpackChunkName: \"admin-panel\" *\u002F \".\u002Fviews\u002FAdminPanel.vue\");\n",[76,2052,2053,2085],{"__ignoreMap":209},[213,2054,2055,2057,2061,2063,2065,2067,2069,2071,2074,2076,2079,2081,2083],{"class":215,"line":216},[213,2056,220],{"class":219},[213,2058,2060],{"class":2059},"sfCm-"," UserProfile",[213,2062,228],{"class":227},[213,2064,2014],{"class":238},[213,2066,490],{"class":219},[213,2068,2019],{"class":470},[213,2070,544],{"class":231},[213,2072,2073],{"class":534},"\u002F* webpackChunkName: \"user-profile\" *\u002F",[213,2075,250],{"class":249},[213,2077,2078],{"class":253},".\u002Fviews\u002FUserProfile.vue",[213,2080,257],{"class":249},[213,2082,523],{"class":231},[213,2084,381],{"class":238},[213,2086,2087,2089,2092,2094,2096,2098,2100,2102,2105,2107,2110,2112,2114],{"class":215,"line":235},[213,2088,220],{"class":219},[213,2090,2091],{"class":2059}," AdminPanel",[213,2093,228],{"class":227},[213,2095,2014],{"class":238},[213,2097,490],{"class":219},[213,2099,2019],{"class":470},[213,2101,544],{"class":231},[213,2103,2104],{"class":534},"\u002F* webpackChunkName: \"admin-panel\" *\u002F",[213,2106,250],{"class":249},[213,2108,2109],{"class":253},".\u002Fviews\u002FAdminPanel.vue",[213,2111,257],{"class":249},[213,2113,523],{"class":231},[213,2115,381],{"class":238},[23,2117,2118,2119,2122,2123,2126],{},"Vite 则使用 ",[76,2120,2121],{},"rollupOptions.output.chunkFileNames"," 或注释 ",[76,2124,2125],{},"\u002F* vitePreload *\u002F"," 等方式控制。",[50,2128,2130],{"id":2129},"_3-懒加载的潜在问题与改善","3. 懒加载的潜在问题与改善",[390,2132,2133,2143],{},[393,2134,2135,2138,2139,2142],{},[182,2136,2137],{},"加载闪烁","：可配合 ",[76,2140,2141],{},"Suspense"," 或路由守卫中的加载进度条（如 NProgress）。",[393,2144,2145,2148,2149,2152,2153],{},[182,2146,2147],{},"预加载","：使用 ",[76,2150,2151],{},"webpackPrefetch: true"," 在空闲时预加载；\n",[204,2154,2156],{"className":206,"code":2155,"language":208,"meta":209,"style":209},"const Home = () => import(\u002F* webpackPrefetch: true *\u002F \".\u002FHome.vue\");\n",[76,2157,2158],{"__ignoreMap":209},[213,2159,2160,2162,2165,2167,2169,2171,2173,2175,2178,2180,2183,2185,2187],{"class":215,"line":216},[213,2161,220],{"class":219},[213,2163,2164],{"class":2059}," Home",[213,2166,228],{"class":227},[213,2168,2014],{"class":238},[213,2170,490],{"class":219},[213,2172,2019],{"class":470},[213,2174,544],{"class":231},[213,2176,2177],{"class":534},"\u002F* webpackPrefetch: true *\u002F",[213,2179,250],{"class":249},[213,2181,2182],{"class":253},".\u002FHome.vue",[213,2184,257],{"class":249},[213,2186,523],{"class":231},[213,2188,381],{"class":238},[42,2190,2192],{"id":2191},"五路由过渡动画","五、路由过渡动画",[23,2194,2195,2196,2199,2200,2203,2204,2207],{},"通过 ",[76,2197,2198],{},"\u003CRouterView>"," 配合 ",[76,2201,2202],{},"\u003CTransition>"," 可以为路由切换添加平滑动画。注意设置 ",[76,2205,2206],{},"key"," 以确保不同路由之间的过渡效果。",[204,2209,2211],{"className":1658,"code":2210,"language":12,"meta":209,"style":209},"\u003Ctemplate>\n  \u003CRouterView v-slot=\"{ Component, route }\">\n    \u003CTransition name=\"fade\" mode=\"out-in\">\n      \u003Ccomponent :is=\"Component\" :key=\"route.path\" \u002F>\n    \u003C\u002FTransition>\n  \u003C\u002FRouterView>\n\u003C\u002Ftemplate>\n\n\u003Cstyle>\n.fade-enter-active,\n.fade-leave-active {\n  transition: opacity 0.3s;\n}\n.fade-enter-from,\n.fade-leave-to {\n  opacity: 0;\n}\n\u003C\u002Fstyle>\n",[76,2212,2213,2221,2240,2272,2302,2310,2318,2326,2330,2339,2350,2359,2380,2385,2395,2405,2418,2423],{"__ignoreMap":209},[213,2214,2215,2217,2219],{"class":215,"line":216},[213,2216,1666],{"class":238},[213,2218,1670],{"class":1669},[213,2220,1673],{"class":238},[213,2222,2223,2225,2227,2229,2231,2233,2236,2238],{"class":215,"line":235},[213,2224,1678],{"class":238},[213,2226,1705],{"class":1669},[213,2228,1762],{"class":1684},[213,2230,1688],{"class":238},[213,2232,257],{"class":249},[213,2234,2235],{"class":253},"{ Component, route }",[213,2237,257],{"class":249},[213,2239,1673],{"class":238},[213,2241,2242,2244,2247,2249,2251,2253,2256,2258,2261,2263,2265,2268,2270],{"class":215,"line":274},[213,2243,1702],{"class":238},[213,2245,2246],{"class":1669},"Transition",[213,2248,319],{"class":1684},[213,2250,1688],{"class":238},[213,2252,257],{"class":249},[213,2254,2255],{"class":253},"fade",[213,2257,257],{"class":249},[213,2259,2260],{"class":1684}," mode",[213,2262,1688],{"class":238},[213,2264,257],{"class":249},[213,2266,2267],{"class":253},"out-in",[213,2269,257],{"class":249},[213,2271,1673],{"class":238},[213,2273,2274,2276,2278,2280,2282,2284,2286,2288,2291,2293,2295,2298,2300],{"class":215,"line":301},[213,2275,1799],{"class":238},[213,2277,1681],{"class":1669},[213,2279,1685],{"class":1684},[213,2281,1688],{"class":238},[213,2283,257],{"class":249},[213,2285,1810],{"class":253},[213,2287,257],{"class":249},[213,2289,2290],{"class":1684}," :key",[213,2292,1688],{"class":238},[213,2294,257],{"class":249},[213,2296,2297],{"class":253},"route.path",[213,2299,257],{"class":249},[213,2301,1708],{"class":238},[213,2303,2304,2306,2308],{"class":215,"line":342},[213,2305,1819],{"class":238},[213,2307,2246],{"class":1669},[213,2309,1673],{"class":238},[213,2311,2312,2314,2316],{"class":215,"line":375},[213,2313,1713],{"class":238},[213,2315,1705],{"class":1669},[213,2317,1673],{"class":238},[213,2319,2320,2322,2324],{"class":215,"line":531},[213,2321,1722],{"class":238},[213,2323,1670],{"class":1669},[213,2325,1673],{"class":238},[213,2327,2328],{"class":215,"line":538},[213,2329,1261],{"emptyLinePlaceholder":1260},[213,2331,2332,2334,2337],{"class":215,"line":553},[213,2333,1666],{"class":238},[213,2335,2336],{"class":1669},"style",[213,2338,1673],{"class":238},[213,2340,2341,2344,2348],{"class":215,"line":559},[213,2342,495],{"class":2343},"stp6e",[213,2345,2347],{"class":2346},"sbgvK","fade-enter-active",[213,2349,506],{"class":238},[213,2351,2352,2354,2357],{"class":215,"line":1323},[213,2353,495],{"class":2343},[213,2355,2356],{"class":2346},"fade-leave-active",[213,2358,528],{"class":238},[213,2360,2361,2365,2367,2370,2374,2378],{"class":215,"line":1473},[213,2362,2364],{"class":2363},"soE4H","  transition",[213,2366,246],{"class":238},[213,2368,2369],{"class":231}," opacity ",[213,2371,2373],{"class":2372},"srdBf","0.3",[213,2375,2377],{"class":2376},"sw1J6","s",[213,2379,381],{"class":238},[213,2381,2382],{"class":215,"line":1956},[213,2383,2384],{"class":238},"}\n",[213,2386,2388,2390,2393],{"class":215,"line":2387},14,[213,2389,495],{"class":2343},[213,2391,2392],{"class":2346},"fade-enter-from",[213,2394,506],{"class":238},[213,2396,2398,2400,2403],{"class":215,"line":2397},15,[213,2399,495],{"class":2343},[213,2401,2402],{"class":2346},"fade-leave-to",[213,2404,528],{"class":238},[213,2406,2408,2411,2413,2416],{"class":215,"line":2407},16,[213,2409,2410],{"class":2363},"  opacity",[213,2412,246],{"class":238},[213,2414,2415],{"class":2372}," 0",[213,2417,381],{"class":238},[213,2419,2421],{"class":215,"line":2420},17,[213,2422,2384],{"class":238},[213,2424,2426,2428,2430],{"class":215,"line":2425},18,[213,2427,1722],{"class":238},[213,2429,2336],{"class":1669},[213,2431,1673],{"class":238},[23,2433,2434,2435,2437],{},"可以基于路由的 ",[76,2436,801],{}," 定义不同的过渡效果（如左滑、右滑）。",[42,2439,2441],{"id":2440},"六路由滚动行为控制","六、路由滚动行为控制",[23,2443,397,2444,2447],{},[76,2445,2446],{},"scrollBehavior"," 方法控制导航后页面的滚动位置。",[204,2449,2451],{"className":206,"code":2450,"language":208,"meta":209,"style":209},"const router = createRouter({\n  history: createWebHistory(),\n  routes,\n  scrollBehavior(to, from, savedPosition) {\n    if (savedPosition) {\n      return savedPosition; \u002F\u002F 浏览器前进\u002F后退时恢复到上次位置\n    }\n    if (to.hash) {\n      return { el: to.hash, behavior: \"smooth\" }; \u002F\u002F 滚动到锚点\n    }\n    return { top: 0, left: 0, behavior: \"smooth\" };\n  },\n});\n",[76,2452,2453,2469,2483,2490,2513,2527,2539,2544,2561,2598,2602,2638,2642],{"__ignoreMap":209},[213,2454,2455,2457,2460,2462,2465,2467],{"class":215,"line":216},[213,2456,220],{"class":219},[213,2458,2459],{"class":223}," router",[213,2461,228],{"class":227},[213,2463,2464],{"class":470}," createRouter",[213,2466,544],{"class":231},[213,2468,661],{"class":238},[213,2470,2471,2474,2476,2479,2481],{"class":215,"line":235},[213,2472,2473],{"class":242},"  history",[213,2475,246],{"class":238},[213,2477,2478],{"class":470}," createWebHistory",[213,2480,473],{"class":231},[213,2482,506],{"class":238},[213,2484,2485,2488],{"class":215,"line":274},[213,2486,2487],{"class":231},"  routes",[213,2489,506],{"class":238},[213,2491,2492,2496,2498,2500,2502,2504,2506,2509,2511],{"class":215,"line":301},[213,2493,2495],{"class":2494},"sVXei","  scrollBehavior",[213,2497,544],{"class":238},[213,2499,615],{"class":514},[213,2501,260],{"class":238},[213,2503,428],{"class":514},[213,2505,260],{"class":238},[213,2507,2508],{"class":514}," savedPosition",[213,2510,523],{"class":238},[213,2512,528],{"class":238},[213,2514,2515,2518,2520,2523,2525],{"class":215,"line":342},[213,2516,2517],{"class":415},"    if",[213,2519,612],{"class":242},[213,2521,2522],{"class":231},"savedPosition",[213,2524,658],{"class":242},[213,2526,661],{"class":238},[213,2528,2529,2531,2533,2536],{"class":215,"line":375},[213,2530,971],{"class":415},[213,2532,2508],{"class":231},[213,2534,2535],{"class":238},";",[213,2537,2538],{"class":534}," \u002F\u002F 浏览器前进\u002F后退时恢复到上次位置\n",[213,2540,2541],{"class":215,"line":531},[213,2542,2543],{"class":238},"    }\n",[213,2545,2546,2548,2550,2552,2554,2557,2559],{"class":215,"line":538},[213,2547,2517],{"class":415},[213,2549,612],{"class":242},[213,2551,615],{"class":231},[213,2553,495],{"class":238},[213,2555,2556],{"class":231},"hash",[213,2558,658],{"class":242},[213,2560,661],{"class":238},[213,2562,2563,2565,2567,2570,2572,2574,2576,2578,2580,2583,2585,2587,2590,2592,2595],{"class":215,"line":553},[213,2564,971],{"class":415},[213,2566,419],{"class":238},[213,2568,2569],{"class":242}," el",[213,2571,246],{"class":238},[213,2573,796],{"class":231},[213,2575,495],{"class":238},[213,2577,2556],{"class":231},[213,2579,260],{"class":238},[213,2581,2582],{"class":242}," behavior",[213,2584,246],{"class":238},[213,2586,250],{"class":249},[213,2588,2589],{"class":253},"smooth",[213,2591,257],{"class":249},[213,2593,2594],{"class":238}," };",[213,2596,2597],{"class":534}," \u002F\u002F 滚动到锚点\n",[213,2599,2600],{"class":215,"line":559},[213,2601,2543],{"class":238},[213,2603,2604,2606,2608,2611,2613,2615,2617,2620,2622,2624,2626,2628,2630,2632,2634,2636],{"class":215,"line":1323},[213,2605,815],{"class":415},[213,2607,419],{"class":238},[213,2609,2610],{"class":242}," top",[213,2612,246],{"class":238},[213,2614,2415],{"class":2372},[213,2616,260],{"class":238},[213,2618,2619],{"class":242}," left",[213,2621,246],{"class":238},[213,2623,2415],{"class":2372},[213,2625,260],{"class":238},[213,2627,2582],{"class":242},[213,2629,246],{"class":238},[213,2631,250],{"class":249},[213,2633,2589],{"class":253},[213,2635,257],{"class":249},[213,2637,831],{"class":238},[213,2639,2640],{"class":215,"line":1473},[213,2641,556],{"class":238},[213,2643,2644,2646,2648],{"class":215,"line":1956},[213,2645,695],{"class":238},[213,2647,523],{"class":231},[213,2649,381],{"class":238},[23,2651,2652,2653,2655],{},"可以结合 ",[76,2654,801],{}," 实现特定路由保持滚动位置。",[42,2657,2659],{"id":2658},"七路由历史记录模式","七、路由历史记录模式",[55,2661,2662,2677],{},[58,2663,2664],{},[61,2665,2666,2668,2671,2674],{},[64,2667,66],{},[64,2669,2670],{},"创建方式",[64,2672,2673],{},"URL 示例",[64,2675,2676],{},"适用场景",[81,2678,2679,2697],{},[61,2680,2681,2684,2689,2694],{},[86,2682,2683],{},"HTML5 History",[86,2685,2686],{},[76,2687,2688],{},"createWebHistory()",[86,2690,2691],{},[76,2692,2693],{},"https:\u002F\u002Fexample.com\u002Fuser\u002F123",[86,2695,2696],{},"需要干净的 URL，服务端需配置 fallback（所有路由回退到 index.html）",[61,2698,2699,2702,2707,2712],{},[86,2700,2701],{},"Hash",[86,2703,2704],{},[76,2705,2706],{},"createWebHashHistory()",[86,2708,2709],{},[76,2710,2711],{},"https:\u002F\u002Fexample.com\u002F#\u002Fuser\u002F123",[86,2713,2714],{},"纯静态站点（无服务端支持）、兼容旧浏览器，无需服务端配置",[23,2716,2717,2148,2720,2723,2724,2727],{},[182,2718,2719],{},"注意",[76,2721,2722],{},"createWebHistory"," 时，线上服务器必须进行配置（如 Nginx 将所有未匹配路径指向 ",[76,2725,2726],{},"index.html","），否则刷新页面会出现 404。",[42,2729,2731],{"id":2730},"八完整权限控制示例组合式-api","八、完整权限控制示例（组合式 API）",[23,2733,1732,2734,189,2737,2740],{},[76,2735,2736],{},"createRouter",[76,2738,2739],{},"pinia"," 存储用户权限和登录状态。",[204,2742,2744],{"className":206,"code":2743,"language":208,"meta":209,"style":209},"\u002F\u002F router\u002Findex.js\nimport { createRouter, createWebHistory } from \"vue-router\";\nimport { useUserStore } from \"@\u002Fstores\u002Fuser\";\n\nconst routes = [\n  { path: \"\u002Flogin\", component: () => import(\"@\u002Fviews\u002FLogin.vue\") },\n  {\n    path: \"\u002Fdashboard\",\n    component: () => import(\"@\u002Fviews\u002FDashboard.vue\"),\n    meta: { requiresAuth: true, roles: [\"user\", \"admin\"] },\n  },\n  {\n    path: \"\u002Fadmin\",\n    component: () => import(\"@\u002Fviews\u002FAdmin.vue\"),\n    meta: { requiresAuth: true, roles: [\"admin\"] },\n  },\n];\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n\nrouter.beforeEach(async (to, from) => {\n  const userStore = useUserStore();\n  \u002F\u002F 检查是否需要登录\n  if (to.meta.requiresAuth && !userStore.isLoggedIn) {\n    return { name: \"login\", query: { redirect: to.fullPath } };\n  }\n  \u002F\u002F 角色校验\n  if (to.meta.roles && !to.meta.roles.includes(userStore.role)) {\n    return { name: \"forbidden\" };\n  }\n  return true;\n});\n\nexport default router;\n",[76,2745,2746,2751,2775,2797,2801,2811,2850,2854,2869,2894,2938,2942,2946,2960,2985,3017,3021,3027,3031,3046,3059,3066,3075,3080,3107,3124,3130,3163,3206,3211,3217,3265,3285,3290,3299,3308,3313],{"__ignoreMap":209},[213,2747,2748],{"class":215,"line":216},[213,2749,2750],{"class":534},"\u002F\u002F router\u002Findex.js\n",[213,2752,2753,2755,2757,2759,2761,2763,2765,2767,2769,2771,2773],{"class":215,"line":235},[213,2754,416],{"class":415},[213,2756,419],{"class":238},[213,2758,2464],{"class":231},[213,2760,260],{"class":238},[213,2762,2478],{"class":231},[213,2764,425],{"class":238},[213,2766,428],{"class":415},[213,2768,250],{"class":249},[213,2770,433],{"class":253},[213,2772,257],{"class":249},[213,2774,381],{"class":238},[213,2776,2777,2779,2781,2784,2786,2788,2790,2793,2795],{"class":215,"line":274},[213,2778,416],{"class":415},[213,2780,419],{"class":238},[213,2782,2783],{"class":231}," useUserStore",[213,2785,425],{"class":238},[213,2787,428],{"class":415},[213,2789,250],{"class":249},[213,2791,2792],{"class":253},"@\u002Fstores\u002Fuser",[213,2794,257],{"class":249},[213,2796,381],{"class":238},[213,2798,2799],{"class":215,"line":301},[213,2800,1261],{"emptyLinePlaceholder":1260},[213,2802,2803,2805,2807,2809],{"class":215,"line":342},[213,2804,220],{"class":219},[213,2806,224],{"class":223},[213,2808,228],{"class":227},[213,2810,232],{"class":231},[213,2812,2813,2815,2817,2819,2821,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841,2844,2846,2848],{"class":215,"line":375},[213,2814,239],{"class":238},[213,2816,243],{"class":242},[213,2818,246],{"class":238},[213,2820,250],{"class":249},[213,2822,1230],{"class":253},[213,2824,257],{"class":249},[213,2826,260],{"class":238},[213,2828,263],{"class":470},[213,2830,246],{"class":238},[213,2832,2014],{"class":238},[213,2834,490],{"class":219},[213,2836,2019],{"class":227},[213,2838,544],{"class":231},[213,2840,257],{"class":249},[213,2842,2843],{"class":253},"@\u002Fviews\u002FLogin.vue",[213,2845,257],{"class":249},[213,2847,658],{"class":231},[213,2849,271],{"class":238},[213,2851,2852],{"class":215,"line":531},[213,2853,885],{"class":238},[213,2855,2856,2858,2860,2862,2865,2867],{"class":215,"line":538},[213,2857,890],{"class":242},[213,2859,246],{"class":238},[213,2861,250],{"class":249},[213,2863,2864],{"class":253},"\u002Fdashboard",[213,2866,257],{"class":249},[213,2868,506],{"class":238},[213,2870,2871,2873,2875,2877,2879,2881,2883,2885,2888,2890,2892],{"class":215,"line":553},[213,2872,906],{"class":470},[213,2874,246],{"class":238},[213,2876,2014],{"class":238},[213,2878,490],{"class":219},[213,2880,2019],{"class":227},[213,2882,544],{"class":231},[213,2884,257],{"class":249},[213,2886,2887],{"class":253},"@\u002Fviews\u002FDashboard.vue",[213,2889,257],{"class":249},[213,2891,523],{"class":231},[213,2893,506],{"class":238},[213,2895,2896,2898,2900,2902,2905,2907,2909,2911,2914,2916,2918,2920,2923,2925,2927,2929,2931,2933,2936],{"class":215,"line":559},[213,2897,1390],{"class":242},[213,2899,246],{"class":238},[213,2901,419],{"class":238},[213,2903,2904],{"class":242}," requiresAuth",[213,2906,246],{"class":238},[213,2908,844],{"class":843},[213,2910,260],{"class":238},[213,2912,2913],{"class":242}," roles",[213,2915,246],{"class":238},[213,2917,1415],{"class":231},[213,2919,257],{"class":249},[213,2921,2922],{"class":253},"user",[213,2924,257],{"class":249},[213,2926,260],{"class":238},[213,2928,250],{"class":249},[213,2930,1420],{"class":253},[213,2932,257],{"class":249},[213,2934,2935],{"class":231},"] ",[213,2937,271],{"class":238},[213,2939,2940],{"class":215,"line":1323},[213,2941,556],{"class":238},[213,2943,2944],{"class":215,"line":1473},[213,2945,885],{"class":238},[213,2947,2948,2950,2952,2954,2956,2958],{"class":215,"line":1956},[213,2949,890],{"class":242},[213,2951,246],{"class":238},[213,2953,250],{"class":249},[213,2955,897],{"class":253},[213,2957,257],{"class":249},[213,2959,506],{"class":238},[213,2961,2962,2964,2966,2968,2970,2972,2974,2976,2979,2981,2983],{"class":215,"line":2387},[213,2963,906],{"class":470},[213,2965,246],{"class":238},[213,2967,2014],{"class":238},[213,2969,490],{"class":219},[213,2971,2019],{"class":227},[213,2973,544],{"class":231},[213,2975,257],{"class":249},[213,2977,2978],{"class":253},"@\u002Fviews\u002FAdmin.vue",[213,2980,257],{"class":249},[213,2982,523],{"class":231},[213,2984,506],{"class":238},[213,2986,2987,2989,2991,2993,2995,2997,2999,3001,3003,3005,3007,3009,3011,3013,3015],{"class":215,"line":2397},[213,2988,1390],{"class":242},[213,2990,246],{"class":238},[213,2992,419],{"class":238},[213,2994,2904],{"class":242},[213,2996,246],{"class":238},[213,2998,844],{"class":843},[213,3000,260],{"class":238},[213,3002,2913],{"class":242},[213,3004,246],{"class":238},[213,3006,1415],{"class":231},[213,3008,257],{"class":249},[213,3010,1420],{"class":253},[213,3012,257],{"class":249},[213,3014,2935],{"class":231},[213,3016,271],{"class":238},[213,3018,3019],{"class":215,"line":2407},[213,3020,556],{"class":238},[213,3022,3023,3025],{"class":215,"line":2420},[213,3024,378],{"class":231},[213,3026,381],{"class":238},[213,3028,3029],{"class":215,"line":2425},[213,3030,1261],{"emptyLinePlaceholder":1260},[213,3032,3034,3036,3038,3040,3042,3044],{"class":215,"line":3033},19,[213,3035,220],{"class":219},[213,3037,2459],{"class":223},[213,3039,228],{"class":227},[213,3041,2464],{"class":470},[213,3043,544],{"class":231},[213,3045,661],{"class":238},[213,3047,3049,3051,3053,3055,3057],{"class":215,"line":3048},20,[213,3050,2473],{"class":242},[213,3052,246],{"class":238},[213,3054,2478],{"class":470},[213,3056,473],{"class":231},[213,3058,506],{"class":238},[213,3060,3062,3064],{"class":215,"line":3061},21,[213,3063,2487],{"class":231},[213,3065,506],{"class":238},[213,3067,3069,3071,3073],{"class":215,"line":3068},22,[213,3070,695],{"class":238},[213,3072,523],{"class":231},[213,3074,381],{"class":238},[213,3076,3078],{"class":215,"line":3077},23,[213,3079,1261],{"emptyLinePlaceholder":1260},[213,3081,3083,3085,3087,3089,3091,3093,3095,3097,3099,3101,3103,3105],{"class":215,"line":3082},24,[213,3084,13],{"class":231},[213,3086,495],{"class":238},[213,3088,719],{"class":470},[213,3090,544],{"class":231},[213,3092,609],{"class":219},[213,3094,612],{"class":238},[213,3096,615],{"class":514},[213,3098,260],{"class":238},[213,3100,428],{"class":514},[213,3102,523],{"class":238},[213,3104,490],{"class":219},[213,3106,528],{"class":238},[213,3108,3110,3113,3116,3118,3120,3122],{"class":215,"line":3109},25,[213,3111,3112],{"class":219},"  const",[213,3114,3115],{"class":223}," userStore",[213,3117,228],{"class":227},[213,3119,2783],{"class":470},[213,3121,473],{"class":242},[213,3123,381],{"class":238},[213,3125,3127],{"class":215,"line":3126},26,[213,3128,3129],{"class":534},"  \u002F\u002F 检查是否需要登录\n",[213,3131,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3154,3156,3159,3161],{"class":215,"line":3132},27,[213,3134,630],{"class":415},[213,3136,612],{"class":242},[213,3138,615],{"class":231},[213,3140,495],{"class":238},[213,3142,801],{"class":231},[213,3144,495],{"class":238},[213,3146,806],{"class":231},[213,3148,793],{"class":227},[213,3150,1544],{"class":227},[213,3152,3153],{"class":231},"userStore",[213,3155,495],{"class":238},[213,3157,3158],{"class":231},"isLoggedIn",[213,3160,658],{"class":242},[213,3162,661],{"class":238},[213,3164,3166,3168,3170,3172,3174,3176,3179,3181,3183,3186,3188,3190,3193,3195,3197,3199,3202,3204],{"class":215,"line":3165},28,[213,3167,815],{"class":415},[213,3169,419],{"class":238},[213,3171,319],{"class":242},[213,3173,246],{"class":238},[213,3175,250],{"class":249},[213,3177,3178],{"class":253},"login",[213,3180,257],{"class":249},[213,3182,260],{"class":238},[213,3184,3185],{"class":242}," query",[213,3187,246],{"class":238},[213,3189,419],{"class":238},[213,3191,3192],{"class":242}," redirect",[213,3194,246],{"class":238},[213,3196,796],{"class":231},[213,3198,495],{"class":238},[213,3200,3201],{"class":231},"fullPath",[213,3203,425],{"class":238},[213,3205,831],{"class":238},[213,3207,3209],{"class":215,"line":3208},29,[213,3210,690],{"class":238},[213,3212,3214],{"class":215,"line":3213},30,[213,3215,3216],{"class":534},"  \u002F\u002F 角色校验\n",[213,3218,3220,3222,3224,3226,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,3258,3261,3263],{"class":215,"line":3219},31,[213,3221,630],{"class":415},[213,3223,612],{"class":242},[213,3225,615],{"class":231},[213,3227,495],{"class":238},[213,3229,801],{"class":231},[213,3231,495],{"class":238},[213,3233,1583],{"class":231},[213,3235,793],{"class":227},[213,3237,1544],{"class":227},[213,3239,615],{"class":231},[213,3241,495],{"class":238},[213,3243,801],{"class":231},[213,3245,495],{"class":238},[213,3247,1583],{"class":231},[213,3249,495],{"class":238},[213,3251,1602],{"class":470},[213,3253,544],{"class":242},[213,3255,3153],{"class":231},[213,3257,495],{"class":238},[213,3259,3260],{"class":231},"role",[213,3262,1610],{"class":242},[213,3264,661],{"class":238},[213,3266,3268,3270,3272,3274,3276,3278,3281,3283],{"class":215,"line":3267},32,[213,3269,815],{"class":415},[213,3271,419],{"class":238},[213,3273,319],{"class":242},[213,3275,246],{"class":238},[213,3277,250],{"class":249},[213,3279,3280],{"class":253},"forbidden",[213,3282,257],{"class":249},[213,3284,831],{"class":238},[213,3286,3288],{"class":215,"line":3287},33,[213,3289,690],{"class":238},[213,3291,3293,3295,3297],{"class":215,"line":3292},34,[213,3294,840],{"class":415},[213,3296,844],{"class":843},[213,3298,381],{"class":238},[213,3300,3302,3304,3306],{"class":215,"line":3301},35,[213,3303,695],{"class":238},[213,3305,523],{"class":231},[213,3307,381],{"class":238},[213,3309,3311],{"class":215,"line":3310},36,[213,3312,1261],{"emptyLinePlaceholder":1260},[213,3314,3316,3319,3322,3324],{"class":215,"line":3315},37,[213,3317,3318],{"class":415},"export",[213,3320,3321],{"class":415}," default",[213,3323,2459],{"class":231},[213,3325,381],{"class":238},[3327,3328,3329],"h2",{"id":3329},"总结",[55,3331,3332,3342],{},[58,3333,3334],{},[61,3335,3336,3339],{},[64,3337,3338],{},"主题",[64,3340,3341],{},"核心要点",[81,3343,3344,3370,3394,3406,3419,3434,3450],{},[61,3345,3346,3351],{},[86,3347,3348],{},[182,3349,3350],{},"动态路由",[86,3352,3353,189,3355,189,3357,3360,3361,3364,3365,119,3367,3369],{},[76,3354,93],{},[76,3356,113],{},[76,3358,3359],{},":pathMatch(.*)*","；",[76,3362,3363],{},"useRoute"," + ",[76,3366,400],{},[76,3368,573],{}," 监听参数变化",[61,3371,3372,3377],{},[86,3373,3374],{},[182,3375,3376],{},"导航守卫",[86,3378,3379,3380,189,3382,3384,3385,3387,3388,3391,3392],{},"全局（",[76,3381,719],{},[76,3383,738],{},"）、路由独享（",[76,3386,864],{},"）、组件内（",[76,3389,3390],{},"onBeforeRouteLeave\u002FUpdate","）；Vue Router 4 推荐直接返回布尔值\u002F路由地址，弃用 ",[76,3393,1125],{},[61,3395,3396,3401],{},[86,3397,3398],{},[182,3399,3400],{},"路由元信息",[86,3402,2043,3403,3405],{},[76,3404,801],{}," 中附加权限、布局、缓存等数据，在守卫中读取并控制",[61,3407,3408,3413],{},[86,3409,3410],{},[182,3411,3412],{},"懒加载",[86,3414,3415,3418],{},[76,3416,3417],{},"() => import()"," 自动代码分割，配合 Magic Comments 命名 chunk",[61,3420,3421,3426],{},[86,3422,3423],{},[182,3424,3425],{},"过渡动画",[86,3427,3428,3364,3431,3433],{},[76,3429,3430],{},"\u003CRouterView v-slot>",[76,3432,2202],{}," 实现路由级转场动画",[61,3435,3436,3441],{},[86,3437,3438],{},[182,3439,3440],{},"滚动行为",[86,3442,3443,3445,3446,1001,3448],{},[76,3444,2446],{}," 控制导航后的滚动位置，支持 ",[76,3447,2522],{},[76,3449,2556],{},[61,3451,3452,3457],{},[86,3453,3454],{},[182,3455,3456],{},"历史模式",[86,3458,3459,3461,3462,3465],{},[76,3460,2722],{},"（需服务端支持）、",[76,3463,3464],{},"createWebHashHistory","（纯前端）",[23,3467,3468],{},"掌握以上知识，可以构建功能完善、性能优良且安全可靠的前端路由系统。",[3327,3470,3471],{"id":3471},"参考文献",[23,3473,3474],{},"以下链接在编写时均可正常访问：",[55,3476,3477,3487],{},[58,3478,3479],{},[61,3480,3481,3484],{},[64,3482,3483],{},"资料",[64,3485,3486],{},"说明",[81,3488,3489,3501,3513,3525],{},[61,3490,3491,3498],{},[86,3492,3493],{},[26,3494,3497],{"href":3495,"rel":3496},"https:\u002F\u002Frouter.vuejs.org\u002Fzh\u002F",[30],"Vue Router 文档",[86,3499,3500],{},"中文",[61,3502,3503,3510],{},[86,3504,3505],{},[26,3506,3509],{"href":3507,"rel":3508},"https:\u002F\u002Frouter.vuejs.org\u002Fzh\u002Fguide\u002Fessentials\u002Fdynamic-matching.html",[30],"Vue Router：动态路由",[86,3511,3512],{},"参数与通配",[61,3514,3515,3522],{},[86,3516,3517],{},[26,3518,3521],{"href":3519,"rel":3520},"https:\u002F\u002Frouter.vuejs.org\u002Fzh\u002Fguide\u002Fadvanced\u002Fnavigation-guards.html",[30],"Vue Router：导航守卫",[86,3523,3524],{},"守卫",[61,3526,3527,3533],{},[86,3528,3529],{},[26,3530,3532],{"href":196,"rel":3531},[30],"Vue Router 4 迁移",[86,3534,3535],{},"从 v3 迁移",[2336,3537,3538],{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stp6e, html code.shiki .stp6e{--shiki-light:#39ADB5;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .soE4H, html code.shiki .soE4H{--shiki-light:#8796B0;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":209,"searchDepth":235,"depth":235,"links":3540},[3541,3542,3543,3544,3545,3546,3547,3548,3549,3550],{"id":44,"depth":274,"text":45},{"id":702,"depth":274,"text":703},{"id":1335,"depth":274,"text":1336},{"id":1965,"depth":274,"text":1966},{"id":2191,"depth":274,"text":2192},{"id":2440,"depth":274,"text":2441},{"id":2658,"depth":274,"text":2659},{"id":2730,"depth":274,"text":2731},{"id":3329,"depth":235,"text":3329},{"id":3471,"depth":235,"text":3471},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FVue\u002Frouter","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FVue\u002Frouter.md","md",{"items":3556},[3557,3561,3565,3569,3573,3577],{"path":3558,"title":3559,"description":3560,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fassets","资源优化","图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。",{"path":3562,"title":3563,"description":3564,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fstatus","状态管理：Pinia 与 Vuex","Pinia 是 Vue 官方推荐的状态管理库（Vue 3 新项目默认选型）。Vuex 4 仍可与 Vue 3 配合，用于维护旧项目。见 Vue 基础。",{"path":3566,"title":3567,"description":3568,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fecology","Vue 生态关键库深度集成","VueUse、Vite 与 Nuxt 是 Vue 3 生态中常用的效率与工程化方案。概览见 Vue 基础。",{"path":3570,"title":3571,"description":3572,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Ftrack","依赖收集与派发更新机制","说明 Vue 3 中 track \u002F trigger 与 targetMap 的工作方式。前置：v2-v3 差异。",{"path":3574,"title":3575,"description":3576,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftest","测试策略","Vue 应用常用 Vitest + @vue\u002Ftest-utils 做单元\u002F组件测试，Playwright 或 Cypress 做 E2E。见 Vue 基础。",{"path":3578,"title":3579,"description":3580,"category":10,"score":375},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fdesign","组合式 API 与逻辑复用设计模式","组合式 API 与 Composables 是 Vue 3 推荐的逻辑组织方式。概览见 Vue 基础。",{"series":3582,"title":12,"items":3583,"index":553,"prev":3631,"next":3632},"booklet\u002Fframe\u002Fvue",[3584,3586,3588,3592,3594,3596,3600,3604,3608,3612,3613,3617,3619,3621,3623,3627],{"path":3558,"title":3559,"date":3585,"pinned":15},"2025-11-06",{"path":35,"title":36,"date":3587,"pinned":15},"2021-07-29",{"path":3589,"title":3590,"date":3591,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fbuild","编译与打包优化","2021-08-02",{"path":3578,"title":3579,"date":3593,"pinned":15},"2024-06-16",{"path":3566,"title":3567,"date":3595,"pinned":15},"2025-08-26",{"path":3597,"title":3598,"date":3599,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fproject","工程化实践","2021-07-20",{"path":3601,"title":3602,"date":3603,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fprops","组件通信与跨层级设计模式","2023-03-23",{"path":3605,"title":3606,"date":3607,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fproxy","响应式进阶","2023-03-21",{"path":3609,"title":3610,"date":3611,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Frender","渲染机制与编译原理","2021-09-23",{"path":7,"title":8,"date":14,"pinned":15},{"path":3614,"title":3615,"date":3616,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Fperformance\u002Fruntime","运行时优化","2021-06-10",{"path":3562,"title":3563,"date":3618,"pinned":15},"2025-10-11",{"path":3574,"title":3575,"date":3620,"pinned":15},"2024-10-23",{"path":3570,"title":3571,"date":3622,"pinned":15},"2025-01-13",{"path":3624,"title":3625,"date":3626,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Ftypescript","TypeScript 深度集成","2023-11-29",{"path":3628,"title":3629,"date":3630,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Fvue\u002Freactivity\u002Fv2-v3","Vue 2 与 Vue 3 响应式差异","2022-07-06",{"path":3609,"title":3610,"date":3611,"pinned":15},{"path":3614,"title":3615,"date":3616,"pinned":15}]