好傢伙,
今天來手寫我們的老夥計vue-router,
1.替換router
新開一個專案,並使用我們手寫的router
2.大致結構
let Vue; // 儲存vue的建構函式
class VueRouter {
constructor(options) {
}
}
VueRouter.install = (_Vue) => {
Vue = _Vue; //備份Vue
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
}
})
Vue.component("router-link", {});
//實現思路,找到對應的元件並將它渲染出來
Vue.component("router-view", {});
}
export default VueRouter;
2.1.這裡使用Vue.mixin(),使任何元件都能呼叫到router
2.2.Vue = _Vue,一會要用到Vue的方法,將某個變數變為響應式的
3.router-link實現
3.1.元件的使用
3.2.實現
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
return h("a", {
attrs: {
href: `#${this.to}`
}
}, this.$slots.default);
}
});
重點來了,為什麼要用個#?
在這段程式碼中,使用 #
的目的是為了在單頁面應用(SPA)中實現基於 hash 的路由。在傳統的單頁面應用中,透過改變 URL 中的 hash 部分來切換頁面內容,而不會導致整個頁面重新載入。這種方式被稱為 hash 模式路由。
具體來說,當使用者點選帶有 #
的連結時,瀏覽器會更新 URL 中的 hash 部分,但不會觸發整個頁面的重新載入,而是根據新的 hash 值來 更新頁面內容,從而實現頁面的切換和路由導航。
在 Vue 中,使用 #
可以幫助我們正確地處理 hash 模式路由。
4.實現router-view
Vue.component("router-view", {
render(h) {
let component = null;
//獲取當前路由所對應的元件並將它渲染出來
const current = this.$router.current;
const route = this.$router.$options.routes.find((route) =>
route.path === current
)
// const route = this.$router.$options.routes.find((route) =>
// {route.path === current}
// )
//!!錯誤
//若使用箭頭函式塊{},必須要有返回值
console.log(route, current)
if (route) {
component = route.component
}
return h(component);
}
});
總體上看,程式碼邏輯非常簡單,在router中找到匹配的元件,然後返回相應的元件就好了,但問題來了,我怎麼知道當前頁面current是什麼?
5.實現VueRouter
class VueRouter {
constructor(options) {
this.$options = options;
this.current = "/";
let initial = window.location.hash.slice(1) || "/"
Vue.util.defineReactive(this, "current", initial)
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1) || "/"
console.log(this.current)
})
}
}
第一步:開始我們預設this.current = "/"; 即首頁,
第二步:將current變為響應式資料,
第三步:讓current動態獲取當前路由的值
問:為什麼要將current變為響應式資料?
答:render的更新依賴於響應式資料curren,若current不為響應式資料,current變化,render不會重新渲染
搞定
6.原始碼
let Vue; // 儲存vue的建構函式
class VueRouter {
constructor(options) {
this.$options = options;
this.current = "/";
let initial = window.location.hash.slice(1) || "/"
Vue.util.defineReactive(this, "current", initial)
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1) || "/"
console.log(this.current)
})
}
}
VueRouter.install = (_Vue) => {
Vue = _Vue; //備份Vue
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
}
})
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
return h("a", {
attrs: {
href: `#${this.to}`
}
}, this.$slots.default);
}
});
//實現思路,找到對應的元件並將它渲染出來
Vue.component("router-view", {
render(h) {
let component = null;
//獲取當前路由所對應的元件並將它渲染出來
const current = this.$router.current;
// const route = this.$router.$options.routes.find((route) =>
// route.path === current
// )
const route = this.$router.$options.routes.find((route) =>
{return route.path === current}
)
//!!錯誤
//若使用箭頭函式塊{},必須要有返回值
console.log(route, current)
if (route) {
component = route.component
}
return h(component);
}
});
}
export default VueRouter;
7.補充
一個小小bug
const route = this.$router.$options.routes.find((route) =>
route.path === current
)
不能寫成
const route = this.$router.$options.routes.find((route) =>
{route.path === current}
)
第一段程式碼使用了簡潔的箭頭函式寫法,直接返回了 route.path === current
的結果。這種寫法適用於只有一行程式碼的情況,箭頭函式會自動將這一行程式碼的結果作為返回值。因此,第一段程式碼會返回第一個滿足條件 route.path === current
的 route
物件。
第二段程式碼使用了程式碼塊 {}
包裹起來,但在程式碼塊中沒有顯式返回值。這種情況下,箭頭函式不會自動返回程式碼塊中的結果,需要手動新增 return
關鍵字來返回值。因此,第二段程式碼中的箭頭函式沒有正確返回值,會導致程式碼出錯。
所以,若要使用程式碼塊 {}
const route = this.$router.$options.routes.find((route) =>
{return route.path === current}
)