}
return vnode || (slot && slot[0])
}
这里我注意到,可以动态的修改这个数组,来使得本来处于保活状态的组件/页面失活。
afterEach
那我们可以在什么时候去维护/修改includes数组呢?vue-router提供了 afterEach 方法来添加路由改变后的回调:
updateRoute (route: Route) {
const prev = this.current
this.current = route
this.cb && this.cb(route)
this.router.afterHooks.forEach(hook => {
hook && hook(route, prev)
})
}
在这里虽然 afterHooks 的执行是晚于路由的设置的,但组件的 render 是在 nextTick 中执行的,也就是说,在keep-alive的render方法判断是否应当从缓存中获取组件时,组件的保活状态已经被我们修改了。
劫持router.push
这里我们将劫持router的push方法:
let dir = 1;
const includes = [];const routerPush = router.push;
router.push = function push(...args) {
dir = 1;
routerPush.apply(router, args);
};
router.afterEach((to, from) => {
if (dir === 1) {
includes.push(to.name);
} else if (dir === -1) {
includes.pop();
}
dir = -1;
});
我们将router.push(当然这里需要劫持的方法不止是push,在此仅用push作为示例)和浏览器的回退行为用不同的 dir 标记,并根据这个值来维护includes数组。
然后,将includes传递给keep-alive组件:
// html
<div id="app">
<keep-alive :include="includes">
<router-view></router-view>
</keep-alive>
</div>// js
const app = new Vue({
router,
data() {
return {
includes,
};
},
}).$mount('#app');
维护滚动
接下来,我们将编写一个 keep-position 指令(directive):
Vue.directive('keep-position', {
bind(el, { value }) {
const parent = positions[positions.length - 1];
const obj = {
x: 0,
y: 0,
};
const key = value;
parent[key] = obj;
obj.el = el;
obj.handler = function ({ currentTarget }) {
obj.x = currentTarget.scrollLeft;
obj.y = currentTarget.scrollTop;
};
el.addEventListener('scroll', obj.handler);
},
});
并对router进行修改,来维护position数组:
const positions = [];router.afterEach((to, from) => {
if (dir === 1) {
includes.push(to.name);
positions.push({});
}
...
});
起初我想通过指令来移除事件侦听(unbind)以及恢复滚动位置,但发现使用unbind并不方便,更重要的是指令的几个生命周期在路由跳转到保活的页面时都不会触发。










