Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue-router 实现 -- 路由变更监听 #27

Open
muwoo opened this issue Jun 4, 2018 · 1 comment
Open

vue-router 实现 -- 路由变更监听 #27

muwoo opened this issue Jun 4, 2018 · 1 comment

Comments

@muwoo
Copy link
Owner

muwoo commented Jun 4, 2018

对于 Vue SPA 页面,改变可以有两种:一种是用户点击链接元素,一种是更新浏览器本身的前进后退导航来更新。

用户点击链接元素

户点击链接交互,即点击了 <router-link>,这个组件是在install的时候被注册的。我们来看一下这个组建的核心内容:

  props: {
    // ...
    // 标签名,默认是 <a></a>
    tag: {
      type: String,
      default: 'a'
    },
    // 绑定的事件
    event: {
      type: eventTypes,
      default: 'click'
    }
  },
  render (h: Function) {
    // ...
    const handler = e => {
      if (guardEvent(e)) {
        if (this.replace) {
          router.replace(location)
        } else {
          router.push(location)
        }
      }
    }

    const on = { click: guardEvent }
    if (Array.isArray(this.event)) {
      this.event.forEach(e => { on[e] = handler })
    } else {
      // 事件绑定处理函数
      on[this.event] = handler
    } 
    // ...
    return h(this.tag, data, this.$slots.default)   
  }
  // ....

该组件主要是通过render函数,默认创建一个a标签,同时为标签绑定click事件。在绑定事件的函数中,有这样一个方法值得注意guardEvent。我们来看看他所做的工作:

function guardEvent (e) {
  // 忽略带有功能键的点击
  if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
  // 调用preventDefault时不重定向
  if (e.defaultPrevented) return
  // 忽略右击
  if (e.button !== undefined && e.button !== 0) return
  // 如果 `target="_blank"` 也不进行
  if (e.currentTarget && e.currentTarget.getAttribute) {
    const target = e.currentTarget.getAttribute('target')
    if (/\b_blank\b/i.test(target)) return
  }
  // 判断是否存在`e.preventDefault`,在 weex 中没有这个方法
  if (e.preventDefault) {
    e.preventDefault()
  }
  return true
}

可以看到,这里主要对是否跳转进行了一些判断。那么我们再看看点击事件的处理函数:

const handler = e => {
  if (guardEvent(e)) {
    if (this.replace) {
      router.replace(location)
    } else {
      router.push(location)
    }
  }
}

可以看到其实他们只是代理而已,真正做事情的还是 history 来做。

浏览器本身的跳转动作

对于这种情况,我们之前文章也简单的分析过,先来看看 hash的方式,当发生变得时候会判断当前浏览器环境是否支持 supportsPushState 来选择监听 popstate还是hashchange

window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => {
  const current = this.current
  if (!ensureSlash()) {
    return
  }
  this.transitionTo(getHash(), route => {
    if (supportsScroll) {
      handleScroll(this.router, route, current, true)
    }
    if (!supportsPushState) {
      replaceHash(route.fullPath)
    }
  })
})

对应的history其实也是差不多。只不顾既然是history模式了,默认也就只用监听popstate就好了:

window.addEventListener('popstate', e => {
  const current = this.current

  // Avoiding first `popstate` event dispatched in some browsers but first
  // history route not updated since async guard at the same time.
  const location = getLocation(this.base)
  if (this.current === START && location === initLocation) {
    return
  }

  this.transitionTo(location, route => {
    if (supportsScroll) {
      handleScroll(router, route, current, true)
    }
  })
})

到这里其实vue-router实现已经介绍的差不多了。相信能看到这里的小伙伴也能对vue-router有个清晰地认识。

@muwoo muwoo changed the title vue-router 实现 -- history 改变 vue-router 实现 -- 路由变更监听 Jun 4, 2018
@cobish
Copy link

cobish commented Sep 1, 2018

没有 <router-view> 的?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants