# activation库分析
- 在全局路由下面装一层
Provider
再装 App - Provider提供了一个全局可访问的状态,当路由切换时可以控制决定是卸载组件还是缓存组件
import { AliveScope } from './activation/index' ReactDOM.render( <BrowserRouter> <AliveScope> <App /> </AliveScope> </BrowserRouter> , document.getElementById('root'));
1
2
3
4
5
6
7
8
9
10
11 - 想要不被卸载的组件,在路由上用
KeepAlive
组件包裹,就可以存const routerMap = [ { path: "/home", element: <KeepAlive cacheKey="Home" ><Home /></KeepAlive>, }, { path: "/about", element: <KeepAlive cacheKey="About" ><About /></KeepAlive>, }, { path: "/detail", element: <Detail />, }, { path: "/", element: <Navigate to="/home" />, }, { path: '*', element: <Err /> } ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 缓存页面
- 缓存方式:用Map缓存配合一个唯一组件ID,将包裹的组件DOM存起来,再移除节点。当要显示这个组件再把他挂载上去
- 问题:需要记住内部各节点的滚动的位置
const wrapper = document.querySelector(".wrapper");
const toggleBtn = document.querySelector(".toggleBtn");
const caches = new Map();
let nodeShow = true;
const toggleShow = () => {
if (nodeShow) {
caches.set("thisId", { node: wrapper});
wrapper.remove(wrapper);
nodeShow = false;
} else {
const cacheNode = caches.get("thisId");
document.body.append(cacheNode.node);
caches.delete("thisId");
nodeShow = true;
}
};
toggleBtn.addEventListener("click", toggleShow);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 记住滚动位置
<body>
<button class="toggleBtn" style="position: fixed; right: 10px">
切换缓存
</button>
<div class="wrapper">
<input type="text" />
<h2>横向长列表</h2>
<div class="row">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<h2>纵向长列表</h2>
<div class="column">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</div>
<style>
.column {
display: flex;
flex-direction: column;
height: 95vh;
overflow: scroll;
}
.row {
display: flex;
width: 90vw;
overflow: scroll;
}
.row div {
flex-shrink: 0;
width: 100px;
height: 100px;
}
</style>
<script>
const wrapper = document.querySelector(".wrapper");
const toggleBtn = document.querySelector(".toggleBtn");
const caches = new Map();
/* 将所有节点打平,方便后续计算 */
const flattenNodes = (...node) => {
const nodes = node.reduce((pre, thisNode) => {
pre.push(thisNode);
if (thisNode.children.length > 0) {
pre.push(...flattenNodes(...thisNode.children));
}
return pre;
}, []);
return nodes;
};
/* 核心:存储滚动位置 */
const savePositions = (node) => {
const nodes = flattenNodes(node);
const positionNodes = nodes.map((node) => ({
node: node,
scrollLeft: node.scrollLeft,
scrollTop: node.scrollTop,
}));
const restore = () => {
positionNodes.forEach((item) => {
item.node.scrollTo({ top: item.scrollTop, left: item.scrollLeft });
});
};
return restore;
};
const toggleShow = (() => {
let nodeShow = true;
let revert = null;
return () => {
if (nodeShow) {
caches.set("thisId", {
node: wrapper,
top: document.body.scrollTop,
left: document.body.scrollLeft,
});
revert = savePositions(wrapper);
wrapper.remove(wrapper);
nodeShow = false;
} else {
const cacheNode = caches.get("thisId");
document.body.append(cacheNode.node);
window.scrollTo({
top: cacheNode.top,
left: cacheNode.left,
behavior: "instant",
});
/* 恢复内部所有节点位置 */
revert();
caches.delete("thisId");
nodeShow = true;
}
};
})();
toggleBtn.addEventListener("click", toggleShow);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# display控制
const wrapper = document.querySelector(".wrapper");
const toggleBtn = document.querySelector(".toggleBtn");
let nodeShow = true;
const toggleShow = () => wrapper.classList.toggle("hidden");
toggleBtn.addEventListener("click", toggleShow);
1
2
3
4
5
6
7
2
3
4
5
6
7
问题:整个window的滚动位置位置没记住。本身只控制了展示隐藏,但是没有记住外层为了展示这个组件的滚动位置,因为是路由组件,所以大部分情况外层滚动条是window
# 配合所有父节点滚动位置缓存
<body>
<button class="toggleBtn" style="position: fixed; right: 10px">
切换缓存
</button>
<div class="wrapper">
<input type="text" />
<h2>横向长列表</h2>
<div class="row">
<div>1</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<h2>纵向长列表</h2>
<div class="column">
<div>1</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</div>
<style>
.column {
display: flex;
flex-direction: column;
height: 95vh;
overflow: scroll;
}
.row {
display: flex;
width: 90vw;
overflow: scroll;
}
.row div {
flex-shrink: 0;
width: 100px;
height: 100px;
}
.hidden {
display: none !important;
}
</style>
<script>
const wrapper = document.querySelector(".wrapper");
const toggleBtn = document.querySelector(".toggleBtn");
/* 反向递归拿到所有父节点的滚动位置 */
const flattenParents = (node) => {
const nodes = [node];
if (node.parentElement) {
return nodes.concat(flattenParents(node.parentElement))
}
return nodes;
}
const storeParentsPosition = (node) => {
const nodes = flattenParents(node);
const positionObj = nodes.map(item => ({
node: item,
top: item.scrollTop,
left: item.scrollLeft,
}));
const restore = () => positionObj.forEach(({node, top, left}) => node.scrollTo({top, left, behavior: "auto"}));
return restore;
}
const toggleShow = (() => {
let nodeShow = true;
let restore = null;
return () => {
if (nodeShow) {
restore = storeParentsPosition(wrapper);
wrapper.classList.add("hidden")
} else {
wrapper.classList.remove("hidden")
restore();
}
nodeShow = !nodeShow;
}
})()
toggleBtn.addEventListener("click", toggleShow);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122