如果我們采用SPA(單網(wǎng)頁應(yīng)用)的設(shè)計(jì)方式,服務(wù)器會(huì)把前端文件一次性發(fā)過來,前端通過監(jiān)聽url的改變,選擇展示那些內(nèi)容,也就是前端路由
一. 如何改變url但是頁面不刷新?
方式一: 改變哈希值hash
比如,我們隨便找一個(gè)網(wǎng)頁

我們?cè)跒g覽器控制臺(tái)輸入

發(fā)現(xiàn)網(wǎng)站的url有了些改變

查看network卻沒有任何請(qǐng)求

方式二 history模式的方法
(1). history.pushState()壓棧式
比如,我們隨便找一個(gè)網(wǎng)頁

我們?cè)跒g覽器控制臺(tái)輸入
history.pushState({},'','home')

發(fā)現(xiàn)網(wǎng)站的url有了些改變

查看network卻沒有任何請(qǐng)求

這種壓棧式方法,可以使用瀏覽器返回上一頁按鈕彈棧,實(shí)際上是在調(diào)用
history.back()
也可以使用history.go() 操作棧
history.forward() 等價(jià)于 history.go(1)
history.back() 等價(jià)于 history.go(-1)
(2): history.replaceState()非壓棧式
history.replaceState({},'','home')
二. 認(rèn)識(shí)vue-router
1.安裝 vue-router
cnpm install vue-router --save
我們也可以在用CLi項(xiàng)目創(chuàng)建時(shí)就選擇好 vue-router 這樣項(xiàng)目創(chuàng)建時(shí)會(huì)送我們一個(gè)hello world的例子,我們可以先體驗(yàn)一下


2.創(chuàng)建文件夾router,創(chuàng)建index.js
用來存放我們的路由配置
3.路由創(chuàng)建過程
首先,我們?cè)趇ndex.js創(chuàng)建一個(gè)vuerouter并暴露
//1.導(dǎo)入VueRouter
import VueRouter from 'vue-router'
//2.導(dǎo)入Vue (因?yàn)楹竺嬉肰ue.use())
import Vue from 'vue'
//3.通過Vue.use安裝一下插件
Vue.use(VueRouter)
//4.創(chuàng)建路由對(duì)象
routes=[]
const router = new VueRouter({
routes
})
//5.暴露路由對(duì)象
export default router
在main.js中,我們掛載這個(gè)vuerouter
import Vue from 'vue'
import App from './App.vue'
import router from './router' //6.導(dǎo)入暴露的vuerouter (/index.js是默認(rèn),路徑中省略了)
Vue.config.productionTip = false
new Vue({
router, //7. 將導(dǎo)入的router傳給自己的router屬性
render: h => h(App),
}).$mount('#app')
4.配置映射關(guān)系
上面的模板中,routes=[], 我們沒有配置映射關(guān)系, 現(xiàn)在我們來配置一下:
第一步,導(dǎo)入頁面組件
第二步,創(chuàng)建映射關(guān)系
第三步,在app.vue模板中(上級(jí)組件),添加router-link標(biāo)簽,這是一個(gè)觸發(fā)url改變的入口
第四步,在app.vue模板中(上級(jí)組件),添加router-view占位符標(biāo)簽,確定請(qǐng)求過來的內(nèi)容的占位
在router/index.js中
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home' //1. 導(dǎo)入第一個(gè)組件Home
import About from '../components/About' //1. 導(dǎo)入第二個(gè)組件About
Vue.use(VueRouter)
const routes = [
{
path: '/', //2.添加映射關(guān)系
component: Home
},
{
path: '/about', //2.添加映射關(guān)系
component: About
}
]
const router = new VueRouter({
routes
})
export default router
在App.js中
<template>
<div id="app">
<router-link to="/home">首頁</router-link>
<!-- 3.使用路由 通過router-link標(biāo)簽,配置to屬性提供鏈接目標(biāo),點(diǎn)擊此標(biāo)簽會(huì)觸發(fā)對(duì)應(yīng)的url請(qǐng)求-->
<router-link to="/about">關(guān)于</router-link>
<!-- 3.使用路由 通過router-link標(biāo)簽,配置to屬性提供鏈接目標(biāo),點(diǎn)擊此標(biāo)簽會(huì)觸發(fā)對(duì)應(yīng)的url請(qǐng)求-->
<router-view></router-view>
<!-- 4.使用路由 通過router-view占位符標(biāo)簽,確定請(qǐng)求過來的內(nèi)容的占位-->
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style scoped>
</style>
路由重定向:
const routes = [
{
path:'',
redirect:'home'
},
]
5.將默認(rèn)的哈希方式改為history模式
vue默認(rèn)是使用hash方式的,

