vue-router路由之路-極簡教程

安木夕發表於2022-12-15

image.png

01、什麼是前端路由?

前端路由的一個大背景就是當下流行的單頁應用SPA,一些主流的前端框架,如vue、react、angular都屬於SPA,那什麼是SPA呢?

1.1、SPA

SPA(single-page application)單頁面應用,就是瀏覽器只載入了一個URL地址,一個頁面,應用的所有功能、互動都在這個頁面內進行。而實現單頁面應用的基礎就是ajax,透過非同步請求動態的切換頁面內容、實現互動,頁面整體沒有重新整理。這避免了頁面URL跳轉,使用者體驗也不會中斷,就像原生應用一樣,體驗比較好。越來越多的系統在使用SPA,尤其是WebApp中使用廣泛。

image.png

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實現前端路由的基本原理。
  • 獲取 hashwindow.location.hash
  • hash 變更事件window.hashchange監聽hash變化。
  • 不同的hash會進入瀏覽器歷史記錄。

http://www.xxx.cn/#/about
http://www.xxx.cn/#/pro-info-list

所以,實現過程就比較簡單了!

監測hash變化:透過hashchange事件監測hash變化 。

載入資源:根據hash值匹配不同資源進行載入、切換,在Vue中切換的其實就是不同的元件。

image

?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>

image.png

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()

pushStatereplaceState 是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深度整合,支援hashhistory兩種模式。

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,選項:hashhistory、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 父級路由,根級的parentundefined
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>

image.png

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) 路由過程中出錯時觸發,算是一個全域性路由異常捕獲
  1. 註冊外掛:Vue.use(VueRouter)
  2. 建立全域性共享的router路由器例項,並配置路由記錄。
  3. 注入router,在根Vue元件上注入router例項,然後所有地方都可以用 this.$router訪問了.
  4. <router-link>顯示路由導航,<router-view>顯示元件檢視。
  5. 愉快的使用了,在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>

1.gif

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 最終渲染的的標籤,預設av4中刪掉了,用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>

1.gif

? 當<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

image.png

原來是vue-router的一個問題,3.*版本中引入了promise時也引入了這個bug,如果路由沒變化(重複)就會丟擲一個異常的promisev4.*版本都出來了,這個bug還沒修復!<router-link>正常,只有導航程式設計才會。

image.png

?解決方法

  1. 判斷一下當前路由是否已存在。
  2. 提供一個空的onAbort回撥,或者promise的方式捕獲異常。
  3. 改造一下VueRouter.prototypepush方法。

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)beforeRouteEnternext接收一個回撥函式,引數為元件vm,可用來請求一些ajax資料,回撥會在元件建立後呼叫。

?導航守衛-鉤子的生命週期流程圖,守衛鉤子測試程式碼: CodePen

image

?使用意見

  • 如果只是對路由做校驗或邏輯處理,建議用路由的全域性鉤子守衛beforeEach,若只是針對某個特定路由,則用路由記錄的獨有鉤子beforeEnter
  • 如果是需要基於元件做一些操作,如資料載入、未儲存提示,則用Vue元件的路由守衛鉤子。
  • 在複用元件時,透過watch監測路由物件$route的變化也是一個途徑。

04、vue-router4 區別

  • 函式建立createRouter({ }),沒有之前的類建立了。
  • mode路由模式:mode沒了,變成了函式建立historycreateWebHistory()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選單,另外一份構建路由配置資料。

image.png

❓多標籤怎麼實現,可以管理使用多個標籤?基本思路:

  • 首先要記錄開啟的路由資訊,可以透過路由守衛攔截監測。
  • 用一個標籤欄來顯示這些開啟的路由資訊,自己實現切換路由即可。
  • 選單、標籤相互聯動,標籤刪除時需按照一定規則路由到下一個標籤上。
  • 重新整理儲存檢視狀態:vuex儲存選單、標籤顯示狀態。

image.png


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

相關文章