0%

常规面试题

面试题

HTML CSS 面试题

1、如何理解HTML 语义化?

  • 让人更容易读懂(增加代码可读性)

  • 让搜索引擎更容易读懂(SEO 搜索引擎优化)

2、默认情况下,哪些HTML 标签是块级元素、哪些是内联元素?

  • 块级元素:display: block/table; 有div h1 h2 table ul ol p 等

  • 内联元素:display: inline/inline-block; 有span img input button 等

3、盒子模型的宽度如何计算?

image-20211210121939411

  • offsetWidth = ( 内容宽度+ 内边距+ 边框),无外边距

  • 因此,答案是122px

  • 补充:如果让offsetWidth 等于100px ,该如何做? 在div1样式做设置盒子模型为:IE盒子模型(box-sizing: border-box;)

    设置了box-sizing: border-box之后;宽度其实变成了78 加上内边距20 边框2 加起来就100了。

4、argin 纵向重叠的问题

image-20211210123149043

  • 相邻元素的margin-top 和margin-bottom 会发生重叠

  • 空白内容的

    也会重叠 —>空白内容重叠可忽略

  • 答案:15px

5、margin 负值的问题

对margin 的top left right bottom 设置负值,有何效果?

  • margin-top 和margin-left 负值,元素向上、向左移动

  • margin-right 负值,右侧元素左移,自身不受影响

  • margin-bottom 负值,下方元素上移,自身不受影响

6.float 布局

如何实现圣杯布局和双飞翼布局? 手写clearfix

圣杯布局和双飞翼布局的目的:

  • 三栏布局,中间一栏最先加载和渲染(内容最重要)
  • 两侧内容固定,中间内容随着宽度自适应
  • 一般用于PC网页

圣杯布局例子:

image-20211210132014561

三者都设置浮动且center宽度设置为百分百加上容器本身设置内边距

使得right的宽度无法在left的右侧排列被挤下来了

image-20211210132236216

若要使得left移动至上图样式 设置margin-left:-100% ,设置这个实则就是父元素宽度的百分之百。也就是center(container)的宽度

(可以看成浮动元素它是别挤下来的 实际上是紧靠灰色部分的右侧。所以设置了这个就到了上图的位置)

之后在设置相对定位 (right:200px即可)因为设置了相对定位是相对于自身移动,对其他元素没有影响。(可以看到定位的坐标为灰色的左上顶点处)

image-20211210133101515

给margin-right设置负150即可成为下图

image-20211210133244236

注意:不好理解!!! 就是给自身元素设置的margin-right:自身宽度。就会使自身宽度不占位置从而上移

上述的布局就是圣杯布局 圣杯布局两边留白是通过padding设置的 而双飞翼布局是通过margin来进行两边留白的

双飞翼布局:

  1. 双飞翼布局是通过margin来进行两边留白。
  2. left直接设置margin-left:-100%即可。

image-20211210134341275

right:也设置margin-left:-190px。

image-20211210134356122

1
2
3
4
5
6
7
手写clearfix  BFC解决清除浮动的问题
.clearfix:after{
content:'';
display:table;
clear:both;
}
将clearfix属性添加到容器的class内。 因为是浮动元素,高度塌陷了。为了防止不塌陷所以在container内设置该样式

7、flex 布局

flex 实现一个三点的色子

  • flex-direction :主轴方向(横向或者纵向)
  • justify-content:主轴对齐方式(开始对齐,居中对齐,结束对齐)
  • align-items:交叉轴的对齐方式(开始对齐,居中对齐,结束对齐)
  • flex-wrap:换行
  • align-self:子元素在交叉轴的对齐方式(开始对齐,居中对齐,结束对齐)
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
<style type="text/css">
.box {
width: 200px;
height: 200px;
border: 2px solid #ccc;
border-radius: 10px;
padding: 20px;

display: flex;
justify-content: space-between;/*对齐方式:两边对齐*/
}
.item {
display: block;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #666;
}
.item:nth-child(2) {
align-self: center; /*第二个子元素垂直居中*/
}
.item:nth-child(3) {
align-self: flex-end;/*第三个子元素垂直方向尾对齐*/
}

</style>
body>
<div class="box">
<span class="item"></span>
<span class="item"></span>
<span class="item"></span>
</div>
</body>

image-20211210145834952

8、absolute 和relative 分别依据什么定位?

relative 依据自身定位

absolute 依据最近一层的定位元素定位(定位元素有:absolute relative fixed body)

9、居中对齐有哪些实现方式?

水平居中

垂直居中

1
2
3
4
5
6
7
8
9
水平居中:
inline 元素:text-align: center
block 元素:margin: auto
absolute 元素:left: 50% + margin-left 负值
垂直居中:
inline 元素:line-height的值等于height 值
absolute 元素:top: 50% + margin-top 负值 **必须知道子元素的尺寸
absolute 元素:transform(-50%, -50%)
absolute 元素:top, left, bottom, right = 0 + margin: auto

10、CSS -图文样式

line-height 如何继承

image-20211210143133796

  • 写具体数值,如30px ,则继承该值(比较好理解)
    • 就直接继承30px
  • 写比例,如2 / 1.5 ,则继承该比例(比较好理解)
    • 直接比例乘以p标签的font-size 2*16=32px
  • 写百分比,如200% ,则继承计算出来的值(考点)
    • 直接body里的font-size的比例乘200%就是20*2=40px

11、CSS -响应式

rem 是什么?

