-
Notifications
You must be signed in to change notification settings - Fork 4
/
pagecache.js
190 lines (173 loc) · 5.6 KB
/
pagecache.js
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* pagecache
* 用于为pagelet请求添加缓存功能
* 用法:pagelet.cache(options);
* options:
* max <Number>: 最大缓存页面数,默认是30页
* defaultPagelet <String>: 要缓存的最外层pagelet的id,默认为null,
* 会缓存整个body元素,推荐设置为pagelet.autoload的第一个参数,详
* 情请看这里: https://github.com/scrat-team/pagelet.js#pageletautoloaddefaultpagelet-eventtype
*/
(function(global){
/**
* 最大缓存数
* @type {number}
*/
var MAX_CACHE_SIZE = 30;
var DEFAULT_PAGELET = null;
var cached = {};
/**
* 访问热度,这是一个自增id
* @type {number}
*/
var hot = 0;
var noop = function(){};
var anchor = document.createElement('a');
anchor.href = '/';
var root = anchor.href.replace(/\/$/, '');
/**
* 获取页面访问热度,用于缓存淘汰算法(LRU)
* @returns {number}
*/
function getHot(){
return hot++;
}
/**
* 格式化url,去除开头的域名
* @param url
* @returns {void|string|XML|*}
*/
function normalize(url){
anchor.href = url;
return anchor.href.replace(root, '');
}
/**
* 检查缓存是否超限
*/
function check(){
var arr = [];
for(var url in cached){
arr.push({
url: url,
hot: cached[url].hot
});
}
if(arr.length > MAX_CACHE_SIZE){
arr.sort(function(a, b){
return b.hot - a.hot;
});
for(var i = MAX_CACHE_SIZE; i < arr.length; i++){
delete cached[arr[i].url];
}
}
}
function getScrollTop(){
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
}
var prevUrl = normalize('');
/**
* 存储页面缓存
* @returns {*}
*/
function save(url){
var selector = DEFAULT_PAGELET ? '[data-pagelet=" + DEFAULT_PAGELET + "]' : 'body';
var dom = document.querySelector(selector);
if(prevUrl){
var scrollTop = getScrollTop();
var fragment = document.createDocumentFragment();
fragment.appendChild(dom.cloneNode(true));
cached[prevUrl] = {
scrollTop: scrollTop,
fragment: fragment,
title: document.title,
hot: getHot()
};
check();
}
prevUrl = url;
return dom;
}
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
/**
* 恢复页面缓存
* @param options
* @returns {boolean}
*/
function revert(options){
var url = normalize(options.url);
var cache = cached[url];
if(cache){
pagelet.abort({ action: 'pagecache' });
delete cached[url];
save(url);
var push = options.isBack ? noop : pagelet.pushState(options);
options.pagelets.forEach(function(pagelet){
var selector = '[data-pagelet="' + pagelet + '"]';
var currentDom = document.querySelector(selector);
var cacheDom = cache.fragment.querySelector(selector);
if(currentDom && cacheDom){
//currentDom.innerHTML = cacheDom.innerHTML;
var parent = currentDom.parentNode;
parent.replaceChild(cacheDom, currentDom);
} else {
// TODO error
location.href = url;
}
});
document.title = cache.title;
var scroll = (options.isBack || options.revertScroll) ? cache.scrollTop : 1;
var evt = {
options: options,
fromCache: true
};
requestAnimationFrame(function(){
window.scrollTo(0, scroll);
cache = null;
});
pagelet.emit(pagelet.EVENT_LOAD_COMPLETED, evt);
push(cache.title);
return true;
}
return false;
}
pagelet.cache = global.pagecache = function(options){
options = options || {};
MAX_CACHE_SIZE = options.max || MAX_CACHE_SIZE;
DEFAULT_PAGELET = options.defaultPagelet || null;
// 监听html插入事件,将页面缓存起来
pagelet.on(pagelet.EVENT_BEFORE_INSERT_HTML, function(e){
if(!e.options.nocache){
save(e.options.url);
}
});
// 插入html之后恢复滚动条位置
pagelet.on(pagelet.EVENT_AFTER_INSERT_HTML, function(e){
if(!e.options.isBack && !e.options.keepScroll){
requestAnimationFrame(function(){
window.scrollTo(0, 1);
});
}
});
// 监听页面跳转事件,判断是可以从缓存恢复页面内容
pagelet.router('*', function(ctx, options, e, next){
options.prevUrl = prevUrl;
if(!options.isBack){
if(e && e.target && e.target.hasAttribute('data-keep-scroll')){
options.revertScroll = true;
}
}
if(revert(options)){
// used cache
} else {
next();
}
});
};
})(window);