這篇 Vue Router 4 學習筆記,大部份都是抄官方文件內容,有簡單實作小記,可參考 GIT 練習,算是自己學習重點筆記。
安裝 Vue router 4(重點提醒!!注意幾點)
1 | npm install vue-router@4 |
設定 main.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import App from './App.vue'
import Home from './components/Home.vue'
import About from './components/About.vue'
// 2. 定義一些路由
// 每個路由都需要映射到一個組件。
// 我們後面再討論嵌套路由。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
// 3. 創建路由實例並傳遞 `routes` 配置
// 你可以在這裡輸入更多的配置,但我們在這裡
// 暫時保持簡單
const router = createRouter({
// 4. 內部提供了 history 模式的實現。為了簡單起見,我們在這裡使用 hash 模式。
history: createWebHashHistory(),
routes, // `routes: routes` 的縮寫
})
createApp(App).use(router).mount('#app')
重點提醒:
- import { createRouter, createWebHashHistory } from ‘vue-router’
- routes 宣告
- router 宣告(使用 createRouter, createWebHashHistory)
- createApp use vue-router
- html
<router-view>
記得加上這個 HTML (重要!!!我很常忘記)
App.vue1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33<template>
<h1>Hello App!</h1>
<p>
<!--使用 router-link 組件進行導航 -->
<!--通過傳遞 `to` 來指定鏈接 -->
<!--`<router-link>` 將呈現一個帶有正確 `href` 屬性的 `<a>` 標籤-->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這裡 -->
<router-view></router-view>
</template>
<script setup>
import Home from './components/Home.vue'
import About from './components/About.vue'
import HelloWorld from './components/HelloWorld.vue'
// This starter template is using Vue 3 experimental <script setup> SFCs
// Check out https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
重點提醒
<router-link to="/">Go to Home</router-link>
超連結指定某路由<router-view></router-view>
路由顯示的Vue元件
GIT:8d7a44b6059b3caf6ad648056817af4c8ec9060c
要在 setup 函數中訪問路由,請調用 useRouter 或 useRoute 函數。我們將在 Composition API 中瞭解更多信息。
在整個文檔中,我們會經常使用 router 實例,請記住,this.$router 與直接使用通過 createRouter 創建的 router 實例完全相同。我們使用 this.\$router 的原因是,我們不想在每個需要操作路由的組件中都導入路由。
引用一下官方這段,setup 函數可以用 useRouter
和 useRoute
。 router 是指整個 router實例。 route 是指 routes
某一個 route 設定。
動態路由匹配
帶參數動態路由
使用 $route.param
匹配模式 | 匹配路徑 | $route.params |
---|---|---|
/users/:username | /users/eduardo | { username: ‘eduardo’ } |
/users/:username/posts/:postId | /users/eduardo/posts/123 | { username: ‘eduardo’, postId: ‘123’ } |
GIT:https://github.com/malagege/vue-router-test/commit/b1afcbb8fe9240e4851a680db958fafc558b6dd9
響應路由參數的變化(重要)
取得資料就是 AJAX 可用這個方式取得,後面會講到。
1 | export default { |
watch 跟 $watch 差別
- $watch : 實體上的函數,使用此函數註冊監聽器。
- watch : 實體上的屬性,此屬性設置的物件在實體建立時會叫用 $watch 註冊監聽器。
$watch 是註冊監聽器的函數,而 watch 是為了開發者方便在實體上設置監聽器而提供的,其實 watch 本身也是使用 $watch 註冊監聽器。
參考:
使用 beforeRouteUpdate 導航守衛1
2
3
4
5
6
7// 使用 beforeRouteUpdate 導航守衛
async beforeRouteUpdate(to, from) {
// 對路由變化做出響應...
// this.userData = await fetchUser(to.params.id)
console.log('to',to,'from',from);
},
}
簡單說明,第一次進來不會觸發上面兩個 function ,但第二次就會了。
GIT: 9649555832b6732ed355f765b0047e733294f3a4
捕獲所有路由或 404 Not found 路由
1 | const routes = [ |
1 | this.$router.push({ |
這邊我改變一下
1 | export default { |
GIT:ef8040a99c655c68c20b92e8aceb47f64b7403c7
路由的匹配語法
基本上就是用正規表達式設定路由。這邊就不簡單實作
1 | const routes = [ |
可重複的參數
1 | const routes = [ |
router.resolve 組出 url
1 | // 給定 { path: '/:chapters*', name: 'chapters' }, |
+
與*
的差別
1 | const routes = [ |
可選參數
請注意,* 在技術上也標誌著一個參數是可選的,但 ? 參數不能重複。
1 | const routes = [ |
嵌套路由(nested-routes)
兩個步驟
- 設定 router 的 children 屬性
1 | const routes = [ |
- 設定目標組件(父層) router-view
1 | <div> |
連結: http://localhost:3000/#/users/1/profile
GIT:a0ee324aefb696a91645c1258181979cd7c35277
設定嵌套路由首頁
1 | const routes = [ |
連結: http://localhost:3000/#/users/1/
GIT:1d362c35c2ad53966a8d1794e845f7a07fc5eafa
官方範例:Nested Views - Vue Router 4 examples - CodeSandbox
編程式導航(Navigation Guards)
跳頁到不同路徑網址
原本標題「導航到不同的位置」,我覺得看不是很懂,看我改得比較白話,哈哈。
注意:在 Vue 實例中,你可以通過 \$router 訪問路由實例。因此你可以調用 this.$router.push。
想要導航到不同的 URL,可以使用 router.push 方法。這個方法會向 history 棧添加一個新的記錄,所以,當用戶點擊瀏覽器後退按鈕時,會回到之前的 URL。
當你點擊
<router-link>
時,內部會調用這個方法,所以點擊<router-link :to="...">
相當於調用 router.push(…) :
聲明式 | 編程式 |
---|---|
router.push(…) |
1 | // 字符串路徑 |
比較特別的是後面三個,用query
和hash
屬性前面沒帶過
稍微查了一下,這篇提到裡面提到使用this.$route.query.參數
也可以抓到值
1 | const username = 'eduardo' |
我有嘗試使用參數帶中文,但沒有發現什麼問題
GIT:75bf26ebf64ec5c2c4dc6f5476e86bb517327e65
由於屬性 to 與 router.push 接受的對象種類相同,所以兩者的規則完全相同。
router.push 和所有其他導航方法都會返回一個 Promise,讓我們可以等到導航完成後才知道是成功還是失敗。我們將在 Navigation Handling 中詳細介紹。
替換當前位置
它的作用類似於 router.push,唯一不同的是,它在導航時不會向 history 添加新記錄,正如它的名字所暗示的那樣——它取代了當前的條目。
聲明式 | 編程式 |
---|---|
router.replace(…) |
1 | router.push({ path: '/home', replace: true }) |
回前一頁、下一頁
該方法採用一個整數作為參數,表示在歷史堆棧中前進或後退多少步,類似於
window.history.go(n)
1 | // 向前移動一條記錄,與 router.forward() 相同 |
命名路由
除了 path 之外,你還可以為任何路由提供 name。這有以下優點:
- 沒有硬編碼的 URL
- params 的自動編碼/解碼。
- 防止你在 url 中出現打字錯誤。
- 繞過路徑排序(如顯示一個)
1 | const routes = [ |
1 | <router-link :to="{ name: 'user', params: { username: 'erina' }}"> |
與下面程式相同
1 | router.push({ name: 'user', params: { username: 'erina' } } |
vue-router/app.js at dev · vuejs/vue-router
命名視圖
簡單來說,設定 router-view
沒設定 name 後,預設名稱會是default
。這邊看範例比較快。
1 | <router-view class="view left-sidebar" name="LeftSidebar"></router-view> |
1 | const router = createRouter({ |
官方範例:Named Views - Vue Router 4 examples - CodeSandbox
嵌套命名視圖
我覺得這部分看官方文件比較清楚。
官方範例: Nested Named Views - Vue Router 4 examples - CodeSandbox
重新導向和别名
官方文件原翻譯: 重定向和别名
重新導向(redirect)
/search/screens -> /search?q=screens
跟後端 redirect 意思差不多
1 | const routes = [{ path: '/home', redirect: '/' }] |
也能指向一個 route 的名稱
1 | const routes = [{ path: '/home', redirect: { name: 'homepage' } }] |
動態導向(帶參數)
1 | const routes = [ |
别名
/
參照 /home
。
1 | const routes = [{ path: '/', component: Homepage, alias: '/home' }] |
通過別名,你可以自由地將 UI 結構映射到一個任意的 URL,而不受配置的嵌套結構的限制。使別名以 / 開頭,以使嵌套路徑中的路徑成為絕對路徑。你甚至可以將兩者結合起來,用一個數組提供多個別名:
1
2
3
4
5
6
7
8
9
10
11
12
13 const routes = [
{
path: '/users',
component: UsersLayout,
children: [
// 為這 3 個 URL 呈現 UserList
// - /users
// - /users/list
// - /people
{ path: '', component: UserList, alias: ['/people', 'list'] },
],
},
]
SEO 需要注意的事情:Consolidate Duplicate URLs with Canonicals | Google Search Central
將 props 傳遞給路由組件(容易忘記?)
1 | const User = { |
替換
1 | const User = { |
簡單來說 route 設定多了 props:true
,prop也需要設定對應變數
Boolean mode
當 props 設置為 true 時,route.params 將被設置為組件的 props。
命名視圖
簡單來說,跟上面用法差不多。
1 | const routes = [ |
物件給值(固定給值)
這邊是固定給值
1 | const routes = [ |
函式給值(重要?)
1 | const routes = [ |
官方範例:vue-router/app.js at dev · vuejs/vue-router
這邊就不實戰,簡單做個結論,動態給值,可使用props,function 方式進去,固定方式可以用 object mode
歷史模式
都是Web 常見的兩種hash
,html5 history api
hash
1 | import { createRouter, createWebHashHistory } from 'vue-router' |
HTML5 模式
用 createWebHistory() 创建 HTML5 模式,推荐使用这个模式:
1 | import { createRouter, createWebHistory } from 'vue-router' |
伺服器配置
官方竟然沒放 Traefik ,我自己爬文找一下。
我竟然犯蠢了…,Traefik 不是 Web Server,所以我應該是要改代理後面的Web Server。
參考:Nginx try_files to traefik ingressrout? - Traefik / Traefik v2 - Traefik Labs Community Forum
Sorry, not clear.
Nginx is a web server not just a proxy like Traefik, and Traefik does not have a feature similar to try_files simply because it’s not a web server.
Also “not working” is not a very good problem description. How do you test, what results are you expecting and what are you observing instead?
導航守衛(Navigation Guards)
全局前置守衛
1 | const router = createRouter({ ... }) |
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處於等待中。
這邊有去做個實作,第一次進去,to 和 from 都會有值,這邊設定一半機率會跳頁!!簡單來說,to 是要轉頁面,from 是來源頁,當回傳 false時候,會轉到 from 。所以這就是為什麼第一次進去,from ,to 都是一樣,不會卡住問題
1 | router.beforeEach((to, from) => { |
每個守衛方法接收兩個參數:
- to: 即將要進入的目標 用一種標準化的方式
- from: 當前導航正要離開的路由 用一種標準化的方式
可以返回的值如下:
- false: 取消當前的導航。如果瀏覽器的 URL 改變了(可能是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
- 一個路由地址: 通過一個路由地址跳轉到一個不同的地址,就像你調用 router.push() 一樣,你可以設置諸如 replace: true 或 name: ‘home’ 之類的配置。當前的導航被中斷,然後進行一個新的導航,就和 from 一樣。
錯誤監聽
如果遇到了意料之外的情況,可能會拋出一個 Error。這會取消導航並且調用 router.onError() 註冊過的回調。
如果什麼都沒有,undefined 或返回 true,則導航是有效的,並調用下一個導航守衛
1 | router.beforeEach((to, from) => { |
可選的第三個參數 next
在之前的 Vue Router 版本中,也是可以使用 第三個參數 next 的。這是一個常見的錯誤來源,可以通過 RFC 來消除錯誤。然而,它仍然是被支持的,這意味著你可以向任何導航守衛傳遞第三個參數。在這種情況下,確保 next 在任何給定的導航守衛中都被嚴格調用一次。它可以出現多於一次,但是只能在所有的邏輯路徑都不重疊的情況下,否則鉤子永遠都不會被解析或報錯。這裡有一個在用戶未能驗證身份時重定向到/login的錯誤用例:
1 | // GOOD |
簡單來說,這邊簡單實作是否登入動作導向登入頁。next()
裡面空值,會進去to
頁面,假如有指定next裡面參數,會進去到指定頁面。
這邊我們看到,這邊沒有return ture/false,我很好奇我這邊return false會怎麼樣?結論,next()
會執行跳頁動作,所以return true/false就沒有什麼意思。
1 | router.beforeEach((to, from, next) => { |
全局解析守衛
你可以用 router.beforeResolve 註冊一個全局守衛。這和 router.beforeEach 類似,因為它在 每次導航時都會觸發,但是確保在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之後,解析守衛就被正確調用。這裡有一個例子,確保用戶可以訪問自定義 meta 屬性 requiresCamera 的路由:
1 | router.beforeResolve(async to => { |
這邊可以進行權限判斷。
全局後置鉤子
1 | router.afterEach((to, from) => { |
或
1 | router.afterEach((to, from, failure) => { |
路由(route)獨享的守衛
1 | const routes = [ |
beforeEnter 守衛 只在進入路由時觸發,不會在 params、query 或 hash 改變時觸發。例如,從 /users/2 進入到 /users/3 或者從 /users/2#info 進入到 /users/2#projects。它們只有在 從一個不同的 路由導航時,才會被觸發。
你也可以將一個函數數組傳遞給 beforeEnter,這在為不同的路由重用守衛時很有用:
1 | function removeQueryParams(to) { |
beforeEnter 這邊是設定在 route,可以帶路陣列方式。
組件內的守衛
可用的配置 API
你可以為路由組件添加以下配置:
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
1 | const UserDetails = { |
常用 beforeRouteLeave
1 | beforeRouteLeave (to, from) { |
完整的導航解析流程
導航被觸發。
在失活的組件裡調用 beforeRouteLeave 守衛。
調用全局的 beforeEach 守衛。
在重用的組件裡調用 beforeRouteUpdate 守衛(2.2+)。
在路由配置裡調用 beforeEnter。
解析異步路由組件。
在被激活的組件裡調用 beforeRouteEnter。
調用全局的 beforeResolve 守衛(2.5+)。
導航被確認。
調用全局的 afterEach 鉤子。
觸發 DOM 更新。
調用 beforeRouteEnter 守衛中傳給 next 的回調函數,創建好的組件實例會作為回調函數的參數傳入。
路由元信息
可針對 route 設定 meta 做一些事情判斷
1 | const routes = [ |
1 | router.beforeEach((to, from) => { |
懶加載
原寫法1
2
3
4
5import About from './components/About.vue'
const routes = [
{ path: '/', component: Home },
{ name:'about', path: '/about', component: About },
新寫法
1 | const About = import('./components/About.vue') |
簡單來說,變動只有第一行。
2021-06-20目前 webpack才能用的樣子,Vite不能樣子
獲取資料(重點)
當初我在想,載入資料不就寫在 create
就好了嗎?後來我發現,不能這樣,所以官方範例都用 $watch
去做監聽參數。
有兩個方式
1 | <template> |
方式一
1 | export default { |
使用 randomuser 參照官方做個簡單範例
GIT: 6995135924b391a6cd9fe3f8564e3aa80051b036
方式二
1 | export default { |
官方忘記加了 setData method,回看 v3 版本文件有這個 method