01、什麼是前端路由?
前端路由的一個大背景就是當下流行的單頁應用SPA,一些主流的前端框架,如vue、react、angular都屬於SPA,那什麼是SPA呢?
1.1、SPA
SPA(single-page application)單頁面應用,就是瀏覽器只載入了一個URL地址,一個頁面,應用的所有功能、互動都在這個頁面內進行。而實現單頁面應用的基礎就是ajax
,透過非同步請求動態的切換頁面內容、實現互動,頁面整體沒有重新整理。這避免了頁面URL跳轉,使用者體驗也不會中斷,就像原生應用一樣,體驗比較好。越來越多的系統在使用SPA,尤其是WebApp中使用廣泛。
與SPA
單頁應用對應的就是多頁應用MPA
,當然兩者不是非此即彼的,主要基於業務需求,是可以共存的。
區別 | 單頁面應用(SPA) | 多頁面應用(MPA) |
---|---|---|
頁面組成 | 一個主頁,包含多個頁面片段 | 多個主頁面 |
重新整理方式 | 區域性重新整理 | 整頁重新整理 |
url模式 | hash 雜湊模式 、history 歷史模式 |
history 歷史模式 |
SEO搜尋引擎最佳化 | 難實現,採用頁面靜態化方式最佳化 | 容易實現 |
資料傳遞 | 同一應用內,容易 | 透過url、cookie、localStorage等傳遞,複雜 |
渲染效能 | 首次載入資源多稍慢,切換快,體驗良好 | 切換載入資源,速度慢,使用者體驗差 |
轉場動畫 | 容易實現 | 好像實現不了 |
維護成本 | 相對容易 | 相對複雜 |
SPA的主要表現就是更新檢視而不重新請求頁面,要實現前端的頁面的自主路由控制,而不會重新整理頁面,涉及兩種主流的技術:hash
模式、history
模式,這算是前端路由的核心原理,簡單瞭解一下吧!
1.2、#hash路由原理
hash
( /hæʃ/ )是URL地址中#號後面的內容(包括#),原本的作用是用於HTML頁面內部定位的描點,描點的變化不會導致頁面重新載入。HTTP請求中也不會帶#,所以重新整理也不影響,這是瀏覽器端的本地行為。
- 頁面不重新整理:
hash
的變化不會重新整理頁面,只會觸發瀏覽器定位錨點,這是hash
實現前端路由的基本原理。 - 獲取
hash
:window.location.hash
hash
變更事件:window.hashchange
監聽hash
變化。- 不同的
hash
會進入瀏覽器歷史記錄。
所以,實現過程就比較簡單了!
❶ 監測hash變化:透過hashchange
事件監測hash
變化 。
❷ 載入資源:根據hash
值匹配不同資源進行載入、切換,在Vue中切換的其實就是不同的元件。
?hash-簡易路由示例:codepen
<div id="app2">
<ul id="nav" v-once>
<li v-for="item in navs" v-if="item.title"><a v-bind:href="'#/'+item.url">{{item.title}}</a></li>
</ul>
<div id="main">
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
</div>
</div>
<script>
//components
const NotFound = { template: '<p>404!Page not found</p>' };
const Home = { template: '<p>首頁<br>Home page</p>' };
const Product = { template: '<p>產品頁面<br>Product page<br><input></p>' };
const About = { template: '<p>關於我們<br>About page</p>' };
//導航路由資料
function Route(title, url, name, component) {
this.title = title; this.url = url; this.component = component; this.name = name;
}
let routes = [
new Route("首頁", "home", 'home', Home), new Route("商品", "protect", 'protect', Product),
new Route("招聘", "hr", null, null), new Route("關於", "about", 'about', About),
new Route(null, "not-found", 'not-found', NotFound)];
let components = {};
routes.forEach(item => { components[item.name] = item.component });
//app
let app2 = new Vue({
el: "#app2",
data: { currentRoute: window.location.hash, navs: Object.freeze(routes) },
computed: {
currentComponent: function () {
const com = this.navs.filter(item => '#/' + item.url === this.currentRoute)[0];
if (com && com.component) {
document.title = com.title;
return com.name;
}
return 'not-found';
}
},
components: components,
created: function () {
window.addEventListener("hashchange", () => {
this.currentRoute = window.location.hash;
});
}
});
</script>
1.3、history路由原理
history 是歷史物件,存放當前文件頁面(或框架)的會話歷史記錄(不是瀏覽器的所有歷史記錄)。
history 屬性/方法 | 描述 |
---|---|
length | 會話歷史列表的記錄數量 |
state | 表示歷史堆疊頂部記錄的狀態值,可以是任意可序列化JavaScript物件,限制為2MB |
pushState(stateObj, title[, url]) | 向當前會話的歷史堆疊中新增一條記錄 |
replaceState(stateObj, title[, url]) | 修改 history 物件的當前(棧頂)記錄 |
back() | 返回到(歷史列表中)上一個URL地址。 |
forward() | 前進,載入(歷史列表中)下一個URL地址 |
go(number) | 載入指定相對當前網頁索引位置的歷史列表URL地址,go(-1)等同於back() |
pushState
、replaceState
是HTML5在history
上新增的API,用來新增、修改當前文件的歷史記錄,這兩個API就是用來實現SPA單頁應用前端路由的關鍵。他們的引數相同:(stateObj, title[, url])
- state:一個關聯歷史會話記錄的狀態物件,主要作用是在觸發
popstate
事件時作為引數傳遞,不需要可以為null,透過history.state
可以獲取到當前會話的state
。 - title:新頁面的標題,大部分瀏覽器都沒有管他,可以空著。
- url:網址,可以相對、絕對地址,但不可跨域。這個
url
會更新到瀏覽器位址列,但並不會載入該url
地址,也不檢查是否存在,頁面也不會重新整理!對,要的就是你不重新整理。
基於這兩個API的特性來實現前端路由。用 pushState
還是 replaceState
呢?兩者作用一樣的,唯一的不同就是pushState
會產生歷史記錄,可用於前進、後退。
① 監測url地址變化,
popstate
事件:當state
變化時觸發該事件,在事件中獲取當前url
地址。pushState、replaceState並不會觸發popstate
事件,前進、後退、跳轉才會觸發。- 點選事件:繫結導航按鈕的
click
事件,pushState()
更新url
。
② 載入資源:根據url
值匹配不同資源進行載入、切換。
?注意,頁面第一次載入的時候,不會觸發
popstate
事件。
history-簡易路由示例:codepen
<div id="app3">
<ul id="nav" v-once>
<li v-for="item in navs" v-if="item.title">
<a href="#" v-on:click.prevent="navClick(item)">{{item.title}}</a></li>
</ul>
<div id="main">
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
</div>
</div>
<script>
//components
const NotFound = { template: '<p>404!Page not found</p>' };
const Home = { template: '<p>首頁<br>Home page</p>' };
const Product = { template: '<p>產品頁面<br>Product page<br><input></p>' };
const About = { template: '<p>關於我們<br>About page</p>' };
//導航路由資料
function Route(title, url, name, component) {
this.title = title; this.url = url; this.component = component; this.name = name;
}
let routes = [
new Route("首頁", "home", 'home', Home), new Route("商品", "protect", 'protect', Product),
new Route("招聘", "hr", null, null), new Route("關於", "about", 'about', About),
new Route(null, "not-found", 'not-found', NotFound)];
let components = {};
routes.forEach(item => { components[item.name] = item.component });
//攔截history.pushState,觸發一個事件。不攔截換其他方式也可以,比如點選事件裡。
history.pushState = (function (type) {
let origin = history[type]; //用閉包來儲存原來的方法
return function () {
let out = origin.apply(this, arguments);
let event = new Event(type); //觸發一個自定義事件pushState
event.arguments = arguments;
window.dispatchEvent(event);
return out;
}
})('pushState');
//app
let app3 = new Vue({
el: "#app3",
data: { currentRoute: history.state?.url, navs: Object.freeze(routes) },
computed: {
currentComponent: function () {
const com = this.navs.filter(item => item.url === this.currentRoute)[0];
if (com && com.component) {
document.title = com.title;
return com.name;
}
return 'not-found';
}
},
components: components,
methods: {
navClick: function (route) {
history.pushState({ url: route.url }, null, route.url);
}
},
created: function () {
window.addEventListener("popstate", () => {
this.currentRoute = history.state?.url; //也可以用location.pathname獲取前端url
});
window.addEventListener("pushState", () => { //監聽自定義事件pushState
this.currentRoute = history.state?.url;
});
}
})
</script>
? 重新整理頁面時會重新載入當前(本地路由的)
url
地址,可能就404了,這就需要服務端支援,修改下nginx代理也是可以解決的。
❗history與hash的主要區別,就是不會出現一個#
,看上去更加美觀?好像也沒啥區別吧!
02、開始vue-router
2.1、簡介
Vue Router是Vue官方推出的路由元件,與Vue深度整合,支援hash
、history
兩種模式。
Vue2.*
版本 ▶ 對應vue-router3.*
版本,vue-router3.* 中文文件Vue3.*
版本 ▶ 對應vue-router4.*
版本,vue-router4.* 中文文件
2.2、安裝使用
- 透過
<script>
標籤直接引用vue-router.js
:
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
// 註冊外掛
Vue.use(VueRouter);
- 透過
vue-cli
腳手架搭建vue
的開發框架,整合了vue-router
元件。 - 註冊外掛:
Vue.use(VueRouter)
03、vue-router3入門
3.1、Router選項
✔️Router選項 | 描述 |
---|---|
routes | 路由記錄配置資訊,Array<RouteConfig> |
mode | 路由模式,預設hash ,選項:hash 、history 、abstract(NodeJS環境) |
base | url的基本路徑,"/app/" ,只有history模式有效? |
linkActiveClass | <route-link> 啟用的class 名稱,預設值為router-link-active |
linkExactActiveClass | 精確匹配啟用的class ,預設值為router-link-exact-active ( exact /ɪɡˈzækt/ 精確) |
scrollBehavior | 路由切換完成後的滾動行為回撥,函式 Func(to, from, savedPosition) |
parseQuery/stringifyQuery | 自定義查詢字串的解析/反解析函式 |
//建立路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
✔️routes.routeRouteConfig |
初始化時配置用的路由記錄RouteConfig ,在後續程式碼中使用的為路由物件 |
---|---|
path | 路由url路徑 path: '/user' |
component | Component 元件,可用函式方式import 懶載入元件,提高初始化的效能 |
components | 命名檢視元件,當有多個命名檢視<router-view> 時,也要配置對應的元件 |
name | 給路由取個名字,自己用,沒其他用途,可作為顯示的中文標題 |
redirect | 重定向路由,重定向到另外的path、route。如果帶有query會解析路由出錯? |
alias | path的別名,可一個或多個(陣列Array<string> )別名,渲染元件一樣 |
parent | 父級路由,根級的parent 為undefined |
children | 子路由Array<RouteConfig> ,元件內用<router-view> 元件作為巢狀元件的容器 |
props | 用於給Vue元件引數Props 傳值:boolean | Object | Function- true:自動傳遞動態路徑參 route.params ;- 物件,函式:把它們的結果賦值給元件props引數(按key) |
beforeEnter(to, from, next) | 執行路由前的一個鉤子,私有的鉤子,目的同全域性的守衛鉤子beforeEach |
meta | 路由元資訊,自定義的個性化配置,在路由鉤子中可以訪問處理。meta:{title:'註冊'} |
✔️執行態的 $route 路由物件 |
元件內this.$route 訪問,鉤子函式、導航函式中的to、from、location都是此路由物件 |
---|---|
path | 路由url路徑 |
fullPath | 解析後的完整url,包含query |
params | 存放動態路徑引數,{key:value }物件,元件內使用this.$route.params.id |
query | url查詢引數,{key:value }物件 |
hash | 當前路由的雜湊hash 值 |
name | 路由名稱 |
meta | 後設資料記錄 |
matched | 匹配到的路由記錄列表 |
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名檢視元件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array<string>,
children?: Array<RouteConfig>, // 巢狀路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配規則是否大小寫敏感?(預設值:false)
pathToRegexpOptions?: Object // 編譯正則的選項
}
//$route路由物件
{
name: "user-box", // 路由名稱
fullPath: "/user/21/vip?key=admin",
hash: "", // 當前路由的雜湊
matched: [{… }],
meta: {},
params: { id: '21', type: 'vip' },
path: "/user/21/vip",
query: { key: 'admin' }
}
?簡單的示例:
<style>
.router-link-active{ background-color: rgb(168, 240, 140); }
.nav-item{ margin: 0 10px; }
</style>
<div id="app">
<router-link v-for="r in this.$router.options.routes" :to="r.path" class="nav-item">{{r.name}}</router-link>
<router-view></router-view> <!-- 顯示路由元件檢視的容器,其實是就是一個動態元件 -->
</div>
<script>
//路由配置RouteConfig
let vroutes = [
{ path: '/user', name: '使用者管理', component: { template: '<div>user component</div>' } },
{ path: '/login', name: '登入', component: { template: '<div>login component</div>' } }];
//建立路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//app
let app = new Vue({
el:"#app",
router: vrouter,
})
</script>
3.2、router例項-建立Router()
✔️router例項-屬性 | 描述 |
---|---|
app、apps | Vue根例項,所有apps例項 |
options | 引數選項 |
currentRoute | 當前啟用的路由資訊物件 |
mode | 路由模式:"hash" | "history" | "abstract" |
START_LOCATION | 初始導航的路由地址,route物件 |
✔️Router例項-方法 | 描述 |
全域性的導航守衛 | beforeEach、beforeResolve、afterEach |
程式設計式導航 | push(route)、replace(route)、go(index)、back()、forward() |
resolve() | ❓解析目標位置 |
addRoute(parent?, RouteConfig) | 新增路由記錄、子路由,還有批次新增的addRoutes(routes) |
getRoutes() | 獲取所有活躍的路由記錄列表 Array<RouteConfig> |
onReady(callback,errorback) | 完成初始化後呼叫,初始化錯誤則呼叫errorback |
onError(callback) | 路由過程中出錯時觸發,算是一個全域性路由異常捕獲 |
- 註冊外掛:
Vue.use(VueRouter)
- 建立全域性共享的
router
路由器例項,並配置路由記錄。 - 注入
router
,在根Vue元件上注入router
例項,然後所有地方都可以用this.$router
訪問了. - 用
<router-link>
顯示路由導航,<router-view>
顯示元件檢視。 - 愉快的使用了,在Vue元件中訪問路由的幾種途徑:
this.$router
,Vue中任意地方可以訪問的路由器。this.$route
,元件所屬的route
路由物件。
?建立一個路由:
<style>
#app4 a { margin: 0 5px; }
.router-link-active { background-color: blueviolet; color: #FFF; }
</style>
<div id="app4">
<p>{{$router.mode}}-->{{$router.currentRoute.name}}</p>
<div>
<router-link to="/user/001">使用者001</router-link>
<router-link to="/login">登入</router-link>
<a href="#" @click.prevent="$router.push('/user/002')">a-使用者0002</a>
</div>
<hr>
<router-view></router-view>
</div>
<script>
// 註冊外掛
Vue.use(VueRouter);
let isAuthenticated = true;
//元件
const UserBox = { Prop: ['userId'], template: '<p>使用者資訊:{{$route.params}}</p>' };
const Login = { template: '<p>使用者登入:<br>使用者名稱:<input></p>' };
//路由配置
let vroutes = [
{ path: '/user/:userId', name: '使用者管理', component: UserBox },
{ path: '/login', name: '登入', component: Login, meta: { type: 'vip' } },
{ path: '/*', redirect: '/login' }];
//建立路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//路由器的鉤子:做一個登入許可權驗證,並更新文件標題
vrouter.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' });
else next();
document.title = to.name;
});
//app
let app4 = new Vue({
el: "#app4",
router: vrouter, //注入路由器,內部透過 this.$router 訪問
})
</script>
3.3、path路徑:string
path
為路由的地址,當瀏覽器url
地址與path
匹配時,就會啟用當前route路由物件,並顯示器對應元件component/components
。
let u1 = { path: '/home', component: Home };
let u2 = { path: '/about', component: About };
let u3 = { path: '/user/register', component: Register };
let u3 = { path: '/*', component: NotFound404 };
//動態路徑
let u1 = { path: '/user/:id/:type', component: UserBox }
//匹配的路徑
<router-link to="/user/1/vip">使用者1</router-link>
?:
動態路徑引數:path
中可以設定動態引數,冒號:
開頭,後面的為引數,支援多個順序組裝:path:'/path/:引數1/:引數2'
。這裡的引數有什麼用呢?
- 引數都會被放到到路由物件
$route.params
中。 - 元件內部直接使用:
$route.params.id
。 - 透過引數專遞,設定路由記錄
props:true
,引數值$route.params
會傳遞給元件的引數Props
。
?*
萬用字元:*
萬用字元匹配任意字元,可放到最後面匹配404,或重定向到預設路由。v4版本里刪了,改用正則。
?優先順序:如果相同的path,匹配哪個呢?按照程式碼的順序,先到先得。
3.4、router-link/router-view
<router-link>
:路由導航元件,繫結路由配置,執行路由跳轉。<router-link>
也是一個元件,實際是一個<a>
元素。<router-view>
:路由檢視元件,用來顯示渲染匹配的檢視元件,內部是一個Vue動態元件。如果需要動畫和快取,可以外面巢狀<transition>
、<keep-alive>
使用。
✔️<router-link> |
描述 |
---|---|
to | path,路由的目標地址,字串、路由物件。 |
replace | 預設false =push ,執行導航是用replace ,還是push ,對應history 的兩個Api |
append❓ | 是否新增基路徑base ,預設false |
tag | 最終渲染的的標籤,預設a 。v4中刪掉了,用v-slot實現自定義 |
active-class | 啟用的類class名 |
exact | 是否精確匹配連線地址,預設false。就是說預設是模糊匹配連線地址的,只要包含就啟用了 |
event | 觸發路由的事件型別,預設click 。不怎麼常用,v4版本中刪掉了 |
v-slot | 作用域插槽,用來接收暴露出來的資料,<router-link> 支援插槽 |
✔️<router-view> |
|
name | 命名檢視,當有多個就需要名字了,如切換框架佈局。在路由記錄components 中配置對映關係 |
<style>
#app a{ margin: 0 10px; }
.router-link-active { background-color: rgb(168, 240, 140); }
/* 動畫css */
.v-enter, .v-leave-to { opacity: 0; }
.v-enter { transform: translateX(30px); }
.v-enter-active, v-leave-active { transition: all 1s; }
</style>
<div id="app">
<div>
<h4>router-link</h4>
<router-link to='/user/1/vip'>使用者管理1</router-link>
<router-link to='/login'>登入</router-link>
<!-- url變了,但沒有觸發路由 -->
<a href="#user/2/vvip">a-使用者2</a>
<a href="#" @click.prevent="$router.push('/user/003/vvip')">a-使用者3</a>
</div>
<div>
<h4>v-for繫結</h4>
<router-link v-for="r in this.$router.options.routes" :to="r.path">{{r.name}}</router-link>
</div><hr>
<transition>
<keep-alive>
<router-view style="margin:10px"></router-view>
</keep-alive>
</transition>
</div>
<script>
//路由配置RouteConfig
let vroutes = [
{ path: '/user/:id/:type', name: '使用者管理', component: { template: '<div>user component{{$route.params}}</div>' } },
{ path: '/login', name: '登入', component: { template: '<div>login component<br><input></div>' } }];
//建立路由器
let vrouter = new VueRouter({ routes: vroutes, mode: 'hash', base: '/vsystem/' });
//app
let app = new Vue({
el: "#app",
router: vrouter,
})
</script>
? 當
<router-link>
需要監聽原生事件時,要加上原生修飾符@click.native="nav_click"
3.5、程式設計式導航
除了使用申明式導航<router-link>
元件,也可也使用程式設計式的導航方法自定義實現導航,就是呼叫router提供的導航方法。
router例項-導航方法 | |
---|---|
push(location, onComplete?, onAbort?) | location 可以是url字元,也可以是route物件 |
replace(location, onComplete?, onAbort?) | 同上,不會新增history 記錄, |
go(index)、back()、forward() | 和瀏覽器的history操作一樣的,歷史頁面裡跳轉 |
- 引數location(route物件)如果使用了
path
,則會忽略params
(param /ˈpærəm/ 引數)。 - 回撥 onComplete?、onAbort?,在 3.1.0+,push、replace支援了Promise,會返回一個Promise物件,可鏈式呼叫了:
this.$router.push('/user').then(onComplete).catch(onAbort)
<div>
<h4>a標籤,程式設計式導航</h4>
<a href="#" @click.prevent="$router.push({path:'user/21/vip',query:{key:'admin'}})">使用者1</a>
<a href="#" @click.prevent="navClick">click登入</a>
<a @.prevent href="#/login?key=hello">原生a</a>
<br>
<a href="#" @click.prevent="$router.back()">後退</a>
<a href="#" @click.prevent="$router.forward()">前進</a>
</div>
<script>
let app = new Vue({
el: "#app",
router: router,
methods: {
navClick() {
if (this.$router.currentRoute.path == '/login')
return;
this.$router.push('/login', null, () => { }); //提供一個空的onAbort
this.$router.replace('/login');
this.$router.push('/user').then(onComplete).catch(onAbort); //promise方式使用
//設定了path,params的設定就忽略了
this.$router.push({ path: '/login', query: { key: 'admin' }, params: { id: 100 } });
//也可以用name進行導航。注意後面的catch,因為push、replace都是用promise執行的
this.$router.push({ name: '登入', query: { key: 'admin' }, params: { id: 12 } }).catch(s => { });
}
}
})
</script>
⚠️ 這裡遇到一個小問題,就是透過程式設計事件導航的
<a>
連結重複點選報錯:NavigationDuplicated
原來是vue-router
的一個問題,3.*
版本中引入了promise時也引入了這個bug,如果路由沒變化(重複)就會丟擲一個異常的promise
。v4.*
版本都出來了,這個bug還沒修復!<router-link>
正常,只有導航程式設計才會。
?解決方法:
- 判斷一下當前路由是否已存在。
- 提供一個空的
onAbort
回撥,或者promise
的方式捕獲異常。- 改造一下
VueRouter.prototype
的push
方法。
3.6、導航守衛-鉤子
在導航過程中提供多種守衛(鉤子函式),需要注意的是,動態path
引數、<keep-alive>
都會複用元件,此時元件的生命週期就不完整了,需要根據實際情況選擇合適的地方。
✔️router例項-全域性鉤子守衛 | 描述 |
---|---|
beforeEach(to, from, next) | 導航執行前,可透過next 取消。可用來驗證登陸許可權,如果沒認證則跳轉到登陸 |
beforeResolve(to, from, next) | beforeEach 執行後,也是前置守衛 |
afterEach(to, from) | 導航已經離開時觸發,這裡沒有next(不可取消路由),因為已經離開了 |
✔️路由配置記錄route-的獨有鉤子 | |
beforeEnter(to, from, next) | 執行路由前呼叫 |
✔️Vue元件中新增的-鉤子守衛 | Vue元件的鉤子 |
beforeRouteEnter(to, from, next) | 進入前:元件路由被confirmed(已確認)前,元件還沒建立,不能獲取this |
beforeRouteUpdate(to, from, next) | 只有動態path 引數複用元件時才觸發,更新當前路由。 |
beforeRouteLeave(to, from, next) | 路由將要離開該元件前觸發,this 可用,next(false) 可取消。 |
?鉤子的引數:(to, from, next)
to: Route
:目標路由物件。from: Route
:當前導航路由物件,也是要離開的。next: Function
:本次路由怎麼執行?內建的回撥,必須呼叫。nex()/next(true)
:允許執行,並繼續,全部鉤子執行完畢,導航狀態為confirmed(已確認)。next(false)
:?不執行,中斷當前導航,重置導航到from。next({route})
:❗中斷當前導航,並進行一個新的導航到route
。特殊next(callback)
:beforeRouteEnter
的next
接收一個回撥函式,引數為元件vm
,可用來請求一些ajax資料,回撥會在元件建立後呼叫。
?導航守衛-鉤子的生命週期流程圖,守衛鉤子測試程式碼: CodePen
?使用意見:
- 如果只是對路由做校驗或邏輯處理,建議用路由的全域性鉤子守衛
beforeEach
,若只是針對某個特定路由,則用路由記錄的獨有鉤子beforeEnter
。 - 如果是需要基於元件做一些操作,如資料載入、未儲存提示,則用Vue元件的路由守衛鉤子。
- 在複用元件時,透過
watch
監測路由物件$route
的變化也是一個途徑。
04、vue-router4 區別
- 函式建立:createRouter({ }),沒有之前的類建立了。
- mode路由模式:mode沒了,變成了函式建立
history
:createWebHistory()
、createWebHashHistory()
。
const router = createRouter({
history:createWebHashHistory() / createWebHashHistory(),
routes: [],
})
base
放到了上面的建立函式引數裡。- 例項函式
router.onReady()
改為IsReady()
,該方法返回一個Promise
。 <router-view>
支援了插槽v-slot
,支援就算了,關鍵是影響有點大。<keep-alive>
和<transition>
只能透過插槽嵌入到<router-view>
裡面,不像之前是放到外面的。<router-view>
元件的模板也只能透過v-slot
+ 動態元件來實現了。
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<router-link>
的tag
沒了,透過插槽實現。- 所有的導航現在都是非同步的。
05、其他問題
❓如何構建多級選單的導航?基本思路:
- 首先是路由選單資料,應該是後臺資料庫統一管理,包括選單名稱、編碼、圖示、路徑path、上下級結構資訊等等。
- 選單是多級的,這由功能架構來決定,路由還是一級的,因為檢視區域是一致的。so,從上述資料中構建2份資料,一份實現多級Dom選單,另外一份構建路由配置資料。
❓多標籤怎麼實現,可以管理使用多個標籤?基本思路:
- 首先要記錄開啟的路由資訊,可以透過路由守衛攔截監測。
- 用一個標籤欄來顯示這些開啟的路由資訊,自己實現切換路由即可。
- 選單、標籤相互聯動,標籤刪除時需按照一定規則路由到下一個標籤上。
- 重新整理儲存檢視狀態:vuex儲存選單、標籤顯示狀態。
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