VueRouter--路由
????這里的路由是SPA(單頁應(yīng)用)的路徑管理器。再通俗的說,vue-router就是WebApp的鏈接路徑管理系統(tǒng)。
????vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,適合用于構(gòu)建單頁面應(yīng)用。vue的單頁面應(yīng)用是基于路由和組件的,路由用于設(shè)定訪問路徑,并將路徑和組件映射起來。傳統(tǒng)的頁面應(yīng)用,是用一些超鏈接來實現(xiàn)頁面切換和跳轉(zhuǎn)的。在vue-router單頁面應(yīng)用中,則是路徑之間的切換,也就是組件的切換。路由模塊的本質(zhì) 就是建立起url和頁面之間的映射關(guān)系。
????至于為什么不能用a標(biāo)簽?這是因為用Vue做的都是單頁應(yīng)用(當(dāng)你的項目準(zhǔn)備打包時,運(yùn)行npm run build時,就會生成dist文件夾,這里面只有靜態(tài)資源和一個index.html頁面),所以你寫的<a></a>標(biāo)簽是不起作用的,你必須使用vue-router來進(jìn)行管理。
一、vue-router實現(xiàn)原理:
????SPA(single page application):單一頁面應(yīng)用程序,只有一個完整的頁面;它在加載頁面時,不會加載整個頁面,而是只更新某個指定的容器中內(nèi)容。單頁面應(yīng)用(SPA)的核心之一是: 更新視圖而不重新請求頁面;vue-router在實現(xiàn)單頁面前端路由時,提供了兩種方式:Hash模式和History模式;根據(jù)mode參數(shù)(mode:'hash')來決定采用哪一種方式。
1、Hash模式:
????vue-router 默認(rèn) hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當(dāng) URL 改變時,頁面不會重新加載。 hash(#)是URL 的錨點,代表的是網(wǎng)頁中的一個位置,單單改變#后的部分,瀏覽器只會滾動到相應(yīng)位置,不會重新加載網(wǎng)頁,也就是說hash 出現(xiàn)在 URL 中,但不會被包含在 http 請求中,對后端完全沒有影響,因此改變 hash 不會重新加載頁面;同時每一次改變#后的部分,都會在瀏覽器的訪問歷史中增加一個記錄,使用”后退”按鈕,就可以回到上一個位置;所以說Hash模式通過錨點值的改變,根據(jù)不同的值,渲染指定DOM位置的不同數(shù)據(jù)。hash 模式的原理是 onhashchange 事件(監(jiān)測hash值變化),可以在 window 對象上監(jiān)聽這個事件。
1、Histoey模式:
????由于hash模式會在url中自帶#,如果不想要很丑的 hash,我們可以用路由的 history 模式,只需要在配置路由規(guī)則時,加入"mode: 'history'",這種模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。這兩個方法應(yīng)用于瀏覽器記錄棧,在當(dāng)前已有的 back、forward、go 基礎(chǔ)之上,它們提供了對歷史記錄修改的功能。只是當(dāng)它們執(zhí)行修改時,雖然改變了當(dāng)前的 URL ,但瀏覽器不會立即向后端發(fā)送請求。
????當(dāng)你使用 history 模式時,URL 就像正常的 url,例如 http://yoursite.com/user/id,比較好看!
不過這種模式要玩好,還需要后臺配置支持。因為我們的應(yīng)用是個單頁客戶端應(yīng)用,如果后臺沒有正確的配置,當(dāng)用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就不好看了。
所以呢,你要在服務(wù)端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。
{path: "*", redirect: "/"}]
//此處就設(shè)置如果URL輸入錯誤或者是URL 匹配不到任何靜態(tài)資源,就自動跳到到Home頁面
二、vue-router使用
1、安裝路由
npm istall vue-router --save
2、在src/router/inde.js文件中引入、使用路由
import VueRouter from 'vue-router' //引入路由
Vue.use(VueRouter) 使用路由
3、在src/main.js文件中引入路由配置文件index.js文件,并將路由對象傳遞給vue實例
import router from './router' //引入路由配置文件
Vue.use(VueRouter) 使用路由
new Vue({
el: '#app',
router, //傳遞路由對象給vue實例
render: h => h(App)
})
4、在src/router/inde.js配置路由
import Index from './路徑/Index' //引入組件
const router = new VueRouter({
mode:'history', //history模式,可以去除URL里面的hash符號(#)
routes:[ //注冊組件路由,
{
path:'/', //path設(shè)置路徑,'/'表示根路徑
conponent:Index, //設(shè)置的路徑對應(yīng)的組件名
name:'index' //組件引用名
}
]
})
5、在app.vue中留坑<router-view></router-view>,Index.vue的內(nèi)容就會展示到router-view這個容器里面
//app.vue中
<template>
<div>
<!-- 留坑,非常重要 -->
<router-view></router-view>
</div>
</template>
三、路由跳轉(zhuǎn)
[一]、<router-link>基本屬性
<router-link to="" tag="">主頁</router-link>
(1)to屬性表示綁定地址。to="/"
(2):to="",綁定動態(tài)路由,與data(){}中的變量進(jìn)行綁定--詳見下文[三]、動態(tài)路由。
綁定路由表中的name屬性。:to="{name:路由表中name值}"
(3)tag屬性表示以什么標(biāo)簽名顯示,默認(rèn)以a標(biāo)簽顯示;tag=div表示將該標(biāo)簽以div呈現(xiàn)
(4)children:[],子路由在這里面配置,children:[{path:'/',component:xxx,name:xxx,cildren:[三級路由]},....]
[二]、路由跳轉(zhuǎn)
1、跳轉(zhuǎn)到上一次瀏覽的頁面--this.$router.go(-1);
<template>
<div class="container">
<h1>home</h1>
<button @click="goToMenu" class="btn btn-success">just do it</button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
goToMenu(){
//跳轉(zhuǎn)到上一次瀏覽的頁面
this.$router.go(-1);
}
}
}
</script>
2、跳轉(zhuǎn)到指定的頁面--this.$router.replace('/about')
<template>
<div class="container">
<h1>home</h1>
<button @click="goToMenu" class="btn btn-success">just do it</button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
goToMenu(){
//指定跳轉(zhuǎn)
this.$router.replace('/about')
}
}
}
</script>
3、跳轉(zhuǎn)到指定路由的名字下--this.$router.replace({name:'nameValue'})
在router/index.js文件下,對Menu.vue組件進(jìn)行name賦值
{
path:'/menu',
component:Menu,
name:'menuLink'
},
在Home.vue文件中
<template>
<div class="container">
<h1>home</h1>
<button @click="goToMenu" class="btn btn-success">just do it</button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
goToMenu(){
//跳轉(zhuǎn)到指定路由的名字
this.$router.replace({name:'menuLink'})
}
}
}
</script>
4、通過this.$router.push()地址,或者name進(jìn)行跳轉(zhuǎn)
- 開發(fā)中這種方法最常用
<template>
<div class="container">
<h1>home</h1>
<button @click="goToMenu" class="btn btn-success">just do it</button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
goToMenu(){
//push地址跳轉(zhuǎn)
this.$router.push('/menu')
//或者是通過name屬性
//this.$router.push({name:'menuLink'})
}
}
}
</script>
[三]、動態(tài)路由
動態(tài)路由是將:to屬性的值設(shè)置為data中的變量(:to="routerDemo"),將具體的路由地址賦值給變量
<li><router-link :to="homeLink">主頁</router-link></li>
export default {
name: "Header",
data(){
return{
homeLink:'/'
}
}
}
</script>
[四]、以導(dǎo)航欄的實現(xiàn)來理解
此導(dǎo)航欄只使用了bootstrapcss樣式,在index.html中插入
<link rel="stylesheet" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
1、創(chuàng)建主頁(Home)、菜單(OrderingGuide)、管理(Admin)、關(guān)于我們(about/About)、登錄(Login)、注冊(Register)組件
<!-- home組件,其余組件略過-->
<template>
<h1>home</h1>
</template>
<script>
export default {
name: "Home"
}
</script>
2、在src/router/index.js中配置路由
import Home from './components/Home'
import Menu from './components/Menu'
import Admin from './components/Admin'
import About from './components/about/About'
import Login from './components/Login'
import Register from './components/Register'
const router = new VueRouter({
mode:'history', //history模式,可以去除URL里面的hash符號(#)
routes:[ //注冊組件路由,
{
path:'/',
component:Home,
},{
path:'/menu',
component:Menu
},{
path:'/admin',
component:Admin
},{
path:'/about',
component:About,
},{
path:'/login',
component:Login
},{
path:'/register',
component:Register
}
]
})
3、新建一個header.vue組件,用于寫頂部導(dǎo)航欄
<template>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- svg矢量圖logo-->
<a class="py-2" href="#" aria-label="Product">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="d-block mx-auto" role="img" viewBox="0 0 24 24" focusable="false"><title>Product</title><circle cx="12" cy="12" r="10"></circle><path d="M14.31 8l5.74 9.94M9.69 8h11.48M7.38 12l5.74-9.94M9.69 16L3.95 6.06M14.31 16H2.83m13.79-4l-5.74 9.94"></path></svg>
</a>
<a href="/" class="navbar-brand">Pizza點餐系統(tǒng)</a>
<!-- 頂部 左側(cè)-->
<ul class="navbar-nav">
<!-- navbar-nav去除li前面的小點-->
<li><router-link to="/" class="nav-link">主頁</router-link></li>
<li><router-link to="/menu" class="nav-link">菜單</router-link></li>
<li><router-link to="/admin" class="nav-link">管理</router-link></li>
<li><router-link to="/about" class="nav-link">關(guān)于我們</router-link></li>
</ul>
<!-- 頂部-右側(cè)-->
<ul class="navbar-nav ml-auto">
<!-- ml-auto實現(xiàn)兩個li在頂部最右側(cè)-->
<li><router-link to="/login" class="nav-link">登錄</router-link></li>
<li><router-link to="/register" class="nav-link">注冊</router-link></li>
</ul>
</nav>
</header>
</template>
<script>
export default {
name: "Header"
}
</script>
4、在app.vue中引入導(dǎo)航欄,以及為導(dǎo)航欄的導(dǎo)航組件(主頁、菜單、管理等)留坑
<template>
<div id="app">
<!-- header導(dǎo)航欄組件將在這展示-->
<div class="container">
<app-header></app-header>
</div>
<!-- 為導(dǎo)航欄的導(dǎo)航組件(主頁、菜單、管理等)留坑-->
<div class="container">
<router-view></router-view>
</div>
</div>
</template>
<script>
import Header from "./components/Header";
export default {
name: 'app',
components:{
//屬性名:組件名
//屬性名駝峰式命名的話 ,ES6會自動解析為app-header;如果屬性名命名為app-header的話,必須使用引號
appHeader:Header,
}
}
</script>
[五]、路由嵌套
1、在router/index.js文件中配置一級、二級、三級路由
//一級路由
import About from './components/about/About'
//二級路由
import Contact from "./components/about/Contact";
import Delivery from "./components/about/Delivery";
import History from "./components/about/History";
import OrderingGuide from "./components/about/OrderingGuide";
//三級路由
import Person from "./components/about/contact/Person";
import Phone from "./components/about/contact/Phone";
{
path:'/about',
component:About,
name:'aboutLink',
redirect:'/about/history',//默認(rèn)顯示
children:[
{
path:'/about/contact',
name:'contactLink',
component:Contact,
children:[
{
path:'/phone',
name:'phoneNumber',
component:Phone
},{
path:'/person',
name:'person',
component:Person
},
]
},{
path:'/about/delivery',
name:'deliveryLink',
component:Delivery
},{
path:'/about/history',
name:'historyLink',
component:History
},{
path:'/about/orderingGuide',
name:'orderingGuideLink',
component:OrderingGuide
},
]
},
2、一級路由的vue文件
導(dǎo)航欄header組件在app.vue中使用
<template>
<div id="app">
<div class="container">
<!--使用header組件-->
<app-header></app-header>
</div>
<div class="container">
<!--header上的每一個按鈕對應(yīng)的vue文件內(nèi)容將展示在此-->
<router-view></router-view>
</div>
</div>
</template>
<script>
import Header from "./components/Header";
export default {
name: 'app',
components:{
//屬性名:組件名
//屬性名駝峰式命名的話 ,ES6會自動解析為app-header;如果屬性名命名為app-header的話,必須使用引號
appHeader:Header,//引用header組件
},
}
</script>
header組件內(nèi)容
<template>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- 頂部 左側(cè)-->
<ul class="navbar-nav">
<!-- navbar-nav去除li前面的小點-->
<li><router-link to="/about" class="nav-link">關(guān)于我們</router-link></li>
</ul>
<!-- 頂部-右側(cè)-->
</nav>
</header>
</template>
3、二級路由的vue文件
<template>
<div>
<div class="row mb-5">
<div class="col-4">
<!-- 導(dǎo)航router-link-->
<div class="list-group mb-5">
<router-link tag="li" class="nav-link" :to="{name:'historyLink'}">
<a class="list-group-item list-group-item-action">歷史訂單</a>
</router-link>
<router-link tag="li" class="nav-link" :to="{name:'contactLink'}">
<a class="list-group-item list-group-item-action">聯(lián)系我們</a>
</router-link>
<router-link tag="li" class="nav-link" :to="{name:'orderingGuideLink'}">
<a class="list-group-item list-group-item-action">點餐</a>
</router-link>
<router-link tag="li" class="nav-link" :to="{name:'deliveryLink'}">
<a class="list-group-item list-group-item-action">快遞信息</a>
</router-link>
</div>
</div>
<div class="col-8">
<!-- 留坑,三級路由的vue文件內(nèi)容將展示在此。about路由下的兩個子路由內(nèi)容,點擊后將展示在此-->
<router-view></router-view>
</div>
</div>
</div>
</template>
[六]、router-view復(fù)用,通過路由的方式重復(fù)使用同一個組件
1、在需要復(fù)用的路由下添加conponents屬性
//src/router/index.js
{
path:'/',
component:Home,
name:'homeLink',
components:{ //需要復(fù)用的組件
'orderingGuide':OrderingGuide,
'delivery':Delivery,
'history':History
}
},
2、在需要復(fù)用組件的vue文件使用router-view-----未完待續(xù)
<!-- router-view復(fù)用-->
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-4">
<router-view name="orderingGuide"></router-view>
</div>
<div class="col-sm-12 col-md-4">
<router-view name="delivery"></router-view>
</div>
<div class="col-sm-12 col-md-4">
<router-view name="history"></router-view>
</div>
</div>
</div>
</div>
四、路由守衛(wèi)
1、全局守衛(wèi)
router.beforeEach((to,from,next)=>{
to:路由地址,即將要進(jìn)入的目標(biāo) 路由對象
from:路由地址,從哪一個路由離開
next:異步函數(shù),決定展示頁面
})
// 全局守衛(wèi)
router.beforeEach((to,from,next)=>{
comsole.log(to);
//這里通常是判斷store.getters.isLogin===false來確定是否登錄
if(to.path == '/login' || to.path == '/register'){
next();
}else{
alert('未登錄')
next('/login')
}
})
2、全局后置鉤子
router.afterEach((to,from)=>{
to:路由地址,即將要進(jìn)入的目標(biāo) 路由對象
from:路由地址,從哪一個路由離開
})
3、路由獨(dú)享守衛(wèi)
beforeEnter:(to,from,next)=>{
//寫在路由配置里面
}
{
path:'/admin',
component:Admin,
name:'adminLink',
beforeEnter:(to,from,next)=>{
alert('非管理員禁止進(jìn)入');
if(to.path == '/login' || to.path == '/register'){
next();
}else{
alert('未登錄')
next('/login')
}
}
},
4、組件內(nèi)的守衛(wèi)
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應(yīng)路由被 confirm 前調(diào)用
// 不!能!獲取組件實例 `this`
// 因為當(dāng)守衛(wèi)執(zhí)行前,組件實例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當(dāng)前路由改變,但是該組件被復(fù)用時調(diào)用
// 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時候,
// 由于會渲染同樣的 Foo 組件,因此組件實例會被復(fù)用。而這個鉤子就會在這個情況下被調(diào)用。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開該組件的對應(yīng)路由時調(diào)用
// 可以訪問組件實例 `this`
}
(1)、beforeRouteEnter
beforeRouteEnter(to,from,next){
//會報錯,Cannot read property 'name' of undefined
alert('查看現(xiàn)在能否拿到data內(nèi)的數(shù)據(jù)'+this.name);
}
????beforeRouteEnter 守衛(wèi) 不能 訪問 this,因為守衛(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場的新組件還沒被創(chuàng)建。
????不過,你可以通過傳一個回調(diào)給 next來訪問組件實例。在導(dǎo)航被確認(rèn)的時候執(zhí)行回調(diào),并且把組件實例作為回調(diào)方法的參數(shù)。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實例
alert('查看現(xiàn)在能否拿到data內(nèi)的數(shù)據(jù)'+vm.name);
})
}
(2)、beforeRouteUpdate
待續(xù).....
(3)、beforeRouteLeave
beforeRouteLeave(to,from,next){
alert('查看現(xiàn)在能否拿到data內(nèi)的數(shù)據(jù)'+this.name)
if(confirm('確定離開嗎???') == true){
next()
}else{
next(false)
}
}