几天vue-route的学习成果
ziwei3749 opened this issue · comments
vue-router学习成果
经过几天的研究,对以下问题做了一些探究:
- 1.前端路由是什么?
- 2.思考实现一个前端路由需要具备哪些功能
- 3.前端路由2种实现方式的区别和注意事项
- 4.【代码实现】基础路由实现代码
- 5.【vue-router源码学习】vue-router源码中学习到的技巧
1.前端路由是什么?
本质上前端路由: 前端监听和解析url的变化,并匹配url对应的页面
2.思考实现一个前端路由需要具备哪些功能
我觉得最起码需要有2个功能
- 一是打开的动作。url是#/login,那你得用JS控制显示login页面
- 二是要有历史记录操作单。就是你url变化后,要在历史栈当中有一个路由对象的记录
打开动作
如果是模拟的话,div的显示隐藏就可以了。
实际上vue-router用起来,有router-view组件,这里渲染的内容,可以通过routes配置
routes是一个数组,里面写着path和components的映射关系
历史记录操作单
那hash变化自动就会有一个历史记录
html5新增加的history.pushState也增加历史记录。
所以这2个api都具备实现前端路由的能力
3.前端路由2种实现方式的区别和注意事项
hash 和 history的区别
- 兼容性上,hash兼容性更好,因为history是html5的api。默认是hash
- 是否美观上
- 是否需要后端配置。
history模式的话需要后端做一个配置,如果这个url匹配到任何路由,那不要返回404,而是返回Index.html
但是这样,你如果url不匹配,也永远没有404了,vue为例子的话,routes里需要配置path:* 对应一个404页面
4.【代码实现】基础路由实现代码,以hash
html
<ul>
<a href="#/"></a>
<a href="#/about"></a>
<a href="#/topics"></a>
</ul>
<div id="router-view"></div>
调用方式
var router = new Router()
var routerView = document.querySelector('#router-view')
router.init()
// 这里是注册路由,指定url对应做什么事情。有点routes里指定path和component的对应关系
router.route('/',() => {
routerView.innerHTML = 'home'
})
router.route('/about',() => {
routerView.innerHTML = 'about'
})
router.route('/topics',() => {
routerView.innerHTML = 'topics'
})
实现hash路由
class Router{
construtor() {
this.routes = {} // 存放path和 fn的映射关系
}
route(path,fn){
this.routes[path] = fn
}
updateView(){
// 获取hash就知道执行哪个fn,显示哪个页面了
var url = window.location.hash.substr(1)
this.routes[url]()
}
init(){
window.addEventListenr('hashchange',() => {
this.updateView()
})
window.addEventListenr('load',() => {
this.updateView()
})
}
}
梳理一下过程
- 点击a标签,hash变化,自动存一个历史记录再历史记录单里
- 同时hash的变化,会被onhashchange监听到,在这个回调里,我们知道hash就能得到对应的页面和参数
那为什么不用history实现呢,因为History要多考虑一些地方,pushState并不能触发popstate的变化
既然通过popstate监听不太靠谱,那如何拦截各种情况下路由的变化的
其实路由的变化,就3个情况
- 浏览器前进后退
- a标签点击
- 或者pushState
- 至于直接修改url的情况,就是所谓需要后端配置,Url错误时不要返回404,而是返回index.html
在点击a标签或者pushState时去updateView一下,在源码中其实有一个render,需要用这个方法去更新页面
基本就是这一个思路,(注意history跳转时跨域问题)
5.【vue-router源码学习】vue-router源码中学习到的技巧
我是看的v2.03版本,后来有看了一眼vue-router@3.01的版本,目录结构和写法也不一样
目录结构
- components (rotuer-link / router-view)
- history (hash.js / html5.js / abstract.js / base.js)
- utils
- index.js
- install.js
- create-matcher.js
- create-route-map.js
技巧1.mian.js入口文件,我们一般引入vue-router后,需要Vue.use(VueRouter)
这个是Vue提供的插件机制,这个机制就是Vue会调用插件的install方法(如果没有的话就把插件自身作为函数调用)
技巧2.插件打包时肯定不希望把整个Vue打包进去,但是又希望使用Vue对象的方法,所以就可以在install时赋值
去看index.js的话,vue-rotuer这个插件是有install方法,单独写install.js文件里
_Vue = Vue
技巧3: Object.defineProperty给Vue.prototype添加$router和$route,这样可以让所有Vue都有这2个属性
技巧4: 有一些嘈杂哦判断你的 mode和fallback
- 默认是hash模式
- 如果你的浏览器不支持history,并且你还设置用history,强制修改为hash模式
- 用switch case的方法,根据mode来判断哪个构造函数来实例化this.history
- HTML5History 、 HashHistory 都是继承 History的
let mode = options.mode || 'hash'
this.fallback = mode === 'history' && !supportsHistory
if (this.fallback) {
mode = 'hash'
}
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this)
break
default:
assert(false, `invalid mode: ${mode}`)
}
写的不错,学习一下