vue源码学习---vueRouter原理 ------ router-view 原理

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 页面,完毕
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值