响应式布局的常见方案?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rem 是什么?
rem 是一个长度单位
px ,绝对长度单位,最常用
em ,相对长度单位,相对于父元素,不常用
rem ,相对长度单位,相对于根元素,常用于响应式布局
html {
font-size: 100px; //等于1rem
}
**任何使用长度的地方都可以用rem 例如宽度高度都可以
响应式布局的常见方案?
media-query,根据不同的屏幕宽度设置根元素font-size
body {
font-size: 0.16rem; /*在iphone6/7 就是16px*/
}
rem,基于根元素的相对单位

JavaScript面试题

1、typeof 能判断的类型?

考点:JS变量类型

2、何时使用===何时使用==

考点:强制类型转换

3、window.onload和 DOMContentLoaded区别

考点:页面加载过程,(页面渲染过程)

4、JS创建10个标签,点击弹出对应的序号

考点:JS作用域

5、手写节流throttle和防抖debounce

考点:性能、体验优化

6、Promise解决什么问题?

考点:JS异步

7、值类型和引用类型的区别

值类型各用各的,不会相互干扰。而引用类型一旦赋值了 b改变了 a也改变了

1
2
3
4
5
6
7
8
9
10
**值类型
let a = 100
let b = a
a=200
log(b)//100
**引用数据类型
let a={age:20}
let b=a
b.age=21
log(a.age)//21

image-20211210160818584

image-20211210160828452

8、typeof 运算符

  • 识别所有值类型

  • 识别函数

  • 判断是否是引用类型(不可再细分)

    image-20211210184526582

9、深拷贝

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
const obj1 = {
age: 20,
name: 'xx×',
address: {
city: 'beijing'
},
arr:[ 'a','b', 'c']
}
//const obj2 = obj1
const obj2 = deepClone(obj1) //这边就是进行了深拷贝

obj2.address.city = 'shanghai'

console.log(obj1.address.city) //这是浅拷贝

//深拷贝就是obj2改了 obj1不会变
function deepClone(obj={}){
if (typeof obj !== 'object'||obj ==null) {
//obj是null,或者不是对象或者不是数组,直接返回
return obj *****************
}
//初始化返回结果 obj是对象和数组
let result
if (obj instanceof Array) {
result = []
}else {
result = {}
}

for ( let key in obj){ //是数组和对象就可以遍历
//保证key(age,name...) 是自己的属性,而不是原型的属性 因为这里没有考虑到原型上的东西可忽略,但是面试的时候不可忽略
if (obj.hasOwnProperty(key)) {
//递归调用!!!
result[key] = deepclone(obj[key])//obj[key]就是key对应的值 首先20进来在*****返回了
}
}

return result

}


***let result = {
age: 20,
name: 'xx×',
address: {
city: 'beijing'
},
arr:[ 'a','b', 'c']
} //深拷贝的结果。

10、变量计算-类型转换

image-20211210183556963

image-20211210190327229

let x1=obj1.x 是干扰人的

11、原型和原型链

