在實務上 Route 也會如 Restful API 一樣,動態在 Route 中夾帶 Data,此時可使用 Dynamic Route Matching,而不用將 Route 寫死。
Version
macOS Mojave 10.14.5
Node 12.4.0
Vue CLI 3.8.4
Vue 2.6.10
Vue-router 3.0.3
Params
- 使用
products/1
在網址傳入1
, - 在 HTML 也會動態顯示
T-Shirts
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
},
{
path: '/products/:id',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue')
}
]
});
21 行
{
path: '/products/:id',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue')
}
在 path
加上 /products/:id
,其中 id
為 param,前面加上 :
。
Products.vue
<template>
<div>
<h1>Products</h1>
<h2>{{ product }}</h2>
</div>
</template>
<script>
let products = {
0: 'Shoes',
1: 'T-Shirts',
2: 'Pants',
};
let product = function() {
return products[this.$route.params.id] || 'N/A';
};
export default {
name: 'Products',
computed: {
product
},
};
</script>
第 4 行
<h2>{{ product }}</h2>
顯示 product
computed。
第 9 行
let products = {
0: 'Shoes',
1: 'T-Shirts',
2: 'Pants',
};
宣告 products
object,把 products
object 當成 map 用。
注意
products
並沒有寫在data
內,因為此為computed
function 所使用資料,而非 HTML template 所使用,故不必寫在data
內
15 行
let product = function() {
return products[this.$route.params.id] || 'N/A';
};
使用 this.$route.params
,之後加上在 router.js
定義的 id
。
若對應不到 products
內資料,會回傳 undefined
,視為 falsy value,將繼續執行 ||
右側的 N/A
。
因為
product()
computed 使用this
context 的$route
,所以只能使用 function expression
App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/products/1">Product</router-link>
</div>
<router-view/>
</div>
</template>
第 6 行
<router-link to="/products/1">Product</router-link>
<router-link>
的 to
也可以使用 /products/1
。
Query String
- 使用
products?id=1
在網址傳入2
, - 在 HTML 也會動態顯示
Pants
route.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
{
path: '/products',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue'),
},
],
});
20 行
path: '/products',
將 path
的 /:id
拿掉。
Products.vue
<template>
<div>
<h1>Products</h1>
<h2>{{ product }}</h2>
</div>
</template>
<script>
let products = {
0: 'Shoes',
1: 'T-Shirts',
2: 'Pants',
};
let product = function() {
return products[this.$route.query.id] || 'N/A';
};
export default {
name: 'Products',
computed: {
product
},
};
</script>
15 行
let product = function() {
return products[this.$route.query.id] || 'N/A';
};
使用 this.$route.query
,加上要抓到的 id
。
App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/products?id=2">Product</router-link>
</div>
<router-view/>
</div>
</template>
第 6 行
<router-link to="/products?id=2">Product</router-link>
<router-link>
的 to
也可以使用 /products?id=2
。
Optional Params
若 Params 可有可無,則可使用 ?
。
即使沒有提供 param,也可以正常顯示 component。
route.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
{
path: '/products/:id?',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue'),
},
],
});
19 行
{
path: '/products/:id?',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue'),
},
在 :id
後面加上 ?
,表示 id
為 optional,若沒提供 /1
,也會切到 Product
component,只是抓不到 product 顯示 N/A
而已。
Matching Priority
Route 明明是 products
,卻顯示 About ??
Route 在 match 時,是依照程式碼順序做 match,也就是 先執行到先贏
,而不是如 CSS 的 後蓋前
。
route.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/:page',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
{
path: '/products/:id?',
name: 'products',
component: () => import(/* webpackChunkName: "products" */ './views/Products.vue'),
},
],
});
15 行
{
path: '/:page',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
將 About
component 的 route 改成 /:page
。
由於 Product.vue
的 route 為 /products/:id?
,因此 /products
會被視為 /:page
的 page
為 product
,而採用 /:page
route,永遠執行不到 /product/:id
,這就是 matching priority。
Route 在設計時要自行考慮 matching priority
Advanced Pattern
若你要更複雜的 route mathing 機制,Vue 官網建議參考 path-to-regexp,因為 Vue Router 底層就是使用 path-to-regexp 實作,因此可使用 path-to-regexp 提供的參數與格式。
Conclusion
- Dynamic route matching 讓我們不用將 route 寫死,只要符合特定格式,就可以搭配特定 component
- 更複雜的 route matching,還可搭配 regular expression
Sample Code
完整範例可在我的 GitHub 上找到