# [ 想入門,我陪你 ] Re Vue 重頭說起|Day 16:前端路由基礎與動態參數 ###### tags: `Vue`、`Re:Vue 重頭說起`、`Alex 宅幹嘛` ## Getting Started (16:00) ### HTML ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Day 16</title> </head> <body> <div id="app"> <!-- <div v-if="show === 1">Page 1</div> --> <!-- div v-if="show === 2">Page 2</div> --> <p></p> <p> <a href="/page1">A Tag - Page 1</a> <a href="/page2">A Tag - Page 2</a> <router-link to="/page1">Page 1</router-link> <router-link to="/page2">Page 2</router-link> </p> <!-- 負責顯示路由內容在畫面 --> <router-view></router-view> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script> <!-- 載入 cdn 網址會被加上# --> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script> <script> <!-- 用 template 製作頁面 --> const Page1 = { template: '<div>Page1</div>' } const Page2 = { template: '<div>Page2</div>' } // 定義好 router 模組 const router = new VueRouter({ routes: [ { path: '/page1', component: Page1, }, { path: '/page2', component: Page2, }, ], }) new Vue({ el: '#app', // 掛載到Vue,vue-devtool可以讀取到網址路由狀態 router, data() { return { show: 1, } } }) </script> </body> </html> ``` > vue-router 關鍵字: router(實體、操縱、控制路由) vs route(讀取、記錄、分配資料) > 用 a tag 要自己分辨 hash/history 模式,router-link 會自動幫你加上 **.router-link-exact-active** ### Javascript $vm0 = this,操作路由實體,可以在 vue-devtool 內 console看看 $vm0.$router 與 $vm0.$route 有什麼差別 ![](https://i.imgur.com/55iRyVM.png) ![](https://i.imgur.com/wDVxcat.png) ![](https://i.imgur.com/1ePoOc1.png) > 此節告訴你可以用 router 去做上下一頁,或是讀取query..等參數可以使用 :::warning Notice that a ``<router-link>`` automatically gets the `.router-link-active` class when its target route is matched. You can learn more about it in its API reference. ::: ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Day 16</title> </head> <style> .router-link-active { background: black; color: white; }; </style> <body> <div id="app"> <!-- <div v-if="show === 1">Page 1</div> --> <!-- div v-if="show === 2">Page 2</div> --> <p> <router-link to="/">Index</router-link> <router-link to="/page1">Page 1</router-link> <router-link to="/page2">Page 2</router-link> </p> <!-- 負責顯示路由內容在畫面 --> <router-view></router-view> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script> <!-- 載入 cdn 網址會被加上# --> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script> <script> <!-- 用 template 製作頁面 --> const Index = { template: '<div>Index</div>' } const Page1 = { template: '<div>Page1</div>' } const Page2 = { template: '<div>Page2</div>' } // 定義好 router 模組 const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/page1', component: Page1, }, { path: '/page2', component: Page2, }, ], }) new Vue({ el: '#app', // 掛載到Vue,vue-devtool可以讀取到網址路由狀態 router, data() { return { show: 1, } } }) </script> </body> </html> ``` 當進入首頁時,Index是黑底白字,正常,但切換到 Page 1 時,Index的黑底白字沒有消失 ![](https://i.imgur.com/lvmhzWZ.png) > **.router-link-active** 是完整 router active 時有經過的(因為 Index是 '/' ,而且Page1的 '/page1' 也算是在 '/' 之下),所以要改用**.router-link-exact-active**才會修改好 > 麵包屑製作可以適用 **.router-link-active** ## Dynamic Route Matching(56:00) ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Day 16</title> </head> <style> .router-link-active { background: black; color: white; }; </style> <body> <div id="app"> <!-- <div v-if="show === 1">Page 1</div> --> <!-- div v-if="show === 2">Page 2</div> --> <p> <router-link to="/">Index</router-link> <router-link to="/page1">Page 1</router-link> <router-link to="/page2">Page 2</router-link> </p> <router-view></router-view> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script> <script> const Index = { template: '<div>Index</div>' } const User = { template: '<div>User {{ $route.params.id }}</div>' } // 定義好 router 模組 const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/:id', component: User, }, ], }) new Vue({ el: '#app', // 掛載到Vue,vue-devtool可以讀取到網址路由狀態 router, data() { return { show: 1, } } }) </script> </body> </html> ``` ![](https://i.imgur.com/gPCwrnx.jpg) >新聞網站每篇新聞有id編號,user可以透過id打API把資料拿回來 ### Reacting to Params Changes (1:21:30) 偵聽路由參數改變,去調整程式 當地一次符合路由器時,Vue Router 幫你去 render 這個 Component,可是這個Component很多事情,例如打API... ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Day 16</title> </head> <body> <div id="app"> <p> <router-link to="/">Index</router-link> <router-link to="/page1">Page 1</router-link> <router-link to="/page2">Page 2</router-link> </p> <router-view></router-view> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script> <script> const Index = { template: '<div>Index</div>' } const User = { template: '<div>User {{ $route.params.id }}</div>', mounted() { setTimeout(function() { window.alert('API!!!') }, 1000) } } const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/:id', component: User, }, ], }) new Vue({ el: '#app', router, data() { return { show: 1, } } }) </script> </body> </html> ``` > 第一次進 user/123 ,成功打API顯示 alert,但是再次切到 user/456,API卻沒打,沒有 alert,因為路由更新了,但是 Component 沒有更新, > 路由規則: 符合路由規則(path)的都是要顯示 User component,可是第一次已經符合了,Vue已經幫你把User放上去了,所以第二次轉換時,Component的mounted不觸發 > 解法1. 可以透過 watch 觀測 router 有沒有變化,去決定要不要打 API > 解法2. 用 router 專屬的 lifeCycle => beforeRouterUpdate,如果router參數變了,做事情 ASK Question 1. user/123 -> user/profile/123 會不會觸發 API alert?(1個路由) ```javascript= const router = new VueRouter({ routes: [ { path: '/user/(profile/)?:id(\\d+)', component: User, }, ], }) ``` > 不會,因為已經進到同一個路由,第二次不會 mounted 2. user/123 -> user/profile/123 會不會觸發 API alert?(2個路由) ```javascript= const router = new VueRouter({ routes: [ { path: '/user/:id(\\d+)', component: User, }, { path: '/user/profile/:id(\\d+)', component: User, }, ], }) ``` > 不會,因為已經進到同一個路由,第二次不會 mounted(Doc沒寫) 如下圖進行驗證: ![](https://i.imgur.com/PTTgqO1.jpg) ![](https://i.imgur.com/VFB5gaS.png) ![](https://i.imgur.com/CfYHk4v.png) > 模組其實沒因為路由切換而改變,因為它仍是同一個User Component,Vue把模組掛上去後,就不幫你換了,為了效能控制,去限制使用者在router上會發生的效能問題 ### Catch all / 404 Not found Route(59:40) 假設試用者亂打網址,畫面就沒了,因為路由的概念是從上到下去判斷符不符合,所以通常我們會有2種做法 ```javascript= const User = { template: '<div>404 Not Found</div>' } const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/:id', component: User, }, { path: '*', component: Error, }, ], }) ``` > 亂打網址時,瀏覽器會保留錯誤網址,畫面呈現 404 Error Component ```javascript= const User = { template: '<div>404 Not Found</div>' } const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/:id', component: User, }, { path: '/404', component: Error, }, { path: '*', redirect: /404, }, ], }) ``` > 亂打網址時,瀏覽器幫我們打到 /404 路由 ### Advanced Matching Patterns(精華) [path-to-regexp](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0) [this example](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js) ```javascript= const router = new VueRouter({ mode: 'history', base: __dirname, routes: [ { path: '/' }, { path: '/params/:foo/:bar' }, // 在參數後面加 ? { path: '/optional-params/:foo?' }, // 通常拿參數網址是需要打 api ,但有api接資料,有時候會告訴使用者id是數字還是純字串,所以可以使用 (\\d+) 做基本篩選 { path: '/params-with-regex/:id(\\d+)' }, // 可以特定path 後再接 * { path: '/asterisk/*' }, // 它可以讓有一段路由是非必填,所以在很多共用網址或是不同網址指向同一component時,就可以使用這招 { path: '/optional-group/(foo/)?bar' } ] }) ``` id是數字就進去 User Component,若不是數字就進到 /404 頁 ```javascript= const User = { template: '<div>404 Not Found</div>' } const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/:id(\\d+)', component: User, }, { path: '/404', component: Error, }, { path: '*', redirect: /404, }, ], }) ``` > d是數字,+是1次或1次以上 /user/1 可以進到 user 頁 /user/profile/1 也可以進到 user 頁 ```javascript= const User = { template: '<div>404 Not Found</div>' } const router = new VueRouter({ routes: [ { path: '/', component: Index, }, { path: '/user/(profile/)?:id(\\d+)', component: User, }, // { // path: '/user/:id(\\d+)', // component: User, // }, // { // path: '/user/profile/:id(\\d+)', // component: User, // }, { path: '/404', component: Error, }, { path: '*', redirect: /404, }, ], }) ``` > ? 代表非必填 ### Matching Priority