一道面试题引发的思考5

算法题1

找出字符串中连续出现最多的字符和个数

1
2
3
'abcaakjbb' => {'a':2,'b':2}
'abbkejsbcccwqaa' => {'c':3}
注意:题目说的是**连续**出现,注意连续二字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var str = 'abbkejsbcccwqaa';
const arr = str.match(/(\w)\1*/g); // 经典
const maxLen = Math.max(...arr.map(s => s.length));
const result = arr.reduce((pre, curr) => {
if (curr.length === maxLen) {
pre[curr[0]] = curr.length;
}
return pre;
}, {});

console.log(result);


// 'aaasdofjaopfjopaiiisjssfopiasdfffff'.match(/(.)\1+/g)
// 得到的结果是

// ["aaa", "iii", "ss", "fffff"]
// 从这个数组里面找长度最长的元素并转化成需要的结果应该简单了吧

算法题2

统计 1 ~ n 整数中出现 1的次数(有多少个1)

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
<script>
function count1(n) {
let res = 0;
for (let i = 0; i <= n; i += 1) {
`${i}`.split('').forEach(item => {
if (item === '1') {
res += 1;
}
})
};
return res;
}
console.time('count1');
console.log(count1(4000000));
console.timeEnd('count1');
</script>

<script>

function count2(n) {
let res = 0;
for (let i = 0; i <= n; i += 1) {
let item = `${i}`.match(/1+?/g);
if (item) {
res += item.length;
}
};
return res;
}
console.time('count2');
console.log(count2(4000000));
console.timeEnd('count2');
</script>


<script>

/*
分析归纳一下,按照每一位上的数字来分析

比如55, 个位可能产生的 1 是 6个(1, 11, 21, 31, 41, 51, 注意这里11只计算的个位的1),
十位5 可能产生的 1是 10个,(10 - 19, 这里的11只计算的十位的1);

比如222, 个位 可能产生的 1 是 23个(1, 11, 21, ... 221, 只关注个位),
十位2 可能产生的 1是 30个(10-19, 110-119, 210-219, 只关注十位),
百位2 产生的 1是100个(100 - 199, 只关注百位).

以此类推, 每一位数字可能产生的1的个数跟他的高位部分和低位部分相关:
其中0和1需要特殊处理,代码如下
*/

function count3(n) {
var factor = 1;
let count = 0;
let next = parseInt(n / factor);
while (next !== 0) {
var lower = n - next * factor
var curr = next % 10;
var high = parseInt(n / (10 * factor));

if (curr === 0) {
count += high * factor;
} else if (curr === 1) {
count += high * factor + lower + 1
} else {
count += (high + 1) * factor
}

factor *= 10;
next = parseInt(n / factor);
}
return count;
}
console.time('count3');
console.log(count3(4000000));
console.timeEnd('count3');
</script>

image

算法题3

如何将 [{id: 1}, {id: 2, pId: 1}, …] 的重复数组(有重复数据)转成树形结构的数组 [{id: 1, child: [{id: 2, pId: 1}]}, …] (需要去重)

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
/*

[{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]

[{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]

*/
let arr = [{ id: 1 }, { id: 2, pId: 1 }, { id: 3, pId: 2 }, { id: 4 }, { id: 3, pId: 2 }, { id: 5, pId: 4 }];
const fn = arr => {
const res = []
const map = arr.reduce((res, item) => ((res[item.id] = item), res), {});

console.log(map);

for (const item of Object.values(map)) {
if (!item.pId) {
res.push(item)
} else {
const parent = map[item.pId]
parent.child = parent.child || []
parent.child.push(item)
}
}
return res
}

console.log(fn(arr));

算法题4

在一个字符串数组中有红、黄、蓝三种颜色的球,且个数不相等、顺序不一致,请为该数组排序。使得排序后数组中球的顺序为:黄、红、蓝。

例如:红蓝蓝黄红黄蓝红红黄红,排序后为:黄黄黄红红红红红蓝蓝蓝。

1
2
3
4
5
6
7
8
9
10
let temp1 = [...'红蓝蓝黄红黄蓝红红黄红'].reduce((a, b) => {
if (a.has(b)) {
a.set(b, a.get(b) + 1)
} else {
a.set(b, 1)
}
return a;
}, new Map())

console.log('黄'.repeat(temp1.get('黄')) + '红'.repeat(temp1.get('红')) + '蓝'.repeat(temp1.get('蓝')));

image

1
2
3
4
5
6
7
8
9
function sort(arr) {
let str = '';
arr = arr.join('');
[ '黄','红', '蓝'].forEach(item => {
let reg = new RegExp(item, 'g');
str += arr.match(reg).join('');
})
return str.split('');
}

image

2道由百度搜索图片想到的面试题

再百度图片里搜索美女二子

在百度搜索图片中,可以看到页面两端对齐,是典型的瀑布流布局,只不过高度固定,
image
当缩放浏览器是,可以看到实现的并不完美:
image
image

下面用flex实现该效果,主要使用flex-grow: 1object-fit: cover
image

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>自适应瀑布流+baguetteBox.js画廊效果 </title>
<link rel="stylesheet" href="css/style.css">
<base href="https://www.17sucai.com/preview/775073/2018-04-16/gallery/">
</head>

<body>
<section class="gallery">
<div>
<a href="img/2-2.jpg" data-caption="标题">
<img src="img/thumbs/2-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-3.jpg" data-caption="标题">
<img src="img/thumbs/2-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-4.jpg" data-caption="标题">
<img src="img/thumbs/2-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-1.jpg" data-caption="标题">
<img src="img/thumbs/4-1.jpg" alt="First image">
</a>
</div>

<div>
<a href="img/4-2.jpg" data-caption="标题">
<img src="img/thumbs/4-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-4.jpg" data-caption="标题">
<img src="img/thumbs/4-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-2.jpg" data-caption="标题">
<img src="img/thumbs/2-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-3.jpg" data-caption="标题">
<img src="img/thumbs/2-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-4.jpg" data-caption="标题">
<img src="img/thumbs/2-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-1.jpg" data-caption="标题">
<img src="img/thumbs/4-1.jpg" alt="First image">
</a>
</div>

<div>
<a href="img/4-2.jpg" data-caption="标题">
<img src="img/thumbs/4-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-4.jpg" data-caption="标题">
<img src="img/thumbs/4-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-2.jpg" data-caption="标题">
<img src="img/thumbs/2-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-3.jpg" data-caption="标题">
<img src="img/thumbs/2-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-4.jpg" data-caption="标题">
<img src="img/thumbs/2-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-1.jpg" data-caption="标题">
<img src="img/thumbs/4-1.jpg" alt="First image">
</a>
</div>

<div>
<a href="img/4-2.jpg" data-caption="标题">
<img src="img/thumbs/4-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-4.jpg" data-caption="标题">
<img src="img/thumbs/4-4.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-2.jpg" data-caption="标题">
<img src="img/thumbs/2-2.jpg" alt="First image">
</a>
</div>
<div>
<a href="img/2-3.jpg" data-caption="标题">
<img src="img/thumbs/2-3.jpg" alt="First image">
</a>
</div>

<div>
<a href="img/4-3.jpg" data-caption="标题">
<img src="img/thumbs/4-3.jpg" alt="First image">
</a>
</div>
</section>
</body>

</html>
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
#baguetteBox-overlay {
display: none;
opacity: 0;
position: fixed;
overflow: hidden;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000000;
background-color: #222;
background-color: rgba(0, 0, 0, 0.8);
-webkit-transition: opacity .5s ease;
transition: opacity .5s ease;
z-index: 998;
}

#baguetteBox-overlay.visible {
opacity: 1;
}

