在new vueRouter的时候咱们能够传入一个mode属性,他能够接管三个值:hash/history/abstract
hash和history的区别
history的门路更好看一点 比方http://yoursite.com/user/id
,history是基于pushState()来实现 URL 跳转而毋庸从新加载页面。 然而强制刷新还是会有问题(服务端来解决这个问题),所以history模式须要后端人员配合应用。
hash的门路会带有#
,比方http://yoursite.com#/user/id
HashHistory
class VueRouter{
constructor(options){
this.matcher = createMatcher(options.routes || []);
//这里为了解说hash模式 所以就不进行判断用户传进来的是哪种模式了
this.history = new HashHistory(this);//this vue-router的实例
}
}
源码这里创立了一个基类咱们这里和源码对立,这个基类封装了三种模式专用的办法和属性,那么咱们在这里创立一个HashHistory和基类History
import History from './base'
// hash路由
export default class HashHistory extends History{
constructor(router){
super(router); //继承调用父类 等于call
}
}
// 路由的基类
export default class History {
constructor(router){
this.router = router;
}
}
如果是hash
路由,关上网站如果没有hash
默认应该增加#/
import History from './base';
function ensureSlash(){
if(window.location.hash){
return
}
window.location.hash = '/'
}
export default class HashHistory extends History{
constructor(router){
super(router);
ensureSlash(); // 确保有hash
}
}
再看一下初始化的逻辑(下面的router.init函数)
init(app){
const history = this.history;
// 初始化时,应该先拿到以后门路,进行匹配逻辑
// 让路由零碎适度到某个门路
const setupHashListener = ()=> {
history.setupListener(); // 监听门路变动
}
history.transitionTo( // 父类提供办法负责跳转
history.getCurrentLocation(), // 子类获取对应的门路
// 跳转胜利后注册门路监听,为视图更新做筹备
setupHashListener
)
}
这里咱们要别离实现 transitionTo
(基类办法)、 getCurrentLocation
、setupListener
getCurrentLocation实现
function getHash(){
return window.location.hash.slice(1);
}
export default class HashHistory extends History{
// ...
getCurrentLocation(){
return getHash();
}
}
setupListener实现
export default class HashHistory extends History{
// ...
setupListener(){
window.addEventListener('hashchange', ()=> {
// 依据以后hash值 适度到对应门路
this.transitionTo(getHash());
})
}
}
TransitionTo实现
export function createRoute(record, location) { // {path:'/',matched:[record,record]}
let res = [];
if (record) { // 如果有记录
while(record){
res.unshift(record); // 就将以后记录的父亲放到后面
record = record.parent
}
}
return {
...location,
matched: res
}
}
export default class History {
constructor(router) {
this.router = router;
// 依据记录和门路返回对象,稍后会用于router-view的匹配
this.current = createRoute(null, {
path: '/'
})
}
// 外围逻辑
transitionTo(location, onComplete) {
// 去匹配门路
let route = this.router.match(location);
// 雷同门路不用过渡
if(
location === route.path &&
route.matched.length === this.current.matched.length){
return
}
//更新路由并且上面会提到扭转根实例上的_route属性
this.updateRoute(route)
onComplete && onComplete();
}
}
export default class VueRouter{
// ...
//做一个代理
match(location){
return this.matcher.match(location);
}
}
macth办法
function match(location){ // 稍后依据门路找到对应的记录
let record = pathMap[location]
if (record) { // 依据记录创立对应的路由
//参数:/about/a:{path:xx,component...},path:'/about/a'
return createRoute(record,{
path:location
})
}
// 找不到则返回空匹配
return createRoute(null, {
path: location
})
}
咱们不难发现门路变动时都会更改current
属性,咱们能够把current
属性变成响应式的,每次current
变动刷新视图即可
在install办法中
install(Vue) {
Vue.mixin({ // 给所有组件的生命周期都减少beforeCreate办法
beforeCreate() {
if (this.$options.router) {
//调用Vue类中双向数据绑定办法
Vue.util.defineReactive(this,'_route',this._router.history.current);
}
}
});
// $route和$router办法 这两个办法仅仅是vue中最常见的代理 仅仅是为了更加不便
Object.defineProperty(Vue.prototype,'$route',{ // 每个实例都能够获取到$route属性
get(){
return this._routerRoot._route;//下面刚进行双向数据绑定的
}
});
Object.defineProperty(Vue.prototype,'$router',{ // 每个实例都能够获取router实例
get(){
return this._routerRoot._router;
}
})
}
切换路由每次初始化时都须要调用更新_route
的办法,因为install的时候把_route
进行双向数据绑定,刚进来是没有this._router.history.current
的,通过公布订阅形式来进行订阅和更新操作;在init办法中减少监听函数
history.listen((route) => { // 须要更新_route属性,出入一个函数
app._route = route
});
export default class History {
constructor(router) {
// ...
this.cb = null;
}
listen(cb){
this.cb = cb; // 注册函数
}
updateRoute(route){
this.current =route;
this.cb && this.cb(route); // 更新current后 更新_route属性
}
}
发表回复