1
2
3
4
5
6
7
8
**两种写的方式**
console. log(
`姓名${this.name} ,学号${this.number}
)

console.log(
'姓名'+ this.name + ',学号'+this.number
)

class

  • constructor

  • 属性

  • 方法

继承

  • extends
  • super
  • 扩展或重写方法
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
//父类
class People {
constructor( name) {
this.name = name
}
eat() {
console. log(`$ithis.name} eat something`)
}
}

//子类
class Student extends People {
constructor(name, number) {
super(name) //继承父类的name
this.number = number
}
sayHi(){
console.log(`姓名 ${this.name}学号${this.number}`)
}
}

const xialuo = new Student('夏洛',100)
log(xialuo.name)
log(xialuo.number)
xialuo.sayHi()
xialuo.eat()

image-20211210192840339

image-20211210193103817

image-20211210195041939

image-20211210195103225

image-20211210195508425

image-20211210201830160

1
2
1、如何准确判断一个变量是不是数组?
a instanceof Array

12、作用域和闭包

1
2
3
1this的不同应用场景,如何取值?
2、手写bind 函数
3、实际开发中闭包的应用场景,举例说明

image-20211210203042511

作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

if for {} 就是块级作用域

image-20211210204400524

自由变量

  • 一个变量在当前作用域没有定义,但被使用了

  • 向上级作用域,一层一层依次寻找,直至找到为止

  • 如果到全局作用域都没找到,则报错xx is not defined

闭包

作用域应用的特殊情况,有两种表现:

  • 函数作为参数被传递
  • 函数作为返回值被返回

image-20211210204753650

答案为100

image-20211210204829747

答案为100

闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找。不是在执行的地方!!!

13、this

应用场景

  • 作为普通函数被调用 ->window
  • 使用call apply bind- >传入什么就是绑定什么
  • 作为对象方法被调用 ->返回对象本身
  • 在class方法中调用 ->当前实例本身
  • 箭头函数 ->上级作用域的this的值来决定

注意:this取什么值,是在函数执行的时候确认的,不是在函数定义的时候确认的

image-20211210205632681

bind和call的区别是 bind会返回一个新的函数再去执行

image-20211210205745060

this===window是因为function这个函数执行本身是setTimeout触发的执行,它并不是zhangsan.方法触发的执行。

image-20211210210004770

特点:箭头函数本身没有this都是取它上级作用域的this

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
手写bind函数
由于fun1的隐式原型===Function的显示原型
bind方法只有在Function.prototype

//模板
function fn1(a,b,c){
log('this',this)
log(a,b,c)
return 'this is fn1'
}
const fn2 =fn1.bind({x:100},10,20,30)
const res = fn2()
log(res)

//模拟bind
Function.prototype.bind1=function(){
//把参数列表变成数组
const args = Array.prototypes.slice.call(arguments)//slice是Array.prototypes上的API
//获取this(数组的第一项)
const t =args.shift()
//fn1.bidn(...)中的fn1
const self = this
return function(){ //因为上面fn1.bind返回的是一个函数 函数是要执行的
return self.apply(t,args)//这里的args已经为20,30,40 因为上面做了shift的操作会改变原数组
}
}


let i a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)

}

如果是上述代码 无论点哪个标签都是显示10,因为此时i是全局作用域,只有当点击的时候才会去执行回调函数,而回调函数的alert(i)是一个自由变量所以会去全局找(循环都结束了),因为都是10 (js引擎在退出循环的时候,迭代变量i保存的是导致循环退出的值)
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {//不会立马执行
e.preventDefault()
alert(i)
})
document.body.appendChild(a)

}
如果是上述代码就可以避免这个问题,因为此时i为块级作用域,(js引擎在后台会为每个迭代循环声明一个新的迭代变量),自由变量i就会在块中去找,因此就可以实现点击什么显示什么。

14、异步和单线程

JS本质是单线程的。也就是说,它并不能像JAVA语言那样,两个线程并发执行。但我们平时看到的JS,分明是可以同时运作很多任务的,这又是怎么回事呢?

首先,JS的代码,大致分为两类,同步代码异步代码。JS引擎的主线程负责执行代码,由于只有这一个线程,执行当然是同步的,即按照顺序来。另外,还有一个叫做任务队列的东西,所有的异步代码都是从队列当中来。

所以实际上我们会发现,JS根本不可能同时执行两个任务,本质上还是单线程。

因为这些任务的发生都不是在当下,而是过段时间以后再执行。因此时间不可控,你不能因为5秒后要执行一个函数,就让主线程闲置5秒什么都不干吧?所以你只能继续执行后续的同步代码。而当你单击鼠标或滚动窗口时,主线程可能正在执行其它代码,忙着呢!没工夫处理,因此,事件触发线程就负责来接收这个事件,并把要执行的任务暂时保存在任务队列当中。等主线程把手里的同步代码执行完,就立刻会向任务队列提取最新的任务。

单线程和异步

  • 遇到等待(网络请求,定时任务)不能卡住
  • 需要异步
  • 回调callback函数形式

image-20211211093254048

异步打印顺序100 300 200 异步不会阻塞 后面代码的执行300会立马出来。

同步 弹出200会卡主 不点确定300不会出来

  • 基于JS是单线程语言
  • 异步不会阻塞代码执行
  • 同步会阻塞代码执行

应用场景

  • 网络请求,如ajax图片加载
  • 定时任务,如setTimeout
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
手写Promise加载一张图片 代码演示
const url = 'http...'

function loadImg(src){
const p = new Promise( //Promise传入一个函数 函数有两个参数
(resolve,reject)=>{
const img = document.createElement('img')
img.onload=()=>{
resolve(img)
}
img.onerror=()=>{
reject(new Error(`图片加载失败${src}`))//或者 const err = new Error()/ reject(err)
}
img.src =src
}
)
return p
}
loadImg(url).then(接收一个函数函数就是上面resolve里面的对象) //Promise对象就有thenloadImg(url).then(img=>{
console.log(img.width)
return img
}).then(img=>{
console.log(img.height)
}).catch(ex=>console.error(ex))

情况2 如果有两张图
thenloadImg(url).then(img=>{
console.log(img.width)
return img //普通对象 **如果是普通对象下一个then函数里参数就会接收到返回的对象
}).then(img=>{
console.log(img.height)
return loadImg(url2)//promise实例**如果是promise实例的话下一个then函数里的参数是loadImg(u2)加载完成之后的图片对象
}).then(img2=>{
console.log(img2.width)
return img2
})
这就是利用promise解决回调地狱的问题

小结

  • 单线程和异步,异步和同步区别
    • 基于JS是单线程语言
    • 异步不会阻塞代码执行
    • 同步会阻塞代码执行
  • 前端异步的应用场景:
    • 网络请求
    • 定时任务
  • Promise解决callback hell

15、JS Web API(DOM)

  • DOM
  • BOM
  • 事件绑定
  • ajax
  • 存储

vue 和React框架应用广泛,封装了DOM操作
但DOM操作一直都会前端工程师的基础、必备知识
只会vue而不懂 DOM操作的前端程序员,不会长久

不要被工具和框架限制了自己的能力

1
2
3
4
5
6
7
8
9
10
11
12
1、DOM的本质是从HTML文件解译出的一棵树(一层一层)

2、DOM是哪种数据结构
是树(DOM树)结构
3、DOM操作的常用API
* DOM节点的操作
* DOM结构操作
* attribute和property的操作
4、attr和property 的区别
* property:修改对象属性,不会体现到 html结构中(尽量用这个)
* attribute:修改html 属性,会改变html结构
* 两者都有可能引起 DOM重新渲染

image-20211211102904678

image-20211211102949224

property本身不是API的一部分,是一种JS属性操作的形式

image-20211211103301872

设置attribute会将设置的属性嵌到dom结构里的(修改标签的属性)

而property修改的是JS变量的一个属性(不会对标签产生任何的影响)

image-20211211103431882

property和attribute

  • property:修改对象属性,不会体现到 html结构中(尽量用这个)
  • attribute:修改html 属性,会改变html结构
  • 两者都有可能引起 DOM重新渲染

DOM结构操作

  • 新增/插入节点
  • 获取子元素列表,获取父元素
  • 删除子元素

image-20211211104105539

移动节点就是原本div1没有p2的 设置了此操作div1就有了p2

image-20211211104241164

获取子元素列表有时候不是我们想要的 因为它会将文本元素一块打印出来

1
2
3
4
5
6
7
8
9
10
11
12
//首先先将divchild变成一个数组再进行遍历
//Array.prototype.slice.call(div1.childNodes)就是将返回的结果变成一个数组
const divchild = Array.prototype.slice.call(div1.childNodes).filter(child=>{
if(child.nodeType===1){
return true
}
return false
})
console.log('divchild',divchild)//结果就是dom元素 3个p标签/就不是text文本

**filter():方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素;不会改变原始数组;
return后面判断结果,取布尔值,true的话就添入新的filter数组中,false的话,不会添进filter的数组中。**

image-20211211105346903

DOM性能

  • DOM操作非常“昂贵”,避免频繁的DOM操作(占用CPU多 )
  • 对DOM查询做缓存
  • 将频繁操作改为一次性操作

image-20211211110819646

1
2
3
4
5
6
7
8
const list = document.getElementById('list')

for(let i=0;i<10;i++){
const li = document.createElement('li')
li.innerHTML = `LIST Item ${i}`
list.appendChild(li)
}
这样就是频繁操作DOM,耗费性能

image-20211211112538924

16、BOM操作(Browser Object Model)

image-20211211143716541

image-20211211143728545

17、事件

1
2
3
1、编写一个通用的事件监听函数
2、描述事件冒泡的流程
3、无限下拉的图片列表,如何监听每个图片的点击?

事件绑定

image-20211211143946225

image-20211211144103348

elem:绑定的按钮id

事件冒泡

image-20211211145246199

stopPropagation组织冒泡 没有这个点击激活的时候由于会冒泡到body就会触发body从而打印取消

事件代理(是基于事件冒泡来做的)

image-20211211145459660

绑定在父元素上 代码简介 (比每一个a标签都绑定一个元素)

减少浏览器的内存占用 (每一个a标签都去挂载事件监听,耗内存)

禁止滥用

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
function bindEvent(elem,type,selector,fn){
if(fn == null) { //传递三个参数和四个参数的对比
fn = selector
selector = null
}

elem.addEventListener(type,event => { //整体对照下面的例子进行观察
const target = event.target//触发的元素
if (selector) { //这里的selector就是a
//代理绑定
if(target.matches(selector)){ //target.matches一个DOM元素是否符合CSS选择器
fn.call(target,event)
}
}else {
//普通绑定
fn.call(target,event)//这里的target就是this的意思
}
})
}

//普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click' , event =>{
//console.log (event.target)//获取触发的元素
event.preventDefault()//阻止默认行为
alert(this.innerHTML)
})
//代理绑定
const div3 = document.getElementById ('div3')
bindEvent(div3, 'click ','a', event =>{
event.preventDefault()
alert(this.innerHTML)
})

const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click' , function(event ){
//console.log (event.target)//获取触发的元素
event.preventDefault()//阻止默认行为
alert(this.innerHTML)
})
//代理绑定
const div3 = document.getElementById ('div3')
bindEvent(div3, 'click ','a', function(event){
event.preventDefault()
alert(this.innerHTML)
})

image-20211211153208756

下图就可以验证target.matches(selector) 其中代理绑定的是a标签不是button标签,因此就可以判断是否符合css选择器

上述代码有错误,因为箭头函数没有this,会向上级作用域查找,为window,而window.innerHTML就不符合要求了

因此得将箭头函数转换为普通函数

描述事件冒泡的流程

  • 基于 DOM 树形结构
  • 事件会顺着触发元素向上冒泡
  • 应用场景:代理(是基于事件冒泡机制才能使用的)

无限下拉图片列表,如何监听每个图片的点击

  • 事件代理
  • 用e.target获取触发元素
  • 用matches来判断是否是触发元素

18、跨域

1
2
3
4
 同源策略
**ajax请求时,浏览器要求当前网页和server 必须同源(安全)
**同源︰协议、域名、端口,三者必须
前端:http://a.com:8080/; server:https://b.com/api/xxx 三者都不相同

image-20211211154807380

加载图片css js可无视同源策略

1
2
3
4
5
6
1、<img src=跨域的图片地址/>
2、<link href=跨域的css地址/>
3、<script src=跨域的js地址></script>
4、<img/>可用于统计打点,可使用第三方统计服务
5、<link /> <script>可使用CDN ,CDN一般都是外域
6、<script>可实现JSONP

跨域

  • 所有的跨域,都必须经过server端允许和配合
  • 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号

JSONP
访问https://imooc.com/,服务端定返回一个html文件吗?

不一定,服务器可以任意动态拼接数据返回,只要符合html格式要求

1
2
3
4
1、<script>可绕过跨域限制
2、服务器可以任意动态拼接数据返回
3、所以,<script>就可以获得跨域的数据,只要服务端愿意返回
(跨域必须经过服务端的允许和配合,服务端如果愿意给你返回一个拼接好的数据的script的JSONP文件的话,这样就可以通过<script>可绕过跨域限制,从而获得跨域的数据)

img

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
function ajax(url){

const p = new Promise((resolve,reject) =>{
const xhr = new XMLHttpRequest()
xhr.open ( 'GET', '/data/test.json' , true)
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status === 200){
resolve(
JSON. parse(xhr.responseText)
)
}else if (xhr.status===404){
reject(new Error(' 404 not found '))
}
}
}
xhr.send(null)
})
return p
}

const url = '/data/test.json'
ajax(url)
.then(res =>console.log(res))
.catch(err =>console.error(err))

ajax知识点

  • XMLHttpRequest
  • 状态码:readyState status
  • 跨域︰同源策略(如何绕过),JSON,CORS

19、存储

1、描述cookie localStorage sessionStorage区别

容量 cookie 4kb 另外一个5M
API 易用性
是否跟随http请求发送出去

cookie

  • 本身用于浏览器和server通讯(本身是http请求的一部分)

  • 被“借用”到本地存储来( localStorage sessionStorage这是html5之后才提出来的,所以之前都是拿cookie做本地存储)

  • 可用document.cookie= ‘..’来修改(前端修改cookie的方式)=>赋值同名覆盖 不同名叠加

  • image-20211211163645518

    注意:cookie的设计并不是用来本地存储,它是为了浏览器和客服端通讯,只是被借用到本地存储来

cookie的缺点

  • 存储大小,最大4KB(限制)
  • http请求时需要发送到服务端,增加请求数据量
  • 只能用document.cookie = ‘.…’来修改,太过简陋

localStorage和sessionStorage

  • HTML5专门为存储而设计,最大可存5M(已经很大了)
  • API简单易用 =>setItem getItem
  • 不会随着http请求被发送出去

image-20211211163925702

localStorage和 sessionStorage

  • localStorage数据会永久存储,除非代码或手动删除
  • sessionStorage 数据只存在于当前会话,浏览器关闭则清空
  • 一般用localStorage会更多一些

20、防抖

防抖 debounce

  • 监听一个输入框的,文字变化后触发chaange事件
  • 直接用keyup 事件,则会频发触发change事件
  • 防抖∶用户输入结束或暂停时,才会触发change事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function debounce(fn, delay = 500){

let timer = null

return function(){.
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this, arguments)
timer = null
}, delay)
}
}
input1.addEventListener('keyup',debounce(function(){
console.log (input1.value)
}),600)

对照下面这个

image-20211211172257947

节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
节流throttle
1、拖拽一个元素时,要随时拿到该元素被拖拽的位置
2、直接用drag事件,则会频发触发,很容易导致卡顿
3、节流:无论拖拽速度多快,都会每隔100ms触发一次

function throttle(fn, delay = 100){

let timer = null

return function(){.
if (timer) {
return
}
timer = setTimeout(()=>{
fn.apply(this, arguments)
timer = null
}, delay)
}
}
input1.addEventListener('drag',throttle(function(e){//这里的e是传给throttle返回的函数 而function包裹的函数没有e
// 由于有fn.apply(this, arguments) 有这个fn是function这个函数 其中arguments有event
console.log (e.offsetX,e.offsetY)
}),200)

image-20211211173501159

关于简历

1
2
3
4
5
简洁明了,突出个人技能和项目经验(技术栈)
可以把个人博客,开源作品放在简历里
不要造假,保证能力上的真实性(斟酌用词,不要使用精通, 了解熟练即可)

谈谈自己的缺点

React框架

1、事件

  1. bind this:修改方法的this指向。使用箭头函数this就指向当前实例,就不用bind(this)

  2. 关于event 参数

    • event. preventDefault()//阻止默认行为

    • event. stopPropagation() //阻止冒泡

    • event.target 指向当前元素,即当前元素触发

    • event.nativeEvent.target 指向当前元素 即当前元素触发

    • event.nativeEvent.currentTarget 绑定是绑定在document上**

  • event是SyntheticEvent事件,模拟出来DOM事件所有能力

  • event.nativeEvent是原生事件对象

  • 所有的事件,都被挂载到document上**

  • 和DOM事件不一样,和Vue事件也不一样

  1. 传递自定义参数

2、setState

  1. 不可变值(不能直接修改state)

    • concat,slice,filter 这些东西不会改变原来的list值。而push pop splice会改变不可用
    • 或者可以通过this.state.list.slice() 不传入值就相当于给list做了一个副本,这样就可以使用push,pop这种API,原本的list不变
    • 扩展对象可以用解构赋值 {…this.state.list,a:100}
  2. 可能是异步更新

    image-20211212205326882

    先打印再累加 想要获取最新的值就在setState传入第二个参数,第二个参数的值是一个函数

    但是在setTimeout中setState是同步的 自己定义的DOM事件,setState是同步的

    image-20211212205931478

    标准写法应该这样

    image-20211212211952584

    要销毁这个自定义的DOM事件

  3. 可能会被合并(仅限对象才会被合并,函数不会被合并)

image-20211212215230884

3、组件生命周期

image-20211212215915750

4、高级特性

  1. 函数组件

    image-20211212220821520

  2. 非受控组件

    image-20211212221502666

    建议写受控组件 因为受控没有ref 因为官网尽量不要写ref

  3. Portals

    image-20211212221609897

    Portals使用场景
    overflow: hidden 父元素设置了BFC影响了子元素的展示,可以让子元素逃离父元素之外展示
    父组件z-index值太小:同理
    fixed需要放在body第一层级:同理

    image-20211212222543204

  4. context

    image-20211212222646168

    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
    1) 创建Context容器对象:
    const XxxContext = React.createContext()

    2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
    <xxxContext.Provider value={数据}>
    子组件
    </xxxContext.Provider>

    <Provider value={{username,age}}>
    <B/>
    </Provider> 这样包裹一下 B组件以及B的子组件都能用只不过需要声明接收

    3) 后代组件读取数据:

    //第一种方式:仅适用于类组件
    static contextType = xxxContext // 声明接收context
    this.context // 读取context中的value数据

    //第二种方式: 函数组件与类组件都可以 函数式没有this所以第一种不适合函数式组件
    <xxxContext.Consumer>
    {
    value => ( // value就是context中的value数据
    要显示的内容
    )
    }
    </xxxContext.Consumer>
    ```
    在应用开发中一般不用context, 一般都它封装react插件
  5. 异步组件

    性能优化:异步组件—>组件比较大,路由需要懒加载

    lazyLoad 懒加载 路由组件最常用 就是避免100个组件去请求100次 最好的就是点哪个就请求哪个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
    const Login = lazy(()=>import('@/pages/Login')) //现点现加载

    //2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
    <Suspense fallback={<h1>loading.....</h1>}>
    <Switch>
    <Route path="/xxx" component={Xxxx}/>
    <Redirect to="/login"/>
    </Switch>
    </Suspense> //这就是如果网络慢慢 迟迟不回来 要fallback指定一个组件component={Xxxx}显示
    //Network选项卡 点谁加载谁 把网络故意调慢 点了About加载 会显示Loading
    //<h1>loading.....</h1>如果Loading要写成一个组件就另外写一个文件就可以 但是引入的时候不要懒加载
  6. 性能组件(永远是面试的重点)

    性能优化对于React更加重要

    image-20211213121121649

    image-20211213121203921

    判断下一个状态的数据和前一个数据是否相同,不相同可以渲染,相同就不可以渲染。优化性能

    背后的逻辑是什么? SCU默认会返回true,但是有返回false的权利

    SCU一定要每次都用嘛?不一定,性能优化需要的时候用。需要的时候才优化

    SCU使用总结**

    • SCU 默认返回true ,即React默认重新渲染所有子组件

    • 必须配合“不可变值”一起使用

    • 可先不用SCU,有性能问题时再考虑使用

      PureComponent 和memo

    • PureComponent , SCU默认实现了浅比较

    • memo ,函数组件中的PureComponent

    • 浅比较已使用大部分情况(尽量不要做深度比较)

    immutable.js

    • 彻底拥抱“不可变质”
    • 基于共享数据(不是深拷贝),速度好
    • 有一定学习和迁移成本,按需使用
  7. 高阶组件HOC

    关于组件公共逻辑的抽离

    • mixin , 已被React 弃用
    • 高阶组件HOC
    • Render Props
    • connect也是高阶组件

    image-20211213143101772

    接收一个组件,返回的是一个由函数拼接而成的组件

    image-20211213144047471

    image-20211213144120493

    image-20211213144230500

  8. Render Props(比HOC更容易理解)

image-20211213145106982

注意:暴露的是什么,组件就传什么,比如下图,暴露的是APP 在入口文件APP相当于子组件所以直接在APP输入props即可

image-20211213145353719

image-20211213145442204

image-20211213145533126

image-20211213145754832

5、Redux

image-20211213150838083

redux怎么去获取异步action

单向数据流的图

image-20211213152256044

6、React原理

函数式编程

1
纯函数,不可变值

vdom和diff

1
diff比较同级,不跨级比较

jsx本质

1
2
3
4
5
6
7
8
9
1、是一个 React.createElement 函数,他接收多个参数,执行返回vnode,vnode通过vdom的patch或者其他方法渲染页面
//调用 React 的 createElement API
2、不是模板引擎,而是语法糖
3、第一个参数,可能是组件(大写),也可能是html 标签名(小写)
4、第二个参数是属性信息,如果没有属性则为 null
第三个参数是子元素;
如果拥有多个子元素,可以依次放在第三个、第四个...
也可以用在数组中存放多个子元素
4、组件名,首字母必须大写( React规定)

合成事件

1
2
3
4
5
6
7
1、所有事件挂载到document
2、event 不是原生的,是 SyntheticEvent合成事件对象
3、和Vue 事件不同,和DOM事件也不同
为何要合成事件机制?
1、更好的兼容性和跨平台
2、载到document ,减少内存消耗,避免频繁解绑
3、方便事件的统一管理(如事务机制)

setState 和 batchUpdata

1
2
3
4
5
6
7
8
9
10
11
12
13
1、setState无所谓异步还是同步
2、看是否能命中batchUpdate机制
3、判断isBatchingUpdates

哪些能命中batchUpdate机制
1、生命周期(和它调用的函数)
2、React 中注册的事件(和它调用的函数)
3、React可以“管理”的入口

哪些不能命中batchUpdate机制
1、setTimeout setInterval 等(和它调用的函数)
2、自定义的DOM事件(和它调用的函数)
3、React“管不到”的入口

image-20211214123112436

Y就是异步的State N 就是同步的State

image-20211214123342209

image-20211214122922788

执行函数前处于batchUpdate 且isBatchingUpdates=ture。执行函数完之后isBatchingUpdates=false。

因为settimeout()这个函数式异步的就直接执行isBatchingUpdates=false。了 之后进行this.set的时候isBatchingUpdates已经是false了。所以判断setState是异步还是同步的时候,就看isBatchUpdates是true还是false 且设置这个是在入口中设置的。

组件之间如何通讯

1
2
3
1、父子组件props
2、自定义事件
3、Redux和Context

Context是什么,如何应用

1
2
3
父组件,向其下所有子孙组件传递信息
如一些简单的公共信息,主题色,语言等。
复杂的公共信息,用redux

