1、Vue.use(vueRouter) 执行完install函数后,
向组件中绑定 beforeCreate生命周期, 绑定_routerRoot,_router
2、new Router执行,创建路由映射表createMatter, pathList, pathMap ,返回 mach,addRoutes,addRoute,getRoutes
根据 mode 获取用哪个路由 base, hash,h5,abstract,
base: current、listen、transitionTo
hash:setupListeners、window.locaiton.hash、window.location.replace、window.history.go、getCurrentLocation(window.location.href)
h5: handleRoutingEvent、history.pushState、history.replaceState、window.history.go、
3、new Vue 创建组件,执行init函数,
设置setupListeners监听器、 获取 this.history.transitionTo()执行,
设置 listen,获取到根路径,app._route = route app是根页面,只要有变化,试图就会更新 发布订阅
_route 会在router-view 使用
4、super继承hash传过来的router,根据地址匹配到对应的 mached
route = this.router.match(location, this.current)
// 更新当前的地址,并且回调执行
updateRoute (route: Route) {
console.log(‘updateRoute’, this.cb);
this.current = route
this.cb && this.cb(route) // cb是 history.listen 传递更新 app._route = route 更新页面视图
}
5、给路由添加响应式,监听history.current 属性
// 响应式数据变化,只要_route 变化,就会更新视图
Vue.util.defineReactive(this, ‘_route’, this._router.history.current) //
6、定义 router-view 函数式组件,他可以获取当前router-view的父组件,就可以拿到 $route,获取 mached,通过h创建组件
如果要渲染多个组件,会先渲染第一个绑定routerView = true,然后 h 函数渲染第一个之后,在调用router-view组件,渲染下一个组件
路由中是如何执行的,参考一下代码。
一、在init函数中调用 history.transitionTo('/', 设置监听器setupListeners,
二、然后执行 base 中的transitionTo,现在只是根路径,此时还没有绑定 listen(监听),
1、执行updateRoute,改变current,执行setupListeners
2、执行onComplete,绑定 addEventLisener('hashchange', handleRoutingEvent)
三、执行history.listen(() => {})传递回调函数,此时 init函数执行完成
四、给_route绑定响应式,
Vue.util.defineReactive(this, '_route', this._router.history.current); // 给路由添加响应式,监听history.current 属性
if (history instanceof HTML5History || history instanceof HashHistory) {
const setupListeners = routeOrError => {
console.log('setupListeners-->', history)
history.setupListeners()
handleInitialScroll(routeOrError)
}
// 传递回调函数,第一个是地址,第二个是回调函数
console.log('init---transitionTo什么时候执行')
history.transitionTo(
history.getCurrentLocation(),
setupListeners,
setupListeners
)
}
// 在这里添加路由监听
history.listen(route => {
this.apps.forEach(app => {
console.log(app._route, '78898989')
app._route = route // app是根页面,只要有变化,试图就会更新
})
})
base.js 中的transitionTo函数
transitionTo (
location: RawLocation, // url
onComplete?: Function, // 回调函数
onAbort?: Function
) {
let route
try {
// 根据当前路由地址,查找记录 比如: location:'/about' record:{path:'/about', macherd: []}
route = this.router.match(location, this.current)
console.log('route-->', route)
}
// 获取到当前路由为默认路由 /
const prev = this.current
console.log('transitionTo', arguments)
this.confirmTransition(
route,
() => {
this.updateRoute(route) // 先更新最新的路由
onComplete && onComplete(route) //回调,将最新的route传过去
)
}
// 修改路由
updateRoute (route: Route) {
console.log('updateRoute', this.cb);
this.current = route // 跟新current
this.cb && this.cb(route) // 执行
}
// 添加路由监听
listen (cb: Function) {
console.log('添加路由监听', cb)
this.cb = cb
}
// hash.js
setupListeners () {
function handleRoutingEvent() {
this.transitionTo(getHash(), route => {})
}
const eventType = supportsPushState ? 'popstate' : 'hashchange'
window.addEventListener(
eventType,
handleRoutingEvent
)
}
//设置侦听器,地址有变化调用transitionTo
function setupListeners() {
popstate:简而言之就是HTML5新增的用来控制浏览器历史记录的api。
hashchange: 当URL的片段标识符更改时,将触发hashchange事件 (跟在#符号后面的URL部分,包括#符号)。
https://huaweicloud.csdn.net/639fe74adacf622b8df8f574.html?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~activity-4-121809439-blog-83268502.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~activity-4-121809439-blog-83268502.pc_relevant_default&utm_relevant_index=7
create-matcher.js
返回值: { path: location.path, mached: [{path: '/a'}, {path: '/a/A'}]}
match获取匹配的 record,需要使用view-router渲染页面使用
match(path) {
const location = {location: '/a'}
const record = pathList[path] // 找到路由地址相对的 记录
return _createRoute(record, location, redirectedFrom)
}
function _createRoute (record) {
const res = []
while (record) {
res.unshift(record)
record = record.parent
}
return res
}
Router-View 函数式组件
### router-view有什么作用?
<router-view>一般用于路由管理。当我们需要对页面进行局部刷新的时候,就可以使用<router-view>。
#### <router-view>具体使用场景
例如,我们创建了一个侧边导航栏,并且需要使用侧边导航栏对主页面内容进行切换,主页面切换的时候保持侧边导航栏不变。(类似于掘金签到页面)。
App.vue
<router-link to="/">
展示home页面吧
</router-link>
<router-link to="/about">
展示about页面吧
</router-link>
<router-view></router-view>
home.vue
<template>
<h1>我是home页面</h1>
</template>
about.vue
<template>
<h1>我是about页面</h1>
</template>
aboutA.vue
<template>
<h1>我是aboutAAA页面</h1>
</template>
aboutB.vue
<template>
<h1>我是aboutAAA页面</h1>
</template>
router.js
routes = [
{
path: '/',
compontent: 'home.vue'
},
{
path: '/about',
compontent: 'about.vue',
children: [
{
path: 'a',
compontent: 'aboutA.vue'
},
{
path: 'b',
compontent: 'aboutB.vue'
}
]
},
]
此时默认展示的/路径,显示home页面,路径发生变化,根据location.path 获取到的record 为
{
path: '/',
marched: [{path: '/', component: 'home.vue'}]
}
通过 h(component, data, children) 将home渲染出来。
### 疑问?????
#### 疑问一:当路由的current发生了变化,怎么做到 router-view页面重新根据route来渲染新的页面呢??
确切的说是先添加 lister,然后当路径发生变化的时候,调用回调函数
history.listen(route => {
this.apps.forEach(app => {
console.log(app._route, '78898989')
app._route = route // app是根页面,只要有变化,试图就会更新
})
})
if (isDef(this.$options.router)) {
this._routerRoot = this //根实例
this._router = this.$options.router
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
#### 解析:
#### 1、在install函数中 根实例可以访问到 this._router,然后this._router中又可以访问到history,然后history中绑定了.current属性,所以给 _route绑定 current 属性为响应式
#### 2、通过 defineProperty 绑定 $route,去获取根路径的current
#### 为什么说app是根页面,只要有变化,试图就会更新??
因为app._route数据是响应式的,发生变化之后,就会调用 getter 跟 sertter 方法,页面会进行虚拟DOM对比,等到了 router-view 组件的时候,重新获取 route中的matchd数组中的第 0 项,开始渲染 根/ 页面中的about页面,等about页面进行虚拟dom对比之后发现又有一个router-view,然后此时获取到上一个router-view 绑定的属性 routerView = true,获取下一个值
#### 3、当只有 current发生改变 ( routerVier ) 组件中会获取Vue根实例有parent.$route,会调用get方法,向上取history.current属性,然后获取里面的history.current.matched,根据depth渲染页面组件,
#### 4、渲染页面的时候会重新执行页面view-router,重新渲染子路由页面
访问 about/a 子页面,如何访问的那?? 详细视频查看 vueRouter.zip 压缩包
显示路由发生了变化,this.current = '/about/a' , route.matched = [{path: '/about', component: About}, {path: '/about/a', component: AboutAPage}] ,首先给Home页面记录 routerView = true, depth = 0,访问到about页面,通过h函数渲染,当渲染中发现有 view-router 组件,继续执行 view-router中render,routerView = true, depth = 0,获取到route.matched中的第 0 位,拿到渲染的 about 页面判断parent && parent._routerRoot(根实例为Vue) !== parent(为home组件)获取到 home组件中保存的routerView = true,将 depth ++ === 1,获取到route.matched中的第 1 位,拿到渲染的 about-a 页面,完毕