<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
react/vue
能夠根據業務需求口噴 router
的關鍵設定,包括但不限於:路由的匹配規則、路由守衛、路由分層等;history
的主要模式,知道 history
和 router
的邊界;router
底層的原始碼,不要求每行都讀,可以口噴關鍵程式碼即可;遠古時期,當時前後端還是不分離的,路由全部都是由伺服器端控制的,前端程式碼和伺服器端程式碼過度融合在一起。
使用者端 -->
前端發起 http
請求 -->
伺服器端 -->
url
路徑去匹配不同的路由 -->
返回不同的資料。
這種方式的缺點和優點都非常明顯:
html
,渲染了頁面結構。SEO
的效果非常好,首屏時間特別快;
url
開始到頁面任意元素載入出來/渲染出來 -->
首屏時間;html
的工作放在的伺服器端;後來 ...隨之 ajax
的流行,非同步資料請求可以在瀏覽器不重新整理的情況下進行。
後來 ...出現了更高階的體驗 —— 單頁應用。
-->
HTML
檔案-->
單個 HTML
檔案在單頁應用中,不僅在頁面中的互動是不重新整理頁面的,就連頁面跳轉也都是不重新整理頁面的。
單頁應用的特點:
a/b/c
,a-> b -> c
);載入過的公共資源,無需再重複載入;而支援起單頁應用這種特性的,就是 前端路由。
前端路由的需求是什麼?
url
渲染不同內容;也就是可以在改變 url
的前提下,保證頁面不重新整理。
Hash
路由和 History
路由的區別?
hash
有 #
,history
沒有 #
;hash
的 #
部分內容不會給伺服器端,主要一般是用於錨點, history
的所有內容都會給伺服器端;hash
路由是不支援 SSR
的,history
路由是可以的;hash
通過 hashchange
監聽變化,history
通過 popstate
監聽變化;hash
的出現滿足了這個需求,他有以下幾種特徵:
url
中帶有一個 #
符號,但是 #
只是瀏覽器端/使用者端的狀態,不會傳遞給伺服器端;
www.baidu.com/#/user
-->
通過 http
請求 -->
伺服器端接收到的 www.baidu.com/
www.baidu.com/#/list/detail/1
-->
通過 http
請求 -->
伺服器端接收到的 www.baidu.com/
hash
值的更改,不會導致頁面的重新整理;location.hash = '#aaa'; location.hash = '#bbb'; // 從 #aaa 到 #bbb,頁面是不會重新整理的
url
會渲染不同的頁面;hash
值的更改,會在瀏覽器的存取歷史中新增一條記錄,所以我們才可以通過瀏覽器的返回、前進按鈕來控制 hash
的切換;hash
值的更改,會觸發 hashchange
事件;location.hash = '#aaa'; location.hash = '#bbb'; window.addEventLisenter('hashchange', () => {});
我們同樣有兩種方式來控制 hash
的變化:
location.hash
的方式:location.hash = '#aaa'; location.hash = '#bbb';
html
標籤的方式:<a href="#user" rel="external nofollow" > 點選跳轉到 user </a> <!-- 等同於下面的寫法 --> location.hash = '#user';
./index.html
<!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>Document</title> <link rel="stylesheet" href="./index.css" rel="external nofollow" /> </head> <body> <div class="container"> <a href="#gray" rel="external nofollow" >灰色</a> <a href="#green" rel="external nofollow" >綠色</a> <a href="#" rel="external nofollow" >白色</a> <button onclick="window.history.go(-1)">返回</button> </div> <script type="text/javascript" src="index.js"></script> </body> </html>
./index.css
.container { width: 100%; height: 60px; display: flex; justify-content: space-around; align-items: center; font-size: 18px; font-weight: bold; background: black; color: white; } a:link, a:hover, a:active, a:visited { text-decoration: none; color: white; }
./index.js
/* 期望看到的效果:點選三個不同的 a 標籤,頁面的背景顏色會隨之變化 */ class BaseRouter { constructor() { this.routes = {}; // 儲存 path 以及 callback 的對應關係 this.refresh = this.refresh.bind(this); // 如果不 bind 的話,refresh 方法中的 this 指向 window // 處理頁面 hash 變化,可能存在問題:頁面首次進來可能是 index.html,並不會觸發 hashchange 方法 window.addEventListener('hashchange', this.refresh); // 處理頁面首次載入 window.addEventListener('load', this.refresh); } /** * route * @param {*} path 路由路徑 * @param {*} callback 回撥函數 */ route(path, callback) { console.log('========= route 方法 ========== ', path); // 向 this.routes 儲存 path 以及 callback 的對應關係 this.routes[path] = callback || function () {}; } refresh() { // 重新整理頁面 const path = `/${location.hash.slice(1) || ''}`; console.log('========= refresh 方法 ========== ', path); this.routes[path](); } } const body = document.querySelector('body'); function changeBgColor(color) { body.style.backgroundColor = color; } const Router = new BaseRouter(); Router.route('/', () => changeBgColor('white')); Router.route('/green', () => changeBgColor('green')); Router.route('/gray', () => changeBgColor('gray'));
hash
有個 #
符號,不美觀,伺服器端無法接受到 hash
路徑和引數。
歷史的車輪無情攆過 hash
,到了 HTML5
時代,推出了 History API
。
window.history.back(); // 後退 window.history.forward(); // 前進 window.history.go(-3); // 接收 number 引數,後退 N 個頁面 window.history.pushState(null, null, path); window.history.replaceState(null, null, path);
其中最主要的兩個 API
是 pushState
和 replaceState
,這兩個 API
都可以在不重新整理頁面的情況下,操作瀏覽器歷史記錄。
不同的是,pushState
會增加歷史記錄,replaceState
會直接替換當前歷史記錄。
pushState
:頁面的瀏覽記錄裡新增一個歷史記錄;replaceState
:替換當前歷史記錄;他們的引數是⼀樣的,三個引數分別是:
state
:是一個物件,是一個與指定網址相關的物件,當 popstate
事件觸發的時候,該物件會傳入回撥函數;title
:新頁面的標題,瀏覽器支援不一,建議直接使用 null
;url
:頁面的新地址;History API
有以下幾個特性:
#
;history.pushState()
或 history.replaceState()
不會觸發 popstate
事件,這時我們需要手動觸發頁面渲染;history.popstate
事件來監聽 url
的變化;JavaScript
呼叫 back
、forward
、go
方法時才會觸發 popstate
;pushState
時,會觸發 popstate
嗎?
pushState/replaceState
並不會觸發 popstate
事件,這時我們需要手動觸發頁面的重新渲染;我們可以使用 popstate
來監聽 url
的變化;
popstate
到底什麼時候才能觸發:
js
呼叫 back
方法;js
呼叫 forward
方法;js
呼叫 go
方法;./index.html
./index.css
.container { width: 100%; height: 60px; display: flex; justify-content: space-around; align-items: center; font-size: 18px; font-weight: bold; background: black; color: white; } a:link, a:hover, a:active, a:visited { text-decoration: none; color: white; }
./index.js
class BaseRouter { constructor() { this.routes = {}; // location.href; => hash 的方式 console.log('location.pathname ======== ', location.pathname); // http://127.0.0.1:8080/green ==> /green this.init(location.pathname); this._bindPopState(); } init(path) { // pushState/replaceState 不會觸發頁面的渲染,需要我們手動觸發 window.history.replaceState({ path }, null, path); const cb = this.routes[path]; if (cb) { cb(); } } route(path, callback) { this.routes[path] = callback || function () {}; } // ! 跳轉並執行對應的 callback go(path) { // pushState/replaceState 不會觸發頁面的渲染,需要我們手動觸發 window.history.pushState({ path }, null, path); const cb = this.routes[path]; if (cb) { cb(); } } // ! 演示一下 popstate 事件觸發後,會發生什麼 _bindPopState() { window.addEventListener('popstate', e => { /* 觸發條件: 1、點選瀏覽器前進按鈕 2、點選瀏覽器後退按鈕 3、js 呼叫 forward 方法 4、js 呼叫 back 方法 5、js 呼叫 go 方法 */ console.log('popstate 觸發了'); const path = e.state && e.state.path; console.log('path >>> ', path); this.routes[path] && this.routes[path](); }); } } const Router = new BaseRouter(); const body = document.querySelector('body'); const container = document.querySelector('.container'); function changeBgColor(color) { body.style.backgroundColor = color; } Router.route('/', () => changeBgColor('white')); Router.route('/gray', () => changeBgColor('gray')); Router.route('/green', () => changeBgColor('green')); container.addEventListener('click', e => { if (e.target.tagName === 'A') { e.preventDefault(); console.log(e.target.getAttribute('href')); // /gray /green 等等 Router.go(e.target.getAttribute('href')); } });
使用 Vue.js
,我們已經可以通過組合元件來組成應用程式,當你要把 Vue Router
新增進來,我們需要做的是,將元件(components
)對映到路由(routes
),然後告訴 Vue Router
在哪裡渲染它們。
舉個例子:
<!-- 路由匹配到的元件將渲染在這裡 --> <div id="app"> <router-view></router-view> </div>
// 如果使用模組化機制程式設計,匯入 Vue 和 VueRouter,要呼叫 Vue.use(VueRouter) // 1、定義(路由)元件 // 可以從其他檔案 import 進來 const Foo = { template: '<div>foo</div>' }; const Bar = { template: '<div>bar</div>' }; // 2、定義路由 //每個路由應該對映一個元件,其中 component 可以是通過 Vue.extend() 建立的元件構造器,或者只是一個元件設定物件 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar }, ]; // 3、建立 router 範例,然後傳 routes 設定 const router = new VueRouter({ routes, }); // 4、建立和掛載根範例 // 記得要通過 router 設定引數注入路由,從而讓整個應用都有路由功能 const app = new Vue({ router, }).$mount('#app');
我們經常需要把某種模式匹配到的所有路由,全部對映到同個元件,比如使用者資訊元件,不同使用者使用同一個元件。
可以通過 $route.params.id
或者引數。
const router = new VueRouter({ routes: [ // 動態路徑引數,以冒號開頭 { path: '/user/:id', component: User }, ], }); const User = { template: '<div>User: {{ $route.params.id }}</div>', };
複用元件時,想對 路由引數 的變化作出響應的話,可以使用 watch
或者 beforeRouteUpdate
:
舉個例子:
const User = { template: '...', watch: { $route(to, from) { // 對路由變化作出響應... }, }, }; const User = { template: '...', beforeRouteUpdate(to, from, next) { // 對路由變化作出響應... // don't forget to call next() }, };
當時用萬用字元路由時,請確保路由的順序是正確的,也就是說含有萬用字元的路由應該在 最後。
舉個例子:
vue-router
提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種方式植入路由導航過程中:
router.beforeEach
router.beforeResolve
router.afterEach
beforeEnter
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
beforeRouteLeave
);beforeEach
守衛;beforeRouteUpdate
守衛;beforeEnter
;beforeRouterEnter
;beforeResolve
守衛;afterEach
勾點;DOM
更新;beforeRouterEnter
守衛中傳給 next
的回撥函數;舉個例子:
// 全域性 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, }); // 全域性的導航守衛 router.beforeEach((to, from, next) => { console.log(`Router.beforeEach => from=${from.path}, to=${to.path}`); // 可以設定頁面的 title document.title = to.meta.title || '預設標題'; // 執行下一個路由導航 next(); }); router.afterEach((to, from) => { console.log(`Router.afterEach => from=${from.path}, to=${to.path}`); }); // 路由獨享 const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // 設定陣列裡針對單個路由的導航守衛 console.log(`TestComponent route config beforeEnter => from=${from.path}, to=${to.path}`); next(); }, }, ], }); // 元件 const Foo = { template: `...`, beforeRouteEnter(to, from, next) { // 在渲染該元件的對應路由被 comfirm 前呼叫 // 不!能!獲取元件範例 this,因為當守衛執行前,元件範例還沒被呼叫 }, beforeRouteUpdate(to, from, next) { // 在當前路由改變,但是該元件被複用時呼叫 // 舉個例子來說,對於一個帶有動態引數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候 // 由於會渲染同樣的 Foo 元件,因此元件範例會被複用。而這個勾點就會在這個情況下被呼叫 // 可以存取元件範例 this }, beforeRouteLeave(to, from, next) { // 導航離開該元件的對應路由時呼叫 // 可以存取元件範例 this }, };
next
必須呼叫:
next()
:進行管道中的下一個勾點。如果全部勾點執行完了,則導航的狀態就是 confirmed
(確認的)。next(false)
:中斷當前的導航。如果瀏覽器的 URL
改變了(可能是使用者手動或者瀏覽器後退按鈕),那麼 URL
地址會重置到 from
路由對應的地址。next("/")
或者 next({ path: "/" })
:跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。可以向 next
傳遞任意位置物件,且允許設定諸如 replace: true
、name: "home"
之類的選項以及任何用在 router-link
的 to prop
或 router.push
中的選項。next(error)
:如果傳入 next
的引數是一個 Error
範例,則導航會被終止且該錯誤會被傳遞給 router.onError()
註冊過的回撥。beforeRouteLeave
router.beforeEach
beforeRouteUpdate
beforeEnter
beforeRouteEnter
router.afterEach
vue-router
裡面,怎麼記住前一個頁面的卷軸的位置???
使用前端路由,當切換到新路由時,想要頁面捲動到頂部,或者是保持原先的捲動位置,就像重新載入頁面那樣。
Vue-router
能做到,而且更好,它讓你可以自定義路由切換時頁面如何捲動。
【注意】:這個功能只在支援 history.pushState
的瀏覽器中可用。
scrollBehavior
生效的條件:
history API
;go
,forward
,back
或者 瀏覽器的前進/返回按鈕;window.history.back(); // 後退 window.history.forward(); // 前進 window.history.go(-3); // 接收 number 引數,後退 N 個頁面
舉個例子:
// 1. 記住:手動點選瀏覽器返回或者前進按鈕,記住卷軸的位置,基於 history API 的,其中包括:go、back、forward、手動點選瀏覽器返回或者前進按鈕 // 2. 沒記住:router-link,並沒有記住卷軸的位置 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, scrollBehavior: (to, from, savedPosition) => { console.log(savedPosition); // 已儲存的位置資訊 return savedPosition; }, });
當打包構建應用時,JavaScript
包會變得非常大,影響頁面載入。如果我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被存取的時候才載入對應元件,這樣就更加高效了。
舉個例子:
const Foo = () => import(/* webpackChunkName: "foo" */ './Foo.vue'); const router = new VueRouter({ routes: [{ path: '/foo', component: Foo }], });
到此這篇關於Vue.js 前端路由和非同步元件介紹的文章就介紹到這了,更多相關Vue.js 非同步元件內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45