shouldComponentUpdate用途

1
2
性能优化
配合不可变值一起使用,否则会出错

image-20211214163434174

两次异步操作合并成一次所以只加1,异步操作玩之后执行同步

纯函数

1
2
3
返回一个新值,没有副作用(不会偷偷修改其他值)
重点:不可变值
如arr1=arr.slice()

函数组件和class组件的区别

1
2
3
纯函数,输入props,输出JSX
没有实例,没有生命周期,没有state
不能扩展其他方法

什么是受控组件

1
2
表单的值,收state 控制
需要自行监听onChange ,更新state

何时使用异步组件

1
2
加载大组件
路由懒加载

多个组件有公共逻辑,如何抽离

1
2
高阶组件
Render Props

Redux如何进行异步请求

1
2
使用异步action
如redux-thunk

PureComponent 有何区别

1
2
3
实现了浅比较的shouldComponentUpdate
优化性能
但要结合不可变值使用

React性能优化

1
2
3
4
5
6
7
8
9
渲染列表时加key
自定义事件、DOM事件及时销毁
合理使用异步组件
减少函数bind this的次数
合理使用SCU PureComponent和memo
合理使用Immutable.js
webpack层面的优化(后面会讲)
前端通用的性能优化,如图片懒加载
使用SSR

React和Vue的区别