#baguetteBox-overlay .full-image {
display: inline-block;
position: relative;
width: 100%;
height: 100%;
text-align: center;
}

#baguetteBox-overlay .full-image figure {
display: inline;
margin: 0;
height: 100%;
}

#baguetteBox-overlay .full-image img {
display: inline-block;
width: auto;
height: auto;
max-height: 100%;
max-width: 100%;
vertical-align: middle;
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}

#baguetteBox-overlay .full-image figcaption {
display: block;
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
line-height: -6.2;
line-height: 42px;
white-space: normal;
color: #ccc;
background-color: #000;
background-color: rgba(0, 0, 0, 0.6);
font-family: sans-serif;
font-size: 1.6rem;
}

#baguetteBox-overlay .full-image:before {
content: "";
display: inline-block;
height: 50%;
width: 1px;
margin-right: -1px;
}

#baguetteBox-slider {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
white-space: nowrap;
-webkit-transition: left .4s ease, -webkit-transform .4s ease;
transition: left .4s ease, -webkit-transform .4s ease;
transition: left .4s ease, transform .4s ease;
transition: left .4s ease, transform .4s ease, -webkit-transform .4s ease, -moz-transform .4s ease;
}

#baguetteBox-slider.bounce-from-right {
-webkit-animation: bounceFromRight .4s ease-out;
animation: bounceFromRight .4s ease-out;
}

#baguetteBox-slider.bounce-from-left {
-webkit-animation: bounceFromLeft .4s ease-out;
animation: bounceFromLeft .4s ease-out;
}

@-webkit-keyframes bounceFromRight {
0% {
margin-left: 0;
}

50% {
margin-left: -30px;
}

100% {
margin-left: 0;
}
}

@keyframes bounceFromRight {
0% {
margin-left: 0;
}

50% {
margin-left: -30px;
}

100% {
margin-left: 0;
}
}

@-webkit-keyframes bounceFromLeft {
0% {
margin-left: 0;
}

50% {
margin-left: 30px;
}

100% {
margin-left: 0;
}
}

@keyframes bounceFromLeft {
0% {
margin-left: 0;
}

50% {
margin-left: 30px;
}

100% {
margin-left: 0;
}
}

.baguetteBox-button#next-button,
.baguetteBox-button#previous-button {
top: 50%;
top: calc(50% - 30px);
width: 54px;
height: 82px;
}

.baguetteBox-button {
position: absolute;
cursor: pointer;
outline: none;
padding: 0;
margin: 0;
border: 0;
-moz-border-radius: 15%;
border-radius: 15%;
background-color: #323232;
background-color: rgba(50, 50, 50, 0.8);
color: #ddd;
font: 1.6em sans-serif;
-webkit-transition: background-color .4s ease;
transition: background-color .4s ease;
}

.baguetteBox-button:focus,
.baguetteBox-button:hover {
background-color: rgba(50, 50, 50, 0.9);
}

.baguetteBox-button#next-button {
right: 3%;
}

.baguetteBox-button#previous-button {
left: 2%;
}

.baguetteBox-button#close-button {
top: 20px;
right: calc(2% + 5px);
width: 40px;
height: 40px;
}

.baguetteBox-button#close-button svg {
position: absolute;
left: 6px;
top: 4px;
}

.baguetteBox-button svg {
left: 13px;
top: 18px;
}

.baguetteBox-button#previous-button svg {
position: absolute;
left: 4px;
top: 18px;
}


.baguetteBox-spinner {
width: 40px;
height: 40px;
display: inline-block;
position: absolute;
top: 50%;
left: 50%;
margin-top: -20px;
margin-left: -20px;
}

.baguetteBox-double-bounce1,
.baguetteBox-double-bounce2 {
width: 48%;
height: 30%;
-moz-border-radius: 50%;
border-radius: 50%;
background-color: #fff;
opacity: .6;
position: absolute;
/* top: 282px; */
top: 600px;
/* bottom: 0; */
left: 0;
right: 0;
margin: 0 auto;
-webkit-animation: bounce 2s infinite ease-in-out;
animation: bounce 2s infinite ease-in-out;
}

