前端面试题目(1)

参考

单页面应用的优缺点

  • react是 单页应用的跳转,也就是说,不管怎么进行页面跳转,整个网站只会加载一次html文件,这也就决定了不能使用<a>标签进行页面跳转。(可参考“首页开发(3)”
    • 如果使用<a>标签进行页面跳转,跳转时会发送HTTP请求。
    • 而借助react-router-dom的Link组件实现跳转则不会,因此加载速度会快很多,借此也可提高性能。
  • 优点:
    • 良好的交互体验:单页应用的内容的改变不需要重新加载整个页面,获取数据也是通过Ajax异步获取,没有页面之间的切换,就不会出现“白屏现象”,也不会出现假死并有“闪烁”现象,页面显示流畅。
    • 良好的前后端工作分离模式:后端不再负责模板渲染、输出页面工作,后端API通用化,即同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端
    • 减轻服务器压力:单页应用相对服务器压力小,服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。
  • 缺点:
    • 首屏加载慢:如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,这必将拖慢加载速度;(可使用react-loadable模块实现异步加载组件
    • 不利于SEO:seo 本质是一个服务器向另一个服务器发起请求,解析请求内容。但一般来说搜索引擎是不会去执行请求到的js的。也就是说,搜索引擎的基础爬虫的原理就是抓取url,然后获取html源代码并解析。
      • 如果一个单页应用,html在服务器端还没有渲染部分数据数据,在浏览器才渲染出数据,即搜索引擎请求到的html是模型页面而不是最终数据的渲染页面。 这样就很不利于内容被搜索引擎搜索到。

轮播图是怎么实现的,后台数据抓取是怎么写的

html

重点就是包裹关系,注意动态添加小圆点(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
<div class="slider-box">
<!-- slider做一个框,和单张图等宽等高,overflow隐藏溢出部分 -->
<!-- 子绝父相,slider相对定位,ul绝对定位,调整ul的left使其移动-->
<div class="slider" id="slider">
<!-- ul宽是所有图的宽,内部li浮动来一排显示-->
<ul class="clearfix">
<li>
<img width="590" height="470" src="./img/1.jpg" alt="">
</li>
<li>
<img width="590" height="470" src="./img/2.jpg" alt="">
</li>
<li>
<img width="590" height="470" src="./img/3.jpg" alt="">
</li>
<li>
<img width="590" height="470" src="./img/4.jpg" alt="">
</li>
<li>
<img width="590" height="470" src="./img/5.jpg" alt="">
</li>
</ul>
<!-- 左箭头,绝对定位,left-box设置为flex容器,实现span水平垂直居中-->
<div class="left-box">
<span>&lt;</span>
</div>
<!-- 右箭头,同上中-->
<div class="right-box">
<span>&gt;</span>
</div>
<!-- 小圆点索引,绝对定位,通过js根据图片数量决定圆点数量-->
<div class="index-box">
<ol>
</ol>
</div>
</div>
</div>

css

重点就是子绝父相(子为ul元素,父为显示在页面上的div),图片浮动为一排通过left移动,注意父元素使用overflow:hidden隐藏滚动条

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
.slider-box {
margin-right: 10px;
}

.slider-box .slider {
position: relative;
width: 590px;
height: 470px;
overflow: hidden;
}

.slider-box .slider ul {
position: absolute;
top: 0px;
left: -590px;
width: 500%;/*所有图片的宽度和*/
height: 100%;
/* transition: left 1s linear; */
}

.slider-box .slider ul li {
float: left;/*浮动以显示在一排,且父元素left移动时会全部一起动*/
}

/*左箭头*/
.slider-box .left-box {
background: rgba(0, 0, 0, 0.2);
position: absolute;
top: 45%;
left: 0px;
width: 50px;
height: 50px;
color: #fff;
display: flex;/*flex容器可使子元素轻松居中*/
justify-content: center;/*水平居中*/
align-items: center;/*垂直居中*/
cursor: pointer;
}

/*右箭头*/
.slider-box .right-box {
background: rgba(0, 0, 0, 0.2);
position: absolute;
top: 45%;
right: 0px;
width: 50px;
height: 50px;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}

.slider-box .left-box, .slider-box .right-box span {
font-size: 24px;
}

/*小圆点索引*/
.slider-box .index-box {
position: absolute;
bottom: 3%;
left: 10%;

}

.slider-box .index-box > ol {
height: 14px;
display: flex;
justify-content: space-evenly;
align-items: center;
background: rgba(0, 0, 0, 0.6);
}

.slider-box .index-box > ol > li {
width: 10px;
height: 10px;
border-radius: 50%;
background: #fff;
list-style: none;
cursor: pointer;
}

.slider-box .index-box > ol > li.active {
background: #e1251b;
}

js中使用class

  • 我们先通过ES6 class创建一个class Slider,这个类的作用就是获取轮播图的父盒子id然后给他内部的轮播图、小圆点、左右按钮添加js逻辑。然后在需要使用轮播图的文件中使用Slider类来new一个对象,同时将Slider需要的DOM元素(轮播图的父盒子)的id传过去,相对应的逻辑就绑上去了
  • class的使用
    1
    2
    3
    4
    5
    6
    <!-- 在html中</body>上方new一个对象引入即可-->
    <script type="text/javascript">
    <!-- 传入的是Slider类构造函数中需要的轮播图父元素的id-->
    const slider = new Slider("#slider");
    <!-- 那么绑定在Slider类构造函数中的属性、函数都会被引入-->
    </script>

动态添加小圆点

  • DOM children
  • slider类中定义的函数initPoint
    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
    class Slider {

    constructor(id) {
    this.box = document.querySelector(id) // 轮播图盒子
    this.picBox = this.box.querySelector("ul") // 图片盒子
    this.indexBox = this.box.querySelector(".index-box") // 小圆点父盒子

    this.init()
    }

    init() {
    this.initPoint()
    }

    // 动态添加小圆点
    initPoint() {
    // 图片的数量就是圆点的数量
    const num = this.picBox.children.length;
    // 减少DOM操作(Fragment)
    let frg = document.createDocumentFragment();

    for (let i = 0; i < num; i++) {
    let li = document.createElement("li")
    // 提前给圆点设置属性data-index,用于点击跳转
    li.setAttribute("data-index", i + 1)
    // 给当前图的小圆点特殊样式,默认第一张图
    if (i == 0) li.className = "active"
    frg.appendChild(li)
    }

    // 添加的样式
    this.indexBox.children[0].style.width = num * 10 * 2 + "px";
    // 一次性添加到小圆点父盒子中
    this.indexBox.children[0].appendChild(frg)
    }
    }

实现图片可无限滚动

  • 实现效果:点击“向右”到最后一张图片时再次点击“向右”可回到第一张图(“向左”也是),非常流畅
  • 思路:
    • 在 绝对定位的ul 的头和尾各增加一个 辅助图,当我们的辅助图进入红框(显示的相对定位父元素)时
    • 将辅助图与对应的真正的图的位置迅速替换实现图片可无限滚动
  • 补充:

添加辅助图

  • Slider类中:
    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
    // 只放相关的部分,没变的部分见上
    class Slider {

    constructor(id) {
    // 整个轮播图的宽度sliderWidth(轮播图容器宽度clientWidth)
    this.sliderWidth = this.box.clientWidth

    this.init()
    }

    init() {
    // copyPic需要在构造函数中被调用,这样才会生效
    this.copyPic()
    }

    // 增加辅助图,实现无限滚动
    copyPic() {
    // 复制 图1节点 给first(辅助图1)
    const first = this.picBox.firstElementChild.cloneNode(true)
    // 复制 最后一张图的节点 给last(辅助图5)
    const last = this.picBox.lastElementChild.cloneNode(true)
    // 在 ul尾部 增加 辅助图1
    this.picBox.appendChild(first)
    // 在 ul的第一个元素 前增加 辅助图5
    this.picBox.insertBefore(last, this.picBox.firstElementChild)
    // 增加后首先显示的是 辅助图5 ,需要改成首先显示图1(也可以在css中设置)
    this.picBox.style.width = this.sliderWidth * this.picBox.children.length + "px"
    this.picBox.style.left = -1 * this.sliderWidth + "px"
    }
    }
  • (copyPic()后面几行代码可在css中设置)添加成功后修改css中ul这个绝对定位元素的left,使其初始值向左一个图片的宽度,达到首先显示图1而不是辅助图5的效果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .slider-box .slider ul {
    position: absolute;
    top: 0px;
    /* 向左移一张图的宽度,使其首先显示图1而不是辅助图5 */
    left: -590px;
    /* 增加辅助图后ul的总宽度也要修改 */
    width: 700%;
    /* 如果js的copyPic()中已经设置,则可不用在css重新设置left和width */
    height: 100%;
    /* transition: left 1s linear; */
    }

抵达辅助图时替换为真正的对应图

  • 【可以看完下面的“预防多次连续点击出错”再来看这里会更加完整】
  • 在Slider类的animate()函数中实现替换(具体animate()实现功能见下“左右按钮”中):
    抵达辅助图时替换为真正的对应图
    构造函数中定义的:sliders轮播图数量(注意是5不是7,此时还未添加辅助图)、整个轮播图的宽度sliderWidth(轮播图容器宽度clientWidth)

左右按钮

  • 实现效果:点击左右按钮可切换图片
  • 思路:
    • leftRight()函数获取左右按钮,绑定点击事件,分别调用move(offset)函数,offset是位移
    • move(offset)函数改变圆点样式,调用animate(offset)函数
    • animate(offset)函数中实现移动的动画效果
  • Slider类中:
    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
    // 只放相关的部分,没变的部分见上
    class Slider {

    constructor(id) {
    // 整个轮播图的宽度sliderWidth(轮播图容器宽度clientWidth)
    this.sliderWidth = this.box.clientWidth

    this.init()
    }

    init() {
    // leftRight 需要在构造函数中被调用,这样才会生效
    this.leftRight()
    }

    // 用于调用轮播图移动效果的函数,改变圆点样式,offset是位移
    move(offset) {
    // 动画效果animate()
    this.animate(offset);

    // 圆点数量num,给当前圆点加特殊样式,其他圆点抹掉样式
    const num = this.indexBox.children[0].children.length
    for (let i = 0; i < num; i++) {
    this.indexBox.children[0].children[i].className = ""
    }
    this.indexBox.children[0].children[this.index - 1].className = "active"

    }


    // 移动的动画效果,offset是位移
    // 【大坑】:不要在css设置left,js获取会很麻烦,parseFloat()都转不成数字,只能得到NAN,去JS(copyPic()中)设置添加辅助图后展示第一张图片的left
    animate(offset) {
    const time = 1000 // 运动的总时间time(ms)
    const rate = 100 // 动一次的时间rate
    let speed = offset / (time / rate) // 每次移动的距离speed=位移/运动次数

    // 移动的目标位置goal=当前位置向左移动offset
    // 注意left带单位,使用parseFloat去掉单位
    let goal = parseFloat(this.picBox.style.left) - offset

    // this.animated = true
    // setInterval重复执行,每隔100ms执行一次
    let animate = setInterval(() => {
    // abs()返回绝对值
    // 到达目标位置goal/非常接近目标位置goal(即,距离目标位置<移动一次的距离)时
    if (this.picBox.style.left == goal || Math.abs(Math.abs(parseFloat(this.picBox.style.left)) - Math.abs(goal)) < Math.abs(speed)) {
    this.picBox.style.left == goal // 直接设置为达到目标位置
    clearInterval(animate) // 清除重复执行
    // this.animated = false

    } else {
    // 没有到达目标位置goal前则每隔100ms移动一次
    this.picBox.style.left = parseFloat(this.picBox.style.left) - speed + "px";
    }
    }, rate);

    }

    // 点击左右按钮可切换图片
    leftRight() {
    // 选中左侧按钮
    this.box.querySelector(".left-box").addEventListener("click", () => {
    console.log("left")

    /* 这是 预防多次连续点击出错 ,具体可看下面部分
    // 如果此时正在移动,直接返回,不允许继续移动
    if (this.animated) { // animated构造函数中默认false
    return
    }
    */

    // 如果此时不是正在移动,则继续
    // index为当前小圆点索引(初始图1为1),sliders为图片数量5
    // 更改this.index,以便move()中改圆点样式
    // 如列表溢出则重置,连续点击出现空白图时为溢出
    if (this.index - 1 < 1) {
    // 当前位置左移1位却小于1时,index重置为5
    this.index = this.sliders
    } else { // 正常则当前图片索引index减1
    this.index--
    }

    // 调用move()移动轮播图,移动位移为 负 轮播图宽度
    this.move(-this.sliderWidth)

    })

    // 选中右侧按钮
    this.box.querySelector(".right-box").addEventListener("click", () => {
    console.log("right")

    /* 这是 预防多次连续点击出错 ,具体可看下面部分
    if (this.animated) {
    return
    }
    */

    // 同理,当前位置+1超出总轮播图数量时,重置index为1
    if (this.index + 1 > this.sliders) {
    this.index = 1
    } else {
    this.index++
    }

    this.move(this.sliderWidth)

    })

    }
    }

点击圆点跳转对应图片

  • 实现效果:点击圆点跳转对应图片
  • 思路:
    • 在动态添加小圆点的initPoint()中调用move(offset)函数中绑定点击事件,复用上面左右按钮中的move(),offset是位移
    • move(offset)函数改变圆点样式,调用animate(offset)函数(见上)
    • animate(offset)函数中实现移动的动画效果(见上)
  • Slider类中: Slider类中

预防多次连续点击出错

  • 实现效果:快速连续点击左右按钮跳转时,限制其跳转一张图结束后才能跳转下一张图片(连续点击出现空白图时为溢出,列表溢出则重置)
  • 思路:
    • 构造函数中添加属性animated,默认false,用来判断此时是否正在移动。如是则需等待移动结束才能继续移动下一张。
    • animate()中设置animated,移动开始则为true,结束则为false
    • **优化leftRight()**,通过animated判断是否处于移动中,否才允许继续移动,当处于轮播图最左端(index=1,辅助图5)时,若还想继续向左则
  • Slider类中优化leftRight():
    1
    2
    3
    4
    5
    6
    7
    // 只展示添加的部分
    class Slider {

    constructor(id) {
    this.animated = false
    }
    }
    Slider类中animate()中设置animated
    Slider类中优化leftRight

自动播放

  • 实现效果:页面自动向右播放轮播图,鼠标移入时停止自动播放,移出时继续
  • 思路:
    • 构造函数中调用的init()中**添加play()**,自动调用
    • play()中,采用setInterval()每隔2秒点击一次“向右”按钮
    • 轮播图盒子 绑定 onmouseenter/onmouseover事件,规定鼠标进入 轮播图盒子 时,清除定时器(停止向右)
    • 轮播图盒子 绑定 onmouseleave/onmouseout事件,规定鼠标移出 轮播图盒子 时,继续执行定时器(继续向右)
  • 补充: onmouseenter、onmouseleave可参考DOM事件
  • Slider类中:
    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
    class Slider {

    constructor(id) {
    this.init()
    }

    init() {
    this.play()
    }

    // 自动播放
    play() {
    // 每隔2秒点击一次“向右”按钮
    this.auto = setInterval(() => {
    this.box.querySelector(".right-box").click()
    }, 2000);

    // 鼠标进入 轮播图盒子 时,清除定时器
    this.box.addEventListener("mouseenter", () => {
    clearInterval(this.auto)
    })

    // 鼠标移出 轮播图盒子 时,继续执行定时器
    this.box.addEventListener("mouseleave", () => {
    this.auto = setInterval(() => {
    this.box.querySelector(".right-box").click()
    }, 2000);
    })
    }

    }

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
191
192
193
194
195
196
197
class Slider {

constructor(id) {
this.box = document.querySelector(id)
this.picBox = this.box.querySelector("ul")
this.indexBox = this.box.querySelector(".index-box")
// 整个轮播图的宽度sliderWidth(轮播图容器宽度clientWidth)
this.sliderWidth = this.box.clientWidth
// sliders轮播图数量(注意是5不是7)
this.sliders = this.picBox.children.length
// 小圆点的索引,初始为1
this.index = 1
// animated当前是否在移动中
this.animated = false
this.auto = null

this.init()
}

init() {
this.initPoint()
this.copyPic()
this.leftRight()
this.play()
}

// 动态添加小圆点
initPoint() {
// 图片的数量就是圆点的数量
const num = this.picBox.children.length;
// 减少DOM操作(Fragment)
let frg = document.createDocumentFragment();

for (let i = 0; i < num; i++) {
let li = document.createElement("li")
// 提前给圆点设置属性data-index,用于点击跳转
li.setAttribute("data-index", i + 1)
// 给当前图的小圆点特殊样式,默认第一张图
if (i == 0) li.className = "active"
frg.appendChild(li)
}

// 添加的样式
this.indexBox.children[0].style.width = num * 10 * 2 + "px";

this.indexBox.children[0].appendChild(frg)

// 点击圆点,跳转对应图片
this.indexBox.children[0].addEventListener("click", (e) => {
console.log("point")
// 获取当前点击的DOM元素的data-index属性值
let pointIndex = (e.target).getAttribute("data-index")
// index是构造函数中定义并初始为1的索引
if (pointIndex == this.index || this.animated) {
return
}

// 跳转相应图片,offset为移动距离
// index为当前小圆点索引(构造函数中定义初始为1)
// sliderWidth为轮播图的宽度
let offset = (pointIndex - this.index) * this.sliderWidth
// 跳转前记得改当前小圆点索引index,以此方便move()中获取index改变小圆点样式
this.index = pointIndex
this.move(offset) // 调用move()移动图片

})
}

// 增加辅助图,实现无限滚动
copyPic() {
// 复制 图1节点 给first(辅助图1)
const first = this.picBox.firstElementChild.cloneNode(true)
// 复制 最后一张图的节点 给last(辅助图5)
const last = this.picBox.lastElementChild.cloneNode(true)
// 在 ul尾部 增加 辅助图1
this.picBox.appendChild(first)
// 在 ul的第一个元素 前增加 辅助图5
this.picBox.insertBefore(last, this.picBox.firstElementChild)
// 增加后首先显示的是 辅助图5 ,需要改成首先显示图1(也可以在css中设置)
this.picBox.style.width = this.sliderWidth * this.picBox.children.length + "px"
this.picBox.style.left = -1 * this.sliderWidth + "px"
}

// 用于调用轮播图移动效果的函数,改变圆点样式,offset是位移
move(offset) {
// 动画效果animate()
this.animate(offset);

// 圆点数量num,给当前圆点加特殊样式,其他圆点抹掉样式
const num = this.indexBox.children[0].children.length
for (let i = 0; i < num; i++) {
this.indexBox.children[0].children[i].className = ""
}
this.indexBox.children[0].children[this.index - 1].className = "active"

}

// 移动的动画效果,offset是位移
animate(offset) {
const time = 1000 // 运动的总时间time(ms)
const rate = 100 // 动一次的时间rate
let speed = offset / (time / rate) // 每次移动的距离speed=位移/运动次数

// 移动的目标位置goal=当前位置向左移动offset
// 注意left带单位,使用parseFloat去掉单位
let goal = parseFloat(this.picBox.style.left) - offset

this.animated = true
// setInterval重复执行,每隔100ms执行一次
let animate = setInterval(() => {
// 到达目标位置goal/非常接近目标位置goal(即,距离目标位置<移动一次的距离)时
if (this.picBox.style.left == goal || Math.abs(Math.abs(parseFloat(this.picBox.style.left)) - Math.abs(goal)) < Math.abs(speed)) {
this.picBox.style.left == goal // 直接设置为达到目标位置
clearInterval(animate) // 清除重复执行
this.animated = false

// 移动到辅助图5时,替换为真正的图5(sliders轮播图数量为5)
if (parseFloat(this.picBox.style.left) == 0) {
this.picBox.style.left = -this.sliders * this.sliderWidth + "px"
} else if (parseFloat(this.picBox.style.left) == -(this.sliders + 1) * this.sliderWidth) { // 移动到辅助图1时,替换为真正的图1
this.picBox.style.left = -this.sliderWidth + "px"
}
} else {
// 没有到达目标位置goal前每隔100ms移动一次
this.picBox.style.left = parseFloat(this.picBox.style.left) - speed + "px";
}
}, rate);

}

// 点击左右按钮可切换图片
leftRight() {
// 选中左侧按钮
this.box.querySelector(".left-box").addEventListener("click", () => {
console.log("left")

// 如果此时正在移动,直接返回,不允许继续移动
if (this.animated) { // animated构造函数中默认false
return
}

// 如果此时不是正在移动,则继续
// index为当前小圆点索引(初始图1为1),sliders为图片数量5
// 如列表溢出则重置,连续点击出现空白图时为溢出
if (this.index - 1 < 1) {
// 当前位置左移1位却小于1时,index重置为5
this.index = this.sliders
} else { // 正常则当前图片索引index减1
this.index--
}

// 调用move()移动轮播图,移动位移为 负 轮播图宽度
this.move(-this.sliderWidth)

})

// 选中右侧按钮
this.box.querySelector(".right-box").addEventListener("click", () => {
console.log("right")

if (this.animated) {
return
}
// 同理,当前位置+1超出总轮播图数量时,重置index为1
if (this.index + 1 > this.sliders) {
this.index = 1
} else {
this.index++
}

this.move(this.sliderWidth)

})

}

// 自动播放
play() {
// 每隔2秒点击一次“向右”按钮
this.auto = setInterval(() => {
this.box.querySelector(".right-box").click()
}, 2000);

// 鼠标进入 轮播图盒子 时,清除定时器
this.box.addEventListener("mouseenter", () => {
clearInterval(this.auto)
})

// 鼠标移出 轮播图盒子 时,继续执行定时器
this.box.addEventListener("mouseleave", () => {
this.auto = setInterval(() => {
this.box.querySelector(".right-box").click()
}, 2000);
})
}

}

跨域的方法有哪些,常用的是?

怎么理解前端,怎么学习前端(途径)

  • “前端”是与用户直接交互的部分,包括你在浏览网页时接触的所有视觉内容。
    • 好比你去快餐店吃饭。你在外面看到的店面装饰、点餐员、菜单、保温柜里面摆着的鸡腿汉堡这些给用户看的都是属于前端,相当于门户网站、用户输入、数据列表等。大概可以归类为网页开发的html、css
    • 你跟点餐员点好餐之后,点餐员会录入你点的菜,向你确认可乐加不加冰、咖啡加不加糖然后让你支付。这些用户与前端的交互就交给了javascript来完成。
    • 然后点餐员向厨房喊一声“一个xx套餐,可乐不加冰”。这就向后端发送了一个ajax请求。这些请求是异步的,你得等一会儿才能收到你点的餐。
    • 而他们厨房里炸鸡的、炸薯条的、做汉堡的,弄好之后递给前台,这些就相当于后端。他们负责把食物(数据)制作好,再返回给前端,前端再将收到的这些东西装盘(将收到的数据转为需要的格式)递给用户。这基本就是一个前后端交互的逻辑了。

对后端语言的了解程度

常用的浏览器及内核

  • 可参考这篇文章
  • 目前最为主流浏览器有五大款,分别是IE、Firefox、Google Chrome、Safari、Opera
  • 浏览器内核又可以分成两部分:渲染引擎(layout engineer 或者 Rendering Engine)和 JS 引擎。(JS 引擎越来越独立,内核倾向于只指渲染引擎
    • 渲染引擎 负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
    • JS 引擎则是解析 Javascript 语言,执行 javascript 语言来实现网页的动态效果。
  • 常见的浏览器内核可以分这四种:Trident、Gecko、Blink、Webkit
  • 我使用的是chrome浏览器+Blink内核
  • 总结一下各常用浏览器所使用的内核
  1. IE浏览器内核:Trident内核,也是俗称的IE内核;
  2. Chrome浏览器内核:统称为Chromium内核或Chrome内核,以前是Webkit内核,现在是Blink内核;
  3. Firefox浏览器内核:Gecko内核,俗称Firefox内核;
  4. Safari浏览器内核:Webkit内核;
  5. Opera浏览器内核:最初是自己的Presto内核,后来是Webkit,现在是Blink内核;
  6. 百度浏览器、世界之窗内核:IE内核;

前端性能优化的方法,举例

  • 可参考前端基础-HTTP/HTML/浏览器(2)/JS 运行环境
  • 让加载更快
    • 减少资源体积:压缩代码
      • 比如使用webpack在production环境下进行打包时,就会自动压缩代码至1/3的大小,在浏览器上进行反解析再进行渲染。
    • 减少请求次数
      • 合并代码:比如webpack中,在index.js中引入a.js、b.js,打包后只生成一个bundle.js,这就是合并代码。(加载3次3kb的文件不如加载1次9kb快,这和网络请求有关系)
      • SSR服务器端渲染:不需要通过ajax发送请求。
      • 缓存:原本需要发送多个请求的数据直接在缓存中获取即可减少访问次数。
        • 应该是尽可能命中强缓存,同时,能在更新版本的时候让客户端的缓存失效
        • 更新版本的时候,顺便把静态资源的路径改了,这样,就相当于第一次访问这些资源,就不会存在问题了(webpack打包时会把静态资源的路径加上hash值)
        • 较为合理的缓存方案:
          • HTML:使用协商缓存
          • CSS&JS&图片:使用强缓存,文件命名带上hash值。
      • 雪碧图
    • 使用更快的网络:CDN
      • CDN是分区域的,也就是使用CDN的时候上海和北京对同一个网站的IP地址是不同的。CDN会选择一个离用户最近的CDN边缘节点来响应用户的请求,这样海南移动用户的请求就不会千里迢迢跑到北京电信机房的服务器(假设源站部署在北京电信机房)上了
      • 图片、js等静态资源采用CDN是很快的,我们经常使用的bootstrap就是使用的CDN
  • 让渲染更快
    • CSS放在head中,JS放在body最下面
      • 原因
        • 页面是边解析边渲染的,如果把CSS写在HTML后,则先解析DOM树渲染在页面上,再生成CSSOM合成渲染树重新渲染在页面上,这样会有一个过程,用户可能会看到一个过程变化。所以在DOM树生成之前就先生成CSSOM会更好,这样当DOM树生成时就可直接和所有CSSOM进行合并,一步渲染完成。
        • 页面是边解析边渲染的,如果把JS写在body中间,虽然JS有异步的处理机制,但极端情况会出现前面已经渲染了一半就卡住的情况,渲染时间会拖长
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存(DOM操作很耗性能)
    • 合并的频繁DOM操作,一起插入DOM结构(利用createDocumentFragment()
    • 让渲染更流畅:
      • **节流throttle**:无论执行动作多快,都固定每隔100ms触发一次(时间自定义)。
      • 防抖debounce:用户输入结束或暂停时,才会触发相关事件。际上是对定时器setTimeout的使用,但我们通常将其封装为debounce函数来使用。

可以通过什么途径查看到网站目前使用的技术

  • google插件WhatRuns
  • 检测的内容:
    • 网页服务器
    • 内容管理系统
    • 网页字体
    • JavaScript 框架
    • Wordpress插件等

react的核心是什么

  • 可参考知乎segmentfault,总得来说就是组件+虚拟DOM
  • 组件:所有React代码基本上就是一个包含许多小组件在内的大组件
  • 组件API:render、setState、constructor(一般会在其中初始化state并做一些函数this的绑定)、生命周期函数等
  • 组件类型:class组件、函数式组件、高阶组件
  • JSX:React的意思就是让我们把HTML和JS代码全都写在一起。React是通过一种叫做JSX的语法扩展(X代表XML)来实现的
  • Props & State:React当中,属性就被称作props(properties的缩写),组件之间可以通过Props进行交互
    • 正因如此,React当中的数据流是单向的:数据只能从父组件传向子组件,反过来则不行。可是组件不可能只接受从父组件传来的数据(例如还有用户在input当中的输入),这时state就派上了用场
  • 声明式渲染(虚拟DOM)
    • 初始化渲染:JSX语法=》render函数渲染出虚拟DOM树=》react将虚拟DOM渲染成真实的DOM
    • 页面更新渲染:state改变=》render函数重新渲染出虚拟DOM=》diff算法比对当前虚拟DOM和需要更新的虚拟DOM=》重新渲染部分真实DOM
  • 总结:
    • React的代码是由一个个的组件构成的。
    • 组件采用了JSX语法扩展的写法。
    • 数据流总是从父组件到子组件,除state可以通过调用方法修改之外。
    • 组件包含一些特定方法和生命周期函数。
    • 你也完全可以用函数声明只有render方法的无状态组件。
    • 区分处理UI和数据逻辑的组件是一种很好的开发实践。
    • 高阶组件函数可以传入一个组件并为其赋予更多功能。

写过项目最难的部分说一下

,