1
2
3
4
5
6
7
都支持组件化
都是数据驱动视图
都使用vdom 操作 DOM

React使用JSX拥抱JS,Vue使用模板拥抱html
React函数式编程,Vue声明式编程
React 更多需要自力更生,Vue把想要的都给你

7、webpack

webpack介绍

1
2
3
4
5
6
7
8
9
10
webpack已经是前端打包构建的不二选择
每日必用,面试必考
成熟的工具,重点在于配置和使用,原理并不高优

前端代码为何要进行构建和打包
module chunk bundle 分别什么意思,有何区别?
loader和plugin的区别?
webpack如何实现懒加载?
webpack常见性能优化
babel-runtime和 babel-polyfill的区别

webpack基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
vue-cli create-react-app  **cli工程师
常用上述脚手架,而不会自己配置webpaclk ?
则面试不会通过

拆分配置和merge (公共的配置common 生产环境下的配置,开发环境下的配置)

跨域问题:开发环境下还会定义一个devserver 里可以通过设置代理proxy将本地/api/XXX代理到 localhost:3000/api/xxx

loader的执行顺序是从后往前
postcss-loader 浏览器的兼容性的
对css文件:先postcss-loader 浏览器的兼容性的一些东西做了再变成css插入到style中
对less文件,先通过less解析语法成css然后插入style中
图片大小小于5kb用base64格式产出否则用file-loader的形式,产出url格式

webpack高级配置