.baguetteBox-double-bounce2 {
-webkit-animation-delay: -1s;
animation-delay: -1s;
}

@-webkit-keyframes bounce {

0%,
100% {
-webkit-transform: scale(0);
transform: scale(0);
}

50% {
-webkit-transform: scale(1);
transform: scale(1);
}
}

@keyframes bounce {

0%,
100% {
-webkit-transform: scale(0);
-moz-transform: scale(0);
transform: scale(0);
}

50% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
transform: scale(1);
}
}




* {
margin: 0;
padding: 0;
font-size: 62.5%;
}

body {
margin: 0px;
font-family: "微软雅黑", Arial;
overflow: -Scroll;
overflow-x: hidden;
color: #000;
}



/*列表布局*/
.gallery {
display: flex;
flex-wrap: wrap;


}

.gallery::after {
content: '';
flex-grow: 999999999;
}

.gallery div {
flex-grow: 1;
margin: 5px;
background-color: violet;
height: 200px;
}

.gallery img {
height: 200px;
object-fit: cover;
max-width: 100%;
min-width: 100%;
vertical-align: bottom;
}




.pressing {
text-align: center;
position: fixed;
bottom: 78px;
left: 0;
right: 0;
z-index: 999;
width: 51%;
border: 2px solid #fff;
border-radius: 50px;
padding: 7px;
margin: 0 auto;
}

.pressing p {
font-size: 1.4rem;
color: #fff;
}

#goTopBtn {
position: fixed;
line-height: 38px;
width: 46px;
bottom: 47px;
height: 46px;
border-radius: 50px;
cursor: pointer;
display: none;
background: #000;
-moz-opacity: 0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
color: #FFFFFF;
text-align: center;
font-size: 5rem;
z-index: 990;
}

@font-face {
font-weight: 400;
font-style: normal;
font-family: SinaHomeFont;
src: url(../img/HomeFont.eot);
src: url(../img/HomeFont.eot) format('embedded-opentype'), url(../img/HomeFont.woff) format('woff'), url(../img/HomeFont.ttf) format('truetype');
}

[class*=" icon_"],
[class^=icon_] {
text-transform: none;
font-weight: 400;
font-style: normal;
font-variant: normal;
font-family: SinaHomeFont;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.icon_top:before {
content: "\e60f";
}

判断鼠标移入移出元素时的方向

悬浮图片上,判断鼠标移动方向

实现思路
image

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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE html>
<html>

<head lang="en">
<meta charset="UTF-8">
<title>鼠标移入移出方向判断</title>
<style>
body,
html,
ul,
p,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}

ul {
list-style: none;
}

body {
padding-top: 100px;
text-align: center;
}

.box {
width: 300px;
height: 200px;
margin: 10px auto;
border: 1px solid #ccc;
position: relative;
overflow: hidden;
display: inline-block;
}

.content {
position: absolute;
z-index: 2;
background-color: lightcoral;
width: 100%;
height: 100%;
left: -100%;
top: -100%;
line-height: 198px;
text-align: center;
color: #fff;
font-size: 70px;
}

.content.trans {
transition: all .2s;
backface-visibility: hidden;
}
</style>
</head>

