Skip to content

Commit d28ae6d

Browse files
committed
classify codes to different files
1 parent 8c1b42e commit d28ae6d

File tree

8 files changed

+384
-316
lines changed

8 files changed

+384
-316
lines changed

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { terser } from 'rollup-plugin-terser';
66
export default {
77
input: 'src/index.js', // 入口文件
88
output: {
9-
name: 'routerHelper', // umd 模式必须要有 name 此属性作为全局变量访问打包结果
9+
name: 'createHelper', // umd 模式必须要有 name 此属性作为全局变量访问打包结果
1010
file: `dist/index.js`,
1111
format: 'umd',
1212
sourcemap: true,

src/extension.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { isDef } from "./utils";
2+
3+
export function extendHistory(history) {
4+
const rstmp = history.replaceState;
5+
history.replaceState = function (state, op, path) {
6+
const old = Object.assign({}, history.state);
7+
const s = Object.assign(old, state);
8+
rstmp.call(history, s, op, path);
9+
}
10+
const historyPushState = history.pushState;
11+
history.pushState = function (state, op, path) {
12+
const old = Object.assign({}, history.state);
13+
const s = Object.assign(old, state);
14+
historyPushState.call(history, s, op, path);
15+
}
16+
}
17+
18+
export function extendVue(Vue) {
19+
const dtmp = Vue.prototype.$destroy;
20+
/**
21+
* @description remove the cache in <keep-alive> component before invoke $destroy
22+
*/
23+
Vue.prototype.$keepAliveDestroy = function () {
24+
if (this.$vnode && this.$vnode.data.keepAlive) {
25+
if (
26+
this.$vnode.parent &&
27+
this.$vnode.parent.componentInstance &&
28+
this.$vnode.parent.componentInstance.cache
29+
) {
30+
if (this.$vnode.componentOptions) {
31+
const key = !isDef(this.$vnode.key)
32+
? this.$vnode.componentOptions.Ctor.cid +
33+
(this.$vnode.componentOptions.tag
34+
? `::${this.$vnode.componentOptions.tag}`
35+
: "")
36+
: this.$vnode.key;
37+
const cache = this.$vnode.parent.componentInstance.cache;
38+
const keys = this.$vnode.parent.componentInstance.keys;
39+
if (cache[key]) {
40+
if (keys.length) {
41+
const index = keys.indexOf(key);
42+
if (index > -1) {
43+
keys.splice(index, 1);
44+
}
45+
}
46+
delete cache[key];
47+
}
48+
}
49+
}
50+
}
51+
dtmp.apply(this, arguments);
52+
};
53+
}

src/helper.js

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import {
2+
getCurrentVM,
3+
isDef,
4+
getStateId,
5+
resolvePushedVm,
6+
genKey,
7+
replaceFirstKeyAndCache,
8+
getFirstComponentChild,
9+
isPlaceHolderVm,
10+
setCurrentVnodeKey,
11+
replaceState,
12+
} from "./utils";
13+
import HistoryStack from "./historyStack";
14+
15+
export default class VueRouterKeepAliveHelper{
16+
get currentVm() {
17+
return getCurrentVM(this.router);
18+
}
19+
get isPush() {
20+
if (!this.isReplace) {
21+
const stateId = getStateId();
22+
return !isDef(stateId) || this.preStateId <= stateId;
23+
}
24+
return false;
25+
}
26+
get stackPointer() {
27+
return this.router._stack;
28+
}
29+
constructor({ Vue, router, replaceStay }) {
30+
this.Vue = Vue;
31+
this.router = router;
32+
this.router._stack = 0;
33+
this.mode = router.mode; // hash or history
34+
this.historyShouldChange = false;
35+
this.isReplace = false;
36+
this.replacePrePath = undefined;
37+
this.preStateId = 0;
38+
this.pre = null;
39+
this.replaceStay = replaceStay || [];
40+
this.hacked = false;
41+
this.historyStack = new HistoryStack();
42+
this.init();
43+
}
44+
init() {
45+
this.routerHooks();
46+
this.hackRouter();
47+
}
48+
/**
49+
* use afterEach hook to set state.key and add the reference of vm to the historyStack
50+
*/
51+
routerHooks() {
52+
const router = this.router;
53+
router.afterEach((to, from) => {
54+
this.historyShouldChange = true;
55+
// get the vm instance after render
56+
this.Vue.nextTick(() => {
57+
const current = this.currentVm;
58+
const pendingToPushVm = resolvePushedVm(current);
59+
if (this.pre === null) {
60+
this.onInitial(pendingToPushVm);
61+
} else if (this.isReplace) {
62+
this.onReplace(pendingToPushVm);
63+
} else if (this.isPush) {
64+
this.onPush(pendingToPushVm);
65+
} else {
66+
this.onBack(pendingToPushVm);
67+
}
68+
this.pre = current;
69+
this.preStateId = this.stackPointer;
70+
if (!isPlaceHolderVm(pendingToPushVm)) {
71+
setCurrentVnodeKey(router, genKey(this.stackPointer, router));
72+
if (!this.hacked && current) {
73+
this.hackKeepAliveRender(current.$vnode.parent.componentInstance);
74+
}
75+
this.historyShouldChange = false;
76+
}
77+
});
78+
});
79+
}
80+
/**
81+
* @description hack router go , replace and push functions to tell replace from back and push
82+
*/
83+
hackRouter() {
84+
const router = this.router;
85+
const rtmp = router.replace;
86+
const rtmpf = (location, onComplete, onAbort) => {
87+
this.isReplace = true;
88+
this.replacePrePath = router.history.current.path;
89+
rtmp.call(router, location, onComplete, (e) => {
90+
this.isReplace = false;
91+
this.replacePrePath = undefined;
92+
isDef(onAbort) && onAbort(e);
93+
});
94+
};
95+
router.replace = function (ocation, onComplete, onAbort) {
96+
rtmpf(ocation, onComplete, onAbort);
97+
};
98+
99+
const gstmp = router.go;
100+
const gstmpf = (number) => {
101+
this.isReplace = false;
102+
return gstmp.call(router, number);
103+
};
104+
router.go = function (num) {
105+
return gstmpf(num);
106+
};
107+
const pstmp = router.push;
108+
const pstmpf = (location, onComplete, onAbort) => {
109+
this.isReplace = false;
110+
if (!onComplete && !onAbort && typeof Promise !== "undefined") {
111+
return pstmp.call(router, location, onComplete, onAbort);
112+
} else {
113+
pstmp.call(router, location, onComplete, onAbort);
114+
}
115+
};
116+
router.push = function (location, onComplete, onAbort) {
117+
return pstmpf(location, onComplete, onAbort);
118+
};
119+
}
120+
/**
121+
* @description hack the render function of keep-alive component, modify the key of vnode for cache
122+
* @param {*} vm keep-alive component instance
123+
*/
124+
hackKeepAliveRender(vm) {
125+
// modify the first keep alive key and catch
126+
replaceFirstKeyAndCache(vm, genKey(this.stackPointer, this.router));
127+
128+
const tmp = vm.$options.render;
129+
const self = this;
130+
const router = this.router;
131+
vm.$options.render = function () {
132+
const slot = this.$slots.default;
133+
const vnode = getFirstComponentChild(slot); // vnode is a keep-alive-component-vnode
134+
if (self.historyShouldChange) {
135+
if (vnode && !isDef(vnode.key)) {
136+
if (self.isReplace) {
137+
vnode.key = genKey(self.stackPointer, router);
138+
} else if (self.isPush) {
139+
vnode.key = genKey(self.stackPointer + 1, router);
140+
} else {
141+
vnode.key = genKey(self.stackPointer - 1, router);
142+
}
143+
}
144+
} else {
145+
// when historyShouldChange is false should rerender only, should not create new vm ,use the same vnode.key issue#7
146+
vnode.key = genKey(self.stackPointer, router);
147+
}
148+
return tmp.apply(this, arguments);
149+
};
150+
this.hacked = true;
151+
}
152+
/********** callback functions ************/
153+
onInitial(vm) {
154+
const currentStateId = getStateId();
155+
if (isDef(currentStateId)) {
156+
this.setStackPointer(currentStateId);
157+
} else {
158+
this.setState(0);
159+
}
160+
this.historyStack.push(vm, this.stackPointer);
161+
}
162+
onPush(vm) {
163+
this.setState(this.increaseStackPointer());
164+
this.historyStack.push(vm, this.stackPointer);
165+
}
166+
onBack(vm) {
167+
this.historyStack.pop();
168+
this.decreaseStackPointer();
169+
this.historyStack.push(vm, this.stackPointer);
170+
}
171+
onReplace(vm) {
172+
const shouldDestroy = !(
173+
isDef(this.replacePrePath) &&
174+
this.replaceStay.includes(this.replacePrePath)
175+
);
176+
if (shouldDestroy) {
177+
this.pre.$keepAliveDestroy();
178+
}
179+
this.setState(this.stackPointer);
180+
this.historyStack.push(vm, this.stackPointer);
181+
this.isReplace = false;
182+
this.replacePrePath = undefined;
183+
}
184+
setState(id) {
185+
this.setStackPointer(id);
186+
replaceState(this.mode, this.router, id);
187+
}
188+
setStackPointer(val) {
189+
this.router._stack = val;
190+
}
191+
increaseStackPointer() {
192+
return (this.router._stack += 1);
193+
}
194+
decreaseStackPointer() {
195+
return (this.router._stack -= 1);
196+
}
197+
}

src/historyStack.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export default class HistoryStack {
2+
constructor() {
3+
this.historyStackMap = [];
4+
}
5+
push(vm, index) {
6+
// const cur = stackPointer();
7+
const stack = this.historyStackMap[index];
8+
if (Array.isArray(stack)) {
9+
!stack.includes(vm) && stack.push(vm);
10+
this.historyStackMap[index] = stack.filter((item) => !item._isDestroyed);
11+
} else {
12+
const vms = [];
13+
vms.push(vm);
14+
this.historyStackMap[index] = vms;
15+
}
16+
}
17+
pop() {
18+
const last = this.historyStackMap.pop();
19+
Array.isArray(last) &&
20+
last.forEach(
21+
(vm) => vm && vm.$keepAliveDestroy && vm.$keepAliveDestroy()
22+
);
23+
}
24+
removeGreater(index) {
25+
while (this.historyStackMap.length >= index) {
26+
this.pop();
27+
}
28+
}
29+
clear() {
30+
this.historyStackMap = [];
31+
}
32+
}

0 commit comments

Comments
 (0)