一.路由的概念與原理
路由的本質(zhì)就是一種對應(yīng)關(guān)系,比如我們在url地址中輸入我們要訪問的url地址之后,瀏覽器要去請求這個url地址對應(yīng)的資源。
那么url地址和真實的資源之間就有一種對應(yīng)的關(guān)系,就是路由。
路由分為前端路由和后端路由
1.1 后端路由
后端路由是根據(jù)不同的用戶URL請求,返回不同的內(nèi)容,本質(zhì)就是URL請求地址與服務(wù)器資源之間的對應(yīng)關(guān)系。
由服務(wù)器端進行實現(xiàn),并完成資源的分發(fā)。
1.2 前端路由
前端路由依靠hash值(錨鏈接)的變化進行實現(xiàn)。
后端路由性能相對前端路由來說較低,所以,我們接下來主要學(xué)習(xí)的是前端路由。
基本概念:根據(jù)不同的事件來顯示不同的頁面內(nèi)容,即用戶事件與事件處理函數(shù)之間的對應(yīng)關(guān)系。
前端路由主要做的事情就是監(jiān)聽事件并分發(fā)執(zhí)行事件處理函數(shù)。
1.3 SPA(Single Page Application)
- 后端渲染(存在性能問題)
- Ajax前端渲染(前端渲染提高性能,但是不支持瀏覽器的后退操作)
- SPA單頁面應(yīng)用程序:整個網(wǎng)站只有一個頁面,內(nèi)容的變化通過Ajax局部更新實現(xiàn),同時支持瀏覽器地址欄的前進和后退操作
- SPA的實現(xiàn)原理之一:基于URL地址的hash(hash的變化會導(dǎo)致瀏覽器訪問歷史記錄的變化,但hash的變化不會觸發(fā)新的URL請求)
- 在實現(xiàn)SPA的過程中,最核心的技術(shù)點就是前端路由
二、前端路由的初體驗
前端路由是基于hash值的變化進行實現(xiàn)的(比如點擊頁面中的菜單或者按鈕改變URL的hash值,根據(jù)hash值的變化來控制組件的切換)
核心實現(xiàn)依靠一個事件,即監(jiān)聽hash值變化的事件
window.onhashchange = function(){
//location.hash可以獲取到最新的hash值
location.hash
}
前端路由實現(xiàn)tab欄切換:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<!-- 導(dǎo)入 vue 文件 -->
<script src="./lib/vue_2.5.22.js"></script>
</head>
<body>
<!-- 被 vue 實例控制的 div 區(qū)域 -->
<div id="app">
<!-- 切換組件的超鏈接 -->
<a href="#/zhuye">主頁</a>
<a href="#/keji">科技</a>
<a href="#/caijing">財經(jīng)</a>
<a href="#/yule">娛樂</a>
<!-- 根據(jù) :is 屬性指定的組件名稱,把對應(yīng)的組件渲染到 component 標簽所在的位置 -->
<!-- 可以把 component 標簽當做是【組件的占位符】 -->
<component :is="comName"></component>
</div>
<script>
// #region 定義需要被切換的 4 個組件
// 主頁組件
const zhuye = {
template: '<h1>主頁信息</h1>'
}
// 科技組件
const keji = {
template: '<h1>科技信息</h1>'
}
// 財經(jīng)組件
const caijing = {
template: '<h1>財經(jīng)信息</h1>'
}
// 娛樂組件
const yule = {
template: '<h1>娛樂信息</h1>'
}
// #endregion
// #region vue 實例對象
const vm = new Vue({
el: '#app',
data: {
comName: 'zhuye'
},
// 注冊私有組件
components: {
zhuye,
keji,
caijing,
yule
}
})
// #endregion
// 監(jiān)聽 window 的 onhashchange 事件,根據(jù)獲取到的最新的 hash 值,切換要顯示的組件的名稱
window.onhashchange = function() {
// 通過 location.hash 獲取到最新的 hash 值
console.log(location.hash); // #/zhuye
vm.comName = location.hash.slice(2)
}
</script>
</body>
</html>
案例效果圖:
點擊每個超鏈接之后,會進行相應(yīng)的內(nèi)容切換,如下:
核心思路:
在頁面中有一個vue實例對象,vue實例對象中有四個組件,分別是tab欄切換需要顯示的組件內(nèi)容
在頁面中有四個超鏈接,如下:
<a href="#/zhuye">主頁</a>
<a href="#/keji">科技</a>
<a href="#/caijing">財經(jīng)</a>
<a href="#/yule">娛樂</a>
當我們點擊這些超鏈接的時候,就會改變url地址中的hash值,當hash值被改變時,就會觸發(fā)onhashchange事件,然后得到當前hash值,根據(jù)這個hash值來讓不同的組件進行顯示。
三、Vue Router簡介
它是一個Vue.js官方提供的路由管理器。是一個功能更加強大的前端路由器,推薦使用。
Vue Router和Vue.js的核心深度集成,因此非常契合,可以一起方便的實現(xiàn)SPA單頁應(yīng)用程序的開發(fā)。
Vue Router依賴于Vue,所以需要先引入Vue,再引入Vue Router
Vue Router的特性:
- 支持H5歷史模式或者hash模式
- 支持嵌套路由
- 支持路由參數(shù)
- 支持編程式路由
- 支持命名路由
- 支持路由導(dǎo)航守衛(wèi)
- 支持路由過渡動畫特效
- 支持路由懶加載
- 支持路由滾動行為
四、Vue Router的使用步驟(★★★)
A. 導(dǎo)入js文件
<script src="lib/vue_2.5.22.js"></script>
為全局window對象掛載VueRouter構(gòu)造函數(shù)
<script src="lib/vue-router_3.0.2.js"></script>
B. 添加路由鏈接
<router-link>
是vue中提供的標簽,默認會被渲染為a標簽,
to屬性默認被渲染為href屬性,
to屬性的值會被渲染為#開頭的hash地址
<router-link to="/user">User</router-link>
<router-link to="/login">Login</router-link>
C. 添加路由填充位(路由占位符)
<router-view></router-view>
最后路由展示的組件就會在占位符的位置顯示
D. 定義路由組件
var User = { template:"<div>This is User</div>" }
var Login = { template:"<div>This is Login</div>" }
E. 配置路由規(guī)則并創(chuàng)建路由實例
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes:[
//每一個路由規(guī)則都是一個對象,對象中至少包含path和component兩個屬性
//path表示 路由匹配的hash地址,component表示路由規(guī)則對應(yīng)要展示的組件對象
{path:"/user",component:User},
{path:"/login",component:Login}
]
})
F. 將路由掛載到Vue實例中
new Vue({
el:"#app",
//通過router屬性掛載路由對象
router:myRouter
})
補充:
路由重定向:可以通過路由重定向為頁面設(shè)置默認展示的組件
在路由規(guī)則中添加一條路由規(guī)則即可,如下:
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//path設(shè)置為/表示頁面最初始的地址 / ,redirect表示要被重定向的新地址,設(shè)置為一個路由即可
{ path:"/", redirect:"/user"},
{ path: "/user", component: User },
{ path: "/login", component: Login }
]
})
五、嵌套路由,動態(tài)路由的實現(xiàn)方式
A. 嵌套路由的概念(★★★)
當我們進行路由的時候顯示的組件中還有新的子級路由鏈接以及內(nèi)容。
嵌套路由最關(guān)鍵的代碼在于理解子級路由的概念:
比如我們有一個/login的路由
那么/login下面還可以添加子級路由,如:
/login/account
/login/phone
參考代碼如下:
var User = { template: "<div>This is User</div>" }
//Login組件中的模板代碼里面包含了子級路由鏈接以及子級路由的占位符
var Login = { template: `<div>
<h1>This is Login</h1>
<hr>
<router-link to="/login/account">賬號密碼登錄</router-link>
<router-link to="/login/phone">掃碼登錄</router-link>
<!-- 子路由組件將會在router-view中顯示 -->
<router-view></router-view>
</div>` }
//定義兩個子級路由組件
var account = { template:"<div>賬號:<input><br>密碼:<input></div>"};
var phone = { template:"<h1>掃我二維碼</h1>"};
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
{ path:"/",redirect:"/user"},
{ path: "/user", component: User },
{
path: "/login",
component: Login,
//通過children屬性為/login添加子路由規(guī)則
children:[
{ path: "/login/account", component: account },
{ path: "/login/phone", component: phone },
]
}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router:myRouter
});
頁面效果大致如下:
B. 動態(tài)路由匹配(★★★)
var User = { template:"<div>用戶:{{$route.params.id}}</div>"}
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//通過 /:參數(shù)名 的形式傳遞參數(shù)
{ path: "/user/:id", component: User },
]
})
可以在路由對應(yīng)的組件中使用$route.params.id
來獲取路徑傳參的id參數(shù)值,但是這種方式不夠靈活。今后可以盡量使用props將組件和路由解耦。
- 通過props來接收參數(shù)
var User = {
props:["id"], //使用props接收id參數(shù)
template:"<div>用戶:{{id}}</div>"
}
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//通過/:參數(shù)名 的形式傳遞參數(shù)
//如果props設(shè)置為true,route.params將會被設(shè)置為組件屬性
{ path: "/user/:id", component: User, props:true },
]
})
- 還有一種情況,我們可以將props設(shè)置為對象,直接將靜態(tài)數(shù)據(jù)傳遞給組件進行使用。
但是這樣id的值就訪問不到了。
var User = {
props:["username","pwd"],
template:"<div>用戶:{{username}}---{{pwd}}</div>"
}
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//通過 /:參數(shù)名 的形式傳遞參數(shù)
//如果props設(shè)置為對象,則傳遞的是對象中的數(shù)據(jù)給組件
{ path: "/user/:id", component: User, props:{username:"jack",pwd:123} },
]
})
- 如果既想要獲取路由傳遞的參數(shù)值,又想獲取傳遞的對象數(shù)據(jù),那么props應(yīng)該設(shè)置為函數(shù)形式。
var User = {
props:["username","pwd","id"],
template:"<div>用戶:{{id}} -> {{username}}---{{pwd}}</div>"
}
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//通過 /:參數(shù)名 的形式傳遞參數(shù)
//如果props設(shè)置為函數(shù),則通過函數(shù)的第一個參數(shù)獲取路由對象
//并可以通過路由對象的params屬性獲取傳遞的參數(shù)
//
{ path: "/user/:id", component: User,props:(route)=>{
return {username:"jack",pwd:123,id:route.params.id}
}
},
]
})
六、命名路由以及編程式導(dǎo)航
A. 命名路由:用name屬性值給路由取別名
案例:
var myRouter = new VueRouter({
//routes是路由規(guī)則數(shù)組
routes: [
//通過name屬性為路由添加一個別名
{ path: "/user/:id", component: User, name:"user"},
]
})
添加了別名之后,可以使用別名進行跳轉(zhuǎn)(給to屬性綁定對象的形式)
<router-link to="/user/123">User</router-link>
等價于
<router-link :to="{ name:'user' , params: {id:123} }">User</router-link>
還可以編程式導(dǎo)航
myRouter.push( { name:'user' , params: {id:123} } )
B. 編程式導(dǎo)航(★★★)
頁面導(dǎo)航的兩種方式:
A. 聲明式導(dǎo)航:通過點擊鏈接的方式實現(xiàn)的導(dǎo)航
B. 編程式導(dǎo)航:調(diào)用js的api方法實現(xiàn)導(dǎo)航,比如調(diào)用location.href
就是
Vue-Router中常見的導(dǎo)航方式:
this.$router.push("hash地址");
this.$router.push("/login");
this.$router.push({ name:'user' , params: {id:123} });
this.$router.push({ path:"/login" });
this.$router.push({ path:"/login",query:{username:"jack"} });
this.$router.go(n);
n為數(shù)字,參考history.go
this.$router.go(-1);
七、實現(xiàn)后臺管理案例(★★★)
案例效果:
點擊左側(cè)的"用戶管理","權(quán)限管理","商品管理","訂單管理","系統(tǒng)設(shè)置"都會出現(xiàn)對應(yīng)的組件并展示內(nèi)容
其中"用戶管理"組件展示的效果如上圖所示,在用戶管理區(qū)域中的詳情鏈接也是可以點擊的,點擊之后將會顯示用戶詳情信息。
案例思路:
1).先將素材文件夾中的11.基于vue-router的案例.html復(fù)制到我們自己的文件夾中。
看一下這個文件中的代碼編寫了一些什么內(nèi)容,
這個頁面已經(jīng)把后臺管理頁面的基本布局實現(xiàn)了
2).在頁面中引入vue,vue-router
3).創(chuàng)建Vue實例對象,準備開始編寫代碼實現(xiàn)功能
4).希望是通過組件的形式展示頁面的主體內(nèi)容,而不是寫死頁面結(jié)構(gòu),所以我們可以定義一個根組件:
//只需要把原本頁面中的html代碼設(shè)置為組件中的模板內(nèi)容即可
const app = {
template:`<div>
<!-- 頭部區(qū)域 -->
<header class="header">傳智后臺管理系統(tǒng)</header>
<!-- 中間主體區(qū)域 -->
<div class="main">
<!-- 左側(cè)菜單欄 -->
<div class="content left">
<ul>
<li>用戶管理</li>
<li>權(quán)限管理</li>
<li>商品管理</li>
<li>訂單管理</li>
<li>系統(tǒng)設(shè)置</li>
</ul>
</div>
<!-- 右側(cè)內(nèi)容區(qū)域 -->
<div class="content right">
<div class="main-content">添加用戶表單</div>
</div>
</div>
<!-- 尾部區(qū)域 -->
<footer class="footer">版權(quán)信息</footer>
</div>`
}
5).當我們訪問頁面的時候,默認需要展示剛剛創(chuàng)建的app根組件,我們可以
創(chuàng)建一個路由對象來完成這個事情,然后將路由掛載到Vue實例對象中即可
const myRouter = new VueRouter({
routes:[
{path:"/",component:app}
]
})
const vm = new Vue({
el:"#app",
data:{},
methods:{},
router:myRouter
})
補充:到此為止,基本的js代碼都處理完畢了,我們還需要設(shè)置一個路由占位符
<body>
<div id="app">
<router-view></router-view>
</div>
</body>
6).此時我們打開頁面應(yīng)該就可以得到一個VueRouter路由出來的根組件了
我們需要在這個根組件中繼續(xù)路由實現(xiàn)其他的功能子組件
先讓我們更改根組件中的模板:更改左側(cè)li為子級路由鏈接,并在右側(cè)內(nèi)容區(qū)域添加子級組件占位符
const app = {
template:`<div>
........
<div class="main">
<!-- 左側(cè)菜單欄 -->
<div class="content left">
<ul>
<!-- 注意:我們把所有l(wèi)i都修改為了路由鏈接 -->
<li><router-link to="/users">用戶管理</router-link></li>
<li><router-link to="/accesses">權(quán)限管理</router-link></li>
<li><router-link to="/goods">商品管理</router-link></li>
<li><router-link to="/orders">訂單管理</router-link></li>
<li><router-link to="/systems">系統(tǒng)設(shè)置</router-link></li>
</ul>
</div>
<!-- 右側(cè)內(nèi)容區(qū)域 -->
<div class="content right">
<div class="main-content">
<!-- 在 -->
<router-view></router-view>
</div>
</div>
</div>
.......
</div>`
}
然后,我們要為子級路由創(chuàng)建并設(shè)置需要顯示的子級組件
//建議創(chuàng)建的組件首字母大寫,和其他內(nèi)容區(qū)分
const Users = {template:`<div>
<h3>用戶管理</h3>
</div>`}
const Access = {template:`<div>
<h3>權(quán)限管理</h3>
</div>`}
const Goods = {template:`<div>
<h3>商品管理</h3>
</div>`}
const Orders = {template:`<div>
<h3>訂單管理</h3>
</div>`}
const Systems = {template:`<div>
<h3>系統(tǒng)管理</h3>
</div>`}
//添加子組件的路由規(guī)則
const myRouter = new VueRouter({
routes:[
{path:"/",component:app , children:[
{ path:"/users",component:Users },
{ path:"/accesses",component:Access },
{ path:"/goods",component:Goods },
{ path:"/orders",component:Orders },
{ path:"/systems",component:Systems },
]}
]
})
const vm = new Vue({
el:"#app",
data:{},
methods:{},
router:myRouter
})
7).展示用戶信息列表:
A.為Users組件添加私有數(shù)據(jù),并在模板中循環(huán)展示私有數(shù)據(jù)
const Users = {
data(){
return {
userList:[
{id:1,name:"zs",age:18},
{id:2,name:"ls",age:19},
{id:3,name:"wang",age:20},
{id:4,name:"jack",age:21},
]
}
},
template:`<div>
<h3>用戶管理</h3>
<table>
<thead>
<tr>
<th>編號</th>
<th>姓名</th>
<th>年齡</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key="item.id" v-for="item in userList">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td><a href="javascript:;">詳情</a></td>
</tr>
</tbody>
</table>
</div>`
}
8).當用戶列表展示完畢之后,我們可以點擊列表中的詳情來顯示用戶詳情信息,首先我們需要創(chuàng)建一個組件,用來展示詳情信息
const UserInfo = {
props:["id"],
template:`<div>
<h5>用戶詳情</h5>
<p>查看 {{id}} 號用戶信息</p>
<button @click="goBack">返回用戶詳情頁</button>
</div> `,
methods:{
goBack(){
//當用戶點擊按鈕,后退一頁
this.$router.go(-1);
}
}
}
然后我們需要設(shè)置這個組件的路由規(guī)則
const myRouter = new VueRouter({
routes:[
{path:"/",component:app , children:[
{ path:"/users",component:Users },
//添加一個/userinfo的路由規(guī)則
{ path:"/userinfo/:id",component:UserInfo,props:true},
{ path:"/accesses",component:Access },
{ path:"/goods",component:Goods },
{ path:"/orders",component:Orders },
{ path:"/systems",component:Systems },
]}
]
})
const vm = new Vue({
el:"#app",
data:{},
methods:{},
router:myRouter
})
再接著給用戶列表中的詳情a連接添加事件
const Users = {
data(){
return {
userList:[
{id:1,name:"zs",age:18},
{id:2,name:"ls",age:19},
{id:3,name:"wang",age:20},
{id:4,name:"jack",age:21},
]
}
},
template:`<div>
<h3>用戶管理</h3>
<table>
<thead>
<tr>
<th>編號</th>
<th>姓名</th>
<th>年齡</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key="item.id" v-for="item in userList">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td><a href="javascript:;" @click="goDetail(item.id)">詳情</a></td>
</tr>
</tbody>
</table>
</div>`,
methods:{
goDetail(id){
this.$router.push("/userinfo/"+id);
}
}
}