<body>
<div class="box">
<div class="content">
1
</div>
</div>
<!-- <div class="box">
<div class="content">
2
</div>
</div>
<div class="box">
<div class="content">
3
</div>
</div>
<div class="box">
<div class="content">
4
</div>
</div> -->
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>//这个模块完成鼠标方向判断的功能
var MouseDirection = function (element, opts) {

var $element = $(element);

//enter leave代表鼠标移入移出时的回调
opts = $.extend({}, {
enter: $.noop,
leave: $.noop
}, opts || {});

var dirs = ['top', 'right', 'bottom', 'left'];

var calculate = function (element, e) {
/*以浏览器可视区域的左上角建立坐标系*/

//表示左上角和右下角及中心点坐标
var x1, y1, x4, y4, x0, y0;

//表示左上角和右下角的对角线斜率
var k;

//用getBoundingClientRect比较省事,而且它的兼容性还不错
var rect = element.getBoundingClientRect();

if (!rect.width) {
rect.width = rect.right - rect.left;
}

if (!rect.height) {
rect.height = rect.bottom - rect.top;
}

//求各个点坐标 注意y坐标应该转换为负值,因为浏览器可视区域左上角为(0,0),整个可视区域属于第四象限
x1 = rect.left; // 左上
y1 = -rect.top;

x4 = rect.left + rect.width; // 右下
y4 = -(rect.top + rect.height);

x0 = rect.left + rect.width / 2; // 中心
y0 = -(rect.top + rect.height / 2);

//矩形不够大,不考虑
if (Math.abs(x1 - x4) < 0.0001) return 4;

//计算对角线斜率
k = (y1 - y4) / (x1 - x4);

var range = [k, -k];

//表示鼠标当前位置的点坐标
var x, y;

x = e.clientX;
y = -e.clientY;

//表示鼠标当前位置的点与元素中心点连线的斜率
var kk;

kk = (y - y0) / (x - x0);

//如果斜率在range范围内,则鼠标是从左右方向移入移出的
if (isFinite(kk) && range[0] < kk && kk < range[1]) {
//根据x与x0判断左右
return x > x0 ? 1 : 3;
} else {
//根据y与y0判断上下
return y > y0 ? 0 : 2;
}
};

$element.on('mouseenter', function (e) {
var r = calculate(this, e);
opts.enter($element, dirs[r]);
}).on('mouseleave', function (e) {
var r = calculate(this, e);
opts.leave($element, dirs[r]);
});
};
</script>
<script>

var DIR_POS = {
left: {
top: '0',
left: '-100%'
},
right: {
top: '0',
left: '100%'
},
bottom: {
top: '100%',
left: '0'
},
top: {
top: '-100%',
left: '0'
}
};

$('.box').each(function () {
new MouseDirection(this, {
enter: function ($element, dir) {
//每次进入前先把.trans类移除掉,以免后面调整位置的时候也产生过渡效果
var $content = $element.find('.content').removeClass('trans');

//调整位置
$content.css(DIR_POS[dir]);

//reflow
$content[0].offsetWidth;

//启用过渡
$content.addClass('trans');

//滑入
$content.css({ left: '0', top: '0' });
},
leave: function ($element, dir) {
$element.find('.content').css(DIR_POS[dir]);
}
});
});


</script>
</body>

</html>

谈谈对BigPipe的理解

BigPipe并不是适用于所有的场合
BigPipe并不是适用于所有的场合,类似于Facebook和微博,他主要适用于:

  • 第一个请求时间较长,后端程序需要读取多个API
  • 页面上的劢态内容可以划分在多个区块内显示,且各个区块之间的关系不大(极弱耦合)
  • SEO需求较弱

BigPipe 是 Facebook 开发的优化网页加载速度的技术。网上几乎没有用 node.js 实现的文章,实际上,不止于 node.js,BigPipe 用其他语言的实现在网上都很少见。
以至于这技术出现很久以后,我还以为就是整个网页的框架先发送完毕后,用另一个或几个 ajax 请求再请求页面内的模块。
直到不久前,我才了解到原来 BigPipe 的核心概念就是只用一个 HTTP 请求,只是页面元素不按顺序发送而已