我們可以給它改成history模式,
這需要,我們?cè)趓outer/index.js中創(chuàng)建VueRouter時(shí)傳入另一個(gè)參數(shù)
const router = new VueRouter({
routes,
mode:'history'
})

此外我們?cè)赾li初始化項(xiàng)目時(shí),也可以默認(rèn)history模式

6.router-link 屬性
to屬性 :指向的url地址
tag屬性: 默認(rèn)是a標(biāo)簽,如果tag='button',則渲染成標(biāo)簽.此外還可以是 <li>等
replace屬性: 采用非壓棧式跳轉(zhuǎn),不可返回
同時(shí),我們注意到,當(dāng)某個(gè)標(biāo)簽被點(diǎn)擊時(shí),他的class會(huì)自動(dòng)添加兩個(gè)類:router-link-exact-active和router-link-active

有了這兩個(gè)class,我們可以方便的寫樣式了

7. 不用router-link,通過代碼修改路徑 $router.push
上面的我們都是使用的 <router-link>來觸發(fā)的url改變 有沒有辦法用普通的表單元素觸發(fā)呢?
有的.比如一個(gè)button 我們綁定了他的單擊事件 btnClick,則我們可以在方法里寫:
每個(gè)組件都有一個(gè)全局對(duì)象叫$router,這個(gè)
btnClick(){
this.$router.push('/home')
}
上面是壓棧式,當(dāng)然也有非壓棧式
btnClick(){
this.$router.replace('/home')
}
三. 動(dòng)態(tài)路由 /:參數(shù)
很多時(shí)候我們的url可能是不固定的,訪問的頁面的具體信息也會(huì)隨著url改變,這就稱之為動(dòng)態(tài)路由. 例如: 我們?cè)L問張三的資料 /user/zhangsan 李四的是 /user/lisi
注冊(cè)動(dòng)態(tài)路由的方法是 /:參數(shù)
我們注冊(cè)映射關(guān)系時(shí),這樣注冊(cè)
{
path:"/user/:userId",
component:User
}
我們觸發(fā)url時(shí)這樣觸發(fā)
//在<router-link>上動(dòng)態(tài)綁定
<router-link :to="'/user/'+userId">用戶</router-link>
//或者通過普通標(biāo)簽的事件:
btnClick(){
this.$router.push('/user/'+this.userId)
}
那么url跳轉(zhuǎn)后如何在先頁面中拿到跳轉(zhuǎn)的參數(shù)呢? 用$route
$route :當(dāng)前活躍路由 每個(gè)組件都有一個(gè)全局對(duì)象$route
一定要和$router區(qū)分開!!!!!
每個(gè)組件都有一個(gè)router,少了一個(gè)r)

我們看到這個(gè) 對(duì)象里有一個(gè)屬性params ,里面有一個(gè)鍵值對(duì) 就是我們想要的,這時(shí)我們制作一個(gè)計(jì)算屬性放在我們想顯示的位置就好了
computed: {
userId(){
return this.$route.params.userId
}
},
四. 路由的懶加載
當(dāng)我們打包時(shí),我們的bundle.js會(huì)變得特別大,太大的bundle會(huì)使得加載變慢,從而出現(xiàn)瀏覽器空白.但其實(shí)即使是單頁程序,也無需浪費(fèi)這段時(shí)間,同時(shí)給用戶不好的體驗(yàn).我們完全可以使用懶加載,將剛開始用不到的代代碼先不加載,等用戶觸發(fā)了相關(guān)路由時(shí)再加載
懶加載說白了就是不同的路由打包到不同的js里去

懶加載如何實(shí)現(xiàn)??
我們不要在一開始就import 而是在用到的地方,用箭頭函數(shù)直接匿名import
path: '/about', //2.添加映射關(guān)系
component: ()=>import('../components/About')
},
這樣,我們可以讓首頁之外的路由選擇懶加載,都懶加載也可以
五. 路由的嵌套
為什么要路由嵌套?看下圖:
我們想在home頁面請(qǐng)求路由,把路徑改為/home/news, 但是頁面還是在home頁,只不過綠框里的內(nèi)容改為了news組件的內(nèi)容

如何實(shí)現(xiàn)路由嵌套?

1.映射路由
因?yàn)檫€是在home頁面,所以不能直接寫路由映射,而是將路由映射嵌套在home的映射中
具體做法是,將子映射寫在父映射的children屬性里
{
path: '/home', //這是home的映射
component: Home,
children:[ //它里面可以定義一個(gè)children,里面放子映射
{
path: '', //添加默認(rèn)子映射
redirect:'news'
},
{
path: 'news', //添加子映射關(guān)系 路徑只寫名字 news
component: ()=>import('../components/HomeNews')
},
{
path: 'msgs', //添加子映射關(guān)系
component: ()=>import('../components/HomeMsg')
},
]
},
接下來我們?cè)趆ome.vue中確定顯示位置 路徑照實(shí)寫
<template>
<div>
<h2>我是首頁</h2>
<p>我是首頁內(nèi)容</p>
<router-link to="/home/news">看看新聞</router-link>
<router-link to="/home/msgs">看看消息</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home"
};
</script>
<style scoped>
</style>
六. 傳遞參數(shù)的路由
傳遞參數(shù)的兩種方式
配置動(dòng)態(tài)路由 /router/:參數(shù)
query類型的傳遞 請(qǐng)求路徑還是/router 但是同時(shí)會(huì)傳一個(gè)query,如: /router?id=123
如果只需要傳遞一個(gè)符號(hào),則選擇第一種,如果信息較多,傳遞第二種
第一種我們之前已經(jīng)接觸了,現(xiàn)在看
1. 如何用query傳遞參數(shù)
只需要在<router-link> 的to屬性里綁定一個(gè)對(duì)象,對(duì)象有如下屬性:
<router-link :to="{
path:'/profile', <--path屬性還是路由鏈接-->
query:{ <--query屬性里面?zhèn)魅胍粋€(gè)對(duì)象,對(duì)象里都是鍵值對(duì)-->
id:'lili',
age:20,
gender:'girl'
}}">Profile</router-link>
新頁面里如何取?還是用之前用過的$route對(duì)象
this.$route.query
2.不用<router-link>怎么傳query??
router.replace傳入上述對(duì)象即可
methods: {
btnClick(){
this.$router.push({
path:'/profile',
query:{
name:"lili",
age:18,
gender:'girl'
}
})
}
}
七. keep-alive
一個(gè)頁面如果跳轉(zhuǎn)到了另一個(gè)頁面,那么這個(gè)頁面會(huì)被銷毀,此時(shí)如果用戶又返回這個(gè)頁面,又需要重新加載.
keep-alive主要解決離開頁面又返回時(shí),頁面需要重構(gòu)的問題,這種重構(gòu)很多時(shí)候都是沒必要的
1. 如何使用keep-alive?
<router-view>其實(shí)也是個(gè)組件,如果它直接被包在<keep-alive>里面,所有涉及到的視圖組件都會(huì)被緩存
<keep-alive>是定義在Vue中的內(nèi)置組件,目的是為了避免重新渲染
例:我們想讓home的兩個(gè)次級(jí)路由的組件來回切換時(shí)不重新構(gòu)建,可以給控制他們顯示的<router-view>用<keep-alive>包起來
<template>
<div>
<h2>我是首頁</h2>
<p>我是首頁內(nèi)容</p>
<router-link to="/home/news">看看新聞</router-link>
<router-link to="/home/msgs">看看消息</router-link>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "Home"
};
</script>
<style>
</style>

2. 因?yàn)楸籯eep-alive,子組件多了兩個(gè)屬性
被keep-alive的組件都會(huì)多兩個(gè)屬性 activated 和 deactivated,里面可以傳入方法,當(dāng)活躍/不活躍時(shí)調(diào)用
舉例:
<template>
<div>
<h1>您有4條短消息</h1>
<ul>
<li>短消息1</li>
<li>短消息2</li>
<li>短消息3</li>
<li>短消息4</li>
</ul>
</div>
</template>
<script>
export default {
name:"HomeMsg",
activated() {
console.log("我真活躍");
},
deactivated() {
console.log("我不活躍了");
},
}
</script>
<style>
</style>

3.<keep-alive> 的包含和例外
如果又4個(gè)標(biāo)簽,我們想其中3個(gè)keep-alive怎么辦???
這時(shí)我們需要用keep-alive的屬性

