持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
前情提要
前面咱们已经实现了history模式,那么咱们就在原来的基础上实现hash模式。
分析
- 首先集成两种模式的话那么便要对options中的mode做一个判断了。
- 其次,因为history模式和hash模式监听的事件是不同的,所以当mode不同的时候,那么我们要注册的全局监听事件也就有所不同。
- history监听的是popstate事件
- hash监听的是hashchange事件
- 在initEvent方法中进行根据mode进行判断,之后再针对性的进行监听
- 其中我们注意到hash模式会让路由地址中出现一个 # 号。所以我们需要在初始化路由地址的时候默认添加一个 # 号
- 之后再根据onhashchange监听到的地址,更新data.current实行,然后再与roteMap进行一一映射获取到对应的组件,最后通过render中的h方法将组件进行渲染。
- 这样一个hash模式就集成了。
具体实现
-
因为hash模式中浏览器地址栏中多了个#号,所以我们需要在mode为hash时初始化地址栏地址
initUrl() { // 为地址增加个#号 let a = location.href.split('#/') location.href = `${ a[0] }#/${ a[1] ? a[1] : '' }` } -
initComponent方法中的router-link组件的clickHandler方法做如下调整:
clickHandler(e) { e.preventDefault(); // 阻止a标签的默认事件,如果不用a标签实现就不需要这个了。 // console.log(this) // 这里的this指向的是组件实例 // console.log(_this) // 这里的this指向的是router类 const isHash = !_this.options.mode || _this.options.mode === 'hash'; // 判断是否是hash模式 if (isHash) { window.location.hash = `#${this.to}` // 注意pushState方法并不会触发hashchange事件,只有直接改变hash值才能触犯 } else { // 此处依然保留history模式的写法 history.pushState({}, '', this.to); this.$router.data.current = this.to; } } -
initComponent方法中的router-view组件的render做如下调整
Vue.component( 'router-view', { render(h) { const isHash = !_this.options.mode || _this.options.mode === 'hash'; // 判断是否是hash模式 const realPath = isHash ? _this.data.current.replace(/^#/, '') : _this.data.current; //如果是hash模式需要去掉地址中的#号,否则无法正确匹配组件。 const component = _this.routeMap[realPath]; return h(component) } } ) -
initEvent方法中判断mode如果不是history模式则更改监听的事件:
window.addEventListener('hashchange', () => { // 注意,只有通过直接给location.hash赋值才能触发hashchange事件,通过pushState是无法触发的。 this.data.current = window.location.hash }); window.addEventListener('load', () => { // 对于hash模式而言重新加载并不会触发hashchange事件,所以需要另外监听load事件 this.data.current = `window.location.hash }); -
到这基本上hash模式就加完了。
-
不过我又另外加了个push方法的实现,这样在组件里就可以通过:this.$router.push方法实现路由跳转了。
push(path) { const isHash = !this.options.mode || this.options.mode === 'hash'; 同样的需要判断是否是hash模式 if (isHash) { window.location.hash = `#${path}` 如果是直接给location的hash属性赋值,这样就会触发hashchange事件进而改变当前要渲染的组件 } else { // history模式没啥好说的。 history.pushState({}, '', path); this.$router.data.current = path; } }
总结
- replaceState,pushState无法触发popstate事件以及hashChange事件
- hashChange事件的触发需要直接给window.location.hash赋值
- 实现的过程中也触及到了this指向的问题,所以一个方法里可能用了两个this,可不要以为我写错了呦。
- 读这篇文章前最好读读这几篇文章,否则可能无法理解哦: