首先拋出這樣一個(gè)問題,vue-router是用來做什么的?
這里不著急回答,也不準(zhǔn)備在這篇文章里回答。這篇文章僅總結(jié)一些使用心得,其實(shí)總結(jié)完所有關(guān)于vue-router的內(nèi)容后,整篇文章也許就剛好能回答這個(gè)問題了。
單純使用Vue.js,我們可以通過組合組件來組成應(yīng)用,不同的頁面有不同的地址,路由依靠鏈接跳轉(zhuǎn)。這顯然不是單頁應(yīng)用,因?yàn)闀?huì)有頁面刷新。
當(dāng)要把vue-router引入進(jìn)來,我們需要做的是,將組件映射到路由,然后告訴路由在哪里渲染組件內(nèi)容。
1. HTML
vue-router
Hello World!
toUser
2. JavaScript
importVuefrom'vue';
importVueRouterfrom'vue-router';
importindexPagefrom'./index.vue';
importuserPagefrom'./user.vue';
// 注冊(cè)路由插件
Vue.use(VueRouter);
// 配置路由
constroutes=[
{
path:'',
component:indexPage,
},{
path:'/user',
component:userPage,
}];
// 創(chuàng)建Router實(shí)例
constrouter=newVueRouter({
routes,
});
// 創(chuàng)建和掛載Vue根實(shí)例
constapp=newVue({
router,
});
app.$mount('#app');
router-link和router-view是兩個(gè)功能性內(nèi)置組件。router-link默認(rèn)被渲染為a標(biāo)簽,負(fù)責(zé)路由跳轉(zhuǎn)功能;router-view是組件內(nèi)容被渲染的位置。
js方面代碼里的注釋已經(jīng)寫得很清楚。
動(dòng)態(tài)路由其實(shí)又可以叫做路由傳參。
constrouter=newVueRouter({
routes:[
// 動(dòng)態(tài)路徑參數(shù) 以冒號(hào)開頭
{path:'/user/:id',component:User}
]
})
形如上述形式的路徑即為動(dòng)態(tài)路由,冒號(hào)后是參數(shù),可以跟多段參數(shù),每個(gè)參數(shù)都被設(shè)置到this.$route.params中。
注意/user/:id和/user/:name,當(dāng)參數(shù)變化時(shí),組件會(huì)被復(fù)用,因此組件生命周期鉤子不會(huì)被再次調(diào)用。復(fù)用組建時(shí),可以通過監(jiān)聽$route對(duì)象的變化來監(jiān)測(cè)路由是否變化。
路由鉤子beforeRouterUpdate也會(huì)執(zhí)行。
vue-router 使用path-to-regexp作為路徑匹配引擎,假如路徑很復(fù)雜可以學(xué)習(xí)高級(jí)的匹配模式。但是路徑一般不應(yīng)設(shè)計(jì)的太復(fù)雜,如果太復(fù)雜,應(yīng)該考慮如何簡(jiǎn)化。
有時(shí)候想同時(shí)(同級(jí))展示多個(gè)視圖,例如創(chuàng)建一個(gè)布局,有 sidebar(側(cè)導(dǎo)航) 和 main(主內(nèi)容)
兩個(gè)視圖,這個(gè)時(shí)候命名視圖就派上用場(chǎng)了。你可以在界面中擁有多個(gè)單獨(dú)命名的視圖,而不是只有一個(gè)單獨(dú)的出口。如果 router-view
沒有設(shè)置名字,那么默認(rèn)為 default。
一個(gè)視圖使用一個(gè)組件渲染,因此對(duì)于同個(gè)路由,多個(gè)視圖就需要多個(gè)組件。確保正確使用 components 配置(帶上 s):
routes:[
{
path:'/',
components:{
default:Foo,
a:SideBar,
b:Header
}
}
]
定義路由的時(shí)候可以配置 meta 字段:
routes:[
{
path:'/foo',
component:Foo,
children:[
{
path:'bar',
component:Bar,
// a meta field
meta:{requiresAuth:true}
}
]
}
]
除了使用 創(chuàng)建 a 標(biāo)簽來定義導(dǎo)航鏈接,我們還可以借助 router 的實(shí)例方法,通過編寫代碼來實(shí)現(xiàn)。
router.push(location)
想要導(dǎo)航到不同的 URL,則使用 router.push 方法。這個(gè)方法會(huì)向 history 棧添加一個(gè)新的記錄,所以,當(dāng)用戶點(diǎn)擊瀏覽器后退按鈕時(shí),則回到之前的 URL。
當(dāng)你點(diǎn)擊 時(shí),這個(gè)方法會(huì)在內(nèi)部調(diào)用,所以說,點(diǎn)擊 等同于調(diào)用 router.push(...)。
router.replace(location)
跟 router.push 很像,唯一的不同就是,它不會(huì)向 history 添加新記錄,而是跟它的方法名一樣 —— 替換掉當(dāng)前的 history 記錄。
router.go(n);
這個(gè)方法的參數(shù)是一個(gè)整數(shù),意思是在 history 記錄中向前或者后退多少步,類似 window.history.go(n)。
當(dāng)打包構(gòu)建應(yīng)用時(shí),Javascript 包會(huì)變得非常大,影響頁面加載。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時(shí)候才加載對(duì)應(yīng)組件,這樣就更加高效了。
異步組件模式:
constFoo=resolve =>{
// require.ensure 是 Webpack 的特殊語法,用來設(shè)置 code-split point
// (代碼分塊)
require.ensure(['./Foo.vue'],()=>{
resolve(require('./Foo.vue'))
})
}
Amd風(fēng)格的require模式:
constroutes=[
{
path:'',
component:resolve =>require(['./index.vue'],resolve),
},{
path:'/user',
component:resolve =>require(['./user.vue'],resolve),
}];
ensure模式寫起來有點(diǎn)繁瑣,我更傾向于后面一種寫法。
鉤子(Hook),早期編程可能有個(gè)概念叫句柄,不知道將兩者類比而且強(qiáng)行歸為一類是不是合適。鉤子的用處是在某個(gè)特定流程中的不同時(shí)機(jī)暴露出一些函數(shù),使得用戶可以通過覆寫這些函數(shù)實(shí)現(xiàn)在原有位置執(zhí)行自己的代碼邏輯的功能。
1. 分類
vue-router中的導(dǎo)航鉤子按定義位置不同(執(zhí)行時(shí)機(jī)也不同)分為全局鉤子、路由級(jí)鉤子和組件級(jí)鉤子。
全局鉤子
全局鉤子有三個(gè),分別是beforeEach、beforeResolve和afterEach,在路由實(shí)例對(duì)象注冊(cè)使用;
路由級(jí)鉤子
路由級(jí)鉤子有beforeEnter,在路由配置項(xiàng)中項(xiàng)定義;
組件級(jí)鉤子
組件級(jí)鉤子有beforeRouteEnter、beforeRouteUpdate和beforeRouteLeave,在組件屬性中定義;
2. 代碼示例
import Vue from 'vue';
import VueRouter from 'vue-router';
// Vue中插件必須use注冊(cè)
Vue.use(VueRouter);
// 路由配置項(xiàng),此處是路由級(jí)鉤子的定義
const routes = [{
? ? ? ? path: '/',
? ? ? ? component: resolve => require(['./index.vue'], resolve),
? ? ? ? keepAlive: true,
? ? },
? ? {
? ? ? ? path: '/user/:userName',
? ? ? ? keepAlive: true,
? ? ? ? beforeEnter(to,from,next){
? ? ? ? ? ? console.log('router beforeEnter');
? ? ? ? ? ? next();
? ? ? ? },
? ? ? ? component: resolve => require(['./user.vue'], resolve),
? ? }];
// 實(shí)例化路由對(duì)象
const router = new VueRouter({
? ? routes
});
// 全局鉤子
router.beforeEach((to,from,next)=>{
? ? console.log('global beforeEach')
? ? next();
});
router.beforeResolve((to,from,next)=>{
? ? console.log('global beforeResolve')
? ? next();
});
router.afterEach((to,from,next)=>{
? ? console.log('global afterEach')
});
// 實(shí)例化Vue對(duì)象并掛載
new Vue({
? ? router
}).$mount('#app');
user.vue
? ?
? ? ? ?
{{ msg }}
? ? ? ?
我是:{{userName}}
? ? export default {
? ? ? ? name: 'user',
? ? ? ? data () {
? ? ? ? ? ? return {
? ? ? ? ? ? ? ? msg: '這里是 User Page.',
? ? ? ? ? ? ? ? userName: '葉落'
? ? ? ? ? ? };
? ? ? ? },
? ? ? ? methods: {},
? ? ? ? mounted () {
? ? ? ? ? ? var me = this;
? ? ? ? ? ? me.userName = me.$route.params.userName;
? ? ? ? ? ? console.log('user mounted.');
? ? ? ? },
? ? ? ? beforeRouteEnter (to, from, next) {
? ? ? ? ? ? console.log('component beforeRouteEnter');
? ? ? ? ? ? next();
? ? ? ? },
? ? ? ? beforeRouteUpdate (to, from, next) {
? ? ? ? ? ? console.log('component beforeRouteUpdate');
? ? ? ? ? ? next();
? ? ? ? },
? ? ? ? beforeRouteLeave(to,from,next){
? ? ? ? ? ? console.log('component beforeRouteLeave');
? ? ? ? ? ? next();
? ? ? ? }
? ? };
3. 執(zhí)行時(shí)機(jī)
由首頁進(jìn)入user頁面:
global beforeEach > router beforeEnter > component
beforeRouteEnter > global beforeResolve > global afterEach >
mounted
由user回到首頁:
component beforeRouteLeave => global beforeEach => global beforeResolve => global afterEach
排除beforeRouteUpdate,其余六個(gè)導(dǎo)航鉤子的執(zhí)行時(shí)機(jī)其實(shí)很好理解。大體按照leave、before、enter、resolve、after的順序并全局優(yōu)先的思路執(zhí)行。beforeRouteUpdate的觸發(fā)是在動(dòng)態(tài)路由情形下,比如
path: '/user/:userName'
這條路由,當(dāng)頁面不變更只動(dòng)態(tài)的改變參數(shù)userName時(shí),beforeRouteUpdate便會(huì)觸發(fā)。
總結(jié):使用vue組件拼湊成整個(gè)應(yīng)用,每個(gè)頁面是獨(dú)立的,路由依靠鏈接跳轉(zhuǎn),會(huì)刷新頁面。使用vue-router則可以不刷新頁面加載對(duì)應(yīng)組件,hash和history模式模擬路徑變化,不刷新頁面。