<keep-alive exclude="HomeMsg,User"> 這會(huì)排除這兩個(gè)組件的keep-alive
<router-view></router-view>
</keep-alive>
八. 導(dǎo)航守衛(wèi)
有時(shí)我們需要監(jiān)聽頁面跳轉(zhuǎn)
比如,我們要監(jiān)聽頁面跳轉(zhuǎn),跳轉(zhuǎn)時(shí),將我們的網(wǎng)站的title該為對(duì)應(yīng)頁面的名稱
1.前置守衛(wèi)
前置守衛(wèi)是跳轉(zhuǎn)前的守衛(wèi)
我們可以在index.js里,給我們的router調(diào)用一個(gè)前置守衛(wèi)(鉤子)函數(shù):router.beforeEach()
router.beforeEach((to,from,next)=>{ //這就是頁面跳轉(zhuǎn)過程
document.title= to.meta.matched[0].title //將頁面標(biāo)題改為了to路由的meta信息里的標(biāo)題
next() // 必須調(diào)用next(),以維護(hù)鏈?zhǔn)?})
前提是to路由里有meta,如: meta(描述信息)
{
path:"/profile",
component:()=>import('../components/Profile'),
meta:{
title:'檔案'
},
},
為什么有個(gè) .matched[0], 因?yàn)橛袝r(shí)我們進(jìn)入嵌套路由,比如, home/news,
但此時(shí)我們還想顯示home的meta, 就可以通過這種方法找到它的第一級(jí)路由的meta
比如: 如果用戶登錄了,其他頁面可以正常跳轉(zhuǎn)和訪問,如果沒登錄,跳轉(zhuǎn)到登錄界面
// 掛載路由導(dǎo)航守衛(wèi)
router.beforeEach((to, from, next) => { // 這就是頁面跳轉(zhuǎn)過程
if (to.path === '/login') next()
const token = window.sessionStorage.getItem('token')
if (!token) return next('/login')
next()
})
2.后置守衛(wèi)
其實(shí)還有后置守衛(wèi)(鉤子):
router.afterEach(to,from)
后置守衛(wèi)沒有next
3.next有大用
next(): 進(jìn)行管道中的下一個(gè)鉤子。如果全部鉤子執(zhí)行完了,則導(dǎo)航的狀態(tài)就是 confirmed (確認(rèn)的)。
next(false): 中斷當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了 (可能是用戶手動(dòng)或者瀏覽器后退按鈕),那么 URL 地址會(huì)重置到 from 路由對(duì)應(yīng)的地址。
next('/') 或者 next({ path: '/' }): 跳轉(zhuǎn)到一個(gè)不同的地址。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個(gè)新的導(dǎo)航。你可以向 next 傳遞任意位置對(duì)象,且允許設(shè)置諸如 replace: true、name: 'home' 之類的選項(xiàng)以及任何用在 router-link 的 to prop 或 router.push 中的選項(xiàng)。
next(error): (2.4.0+) 如果傳入 next 的參數(shù)是一個(gè) Error 實(shí)例,則導(dǎo)航會(huì)被終止且該錯(cuò)誤會(huì)被傳遞給 router.onError() 注冊(cè)過的回調(diào)。
4.路由獨(dú)享守衛(wèi)
你可以在路由配置上直接定義 beforeEnter 守衛(wèi):
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
5.組件內(nèi)守衛(wèi)
你可以在路由組件內(nèi)直接定義以下路由導(dǎo)航守衛(wèi):
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對(duì)應(yīng)路由被 confirm 前調(diào)用
// 不!能!獲取組件實(shí)例 `this`
// 因?yàn)楫?dāng)守衛(wèi)執(zhí)行前,組件實(shí)例還沒被創(chuàng)建
},
beforeRouteUpdate (to, from, next) {
// 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用
// 舉例來說,對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候,
// 由于會(huì)渲染同樣的 Foo 組件,因此組件實(shí)例會(huì)被復(fù)用。而這個(gè)鉤子就會(huì)在這個(gè)情況下被調(diào)用。
// 可以訪問組件實(shí)例 `this`
},
beforeRouteLeave (to, from, next) {
// 導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用
// 可以訪問組件實(shí)例 `this`
}
}
beforeRouteEnter 守衛(wèi) 不能 訪問 this,因?yàn)槭匦l(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場的新組件還沒被創(chuàng)建。
不過,你可以通過傳一個(gè)回調(diào)給 next來訪問組件實(shí)例。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù)。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實(shí)例
})
}
注意 beforeRouteEnter 是支持給 next 傳遞回調(diào)的唯一守衛(wèi)。對(duì)于 beforeRouteUpdate 和 beforeRouteLeave 來說,this 已經(jīng)可用了,所以不支持傳遞回調(diào),因?yàn)闆]有必要了。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
這個(gè)離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開。該導(dǎo)航可以通過 next(false) 來取消。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}