传统web模式采用了顺序处理的流程来处理用户请求.即用户向客户端发送一个请求后,服务器处理请求,加载数据并渲染页面;最后将页面返回给客户端.整个过程是串行执行的,具体流程如下:

  • 浏览器发送一个HTTP请求到Web服务器。
  • Web服务器解析请求,然后读取数据存储层,制定一个HTML文件,并用一个HTTP响应把它发送到客户端。
  • HTTP响应通过互联网传送到浏览器。
  • 浏览器解析Web服务器的响应,使用HTML文件构建了一个的DOM树,并且下载引用的CSS和JavaScript文件。
  • CSS资源下载后,浏览器解析它们,并将它们应用到DOM树。
  • JavaScript资源下载后,浏览器解析并执行它们。

整个流程按必须顺序执行,不能重叠,这也是为什么传统模式随着网络速度的提升访问速度没有很大提升的原因.

解决顺序执行的速度问题,一般能想到的就是多线程并发执行.Facebook 的前端性能研究小组采用浏览器和服务端并发执行的思路,经过了六个月的努力,开发出了BigPipe页面异步加载技术,成功的将个人空间主页面加载耗时由原来的5 秒减少为现在的2.5 秒。

BigPipe设计原理

BigPipe的主要思想是实现浏览器和服务器的并发执行,实现页面的异步加载从而提高页面加载速度.

为了达到这个目的,BigPipe首先根据页面的功能或位置将一个页面分成若干模块(模块称作pagelet),并对这几个模块进行标识.举个例子,在博客园个人首页包括几大板块,如头部信息,左边信息,博文列表,footer等.我们可以将首页按这些功能分块,并用唯一id或名称标识pagelet.客户端向服务端发送请求后(发出一次访问请求,如请求访问个人博客首页),服务端采用并发形式获取各个pagelet的数据并渲染pagelet的页面效果.一旦某个pagelet页面渲染完成则立刻采用json形式将该pagelet页面显示结果返回给客户端.客户端浏览器会根据pagelet的id或标识符,在页面的制定区域对pagelet进行转载渲染.客户端的模块加载采用js技术.具体流程如下:

  • 请求解析:Web服务器解析和完整性检查的HTTP请求。
  • 数据获取:Web服务器从存储层获取数据。
  • 标记生成:Web服务器生成的响应的HTML标记。
  • 网络传输:响应从Web服务器传送到浏览器。
  • CSS的下载:浏览器下载网页的CSS的要求。
  • DOM树结构和CSS样式:浏览器构造的DOM文档树,然后应用它的CSS规则。
  • JavaScript中下载:浏览器下载网页中JavaScript引用的资源。
  • JavaScript执行:浏览器的网页执行JavaScript代码

前三个阶段由Web服务器执行,最后四个阶段(5,6,7,8)是由浏览器执行。所以在服务器可以采用多线程并发方式对每个pagelet进行数据获取和标记生成页面,生成好的pagelet页面发送给前端.同时在浏览器端,对css,js的下载可以采用并行化处理.这就达到了浏览器和服务器的并发执行的效果,这样使得多个流程可以叠加执行,加少了整体页面的加载时间.浏览器端的并行化交给浏览器自己处理不需要我们做额外工作.在BigPipe中主要是处理服务端的并行性.
在BigPipe,一个用户请求的生命周期是这样的:在浏览器发送一个HTTP请求到Web服务器。在收到的HTTP请求,并在上面进行一些全面的检查, 网站服务器立即发回一个未关闭的HTML文件,其中包括一个HTML 标签和标签的开始标签。标签包括BigPipe的JavaScript库来解析Pagelet以后收到的答复。在标签,有一个模板,它指定了页面的逻辑结构和Pagelets占位符。

每日壹题

第一名的小蝌蚪

前端每日一问

专注前端与可视化

前端进阶积累

数组扁平化5种方法,多维数组转一维数组(降维的多种方式)


(建议收藏)原生JS灵魂之问, 请问你能接得住几个?(上)

🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥(建议精读)原生JS灵魂之问(中),检验自己是否真的熟悉JavaScript?

(2.4w字,建议收藏)😇原生JS灵魂之问(下), 冲刺🚀进阶最后一公里(附个人成长经验分享)