面试题 HTML CSS 面试题 1、如何理解HTML 语义化?
让人更容易读懂(增加代码可读性)
让搜索引擎更容易读懂(SEO 搜索引擎优化)
2、默认情况下,哪些HTML 标签是块级元素、哪些是内联元素?
3、盒子模型的宽度如何计算?
4、argin 纵向重叠的问题
5、margin 负值的问题
对margin 的top left right bottom 设置负值,有何效果?
margin-top 和margin-left 负值,元素向上、向左移动
margin-right 负值,右侧元素左移,自身不受影响
margin-bottom 负值,下方元素上移,自身不受影响
6.float 布局
如何实现圣杯布局和双飞翼布局? 手写clearfix
圣杯布局和双飞翼布局的目的:
三栏布局,中间一栏最先加载和渲染(内容最重要)
两侧内容固定,中间内容随着宽度自适应
一般用于PC网页
圣杯布局例子:
三者都设置浮动且center宽度设置为百分百加上容器本身设置内边距
使得right的宽度无法在left的右侧排列被挤下来了
若要使得left移动至上图样式 设置margin-left:-100% ,设置这个实则就是父元素宽度的百分之百。也就是center(container)的宽度
(可以看成浮动元素它是别挤下来的 实际上是紧靠灰色部分的右侧。所以设置了这个就到了上图的位置)
之后在设置相对定位 (right:200px即可)因为设置了相对定位是相对于自身移动,对其他元素没有影响。(可以看到定位的坐标为灰色的左上顶点处)
给margin-right设置负150即可成为下图
注意:不好理解!!! 就是给自身元素设置的margin-right:自身宽度。就会使自身宽度不占位置从而上移
上述的布局就是圣杯布局 圣杯布局两边留白是通过padding设置的 而双飞翼布局是通过margin来进行两边留白的
双飞翼布局:
双飞翼布局是通过margin来进行两边留白。
left直接设置margin-left:-100%即可。
right:也设置margin-left:-190px。
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: 200 px; height: 200 px; border: 2px solid #ccc; border-radius: 10 px; padding: 20 px; display: flex; justify-content: space-between; } .item { display: block; width: 40 px; height: 40 px; 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>
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 如何继承
写具体数值,如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: 100 px; } **任何使用长度的地方都可以用rem 例如宽度高度都可以 响应式布局的常见方案? media-query,根据不同的屏幕宽度设置根元素font-size body { font-size: 0.16 rem; } rem,基于根元素的相对单位
JavaScript面试题 1、typeof 能判断的类型?
考点:JS变量类型
2、何时使用===何时使用==
考点:强制类型转换
3、window.onload和 DOMContentLoaded区别
考点:页面加载过程,(页面渲染过程)
考点: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 = aa=200 log(b) **引用数据类型 let a={age :20 }let b=ab.age=21 log(a.age)
8、typeof 运算符
识别所有值类型
识别函数
判断是否是引用类型(不可再细分)
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 = deepClone(obj1) obj2.address.city = 'shanghai' console .log(obj1.address.city) function deepClone (obj={} ) { if (typeof obj !== 'object' ||obj ==null ) { return obj ***************** } let result if (obj instanceof Array ) { result = [] }else { result = {} } for ( let key in obj){ if (obj.hasOwnProperty(key)) { result[key] = deepclone(obj[key]) } } return result } ***let result = { age: 20 , name: 'xx×' , address: { city: 'beijing' }, arr:[ 'a' ,'b' , 'c' ] }
10、变量计算-类型转换
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
继承
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) 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()
1 2 1 、如何准确判断一个变量是不是数组? a instanceof Array
12、作用域和闭包 1 2 3 1 、this 的不同应用场景,如何取值?2 、手写bind 函数3 、实际开发中闭包的应用场景,举例说明
作用域
if for {} 就是块级作用域
自由变量
闭包
作用域应用的特殊情况,有两种表现:
答案为100
答案为100
闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找。不是在执行的地方!!!
13、this
应用场景
作为普通函数被调用 ->window
使用call apply bind- >传入什么就是绑定什么
作为对象方法被调用 ->返回对象本身
在class方法中调用 ->当前实例本身
箭头函数 ->上级作用域的this的值来决定
注意:this取什么值,是在函数执行的时候确认的,不是在函数定义的时候确认的
bind和call的区别是 bind会返回一个新的函数再去执行
this===window是因为function这个函数执行本身是setTimeout触发的执行,它并不是zhangsan.方法触发的执行。
特点:箭头函数本身没有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) Function .prototype.bind1=function ( ) { const args = Array .prototypes.slice.call(arguments ) const t =args.shift() const self = this return function ( ) { return self.apply(t,args) } } 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函数形式
异步打印顺序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 ( (resolve,reject)=>{ const img = document .createElement('img' ) img.onload=() => { resolve(img) } img.onerror=() => { reject(new Error (`图片加载失败${src} ` )) } img.src =src } ) return p } loadImg(url).then(接收一个函数函数就是上面resolve里面的对象) 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(img => { console .log(img.height) return loadImg(url2) }).then(img2 => { console .log(img2.width) return img2 }) 这就是利用promise解决回调地狱的问题
小结
单线程和异步,异步和同步区别
基于JS是单线程语言
异步不会阻塞代码执行
同步会阻塞代码执行
前端异步的应用场景:
Promise解决callback hell
15、JS Web API(DOM)
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重新渲染
property本身不是API的一部分,是一种JS属性操作的形式
设置attribute会将设置的属性嵌到dom结构里的(修改标签的属性)
而property修改的是JS变量的一个属性(不会对标签产生任何的影响)
property和attribute
property:修改对象属性,不会体现到 html结构中(尽量用这个)
attribute:修改html 属性,会改变html结构
两者都有可能引起 DOM重新渲染
DOM结构操作
新增/插入节点
获取子元素列表,获取父元素
删除子元素
移动节点就是原本div1没有p2的 设置了此操作div1就有了p2
获取子元素列表有时候不是我们想要的 因为它会将文本元素一块打印出来
1 2 3 4 5 6 7 8 9 10 11 12 const divchild = Array .prototype.slice.call(div1.childNodes).filter(child => { if (child.nodeType===1 ){ return true } return false }) console .log('divchild' ,divchild)**filter():方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素;不会改变原始数组; return 后面判断结果,取布尔值,true 的话就添入新的filter数组中,false 的话,不会添进filter的数组中。**
DOM性能
DOM操作非常“昂贵”,避免频繁的DOM操作(占用CPU多 )
对DOM查询做缓存
将频繁操作改为一次性操作
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,耗费性能
16、BOM操作(Browser Object Model)
17、事件 1 2 3 1 、编写一个通用的事件监听函数2 、描述事件冒泡的流程3 、无限下拉的图片列表,如何监听每个图片的点击?
事件绑定
elem:绑定的按钮id
事件冒泡
stopPropagation组织冒泡 没有这个点击激活的时候由于会冒泡到body就会触发body从而打印取消
事件代理(是基于事件冒泡来做的)
绑定在父元素上 代码简介 (比每一个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) { if (target.matches(selector)){ fn.call(target,event) } }else { fn.call(target,event) } }) } const btn1 = document .getElementById('btn1' )bindEvent(btn1, 'click' , event =>{ 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 ) { event.preventDefault() alert(this .innerHTML) }) const div3 = document .getElementById ('div3' )bindEvent(div3, 'click ' ,'a' , function (event ) { event.preventDefault() alert(this .innerHTML) })
下图就可以验证target.matches(selector) 其中代理绑定的是a标签不是button标签,因此就可以判断是否符合css选择器
上述代码有错误,因为箭头函数没有this,会向上级作用域查找,为window,而window.innerHTML就不符合要求了
因此得将箭头函数转换为普通函数
描述事件冒泡的流程
基于 DOM 树形结构
事件会顺着触发元素向上冒泡
应用场景:代理(是基于事件冒泡机制才能使用的)
无限下拉图片列表,如何监听每个图片的点击
事件代理
用e.target获取触发元素
用matches来判断是否是触发元素
18、跨域 1 2 3 4 同源策略 **ajax请求时,浏览器要求当前网页和server 必须同源(安全) **同源︰协议、域名、端口,三者必须 前端:http:
加载图片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>可绕过跨域限制,从而获得跨域的数据)
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的方式)=>赋值同名覆盖 不同名叠加
注意 :cookie的设计并不是用来本地存储,它是为了浏览器和客服端通讯,只是被借用到本地存储来
cookie的缺点
存储大小,最大4KB(限制)
http请求时需要发送到服务端,增加请求数据量
只能用document.cookie = ‘.…’来修改,太过简陋
localStorage和sessionStorage
HTML5专门为存储而设计,最大可存5M(已经很大了)
API简单易用 =>setItem getItem
不会随着http请求被发送出去
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 )
对照下面这个
节流
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 、节流:无论拖拽速度多快,都会每隔100 ms触发一次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 ) { console .log (e.offsetX,e.offsetY) }),200 )
关于简历
1 2 3 4 5 简洁明了,突出个人技能和项目经验(技术栈) 可以把个人博客,开源作品放在简历里 不要造假,保证能力上的真实性(斟酌用词,不要使用精通, 了解熟练即可) 谈谈自己的缺点
React框架 1、事件
bind this:修改方法的this指向。使用箭头函数this就指向当前实例,就不用bind(this)
关于event 参数
event. preventDefault()//阻止默认行为
event. stopPropagation() //阻止冒泡
event.target 指向当前元素,即当前元素触发
event.nativeEvent.target 指向当前元素 即当前元素触发
event.nativeEvent.currentTarget 绑定是绑定在document上**
传递自定义参数
2、setState
不可变值(不能直接修改state)
concat,slice,filter 这些东西不会改变原来的list值。而push pop splice会改变不可用
或者可以通过this.state.list.slice() 不传入值就相当于给list做了一个副本,这样就可以使用push,pop这种API,原本的list不变
扩展对象可以用解构赋值 {…this.state.list,a:100}
可能是异步更新
先打印再累加 想要获取最新的值就在setState传入第二个参数,第二个参数的值是一个函数
但是在setTimeout中setState是同步的 自己定义的DOM事件,setState是同步的
标准写法应该这样
要销毁这个自定义的DOM事件
可能会被合并(仅限对象才会被合并,函数不会被合并)
3、组件生命周期
4、高级特性
函数组件
非受控组件
建议写受控组件 因为受控没有ref 因为官网尽量不要写ref
Portals
Portals使用场景 overflow: hidden 父元素设置了BFC影响了子元素的展示,可以让子元素逃离父元素之外展示 父组件z-index值太小:同理 fixed需要放在body第一层级:同理
context
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插件
异步组件
性能优化:异步组件—>组件比较大,路由需要懒加载
lazyLoad 懒加载 路由组件最常用 就是避免100个组件去请求100次 最好的就是点哪个就请求哪个
1 2 3 4 5 6 7 8 9 10 11 12 const Login = lazy(() =>import ('@/pages/Login' )) <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要写成一个组件就另外写一个文件就可以 但是引入的时候不要懒加载
性能组件(永远是面试的重点)
性能优化对于React更加重要
判断下一个状态的数据和前一个数据是否相同,不相同可以渲染,相同就不可以渲染。优化性能
背后的逻辑是什么? SCU默认会返回true,但是有返回false的权利
SCU一定要每次都用嘛?不一定,性能优化需要的时候用。需要的时候才优化
SCU使用总结**
SCU 默认返回true ,即React默认重新渲染所有子组件
必须配合“不可变值”一起使用
可先不用SCU,有性能问题时再考虑使用
PureComponent 和memo
PureComponent , SCU默认实现了浅比较
memo ,函数组件中的PureComponent
浅比较已使用大部分情况(尽量不要做深度比较)
immutable.js
彻底拥抱“不可变质”
基于共享数据(不是深拷贝),速度好
有一定学习和迁移成本,按需使用
高阶组件HOC
关于组件公共逻辑的抽离
mixin , 已被React 弃用
高阶组件HOC
Render Props
connect也是高阶组件
接收一个组件,返回的是一个由函数拼接而成的组件
Render Props(比HOC更容易理解)
注意 :暴露的是什么,组件就传什么,比如下图,暴露的是APP 在入口文件APP相当于子组件所以直接在APP输入props即可
5、Redux
redux怎么去获取异步action
单向数据流的图
6、React原理 函数式编程
vdom和diff
jsx本质
1 2 3 4 5 6 7 8 9 1 、是一个 React.createElement 函数,他接收多个参数,执行返回vnode,vnode通过vdom的patch或者其他方法渲染页面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“管不到”的入口
Y就是异步的State N 就是同步的State
执行函数前处于batchUpdate 且isBatchingUpdates=ture。执行函数完之后isBatchingUpdates=false。
因为settimeout()这个函数式异步的就直接执行isBatchingUpdates=false。了 之后进行this.set的时候isBatchingUpdates已经是false了。所以判断setState是异步还是同步的时候,就看isBatchUpdates是true还是false 且设置这个是在入口中设置的。
组件之间如何通讯
1 2 3 1 、父子组件props2 、自定义事件3 、Redux和Context
Context是什么,如何应用
1 2 3 父组件,向其下所有子孙组件传递信息 如一些简单的公共信息,主题色,语言等。 复杂的公共信息,用redux
shouldComponentUpdate用途
两次异步操作合并成一次所以只加1,异步操作玩之后执行同步
纯函数
1 2 3 返回一个新值,没有副作用(不会偷偷修改其他值) 重点:不可变值 如arr1=arr.slice()
函数组件和class组件的区别
1 2 3 纯函数,输入props,输出JSX 没有实例,没有生命周期,没有state 不能扩展其他方法
什么是受控组件
1 2 表单的值,收state 控制 需要自行监听onChange ,更新state
何时使用异步组件
多个组件有公共逻辑,如何抽离
Redux如何进行异步请求
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中 图片大小小于5 kb用base64格式产出否则用file-loader的形式,产出url格式
webpack高级配置
1 2 3 4 5 基本配置只能做demo,不能做线上项目 多入口:entry要写两个。 output要通过[name]的变量去操作 plugins要:每一个入口都要创建一个新的HtmlWebpackPlugin的实例(new ) chunks:会帮你只引入什么名字的js文件 不写chunk会把html文件一块引入。
抽离css文件
1 2 3 开发环境下用的是通过css-loader转换成出css文件插入到style-loader里面去的 生成环境下需要用MiniCssExtractPlugin.loader,通过这种方式就不再是塞到style-loader里面去了 单独拧出来,(在plugins里面加了一个抽离css文件的plugins的配置)还是用new MiniCssExtractPlugin标志一下抽离出的css文件是什么。抽离完还要压缩(optimization配置里面)抽离出来就可以在index.html引入抽离的css文件了
抽离公共代码
1 2 3 4 5 6 7 抽离公共部分相互引用,减少加载和执行的次数 开发环境下没必要做。 第三方模块复用一次的原因是是避免细微的改动重新加载这个模块,影响加载速度,耗费性能。 test 是模块来自 大小限制最好写3 kb 5 kB这样 name就是代码分割产出的chunk 多入口plugins里面引用new HtmlWebpackPlugin下也要考虑代码分割的chunk
懒加载
1 2 引入动态数据->懒加载 异步代码也会产出一个chunk 文件名不是自己规定的
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
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
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 (让大佬帮你浏览一遍)