學習使用 Vue 路由

前言:現在 Vue 的路由已經開始大規模應用在單頁面應用上了。比較常見的就是路由網址中的 URL 里面的hash(#) ,這個 hash(#)來源于哪里那?沒錯就來自HTML 的錨點技術。大概如下原理:

HTML錨點

一圖勝千言。
源代碼(省略好多h6標簽):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>#的作用</title>
</head>
<body>
    <h1><a href="#title">跳轉到標記一 </a></h1>
    <h6>一行車馬向東疾馳,行不數里,便有數騎馬迎來,馳到車前,翻身下馬,高聲向令狐沖致意,言語禮數,甚是恭敬。</h6>
    <h1><a id="title">我是標記一</a></h1>
    <h6>一行車馬向東疾馳,行不數里,便有數騎馬迎來,馳到車前,翻身下馬,高聲向令狐沖致意,言語禮數,甚是恭敬。</h6>
</body>
</html>

除了使用錨點技術之外,很明顯無論 React 還是 Vue 的路由大量使用了 H5 里面的,window.history,最常用的就包含window.pushState({},"",url)=>添加一條歷史記錄。window.history.replaceState()=>替換當前URL, window.history.forward() => 瀏覽器的前進按鈕,window.history.back() => 瀏覽器的后退按鈕,window.history.go(number)=>瀏覽器前進后退幾步,還有一個 hash 路由事件onhashchange

一、配置一個路由

Vue 官網:https://router.vuejs.org/zh/
項目安裝路由:

npm install --save vue-router

項目目錄的結構:這次目錄的結構做的真好看,制作方法是使用搜狗輸入法的(ctr+shift+z)制表符。

┏ components
┃   ┣ HaHa.vue
┃   ┗ XiXi.vue
┣ router
┃   ┗ router.js
┣ App.vue
┣ main.js
┗ index.html

最終的路由效果:


最終的路由效果

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 學習</title>
</head>
<body>
    <div id="app"></div>
    <script src = "./virtual/bundle.js"></script>
</body>
</html>

main.js 里 vue-router 配置四步走

import Vue from "vue";
import App from "./App.vue";
// ①
import VueRouter from "vue-router";
import routes from "./router/router.js";

// 使用路由②
Vue.use(VueRouter);
// ③
const router = new VueRouter({
   routes
})
new Vue({
    el:"#app",
    render(h){
        return h(App);
    },
    // ④
    router
});

router.js

// 引入組件
import XiXi from "../components/XiXi.vue";
import HaHa from "../components/HaHa.vue";
// 暴露路由需要的數組
export default [
    {path : "/XiXi",component : XiXi},
    {path : "/HaHa",component : HaHa}
];

App.vue

<template>
    <div>
        <h1>路由測試</h1>
        <div class="box">
            <p>我是一個盒子組件將會在這里顯示</p>
            <!-- 使用 router-link 組件來導航. -->
            <!-- 通過傳入 `to` 屬性指定鏈接. -->
            <!-- <router-link> 默認會被渲染成一個 `<a>` 標簽 -->
            <router-link to="/HaHa">Go to HaHa</router-link>
            <router-link to="/XiXi">Go to XiXi</router-link>
            <router-link to="/">Go to home</router-link>
            <!-- 路由出口 -->
            <!-- 路由匹配到的組件將渲染在這里 -->
            <router-view></router-view>
        </div>
    </div>
</template>

<script>
    export default {
        
    }
</script>

<style scoped>
.box{
    width: 500px;
    height: 200px;
    border:20px solid #5e5ed9;
}
</style>

components 里面就是單純的兩個組件。

二、子路由

我們在子組件 HaHa 里面再放入孫子組件 Bar 。URL 變成http://127.0.0.1:8080/#/HaHa/bar

演示效果大約如下:


我們需要改動的地方有:
router.js 里面在組件 HaHa 下面加入 childrren 書寫子路由。與 react 不同的是子路由 不需要在前面加上 /

// 引入組件
import XiXi from "../components/XiXi.vue";
import HaHa from "../components/HaHa.vue";
import Bar from "../components/Bar.vue";
// 暴露路由需要的數組
export default [
    {path : "/XiXi",component : XiXi},
    {
        path : "/HaHa",
        component : HaHa,
        children:[
            {path : "Bar",component:Bar}
        ]
    }
];

除此之外還需要在 HaHa.vue 組件里面放上標簽<router-view></router-view>,用來顯示孫子組件 Bar 。

嵌套路由如何指定默認路由:

{
    path:"user",
    name:user,
    component: ()=>import("@/components/user.vue"),
    children:[
        {
            path:"/",//path:"/"就是默認路由,同時path:""也是,自動訪問
            name:index,
            component: ()=>import("@/components/index.vue")
        }
    ]
}
三、兩種路由跳轉方式
  • 第一種 HTMl 方法
    就是我們上面使用的 <router-link to="/HaHa">Go to HaHa</router-link> 標簽,這個標簽的頁面效果就類似HTML里面的a標簽。
  • JS 方法
    <a @click="$router.push('/HaHa/bar')">Go to HaHa</a> 我們在 a 標簽添加事件,因為配置過路由,$router就像vuex的$store一樣可以在任何子組件使用,我們只需要調用push就能指定跳轉目標。

路由跳轉路徑可為分為兩種:字符串和對象

  1. 字符串上面我們使用的就是字符串
  2. 對象字面量方法,使用對象的時候我們可以:
    • {path:'/HaHa'},還可以傳參分為 params和query的方式,{path:'/HaHa',params:{userId:123}},{path:'/HaHa',query:{name:'hero'}}
    • {name:'haha'} 命名路由,也可以傳參也分為 params和query的方式,{path:'/HaHa',params:{userId:123}},{path:'/HaHa',query:{name:'hero'}}
四、使用路由進行頁面布局

有時候想同時 (同級) 展示多個視圖,而不是嵌套展示,就例如我們的有一個網頁頁面的頭部是一個 Header 組件,頁面底部是一個Footer 組件,頁面中間左邊是一個側邊欄 Slider 組件。右邊是頁面內容 Content 組件。而我們要求輸入的網址必須為:http://127.0.0.1:8080/#/index.html

原型圖如下:


首先我們要是用路由的重定向功能,讓網址一打開http://127.0.0.1:8080/#/就跳轉到http://127.0.0.1:8080/#/index.html。路由文件添加代碼:

{ path: '/', redirect: "/index.html"}

插題路由重定向是可以重定向到自己內部的。

{
    path:"/home",
    redertec:"/index",
    children:[
        {
            path:"/index",
            name:"index",
            children:[
                
            ]
        }
    ]
}

下面來講講如何實現,默認已經完成基本布局HTML大致布局代碼:

<div class="box">
            <!-- 網頁頭部 -->
            <header>我是Header組件</header>
            <!-- 網頁主體 -->
            <main>
                <aside>我是Slider側邊欄組件</aside>
                <section>我是Content組件</section>
            </main>
            <!-- 網頁的底部 -->
            <footer>我是Footer組件</footer>
        </div>

我們只需要把每部分單獨提出來變成組件然后使用路由進行顯示。

  • 命名視圖法
    <header><router-view></router-view></header> 組件 router-view 不加 name 屬性在路由文件里面組件將會默認填充到這里,如果寫了name屬性那么相應的組件會映射到相應的name標簽。
    我們現在把內容給提成四個組件:App.vue 配上路由就變成了
<!-- 網頁頭部 -->
<header><router-view></router-view></header>
<!-- 網頁主體 -->
<main>
    <aside><router-view name="Slider"></router-view></aside>
    <section><router-view name="Content"></router-view></section>
</main>
<!-- 網頁的底部 -->
<footer><router-view name="Footer"></router-view></footer>

然后我們只需要改變路由文件 router.js 文件,讓不同的路由視圖,顯示顯示對應的組件,只需要采用組件組的形式:

// 引入組件
import Header from "../components/Header.vue";
import Footer from "../components/Footer.vue";
import Slider from "../components/Slider.vue";
import Content from "../components/Content.vue";
// 暴露路由需要的數組
export default [
    { path: '/', redirect: "/index.html"},
    { path: '/index.html', components:{
        default:Header,
        Footer,
        Slider,
        Content
    }}
];
五、面包屑導航
面包屑導航

左邊的 Slider 組件作用使用 HTML 進行路由跳轉。代碼為:

<template>
    <div>
        <aside>
            <div>
                <router-link class="cur" to="/">首頁</router-link>
            </div>
            <div>
                <router-link class="cur" to="/index.html/yule">娛樂</router-link>
            </div>
            <div>
                <router-link class="cur" to="/index.html/news">新聞</router-link>
            </div>
        </aside>
    </div>
</template>

<script>
    export default {
        
    }
</script>

<style scoped>
.cur{
    display:inline-block;
    margin:20px 30px;
    text-decoration: none;
    color:#666;
    font-size: 16px;
}
</style>

然后去更改路由文件router.js:

{ 
    path: '/index.html',
    components : {
        default:Header,
        Footer,
        Slider,
        Content
    },
    meta:{chinese:"首頁"},
    children : [
        { path : "news" ,component : News,meta:{chinese:"新聞"}},
        { path : "yule" ,component : Yule,meta:{chinese:"娛樂"}}
    ]
}

路由元信息(meta)?
定義路由的時候可以配置 meta 字段:一個路由匹配到的所有路由記錄會暴露為 $route 對象的 $route.matched 數組。因此,我們需要遍歷 $route.matched 來檢查路由記錄中的 meta 字段。

我們增加了兩個子組件,news 和 yule(娛樂)。
再回到 Content 組件里面。進行配置。

<div>
    當前所在位置:
    <router-link to="/">首頁</router-link> 
    {{$router.currentRoute.matched[1] ? "→ "+$router.currentRoute.matched[1].meta.chinese : null}}
    <router-view></router-view>
</div>

猛一看可能比較懵,我們來看看 $router 身上攜帶了什么參數,我們直接在生命周期beforeCreate 里面打印出來$router


就一個 API 有用:currentRoute,在把currentRoute 打印出來console.log(this.$router.currentRoute);


各個參數用處大約如下:

// 路由模式
console.log(this.$router.mode);
//當前路由的路徑
console.log(this.$router.currentRoute.fullPath);
console.log(this.$router.currentRoute.path);
// URL路徑的查詢結果index.html?name=hero后面的東西{name: "hero"}
console.log(this.$router.currentRoute.query);
// 動態參數/:id,用戶輸入的id格式{id: "876543"}
console.log(this.$router.currentRoute.params);
// <router-view></router-view> 上的name屬性
console.log(this.$router.currentRoute.name);
// meta自定義傳入的參數,可以被我們使用
console.log(this.$router.currentRoute.meta.chinese);
// matched是一個數組,里面存放著滿足當前路徑的所有路由組件的路徑
console.log(this.$router.currentRoute.matched);

需要注意的是用到路由參數的時候,可能會失效,例如: /index.html/news 導航到 /index.html/yule,原來的組件實例會被復用。因為兩個路由都渲染同個組件,比起銷毀再創建,復用則顯得更加高效。重要的是生命周期鉤子不會再被調用。在視圖中如果使用路由參數(參數是對象或數組)插入,造成結果是視圖不更新,所以聲明式寫法必須沒有引用類型。除此之外解決辦法就只能使用官方推薦的方法:

使用 watch (監測變化) $router 對象:

  watch: {
    '$router' (to, from) {
      // 對路由變化作出響應...
    }
  }

或者使用生命周期的 updated 來實現。親測可行,Vue 就是亂,官網上講不行,結果還是的以實際為準。

六、導航守衛(路由鉤子)
1. 組件內的守衛
  • beforeRouteEnter 進入組件觸發
  • beforeRouteUpdate (2.2 新增) 組件復用觸發
  • beforeRouteLeave 離開組件觸發

接下來以 XiXi 組件為例,修改代碼加入路由進入和路由離開:

<template>
    <div>
        <h1>我是嘻嘻組件</h1>
    </div>
</template>

<script>
    export default {
        // 組件內的守衛
        // 導航離開該組件的對應路由時調用
        beforeRouteLeave (to, from , next) {
            // 這個離開守衛通常用來禁止用戶在還未保存修改前突然離開
            const answer = window.confirm("你確定要離開當前頁面!");
            if (answer) {
                next();//確定跳轉
            } else {
                next(false);//導航可以通過 next(false) 來取消
            }
        },
        beforeRouteEnter (to, from, next) {
        // 進入該組件觸發
        // 不!能!獲取組件實例 `this`
        // 因為當守衛執行前,組件實例還沒被創建
        const answer = window.confirm("你確定進入該頁面!");
            if (answer) {
                next(vm => {
                // 通過 `vm` 訪問本組件的實例
                console.log(vm)
                });//確定進入
            } else {
                next(false);//不進入
            }
        }
    }
</script>

<style lang="scss" scoped>

</style>

演示結果:

組件內的守衛

最最重要的就是beforeRouteLeave(將要離開當前路由),這個離開守衛通常用來禁止用戶在還未保存修改前突然離開,簡書寫文章時未保存,或者平時填寫一些表單退出時給出提示用到的就是這個。除了進入路由和離開路由還有個特殊的路由。為此特地新建一個組件 Foo,并配置路由為:{path : "/foo/:id",component : Foo}
Foo組件的內容為:

<template>
    <div>
        <h1>被復用的組件</h1>
    </div>
</template>

<script>
    export default {
        beforeRouteUpdate (to, from, next) {
        // 在當前路由改變,但是該組件被復用時調用
        // 舉例來說,對于一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
        // 由于會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鉤子就會在這個情況下被調用。
        // 可以訪問組件實例 `this`
        const answer = window.confirm("你確定從/foo/1 => /foo/2 !");
            if (answer) {
                next(vm => {
                // 通過 `vm` 訪問本組件的實例
                console.log(vm)
                });//確定進入
            } else {
                next(false);//不進入
            }
        }
    }
</script>

<style lang="scss" scoped>

</style>

在 APP 組件在添加兩個跳轉按鈕:

<router-link to="/foo/1">/foo/1</router-link>
<router-link to="/foo/2">/foo/2</router-link>

查看演示效果:


beforeRouteUpdate

每個守衛方法接收三個參數:

  • to: Route: 即將要進入的目標 路由對象
  • from: Route:當前導航正要離開的路由。
  • next: Function:next(): 進行管道中的下一個鉤子。next(false): 中斷當前的導航。next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。
2. 路由獨享的守衛

可以在路由配置上直接定義 beforeEnter 守衛:此處以 HaHa 組件為例

beforeEnter: (to, from, next) => {
    // 路由獨享的守衛
    const answer = window.confirm("路由獨享的守衛!");
    if (answer) {
        next();//確定進入
    } else {
        next(false);//不進入
    }
}

查看演示:


路由獨享的守衛
3. 全局前置守衛

當一個導航觸發時,全局前置守衛按照創建順序調用。
官網代碼案例:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

太簡單不演示了。

七、路由懶加載

當打包構建應用時,JavaScript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊,然后當路由被訪問的時候才加載對應組件,這樣就更加高效了。

結合 Vue 的異步組件和 Webpack 的代碼分割功能,輕松實現路由組件的懶加載。

使用的形式其實就是組件的函數化:
以前:

component:About

現在:

component: () => import('./About.vue')

這是 Vue 的路由懶加載,還有 webpack 的懶加載方式,我同學項目中也有人用。基本沒變,看了秒懂:

component: r => require.ensure([], () => r(require('../page/home')), 'home')

詳情請去查看這個博客,里面最重要的話就是:

當然其實講了那么長的 require.ensure并沒有什么用,因為這個函數已經被 import() 取代了,但是考慮到之前的版本應該有很多人都是用 require.ensure 方法去加載的,所以還是講一下,而且其實 import 的執行過程跟 require.ensure 是一樣的,只不過用了更友好的語法而已,所以關于 import 的執行流程我也沒啥好講的了,感興趣的人看一下兩者的 API介紹就好了。

八、404 路由設置

常規參數只會匹配被/分隔的 URL 片段中的字符。如果想匹配任意路徑,我們可以使用通配符 (*):

{
  // 會匹配所有路徑
  path: '*'
}
{
  // 會匹配以 `/user-` 開頭的任意路徑
  path: '/user-*'
}

當使用通配符路由時,請確保路由的順序是正確的,也就是說含有通配符的路由應該放在最后。路由 { path: '*' } 通常用于客戶端 404 錯誤。當使用一個通配符時,$router.currentRoute.params 內會自動添加一個名為 pathMatch 參數。它包含了 URL 通過通配符被匹配的部分:
例如輸入錯誤的網址為:http://127.0.0.1:8080/#/index
$router.currentRoute.params.pathMatch 匹配輸出為:/index

八、滾動行為

滾動行為:使用前端路由,當切換到新路由時,想要頁面滾到自己指定位置,或者是保持原先的滾動位置,就像重新加載頁面那樣。

const router = new VueRouter({
   routes,
   scrollBehavior (to, from, savedPosition) {
    // return 期望滾動到哪個的位置切換滾動到
    return { x: 0, y: 1000 }
  }
});
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容