作用:讀取位於連接對象鏈深處的屬性的值,而不必明確驗證鏈中的每個引用是否有效。運算符的功能類似於 . 鏈式運算符,不同之處在於,在引用為空 (nill 或者 undefined) 的情況下不會引起錯誤,該表達式短路返回值是 undefined。與函數調用一起使用時,如果給定的函數不存在,則返回 un ...
大家好,歡迎來到程式視點!
今天跟大家簡單聊聊Router的實現原理,以及我們如何去實現這樣一個插件。
Vue Router
是Vue.js
官方的路由管理器。它和 Vue.js 的核心深度集成,讓構建單頁面應用變得易如反掌。關於Vue Router
的使用就不做過多介紹了,大家可以前往Vue Router
官網去學習哦~
vue-router插件的基本使用
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({routes:[]})
export default router
import router from './route'
new Vue({
render: h => h(APP),
router
})
從上述代碼可以看出,router
也是作為一個插件去使用的,所以在進行原理實踐時候,我們開發的就是一個插件。
插件開發思路
定義一個Router
類,用來進行所有的router操作。定義一個install方法,將router掛載到Vue的實例上去。註冊全局組件router-link
和router-view
,router-link
組件解析為一個a標簽,router-view
解析為一個div標簽,內容為當前路由對應的component。
監聽hashchange
事件來改變當前路由對應的component,監聽load
事件做同樣的事情。
對於嵌套路由而言,在渲染router-view
時候,先去判斷當前router-view
的深度,即當前router-view
是處在哪個層級,然後在解析routes時候判斷當前路由。
如果當前路由和routes中某個路由都為'/'根路由,則直接放到路由渲染數組中,如果當前路由不是根路由,並且routes中的某個路由包含當前路由,則意味著routes數組中的這個路由要麼是當前路由的父路由,要麼就是當前路由。
然後把routes數組中的這個路由放到路由渲染數組中去,放完之後如果還有childrens,遞歸去做就行。
最後得到了一個路由匹配數組,這個數組裡麵包含當前路由和當前路由的父路由,並且數組中子路由的下標與之前router-view
的層級下標相等,這樣就能正確的將子路由的component正確的渲染到對應的router-view
中去了。
譬如當前路由表如下:
routes:[
{
path: '/',
component: () => import ('../views/index.vue')
},
{
path: '/second',
component: () => import ('../views/second.vue'),
childrens: [
{
path: '/seconde/article',
component: import ('../view/article.vue')
}
]
}
]
此時second組件下有一個router-view
,用來渲染子路由——article組件,在app下還有一個父router-view
,用來渲染index、second組件,所以此時second組件下的router-view
的層級是1(初始化為0)。
如果此時瀏覽器訪問路由 /second/article 時候,觸發我們的路由匹配方法,遍歷routes數組和當前路由對比,當前路由不是根路由,並且包含 /second 路由,所以path為 /second 的選項被push進入路由渲染數組中,然後此路由還有childrens,進行遞歸,好家伙,當前路由和 /second/article 完全相等,所以也被push到了渲染數組中。
最後我們得到了一個數組,包含兩個路由選項,父路由下標0,子路由下標1,之前我們也將router-view
做了層級標記,這樣就能得到子router-view
對應渲染的component了。~nice
插件開發
先來一個cRouter文件夾,下麵搞一個index.js,裡面就是我們傳統的router使用,上面有,然後再搞一個crouter.js:
import Link from './cLink'
import View from './cView'
var Vue
class cRouter {
constructor(options) {
this.$options = options
this.courrentRoute = window.location.hash.slice(1) || '/'
//定義一個響應式的路由渲染數組
Vue.util.defineReactive(this,'routeMap',[])
// 遍歷匹配路由
this.initRouterMap()
// 初始化route改變事件
this.initRouteChange()
}
initRouterMap(route) {
let routes = route || this.$options.routes
for (const routeItem of routes) {
if (this.courrentRoute === '/' && routeItem.path === '/') {
this.routeMap.push(routeItem)
return
}
if (
routeItem.path !== '/'
&&
this.courrentRoute.indexOf(routeItem.path) !== -1) {
this.routeMap.push(routeItem)
if (routeItem.childrens && routeItem.childrens.length > 0) {
this.initRouterMap(routeItem.childrens)
}
return
}
}
}
initRouteChange() {
window.addEventListener('hashchange', this.routeChange.bind(this))
window.addEventListener('load', this.routeChange.bind(this))
}
routeChange() {
this.courrentRoute = window.location.hash.slice(1)
this.routeMap = []
this.initRouterMap()
}
}
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.crouter) {
Vue.prototype.$crouter = this.$options.crouter
}
},
})
Vue.component('router-link', Link)
Vue.component('router-view', View)
}
export default {
cRouter,
install,
}
cview.js用來渲染router-view
export default {
render(h) {
// 將自身標記為一個router-view,避免和其他元素搞混
this.$vnode.data.routerView = true
let parent = this.$parent
//預設自己層級為0
let routeDeep = 0
while(parent) {
// 判斷是否存在父元素並且父元素有值
const vodeData = parent.$vnode && parent.$vnode.data
if (vodeData) {
// 如果父router-view是true,則自身層級增加
if (vodeData.routerView) {
routeDeep++
}
}
//繼續尋找父元素,進行遞歸
parent = parent.$parent
}
let component = null
const route = this.$crouter.routeMap[routeDeep]
if (route) {
component = route.component
}
return h(component)
}
}
cLink.js用來渲染router-link
export default {
props: {
to: {
type: String,
default: '/',
},
},
render(h) {
return h(
'a',
{ attrs: { href: `#${this.to}` } },
this.$slots.default
)
}
}
文章到這裡,我們簡單實現了類似vue aouter
路由的功能~
我想說的是:如今開源框架大大方便了我們的開發效率,但是單純的使用三方框架並不能讓我們學到更多知識我們應該是研究去探索他的實現原理以及設計理念,去思考如果讓我們設計一個框架,我們需要掌握哪些知識,該如何設計? 我想,這樣的學習才能學到更多的知識~