1
2
3
4
5
基本配置只能做demo,不能做线上项目
多入口:entry要写两个。
output要通过[name]的变量去操作
plugins要:每一个入口都要创建一个新的HtmlWebpackPlugin的实例(new
chunks:会帮你只引入什么名字的js文件 不写chunk会把html文件一块引入。

image-20211214193227860

抽离css文件

1
2
3
开发环境下用的是通过css-loader转换成出css文件插入到style-loader里面去的
生成环境下需要用MiniCssExtractPlugin.loader,通过这种方式就不再是塞到style-loader里面去了
单独拧出来,(在plugins里面加了一个抽离css文件的plugins的配置)还是用new MiniCssExtractPlugin标志一下抽离出的css文件是什么。抽离完还要压缩(optimization配置里面)抽离出来就可以在index.html引入抽离的css文件了

image-20211214195006905

抽离公共代码

1
2
3
4
5
6
7
抽离公共部分相互引用,减少加载和执行的次数
开发环境下没必要做。

第三方模块复用一次的原因是是避免细微的改动重新加载这个模块,影响加载速度,耗费性能。
test 是模块来自
大小限制最好写3kb 5kB这样 name就是代码分割产出的chunk
多入口plugins里面引用new HtmlWebpackPlugin下也要考虑代码分割的chunk

image-20211214200446809

懒加载

1
2
引入动态数据->懒加载
异步代码也会产出一个chunk 文件名不是自己规定的

image-20211214205110387

moudle chunk bundle的区别

1
2
3
4
5
module -各个源码文件,webpack 中一切皆模块 只要是引入的就是模块
chunk -多模块合并成的,如entry(一个入口就可以生成一个至多个chunk,因为入口定义的文件也包括其他文件一块引进来)
import() splitChunk 拆分代码也可以定义chunk 懒加载引入动态数据也是一个chunk
可以理解成内存中的一个概念,还没整理的输出
bundle -最终的输出文件(一个chunk对应一个bundle)bundle可能是很多个文件不止一个文件

webpack性能优化

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
1、优化打包构建速度-开发体验和效率
优化 babel-loader - 缓存 加一个cacheDirectory参数,加上之后只要ES6代码没有改,就不会重新编译,缓存一下。
- 有include和exclude两者选一个去明确打包范围

happyPack -多进程打包工具 -JS单线程,开启多进程打包
-提高构建速度(特别是多核CPU )
-把对.js文件的处理转交给id为babel的HappyPack实例 图3

IgnorePlugin -避免引入无用模块 -例如moment.js这个日期处理的库 加载时间的库,200多kb 就是因为把语言的库引进来了,可以通 -过引入这个插件来避免引入这个模块,至于语言的模块就自己手动引入 -还能优化产出的体积 图1

ParallelUglifyPlugin -开启多进程进行代码压缩JS JS单线程,开启多进程压缩更快和HappyPack原理类似 图四压缩就是直接输结果

noParse -避免重复打包 像一些min.js文件基本都是采用模块化处理过了,不用再重新打包 在moudule文件下加一下就好了如下图2

自动刷新 -一保存代码编译完之后浏览器自动刷新 开发环境用(devServer)默认开启会用

热更新 -自动刷新的升级版,改完代码之后浏览器不要刷新,代码就生效,体验更好
自动刷新:整个网页全部刷新,速度较慢
自动刷新:整个网页全部刷新,状态会丢失 包括你点击的路由 自动刷新玩就会回到首页和你输入的状态下次就不见
热更新:新代码生效,网页不刷新,状态不丢失
引入插件,new实例,devServer设置hot为true
如果是改js文件的话就会导致自动刷新,原因是因为某些模块没有开启热更新的监听 图4
自动刷新会影响你开发的体验再去开启热更新

DllPlugin-针对大的库和大的第三方插件没必要每一次都把所有打包一遍,我们可以实现把第三方库打包好之后引用 *动态链接库插件
- 前端框架如vue React,体积大,构建慢
- 较稳定,不常升级版本
- 同一个版本只构建一次即可,不用每次都重新构建
webpack 已内置 DllPlugin支持
DllPlugin插件 -先把react进行预打包出dll文件(webpack.dull.js) 图5 产出两个文件 一个是加载了打包所有关于入口 - react-react-dom的所有文件,另一个是索引
DlIReferencePlugin插件 -使用dll文件。使用就不要重新打包
使用首先现在入口文件的index.html引入这个src = ‘./react.dull.js’这个文件 然后new实例 图6

webpack优化构建速度(可用于生产环境) 不可用生产环境
优化 babel-loader 自动刷新
happyPack 热更新
IgnorePlugin DllPlugin
ParallelUglifyPlugin *必须用
noParse


**总结**
IgnorePlugin直接不引入,代码中没有
noParse 引入,但不打包
项目较大,打包较慢,开启多进程能提高速度
项目较小,打包很快,开启多进程会降低速度(进程开销)
按需使用

**ES6 Module和Commonjs区别**
ES6 Module静态引入,编译时引入 必须放在最上面 否则报错 图7
Commonjs动态引入,执行时引入,有可能需要有可能不需要 (图7 里面的if就不知道是否会被执行)
只有ES6 Module 才能静态分析,实现Tree-Shaking(webpack打包的时候执行,打包的时候代码还没执行)
webpack只是一个静态分析,静态构建,编译,代码还没正式在线上被用户去运行


2、优化产出代码-产品性能 **更重要
体积更小
合理分包,不重复加载
速度更快,内存使用更少
****每一个都是优化
小图片base64编码
bundle加hash(打包出的bunlde代码时加上hash)
懒加载
提前公共代码->做一个公共的包,不需要重复打包公共的模块
IgnorePlugin 还能优化产出的体积
使用CDN加速: 打包出的html 引入的文件前缀都加了cdn 图7 这仅仅是第一步还需要将打包的文件上传到cdn的网址
使用mode:production:去打包生成环境下的代码 自动开启代码压缩 Vue React 等会自动删掉调试代码(如开发环境的warning )
自动开启Tree-shaking=>对未使用的代码删除 只要mode为production即可
ES6 Module才能让tree-shaking生效commonjs就不行


Scope Hosting 将两个文件合并,多个函数放在一个函数里,使得作用域少 使得内存占用少一些,代码体积更小 图8

image-20211214213424143

image-20211214213751310

image-20211214214448414

image-20211214214856230

image-20211214215938574

image-20211214221035644

image-20211214221515068

image-20211214222435551

image-20211214223716971

image-20211214224034222

babel

1
2
3
4
5
6
7
8
9
10
11
12
13
前端开发环境必备工具 同webpack,需要了解基本的配置和使用
环境搭建&基本配置
babel-demo npx babel src/index.js 这样就可以通过babel编译index,js文件
babel其实就是通过plugin将ES6语法转换为ES5

babel-polyfill(po里),core-js标准库集成了所有ES6 ES7新语法的polyfill的补丁(兼容性)但是这个库对ES6的generator函数(处理异步),被async/await代替不支持, 但是regenerator这个库就支持了。 babel-polyfill就是这两个库的集合
Babel 7.4之后弃用babel
推荐直接使用core-js和regenerator
问题:会污染全局环境 如果是自己独立开发可以用



babel-runtime:不会污染全局环境。产出第三方lib就要用这个,否则会污染全局环境

8、 面试真题演练

1.前端为何要进行打包和构建

1
2
3
4
5
6
7
8
代码层面
体积更小(Tree-Shaking、压缩、合并),加载更快
开发而言:编译高级语言或语法(TS,ES6+,模块化,scss )
兼容性和错误检查(Polyfill,postcss,eslint)
研发层面(前端工程化)
统一、高效的开发环境
统一的构建流程和产出标准
集成公司构建规范(提测、上线等)

loader和plugin区别

1
2
3
loader模块转换器,如less >css
plugin扩展插件,如HtmlWebpackPlugin
常用的loader和plugin有什么? 之前讲过的能掌握即可

babel和 webpack的区别

1
2
babel - JS新语法编译工具,不关心模块化
webpack-打包构建工具,是多个loader、plugin 的集合

如何产出一个lib

1
参考webpack.dll.js里面有一个output.library

image-20211215192019355

webpack如何实现懒加载

1
2
3
import语法
结合Vue React异步组件
结合React-router异步加载路由

为何Proxy不能被Polyfill

1
Proxy 的功能用 Object.defineProperty 无法模拟

组件和状态设计

1
2
3
4
5
6
todoList:
1、用数据描述内容
2、结构化
3、可扩展性
4、功能上拆分层次
5、容器组件(只管理数据)、UI(只显示视图)

9、项目流程

PM(项目管理)想在项目开发过程中增加需求,该怎么办

1
2
3
不能拒绝,走需求变更流程即可
公司如果有规定,按规定走
否则,发起项目组和leader的评审,重新评估排期

项目即将延期了,该怎么办

1
2
3
4
项目沟通
多人协作,沟通是最重要的事情
每日一沟通(如站会),有事说事,无事报平安
及时识别风险,及时汇报

你将如何保证项目质量

1
2
3
4
5
符合开发规范
写出开发文档
及时单元测试
Mock API
Code Review (让大佬帮你浏览一遍)
万一真有土豪呢!!!