Vue路由初識
- 兩種模式: hash 模式或HTML5 歷史模式
- vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新加載。
- 如果不想要很醜的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新加載頁面。
Vuehash路由模擬
Vue-histroy路由模擬
vue-router 使用初識
路由:一個路徑對應一個組件
1.創建組件
2.配置路由映射表
3.註冊路由映射表
4. 把路由掛載到根實例上;
<body>
<div id="app">
<!-- to : 跳轉的路由 tag:指定router-link渲染標籤 -->
<!-- 使用 router-link 組件來導航. -->
<!-- 通過傳入 `to` 屬性指定鏈接. tag:指令router-link渲染成啥標籤-->
<!-- <router-link> 默認會被渲染成一個 `<a>` 標籤 -->
<router-link to="/allhome" tag="button">首頁</router-link>
<router-link to="/allperson" tag="button">個人中心</router-link>
<!-- 用於顯示路由對應組件的地方 -->
<!-- 根據路由顯示對應的組件 -->
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這裏 -->
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 路由:一個路徑對應一個組件
// 1.創建組件
// 2.配置路由映射表
// 3.註冊路由映射表
// 4. 把路由掛載到根實例上;
let home = {
data() {
return {
con: '首頁'
}
},
template: "<div>{{con}}</div>"
}
let person = {
data() {
return {
con: '個人中心頁'
}
},
template: "<div>{{con}}</div>"
}
// 配置路由映射表: 是路由和組件的配對情況
let routes = [{
path: "/allhome",
component: home
}, {
path: "/allperson",
component: person
}];
// 註冊路由映射表
let router = new VueRouter({
routes: routes // routes 屬性名不可以改
})
// 將路由掛載到根實例上
let vm = new Vue({
el: "#app",
data: {
},
components: {
home,
person
},
router
})
</script>
</body>
VueRouter中的方法
引入時:vue-router一定要放vue.js的後面
當切換組件時,組件會銷燬;
當每一個被路由渲染出來的組件上有一個$router屬性,在這個屬性的原型上有一些操作路由的方法
1.push : 直接跳轉到當前路徑對應的路由上 push(路徑)
2.back : 回退到上一次的路由上
3.go(number):可以往前可以往後回退後前進幾個
原生:
window.history
window.history.go(-1)
<body>
<div id="app">
<!-- to屬性 -->
<router-link to="/home" tag="button">首頁</router-link>
<router-link to="/list" tag="button">列表頁</router-link>
<!-- 展示對應的組件,當組件切換時,組件的DOM元素刪除 -->
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
//window.history
//window.history.go(-1)
// vue-router一定要放vue.js的後面
// 當切換組件時,組件會銷燬;
// 當每一個被路由渲染出來的組件上有一個$router屬性,在這個屬性的原型上有一些操作路由的方法
// 1.push : 直接跳轉到當前路徑對應的路由上 push(路徑)
// 2.back : 回退到上一次的路由上
// 3.go(number):可以往前可以往後回退後前進幾個
let home = {
data() {
return {}
},
created() {
// 顯示組件時,需要再次創建組件實例;調用鉤子函數
console.log("創建首頁");
},
methods: {
goList() {
console.log(this);
this.$router.push("/list")
}
},
template: "<div>首頁內容<button @click='goList'>去列表頁</button></div>",
beforeDestroy() {
console.log("銷燬首頁");
},
};
let list = {
data() {
return {}
},
methods: {
goBack() {
console.log(this);
// this.$router.back("/home")
this.$router.go(-1)
}
},
template: "<div>列表頁<button @click='goBack'>返回</button></div>"
};
// 路由映射表
let routes = [{
path: "/home",
component: home
}, {
path: "/list",
component: list
}];
// 註冊路由映射表
let router = new VueRouter({
routes
});
// 掛載到根實例上
let vm = new Vue({
el: "#app",
router
});
</script>
</body>
路由的嵌套
在組件路由配置是,對象中有children屬性,屬性值是一個數組,裏面配置了子路由,子路由中不需要加父路由路徑地址,同時也不需要加"/",當子路由進行匹配式,會自動加上父路由和/到子路由的前面;
子路由path後面也可以從一級路由到耳機路由
二級路由不能直接配置到routes,應該找到它對應的以及路由,配置到其children屬性上;
下面例子:detail 和login都是list組件的子路由組件
<body>
<div id="app">
<router-link to="/home" tag="button" class="a">首頁</router-link>
<router-link to="/list" tag="button" class="b">列表頁</router-link>
<router-view></router-view>
</div>
<template id="list">
<div>
列表頁
<router-link to="/list/detail">詳情頁</router-link>
<router-link to="/list/login">登錄頁</router-link>
<router-view></router-view>
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={
template:"<div>首頁</div>"
}
let list={
template:"#list"
};
let detail = {
template:"<div>詳情頁</div>"
}
let login = {
template:"<div>登錄註冊頁</div>"
};
// detail 和login都是list組件的子路由組件
// 在組件路由配置是,對象中有children屬性,屬性值是一個數組,裏面配置了子路由,
路由中不需要加父路由路徑地址,同時也不需要加"/",當子路由進行匹配式,會自動加上父路由和/到子路由的前面;
// 二級路由不能直接配置到routes,應該找到它對應的以及路由,配置到其children屬性上;
let routes =[
{path:"/home",component:home},
{path:"/list",component:list,children:[
{path:"detail",component:detail},
{path:"login",component:login}
]}
];
let router = new VueRouter({
routes
})
let vm = new Vue({
el:"#app",
data:{
},
router
})
</script>
</body>
命名路由
將to改成動態屬性 :to={name:組件的name名稱}
通過名字取匹配路由
<body>
<div id="app">
<!-- 將to改成動態屬性 :to={name:組件的name名稱} -->
<router-link :to="{name:'first'}">首頁</router-link>
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 通過名字取匹配路由
let home={
template:"<div>首頁</div>"
}
let routes =[
{path:"/home",component:home,name:"first"}
];
let router = new VueRouter({
routes
})
let vm = new Vue({
el:"#app",
data:{
},
router
})
</script>
</body>
動態路由
動態路由:路由傳參;路徑後面是一個 :變量 這就是動態路由,也可以叫路由動態傳參;會把id以屬性方式放到$route的params屬性上,屬性值就是路由實際的路徑值
1.代碼量少
2. 由於動態路由渲染的是同一個home組件,所以home組件不再銷燬,當然也不再創建,複用了之前的組件,性能高;但是生命週期的前四個鉤子函數也不再執行;
<body>
<div id="app">
<router-link to="/home/1">第一本</router-link>
<router-link to="/home/2">第二本</router-link>
<router-link to="/home/3">第三本</router-link>
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
// $router:push go back forward
// $route:
// 路由的動態傳參:
// 路由傳參有哪些方式;
let home = {
// created(){
// //
// console.log(this.$route);
// },
// watch:{
// '$route'(to,from){
// console.log(to);// to: 要到達的組件的$route
// console.log(from);// from :上一個$route
// }
// },
// 路由守衛
// 在路由更新之前會默認調用該鉤子函數
beforeRouteUpdate(to, from, next) {
console.log(to); // 即將進入的目標路由的對象信息
console.log(from); // 即將離開路由的對象信息
console.log(next); // 函數
// 1. next 函數
// next(): 會立即進入到目標路由
// next(false):中斷當前的導航;不再去訪問下一個路由
// if(to.params.id==3){
// next({path:"/home/1"});
// return;
// }
// 權限校驗
next();
},
template: "<div>這是我喜歡的第{{$route.params.id}}本書</div>"
}
// 動態路由:路由傳參;路徑後面是一個:變量;這就是動態路由,也可以叫路由動態傳參;
會把id以屬性方式放到$route的params屬性上,屬性值就是路由實際的路徑值
// 1.代碼量少
// 2. 由於動態路由渲染的是同一個home組件,所以home組件不再銷燬,當然也不再創建,
複用了之前的組件,性能高;但是生命週期的鉤子函數也不再執行;
let routes = [{
path: "/home/:id",
component: home
}];
let router = new VueRouter({
routes
})
let vm = new Vue({
el: "#app",
data: {},
router
})
</script>
</body>
路由傳參
- 路由傳參有哪些方式;
- id 動態路由傳參;路徑後面是一個:變量;這就是動態路由,也可以叫路由動態傳參;會把id以屬性方式放到$route的params屬性上,屬性值就是路由實際的路徑值
- query傳參 path
- params傳參 name 命名路由
<body>
<div id="app">
<router-link :to="{name:'first'}">首頁</router-link>
<router-link :to="{name:'second'}">列表頁</router-link>
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 通過名字取匹配路由
// 1. id 動態路由傳參;路徑後面是一個:變量;這就是動態路由,也可以叫路由動態傳參;
會把id以屬性方式放到$route的params屬性上,屬性值就是路由實際的路徑值
// 2.query傳參 path
// 3.params傳參 name
let home = {
methods: {
goList() {
// this.$router.push({
// path: "/list",
// query: {
// id: 100
// }
// }),
this.$router.push({
name: "second",
params: {
id: 500
}
})
}
},
template: "<div>首頁 <button @click='goList'>1去列表</button></div>"
}
let list = {
created() {
// console.log(this.$route)
// let id = this.$route.query.id;
// console.log(id);
console.log(this.$route);
let id = this.$route.params.id;
console.log(id);
},
template: "<div>列表頁</div>"
}
let routes = [{
path: "/home",
component: home,
name: "first"
},
{
path: "/list",
component: list,
name: "second"
}
];
let router = new VueRouter({
routes
})
let vm = new Vue({
el: "#app",
data: {
},
router
})
</script>
</body>
命名視圖
沒有name屬性,會顯示屬性爲default的組件
let routes =[
{path:"/home",components:{
default:home,// home對應沒有name屬性的router-view
// 這個對象屬性名和router-view的name屬性值對應
b:foo,
c:bar
},name:“first”}
];
<body>
<div id="app">
<!-- 將to改成動態屬性 :to={name:組件的name名稱} -->
<router-link :to="{name:'first'}">首頁</router-link>
<!-- 沒有name屬性,會顯示屬性爲default的組件 -->
<router-view></router-view>
<router-view name="b"></router-view>
<router-view name="c"></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={
template:"<div>首頁</div>"
}
let foo = {
template:"<div>foo</div>"
}
let bar={
template:"<div>bar</div>"
}
let routes =[
{path:"/home",components:{
default:home,// home對應沒有name屬性的router-view
// 這個對象屬性名和router-view的name屬性值對應
b:foo,
c:bar
},name:"first"}
];
let router = new VueRouter({
routes
})
let vm = new Vue({
el:"#app",
data:{
},
router
})
</script>
</body>
redirect: 路由地址重定向
redirect: 重定向
- routes:[{path:"/a",redirect:"/home"}]
- routes:[{path:"/a",redirect:{name:“路由名字”}}]
- 最後匹配不到重定向到首頁或者404頁面
<body>
<div id="app">
<router-link to="/home" tag="button">首頁</router-link>
<router-link to="/list" tag="button">列表頁</router-link>
<router-view></router-view>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 路由{path:"",component:"",children:[{path:"",component:""},
{path:"",component:""}]}
let home = {
template: "<div>首頁內容</div>"
};
let list = {
template: "<div>列表頁內容</div>"
};
let found = {
template: "<div>頁面404</div>"
};
// redirect: 重定向
// 1. routes:[{path:"/a",redirect:"/home"}]
// 2. routes:[{path:"/a",redirect:{name:"路由名字"}}]
let routes = [{
path: "/",
// component: home
// 一個地址一個組件,如果想用就重定向,
}, {
path: "/home",
component: home
}, {
path: "/list",
component: list
}, {
path: "/a",
component: found
}, {
path: "*",
redirect: "/home"
}];
let router = new VueRouter({
routes
});
let vm = new Vue({
el: "#app",
router
})
</script>
</body>
路由的守衛(七個鉤子函數)
導航守衛:當切換導航時,會默認調用一些鉤子函數,那麼這些鉤子函數就是導航的守衛;可以在進入這個導航或者離開這個導航時,在鉤子函數中做一些事情
守衛: 7個
全局守衛: beforeEach afterEach beforeResolve
路由獨享守衛: beforeEnter
組件內部守衛: beforeRouteEnter beforeRouteUpdate beforeRouteLeave
<body>
<div id="app">
<router-link to="/home/1" tag="button">第一本</router-link>
<router-link to="/home/2" tag="button">第二本</router-link>
<router-link to="/home/3" tag="button">第三本</router-link>
<router-link to="/list" tag="button">列表</router-link>
<router-view></router-view>
</div>
<!-- IMPORT JS -->
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vue-router/dist/vue-router.js"></script>
<script>
//切換到另一個組件,組件時會銷燬的;
// 導航守衛:當切換導航時,會默認調用一些鉤子函數,那麼這些鉤子函數就是導航的守衛;
可以在進入這個導航或者離開這個導航時,在鉤子函數中做一些事情
// 生命週期 11個
// 守衛: 7個
// 全局守衛: beforeEach afterEach beforeResolve
// 路由獨享守衛: beforeEnter
// 組件內部守衛: beforeRouteEnter beforeRouteUpdate beforeRouteLeave
let home = {
beforeDestroy() {
// console.log("銷燬");
},
template: "<div>這是第{{$route.params.id}}本</div>",
beforeRouteEnter(to, from, next) {
// 這個鉤子函數執行時進入組件實例之前,此時組件實例還沒有創建;
// console.log(this); //this->window
console.log("組件內的守衛-home-beforeRouteEnter");
next(vm => {
// 最後執行
// 當next執行傳入回調函數,回調函數不能立即執行,等到組件實例創建好之後,
纔會觸發這個回調函數;其中vm就是組件實例
//console.log(vm);
})
},
beforeRouteUpdate(to, from, next) {
// 當複用這個組件並且更新了組件時,這個函數纔會被調用
console.log("組件內的守衛-home-beforeRouteUpdate");
next();
},
beforeRouteLeave(to, from, next) {
// 當離開list這個組件時,會調用這個鉤子函數
console.log("組件內的守衛-home-beforeRouteLeave");
next();
}
};
// 當第一次進入到list組件時,只觸發了beforeRouteEnter
let list = {
template: "<div>列表內容</div>",
// 組件內的守衛
beforeRouteEnter(to, from, next) {
// 這個鉤子函數執行時進入組件實例之前,此時組件實例還沒有創建;
// console.log(this); //this->window
console.log("組件內的守衛-list-beforeRouteEnter");
next(vm => {
// 當next執行傳入回調函數,回調函數不能立即執行,會等到組件實例創建好之後,
纔會觸發這個回調函數,其中vm就是組件實例
console.log(vm);
})
},
// beforeRouteUpdate(to,from,next){
// 當複用這個組件並且更新了組件時,這個函數纔會被調用;
// this--> 當前的組件實例
// console.log("組件內的守衛-list-beforeRouteUpdate");
// next()
// },
beforeRouteLeave(to, from, next) {
// 當離開list這個組件時,會調用這個鉤子函數
console.log("組件內的守衛-list-beforeRouteLeave");
next();
}
};
let routes = [{
path: "/home/:id",
component: home,
beforeEnter: (to, from, next) => {
// 路由獨享的守衛
console.log("home-路由獨享的守衛");
next()
}
}, {
path: "/list",
component: list,
beforeEnter: (to, from, next) => {
// 路由獨享的守衛
console.log("list-路由獨享的守衛");
next()
}
}];
let router = new VueRouter({
routes
})
// 全局的前置鉤子函數;只要切換組件,就會執行
router.beforeEach((t0, form, next) => {
console.log("beforeEach-全局前置的鉤子函數");
// console.log(to);// 到哪去
// console.log(from);// 從哪來
// console.log(1);
// 在這個鉤子函數中獲取到用戶的信息,進行權限的校驗,如果不符合要求,
那麼next不需要運行;或者直接跳轉到首頁或 403頁面
next(); // 只有執行了next,纔會往下繼續跳轉路由;
})
// 全局解析守衛
router.beforeResolve((t0, form, next) => {
console.log("beforeResolve-全局解析守衛");
// console.log(to);// 到哪去
// console.log(from);// 從哪來
// console.log(2);
next(); // 只有執行了next,纔會往下繼續跳轉路由;
})
// 全局後置的鉤子函數
router.afterEach((t0, form) => {
// 路由切換成功以後執行的鉤子函數
console.log("afterEach-全局後置的鉤子函數");
// console.log(to);
// console.log(3);
})
// 用戶 /list 管理員 : /list /edit /home
// 切換路由時:路由守衛執行順序beforeEach==>beforeEnter==>beforeRouteUpdate==>
beforeResolve==>afterEach
//當進入組件時,先觸發全局的前置鉤子,然後觸發進入組件的路由獨享守衛,
然後觸發組件內部的beforeRouteEnter,最後觸發全局的beforeResolve和全局後置鉤子函數
let vm = new Vue({
el: '#app',
router
});
// 從第三本到列表
// 組件內的守衛-home-beforeRouteLeave beforeEach-全局前置的鉤子函數
list-路由獨享的守衛 組件內的守衛-list-beforeRouteEnter beforeResolve-全局解析守衛
afterEach-全局後置的鉤子函數 調用beforeRouteEnter守衛中傳給next的回調函數
// 從列表到第三本
// 組件內的守衛-list-beforeRouteLeave beforeEach-全局前置的鉤子函數
home-路由獨享的守衛 組件內的守衛-home-beforeRouteEnter beforeResolve-全局解析守衛
afterEach-全局後置的鉤子函數 調用beforeRouteEnter守衛中傳給next的回調函數
// 從第三本到第二本
// beforeEach-全局前置的鉤子函數 組件內的守衛-list-beforeUpdata
beforeResolve-全局解析守衛 afterEach-全局後置的鉤子函數
</script>
</body>
VueRouter核心源碼
class VueRouter{
constructor(options){
const {routes}=options;
// 監聽當前頁面的hash值的切換
// 當第一次解析頁面時,會有一個默認的hash值
/// 循環遍歷routes,把path和component重新放入一個新的對象中
// {"/home/:id":home}
this.routeMap = routes.reduce((prev,next)=>{
prev[next.path]=next.component;
return prev;
},{});
//
// this ==> VueRouter的實例,也是每一個組件上的_router
Vue.util.defineReactive(this.route={},'path',"/");
window.addEventListener("load",()=>{
// 如果沒有hash值,那麼給其賦默認值/;如果本來就有hash,什麼也不做;
location.hash?null:location.hash="/";
})
window.addEventListener("hashchange",()=>{
// 當頁面hash值發生改變以後,會觸發這個方法;1.a標籤 2.手動
// 獲取噹噹前頁面的hash值,獲取到#後面的字符串;
let path = location.hash.slice(1);
this.route.path = path;
})
}
}
//在Vuex注入了$store,在路由注入_router
VueRouter.install=function(_Vue){
_Vue.mixin({
// 給每一個組件新增一個_router的屬性,這個屬性的屬性值是VueRouter的實例
beforeCreate(){
// this==> 每一個組件實例
if(this.$options&&this.$options.router){
// 給每一個組件實例新增_router屬性,屬性值就是VueRouter的實例;
這是給Vm這個Vue實例新增
this._router=this.$options.router;
}else{
// 給vm的組件的實例新增
this._router=this.$parent && this.$parent._router;
}
// 給每一個實例添加$route屬性,
Object.defineProperty(this,"$route",{
value:{
route:this._router.route// ? 這個route
}
});
// 註冊兩個內置組件
// router-link router-view
// 註冊全局組件
<router-link to="/home"></router-link>
let child = {}
Vue.component("router-link",{
props:{
to:String
},
// template:"<div></div>",
render(createElement){// h是一個createdElement,這個方法可以直接接受一個組件;
createElement 用來創建虛擬的DOM
//return createElement("a",{},首頁)
// render : 渲染函數
// render: 將虛擬DOM可以轉成真實的DOM;這個函數返回什麼,
那麼最終router-link就渲染成什麼
// this==> 當前的組件實例
// return + 組件;可以把組件渲染成一個真實的DOM;
// return h(child);
// return <child></child>
// $slots
return <a href={`#${this.to}`}>this.$slots.default</a>
}
});
// router-view : 根據頁面的hash值顯示對應的組件
Vue.component("router-view",{
render(createdElement){
// 這個傳遞一個動態的組件名字
return createElement(this._router.routeMap[this._router.route.path])
}
})
}
})
};
let router=new VueRouter({
routes:[]
})
let vm = new Vue({
router,
render(){
}
})
export default VueRouter;