vuex
函数过程
new Store() -> (resetStoreVM)installModule -> (getNamespace, registerMutation, registerAction, registerGetter) makeLocalContext ->(dispatch、commit、getters、state)
resetStoreVM -> (wrappedGetters, new Vue(…))
vue-router
导航守卫中为什么需要手动触发next
其主要代码如下
function runQueue (queue, fn, cb) {
var step = function (index) {
if (index >= queue.length) {
cb();
} else {
if (queue[index]) {
fn(queue[index], function () {
step(index + 1);
});
} else {
step(index + 1);
}
}
};
step(0);
}
通过runQueue触发守卫队列函数
var iterator = function (hook, next) {
if (this$1.pending !== route) {
return abort()
}
try {
hook(route, current, function (to) {
if (to === false || isError(to)) {
// next(false) -> abort navigation, ensure current URL
this$1.ensureURL(true);
abort(to);
} else if (
typeof to === 'string' ||
(typeof to === 'object' && (
typeof to.path === 'string' ||
typeof to.name === 'string'
))
) {
// next('/') or next({ path: '/' }) -> redirect
abort();
if (typeof to === 'object' && to.replace) {
this$1.replace(to);
} else {
this$1.push(to);
}
} else {
// confirm transition and pass on the value
next(to);
}
});
} catch (e) {
abort(e);
}
};
通过iterator实现一个迭代器
runQueue方法调用
runQueue(queue, iterator, function () {
var postEnterCbs = [];
var isValid = function () { return this$1.current === route; };
// wait until async components are resolved before
// extracting in-component enter guards
var enterGuards = extractEnterGuards(activated, postEnterCbs, isValid);
var queue = enterGuards.concat(this$1.router.resolveHooks);
runQueue(queue, iterator, function () {
if (this$1.pending !== route) {
return abort()
}
this$1.pending = null;
onComplete(route);
if (this$1.router.app) {
this$1.router.app.$nextTick(function () {
postEnterCbs.forEach(function (cb) { cb(); });
});
}
});
});
有runQueue方法可知, iterator即为指向runQueue中的fn参数
iterator方法中的hook(route, current, function (to) {…})
即为触发的钩子函数
所以当触发钩子函数而不执行function (to){}这个方法时,也就没有触发iterator中的next方法。此时导航守卫钩子函数队列的执行被终止。
vue-router有哪几种导航钩子
在回答这个问题之前我先多说几句做个铺垫
先说钩子函数吧,钩子函数在前端是个神奇的存在,在框架模型的生命周期中的不同有时候你想做一些不可描述的事情,怎么办那就注册回调呗。而钩子函数用来挂载这些回调的。
再说生命周期吧,个人意见呀,前端开发多涉及到视图变换,而视图变换的过程可以将其理解为一个生命周期
接下来说说路由这件事情。 举个栗子,假设存在 a, b两个路径 有a切换到B这是一个过程在, 在这个过程中涉及到A的离开 B的进入,A离开前、离开后、B进入前进入后,我们希望在这些节点可以做一些事情,这时就要考虑设计自身的钩子函数; 我们有时候也会希望 在进入A的过程中、进入B的过程中都调用同样的方法,那么全局钩子就应运而生了。
在窥探源码可知,实现导航队列时,会将这些钩子函数进行封装,然后放入queue这个变量中。
queue的主要代码如下所示
var queue = [].concat(
// in-component leave guards
extractLeaveGuards(deactivated),
// global before hooks
this.router.beforeHooks,
// in-component update hooks
extractUpdateHooks(updated),
// in-config enter guards
activated.map(function (m) { return m.beforeEnter; }),
// async components
resolveAsyncComponents(activated)
);
var queue = enterGuards.concat(this$1.router.resolveHooks);
vue-router的导航守卫 包括全局守卫 beforeenter, beforeresolved, afterEnter 独立路由守卫 beforeenter ; 还有就是组件级的包括 beforerouterenter 、 beforerouterupdated、 beforerouterleaved ; 从思路上来说大概就这些。
然后接下来在举过例子阐述这些钩子发生的过程
还用AB这两个导航来说吧 。 第一种情况 我们首次进入A 发生的过程是 beforeenter- beforeenter-beforerouterenter-beforesolved-模板更新-afterenter ;
第二中情况有A切换进入B 多了一个A离开时的回调
第三种情况是由B的参数改变而产生的一系列的变化 beforeenter—beforerouterupdated-beforesolved-模板更新-afterenter ;
路径切换流程
init -> transitionTo -> confirmTransition -> updateRoute -> afterHooks
push -> transitionTo ->pushHash -> pushState、getUrl -> saveScrollPosition
监听浏览器路径切换
setupListeners -> window.addEventListener(supportsPushState ? ‘popstate’ :’hash change’
** popstate事件**
当活动历史记录条目更改时,将触发popstate事件。
需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())
window.addEventListener('popstate', function (e) {
var current = this$1.current;
// Avoiding first `popstate` event dispatched in some browsers but first
// history route not updated since async guard at the same time.
var location = getLocation(this$1.base);
if (this$1.current === START && location === initLocation) {
return
}
this$1.transitionTo(location, function (route) {
if (expectScroll) {
handleScroll(router, route, current, true);
}
});
});
ensureURL函数
当前路径不等于getHash时所进行的路径操作
HashHistory.prototype.ensureURL = function ensureURL (push) {
var current = this.current.fullPath;
if (getHash() !== current) {
push ? pushHash(current) : replaceHash(current);
}
};
replaceHash->replaceState、 getUrl -> pushState -> saveScrollPosition(history.pushState)-> getStateKey ->
路由切换视图变更
在路由view组件渲染时会获取其父元素上挂载的$router对象,在初始化阶段对该对象进行自动监听的绑定。
当改值发生变化时会引起其父组件的重新渲染
主要代码如下
Vue.mixin({
beforeCreate: function beforeCreate () {
if (isDef(this.$options.router)) {
this._routerRoot = this;
this._router = this.$options.router;
this._router.init(this);
Vue.util.defineReactive(this, '_route', this._router.history.current);
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;
}
registerInstance(this, this);
},
destroyed: function destroyed () {
registerInstance(this);
}
});
Object.defineProperty(Vue.prototype, '$route', {
get: function get () { return this._routerRoot._route }
});
this$1.updateRoute(route);
var handler = function (e) {
if (guardEvent(e)) {
if (this$1.replace) {
router.replace(location);
} else {
router.push(location);
}
}
};
location指向当前link对应的路由对象
window.history 与 hash模式
1.hash ---- 利用URL中的hash(“#”)
2.利用History interface在 HTML5中新增的方法
window.history是用来保存用户在一个会话期间的网站访问记录,并提供相应的方法进行追溯。其对应的成员如下:
方法:back()、forward()、go(num)、pushState(stateData, title, url)、replaceState(stateData, title, url)
属性:length、state
事件:window.onpopstate
back():回退到上一个访问记录;
forward():前进到下一个访问记录;
go(num):跳转到相应的访问记录;其中num大于0,则前进;小于0,则后退;
pushState(stateData, title, url):在history中创建一个新的访问记录,不能跨域,且不造成页面刷新;
replaceState(stateData, title, url):修改当前的访问记录,不能跨域,且不造成页面刷新;
值得注意的是,通过pushState新增或者修改的history记录,被访问时,当前页面不刷新。而locaiton.href生成的记录被访问时,页面将进行刷新。
浏览器history变化图示
网友评论