1. 说说React生命周期中有哪些坑?如何避免?
getDerivedStateFromProps
容易编写反模式代码,使受控组件和非受控组件区分模糊componentWillMount
在React
中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用, 同时在这里如果绑定事件, 将无法解绑,导致内存泄漏 ,所以网络请求以及事件绑定应该放到componentDidMount
中componentWillReceiveProps
同样也被标记弃用,被getDerivedStateFromProps
所取代,主要原因是性能问题。shouldComponentUpdate
通过返回true
或者false
来确定是否需要触发新的渲染。主要用于性能优化。componentWillUpdate
同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合getSnapshotBeforeUpdate
与componentDidUpdate
改造使用。
如果在componentWillUnmount
函数中忘记解除事件绑定,取消定时器等清理操作,容易引发bug
。
如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。
2. 说说Real diff算法是怎么运作的?
Diff
算法是虚拟DOM
的一个必然结果,它是通过新旧DOM
的对比,将在不更新页面的情况下,将需要内容局部更新Diff
算法遵循深度优先,同层比较的原则- 可以使用
key
值,可以更加准确的找到DOM
节点react
中diff
算法主要遵循三个层级的策略:
tree层级
conponent 层级
element 层级tree
层不会做任何修改,如果有不一样,直接删除创建component
层从父级往子集查找,如果发现不一致,直接删除创建element
层有key值做比较,如果发现key
值可以复用的话,就会将位置进行移动,如果没有,则执行删除创建
3. 调和阶段setState干了什么?
(1)代码中调用 setState
函数之后,React
会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation
)。
(2)经过调和过程,React
会以相对高效的方式根据新的状态构建 React
元素树并且着手重新渲染整个 UI 界面;
(3)在 React
得到元素树之后,React
会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
(4)在差异计算算法中,React
能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
4. 说说redux的实现原理是什么,写出核心代码?
原理
- 将应用的状态统一放到
state
中,由store
来管理state
。 reducer
的作用是 返回一个新的state
去更新store
中对用的state
。- 按
redux
的原则,UI层
每一次状态的改变都应通过action
去触发,action
传入对应的reducer
中,reducer
返回一个新的state
更新store
中存放的state
,这样就完成了一次状态的更新 subscribe
是为store
订阅监听函数,这些订阅后的监听函数是在每一次dipatch
发起后依次执行- 可以添加中间件对提交的
dispatch
进行重写
核心API createStore
创建仓库,接受reducer
作为参数bindActionCreator
绑定store.dispatch
和action
的关系combineReducers
合并多个reducers
applyMiddleware
洋葱模型的中间件,介于dispatch
和action
之间,重写dispatch
compose
整合多个中间件
5. React合成事件的原理?
React
并不是将click事件绑在该div
的真实DOM
上,而是在document
处监听所有支持的事件,当事件发生并冒泡至document
处时,React
将事件内容封装并交由真正的处理函数运行。
收集的事件放在dispatchQueue
数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
首先会在fiber
节点进入render
阶段的complete
阶段时,将事件监听绑定在root
上。然后调用ensureListeningTo
进行事件绑定,生成事件合成对象、收集事件、触发真正的事件。
React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。
将事件绑定在document - v16/容器元素 - v17统一管理,防止很多事件直接绑定在原生的dom元素上。造成一些不可控的情况
React 想实现一个全浏览器的框架, 为了实现这种目标就需要提供全浏览器一致性的事件系统,以此抹平不同浏览器的差异。
6. React组件之间如何通信?
- 父组件向子组件通讯:
父组件可以向子组件传入props
的方式,向子组件进行通讯。 - 子组件向父组件通讯:
props
+回调的方式,父组件向子组件传递props
进行通讯,此props
为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。 - 兄弟组件通信:
兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递
例:组件A – 传值 --> 父组件 – 传值 --> 组件B - 跨层级通讯:
Context
设计⽬的是为了共享那些对于⼀个
组件树⽽⾔是“全局”的数据,
使用context
提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过Context
通信再适合不过。 - 发布订阅者模式:
发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event
模块进⾏通信。 - 全局状态管理工具:
借助Redux
或者Mobx
等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store
,并根据不同的事件产⽣新的状态。
7. 为什么react元素有一个$$type属性?
目的是为了防止 XSS 攻击
。因为 Synbol
无法被序列化,所以 React
可以通过有没有 $$typeof
属性来断出当前的 element
对象是从数据库来的还是自己生成的。如果没有 $$typeof
这个属性,react
会拒绝处理该元素。
8. 说说Connect组件的原理是什么?
connect
是一个高阶函数,它真正连接 Redux
和 React
,包在我们的容器组件的外一层,接收上面 Provider
提供的 store
里面的 state
和 dispatch
,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
原理:
首先传入mapStateToProps
、mapDispatchToProps
,然后返回一个生产Component
的函数(wrapWithConnect
),然后再将真正的Component
作为参数传入wrapWithConnect
,这样就生产出一个经过包裹的Connect
组件,该组件具有:
(1)通过props.store
获取祖先Component
的store
(2)props
包括stateProps
、dispatchProps
、parentProps
,合并在一起得到nextState
,作为props
传给真正的Component
(3)componentDidMount
时,添加事件this.store.subscribe(this.handleChange)
,实现页面交互
(4)shouldComponentUpdate
时判断是否有避免进行渲染,提升页面性能,并得到nextState
(5)componentWillUnmount
时移除注册的事件this.handleChange
首先connect之所以会成功,是因为Provider组件:
- 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,接收Redux的store作为props,通过context对象传递给子孙组件上的connect,它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
9. 说说你对fiber架构的理解?解决了什么问题?
解决的问题:
JavaScript
引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待;如果 JavaScript
线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿
理解:
React Fiber
是对 React
做出的一个重大改变与优化,是对 React
核心算法的一次重新实现
主要做了:
- 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
- 增加了异步任务,调用
requestIdleCallback api
,浏览器空闲的时候执行 dom diff
树变成了链表,一个dom
对应两个fiber
(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?
理解:
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
Redux
中,中间件就是放在就是在dispatch
过程,在分发action
进行拦截处理
本质上一个函数,对store.dispatch
方法进行了改造,在发出 Action
和执行 Reducer
这两步之间,添加了其他功能
常用中间件:
redux-thunk
:用于异步操作
redux-logger
:用于日志记录
实现原理:
所有中间件被放进了一个数组chain
,然后嵌套执行,最后执行store.dispatch
。可以看到,中间件内部(middlewareAPI
)可以拿到getState
和dispatch
这两个方法内部会将dispatch
进行一个判断,然后执行对应操作
11. React性能优化的手段有哪些?
- 使用纯组件;
- 使用
React.memo
进行组件记忆(React.memo
是一个高阶组件),对 于相同的输入,不重复执行; - 如果是类组件,使用
shouldComponentUpdate
(这是在重新渲染组件之前触发的其中一个生命周期事件)生命周期事件,可以利用此事件来决定何时需要重新渲染组件; - 路由懒加载;
- 使用
React Fragments
避免额外标记; - 不要使用内联函数定义(如果我们使用内联函数,则每次调用“
render
”函数时都会创建一个新的函数实例); - 避免在
Willxxx
系列的生命周期中进行异步请求,操作dom
等; - 如果是类组件,事件函数在
Constructor
中绑定bind
改变this
指向; - 避免使用内联样式属性;
- 优化
React
中的条件渲染; - 不要在
render
方法中导出数据; - 列表渲染的时候加
key
; - 在函数组件中使用
useCallback
和useMemo
来进行组件优化,依赖没有变化的话,不重复执行; - 类组件中使用
immutable
对象;
12. 说说你对事件循环event loop的理解?
任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。
JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,
但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript中,所有的任务都可以分为
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
异步任务还可以细分为微任务与宏任务
微任务一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
13、前端跨域的解决方案?
一、什么是跨域?
在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。
二、什么是同源策略?
同源策略是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM和JS对象无法获得
AJAX 请求不能发送
三、9种跨域解决方案
1、JSONP跨域
2、跨域资源共享(CORS)
3、nginx代理跨域
4、nodejs中间件代理跨域
5、document.domain+ iframe跨域
6、location.hash + iframe跨域
7、window.name + iframe跨域
8、postMessage跨域
9、WebSocket协议跨域
小结
以上就是9种常见的跨域解决方案,jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。document.domain+iframe适合主域名相同,子域名不同的跨域请求。postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。
14. 数组常用方法及作用,至少15个?
Array.length:
返回或设置一个数组中的元素个数Array.from() :
对伪数组或可迭代对象(包括arguments
,Array
,Map
,Set
,String
…)转换成数组对象Array.isArray():
用于确定传递的值是否是一个Array
concat():
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。every(callback):
方法测试数组的所有元素是否都通过了指定函数的测试filter():
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素find():
返回数组中满足提供的测试函数的第一个元素的值forEach():
方法对数组的每个元素执行一次提供的函数includes():
用来判断一个数组是否包含一个指定的值,如果是,酌情返回true
或false
indexOf():
返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1join():
将数组(或一个类数组对象)的所有元素连接到一个字符串中lastIndexOf():
返回指定元素(也即有效的JavaScript
值或变量)在数组中的最后一个的索引,如果 不存在则返回 -1。从数组的后面向前查找map():
创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果pop():
从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度push():
将一个或多个元素添加到数组的末尾reduce():
累加器和数组中的每个元素(从左到右)应用一个函数shift():
从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度slice():
返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象some():
测试数组中的某些元素是否通过由提供的函数实现的测试。sort():
当的位置对数组的元素进行排序,并返回数组。splice():
通过删除现有元素和/或添加新元素来更改一个数组的内容toString():
返回一个字符串,表示指定的数组及其元素unshift():
将一个或多个元素添加到数组的开头,并返回新数组的长度toLocaleString():
返回一个字符串表示数组中的元素。数组中的元素将使用各自的toLocaleString
方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开
15. React render方法的原理,在什么时候会触发?
原理:
在类组件中render
函数指的就是render
方法;而在函数组件中,指的就是整个函数组件
render
函数中的jsx
语句会被编译成我们熟悉的js代码,在render
过程中,react
将新调用的render
函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff
比较,更新dom树
触发机:
类组件调用 setState
修改状态
函数组件通过useState hook
修改状态
一旦执行了setState
就会执行render
方法,useState
会判断当前值有无发生改变确定是否执行render
方法,一旦父组件发生渲染,子组件也会渲染
16. 说说你对vue中mixin的理解?
mixin
是一种类,在vue
中就是js
文件,主要的作用是作为功能模块引用。因为在项目中,可能不同组件会有相同的功能,比如控制元素的显示和隐藏,如果他们的变量和规则也完全相同的话,就可以把这个功能单独提取出来,放在mixin.js
中,再引入,就可以实现一样的功能了。引入的方法也分为全局混入和局部混入,局部混入就是在每个组件中引入,全局混入就是在main.js中通过Vue.mixin()
引入。
17. for…in循环和for…of循环的区别?
for...in
循环:只能获得对象的键名,不能获得键值
for…in
循环有几个缺点
①数组的键名是数字,但是for…in
循环是以字符串作为键名“0”、“1”、“2”等等。
②for…in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
③某些情况下,for…in
循环会以任意顺序遍历键名。
for…in
循环主要是为遍历对象而设计的,不适用于遍历数组。
for...of
循环:允许遍历获得键值
for…of
循环
①有着同for…in
一样的简洁语法,但是没有for…in
那些缺点。
②不同于forEach
方法,它可以与break
、continue
和return
配合使用。
③提供了遍历所有数据结构的统一操作接口
18. Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?
1. typeof
判断
typeof
返回的类型都是字符串形式
2. Constructor
实例constructor
属性指向构造函数本身
constructor
判断方法跟instanceof
相似,但是constructor
检测Object
与instanceof
不一样,constructor
还可以处理基本数据类型的检测,不仅仅是对象类型
3. Instanceof
instanceof
可以判类型是否是实例的构造函数
instanceof
后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
4. Object.prototype.toString.call()
判断类型的原型对象是否在某个对象的原型链上
5. 通过object
原型上的方法判断
比如array.isArray()
来判断是不是一个数组
6. ===(严格运算符)
通常出现在我们的条件判断中,用来判断数据类型的话就会非常的有局限性,比如判断一个变量是否为空,变量是否为数据等
19. 说说你对Object.defineProperty()的理解?
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
该方法接受三个参数
- 第一个参数是
obj
:要定义属性的对象, - 第二个参数是
prop
:要定义或修改的属性的名称或Symbol
, - 第三个参数是
descriptor
:要定义或修改的属性描述符
函数的第三个参数descriptor
所表示的属性描述符有两种形式:数据描述符和存取描述符。 - 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
- 存取描述符是由
getter
函数和setter
函数所描述的属性。
一个描述符只能是这两者其中之一;不能同时是两者。
这两种同时拥有下列两种键值:configurable
: 是否可以删除目标属性或是否可以再次修改属性的特性(writable
,configurable
,enumerable
)。设置为true
可以被删除或可以重新设置特性;设置为false
,不能被可以被删除或不可以重新设置特性。默认为false
。enumerable
: 当且仅当该属性的enumerable
键值为true
时,该属性才会出现在对象的枚举属性中。默认为false
。
20. 说说你对webSocket的理解?
理解
WebSocket
是HTML5
下一种新的协议(websocket
协议本质上是一个基于tcp
的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议
原理
websocket
约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp
的连接,从而方便它们之间的通信
在websocket
出现之前,web
交互一般是基于http协议
的短连接或者长连接
websocket
是一种全新的协议,不属于http无状态协议,协议名为"ws"
21.props和state相同点和不同点?render方法在哪些情况下会执行?
不同点:
1.props不可以在组件内部修改,但state可以在组件内部修改
2.可以从父组件修改自组件的props,而不能从父组件修改自组件的state
相同点:
1.props和state都是导出HTML的原始数据。
2.props和state都是确定性的,如果我们写的组件为同一props和state的组合生成了不同的输出,那木我们肯定在哪里做错了
4.props和state都会触发渲染更新
5.props和state都是纯JS对象(用typeof来判断,结果都是object)
6.可以从父组件得到初始值props和state的初始值
触发:
类组件调用 setState
修改状态
函数组件通过useState hook
修改状态
一旦执行了setState
就会执行render
方法,useState
会判断当前值有无发生改变确定是否执行render
方法,一旦父组件发生渲染,子组件也会渲染
21.shouldComponentUpdate有什么作用?
shouldComponentUpdate () 的返回值用于判断 React 组件的输出是否受当前 state 或 props 更改的影响,当 props 或 state 发生变化时,shouldComponentUpdate () 会在渲染执行之前被调用。
23.说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?
(1)挂载阶段:
constructor() 在 React 组件挂载之前,会调用它的构造函数。
componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用
(2)更新运行阶段:
componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
render(): render() 方法是 class 组件中唯一必须实现的方法。
componentWillUpdate()*: shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
(3)卸载或销毁阶段:
componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。
24.什么是强缓存和协商缓存?
12.1 浏览器缓存(Brower Caching):
是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力 HTTP 1.0协议中的。简而言之,就 是告诉浏览器在约定的这个时间前,可以直接从缓存中获取资源而无需跑到服务器去获取。 http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、Etag
12.2强缓存:
浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
12.3协商缓存
协商缓存: 向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
25.说说react 中jsx语法糖的本质?
jsx是JavaScript的一种语法扩展,它跟模板语言很接近,但是它充分具备JavaScript的能力
JSX就是用来声明React当中的元素,React使用JSX来描述用户界面
JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟DOM在降低学习成本
26.说说AMD、CMD、commonJS模块化规范的区别?
CommonJS用同步的方式加载模块。在服务端,模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行
CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。
27.Vue中自定义指令的理解,应用场景有哪些?
指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
注册一个自定义指令有全局注册与局部注册
全局注册主要是通过Vue.directive
方法进行注册
Vue.directive
第一个参数是指令的名字(不需要写上v-
前缀),第二个参数可以是对象数据,也可以是一个指令函数
局注册通过在组件options
选项中设置directive
属性
然后你可以在模板中任何元素上使用新的 v-focus
property,如下:
- 表单防止重复提交
- 图片懒加载
- 一键 Copy的功能
- 拖拽指令、
- 页面水印、
- 权限校验
28.大文件如何做断点续传?
分片传
- 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
- 初始化一个分片上传任务,返回本次分片上传唯一标识;
- 按照一定的策略(串行或并行)发送各个分片数据块;
- 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件
断电续传
断点续传指的是在下载或上传时,将下载或上传任务人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度
29.说说你对koa中洋葱模型的理解?
Koa
的洋葱模型指的是以 next()
函数为分割点,先由外到内执行 Request
的逻辑,再由内到外执行 Response
的逻辑。通过洋葱模型,将多个中间件之间通信等变得更加可行和简单
在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能, 也就是说每一个中间件都有两次处理时机
30.react新出来两个钩子函数是什么?和删掉的will系列有什么区别
新生命周期中新增了两个钩子,分别为getDerivedStateFromProps(从props中得到衍生的state)和getSnapshotBeforeUpdate。
区别
1、componentWillMount中可能需要做的事(一些数据初始化的操作就应该放在这个钩子中处理),constructor与componentDidMount也能做,甚至做的更好,此方法被废弃。
2、componentWillReceiveProps实际行为与命名并不相符,由于不稳定性已由getDerivedStateFromProps代替;
3、而componentWillUpdate同等理由被getSnapshotBeforeUpdate代替
32.在虚拟dom计算的时候diff和key之间有什么关系?
key 当同一层级的某个节点添加了对于其他同级节点唯一的key属性,当它在当前层级的位置发生了变化后。react diff算法通过新旧节点比较后,如果发现了key值相同的新旧节点,就会执行移动操作(然后依然按原策略深入节点内部的差异对比更新),而不会执行原策略的删除旧节点,创建新节点的操作。
33. withRouter的作用?
把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上
不是所有组件都直接与路由相连(比如拆分的子组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,将react-router的history、location、match三个对象传入props对象上,此时就可以使用this.props.history跳转页面了或者接受参数了
34.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历?
在reactJS中props.children不一定是数组
有三种可能 :
1当前组件没有子节点数据类型就是undefined,
2有一个子节点数据类型就是object 。
3 有多个子节点的时候才会是array ,只有在多个节点的时候才可以直接调用map方法,react资深提供了一个react.children.map()方法,可以安全遍历子节点对象。
35.谈谈你对immutable.js的理解?
Immutable.js采用了 持久化数据结构 ,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过 结构共享 等方式大幅提高性能
36.redux中同步action与异步action最大的区别是什么?
同步: Redux的教程中反复使用todo列表的例子,那就是个典型的同步action,每当disptach action时,state就会被立即更新[当然setState是异步的]
异步: 一般异步处理都会使用中间件,比如redux-thunk或者redux-saga,他们做的事情是包装dispatch,request action由view触发,receive action由这些中间件触发
37.redux-saga和redux-thunk的区别与使用场景?
redux-thunk:通过执行action中的函数实现业务逻辑,没有拓展API
redux-saga:通过定义saga函数进行监控,同时提供一些常用的API
redux-thunk将部分异步处理业务逻辑写在action中,redux-sagasaga则是放在监控的函数中。
38.CDN的特点及意义?
DN的功能特点:
(1)节省骨干网带宽,减少带宽需求量;
(2)提供服务器端加速,解决由于用户访问量大造成的服务器过载问题;
(3)服务商能使用Web
Cache技术在本地缓存用户访问过的Web页面和对象,实现相同对象的访问无须占用主干的出口带宽,并提高用户访问因特网页面的相应时间的需求;
(4)能克服网站分布不均的问题,并且能降低网站自身建设和维护成本;
(5)降低“通信风暴”的影响,提高网络访问的稳定性
意义
使用CDN可以获取一些好处,无论它们是公有CDN还是提供静态内容的私有CDN,你的里程可能会有所不同,具体取决于通过CDN传递的流量以及你产生的流量。
39.谈谈你对react的理解,他有哪些特性
React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流
帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容
特性
React特性有很多,如:
JSX语法
单向数据绑定
虚拟DOM
声明式编程
Component
40.Vue组件通信?
1.父组件通过 props 向子组件传递数据,子组件通过 e m i t 和父组件通信 2. r e f :这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法 3. emit 和父组件通信 2.ref:这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法 3.emit和父组件通信2.ref:这个属性用在子组件上,指向子组件的实例,可以通过实例来访问组件的数据和方法3.emit 绑定一个自定义事件,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on监听并接收参数
4.eventBus事件总线($emit / $on)
.sync
v-model
provide
eventBus
localStorage
$root
$attrs与 $listeners
41.说说你对vuex的理解?写出其原理的核心代码?
Vuex采用MVC模式中的Model层,规定所有的数据必须通过action—>mutaion—>state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰,同时在调试时也可以通过DEVtools去查看状态
42.函数组件中如何使用useReducer
创建初始值的状态initialState
创建所有对状态的操作reducer(state,action)
传给useReducer,得到读和写的接口
调用写({‘type’:‘操作类型’})
useReducer
接受的第一个参数是一个函数,我们可以认为它就是一个 reducer
, reducer
的参数就是常规 reducer
里面的 state
和 action
,返回改变后的 state
, useReducer
第二个参数为 state
的初始值 返回一个数组,数组的第一项就是更新之后 state
的值 ,第二个参数是派发更新的 dispatch
函数。
43.说说你对高阶组件的理解?应用场景有哪些?
如果一个函数 接受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数。
什么是React高阶组件:一个组件的参数是组件,并且返回值是一个组件,我们称这类组件为高阶组件
withRouter() memo() react-redux中connect方法是高阶组件
44.节流防抖的区别是什么,具体解释一下?
防抖:1.当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间(非常短的时间);
2.当事件密集触发时,函数的触发会被频繁的推迟;
3.只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
function debounce(func, wait) { let timeout; return function () { let context = this; // 保存this指向 let args = arguments; // 拿到event对象 clearTimeout(timeout) timeout = setTimeout(function(){ func.apply(context, args) }, wait); } }
节流:1.当事件触发时,会执行这个事件的响应函数;
2.如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数
3.不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的
function throttled1(fn, delay = 500) { let oldtime = Date.now() return function (...args) { let newtime = Date.now() if (newtime - oldtime >= delay) { fn.apply(null, args) oldtime = Date.now() } } }
45. 说说对React refs 的理解?应用场景?
创建ref的形式有三种:
1.传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
2.传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
3.传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素
但下面的场景使用refs非常有用:
1.对Dom元素的焦点控制、内容选择、控制
2.对Dom元素的内容设置及媒体播放
3.对Dom元素的操作和对组件实例的操作
4.集成第三方 DOM 库
46.applyMiddleware的作用是什么?
1.中间件都需要通过applyMiddlewares进行注册,
2.将所有的中间件组成一个数组,依次执行然后作为第二个参数传入到createStore中
3.applyMiddlewares调用实例:返回store对象
我们用 applyMiddleware 是为了改造 dispatch 的,所以 applyMiddleware 执行完后,dispatch 是变化了的,而 middlewareAPI 是 applyMiddleware 执行中分发到各个 middleware,所以必须用匿名函数包裹 dispatch, 这样只要 dispatch 更新了, middlewareAPI 中的 dispatch 应用也会发生变化
47.如何解决react页面不刷新?
\1. 深拷贝或者改变引用类型数据的地址
\2. Key最好不用index
\3. 在this.setState中使用一个回调来获取一个新的值
\4. 不要直接修改store中的值
\5. 在render中判断state.value值不等于空,再加载子组件
48 . 说说React中setState执行机制?
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state
当需要修改里面的值的状态需要通过调用setState
来改变,从而达到更新组件内部数据的作用 setState
第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据 在使用setState
更新数据的时候,setState
的更新类型分成: 同步和异步,在组件生命周期或React合成事件中,setState是异步在setTimeout或者原生dom事件中,setState是同步
49.说说你对受控组件和非受控组件的理解?应用场景?
受控组件,简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据 非受控组件,简单来讲,就是不受我们控制的组件一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态大部分时候推荐使用受控组件来实现表单,因为在受控组件中,表单数据由React
组件负责处理如果选择非受控组件的话,控制能力较弱,表单数据就由DOM
本身处理,但更加方便快捷,代码量少
50.说说React jsx转换成真实DOM的过程?
react中的jsx语法会通过babel转化为 js代码,以React.createElement函数形式存在,createElement函数返回一个ReactElement函数,ReactElement函数返回一个的虚拟节点,虚拟节点中嵌套虚拟节点,就形成了虚拟DOM,最后通过ReactDOM.render方法转化为真实DOM。babel在转化jsx过程中,会判断首字母的大小写当首字母为小写的时候,会被认为是原生DOM标签, 那么createElement中的第一个参数就是一个字符串,表示标签的名称当首字母为大写的时候,会被认为是组件,那么createElement中的第一个参数就是组件的名称,
51.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
react-redux 是的官方 React UI 绑定层,允许您的 React 组件从 Redux 存储中读取数据,并将操作分派到存储以更新状态。
@reduxjs/toolkit 是对 Redux 的二次封装,开箱即用可的一个高效的 Redux 开发工具集,使得创建store、更新store更加方便
52.说说webpack中常见的loader?解决了什么问题?
Image-loader 加载并且压缩图片文件
Babel-loader 将es6转换为es5
Css-loader 加载css 支持模块化 压缩 文件导入
Style-loader 把css代码注入到js中 通过dom操作去加载css
css-loader:分析css 模块之间的关系,并合成⼀个css style-loader:把css-loader生成的内容,用style标签挂载到页面的head中 less-loader:开发中,我们也常常会使用less、sass、stylus预处理器编写css样式,使开发效率提高,这里需要使用less-loader raw-loader:在webpack中通过import方式导入文件内容,该loader并不是内置的,所以首先要安装,然后在 webpack.config.js 中进行配置 file-loader:把识别出的资源模块,移动到指定的输出⽬目录,并且返回这个资源在输出目录的地址(字符串) url-loader:可以处理理file-loader中的所有事情,但是遇到图片格式的模块,可以选择性的把图片转成base64格式的字符串,并打包到js中,对小体积的图片比较合适,大图片不合适
53.知道react里面的createPortal么,说说其使用场景?
Portal 将提供一种将子节点渲染到 DOM 节点中的方式,该节点存在于 DOM 组件的层次结构之外。
因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等
54、 React中的错误边界是什么,如何使用
错误边界(Error Boundary)是React提供的一种机制,用于捕获并处理组件在渲染过程中发生的错误,防止整个应用崩溃。
使用错误边界可以将错误限制在组件树的边界内,从而提高应用的稳定性和可靠性。
使用方式:
- 创建一个错误边界组件,通过继承React.Component并实现
componentDidCatch
方法来捕获错误。 - 在需要使用错误边界的组件上使用错误边界组件进行包裹。
示例:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { this.setState({ hasError: true }); // 可以在此处记录错误信息或发送错误报告 } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } // 使用错误边界组件包裹需要捕获错误的组件 <ErrorBoundary> <MyComponent /> </ErrorBoundary>
当MyComponent组件中发生错误时,错误将被ErrorBoundary组件捕获,从而避免了整个应用的崩溃。
注意:错误边界只能捕获其子组件中的错误,无法捕获自身或其父组件中的错误。
55. React中的事件处理机制
在React中,事件处理机制与原生的DOM事件处理有所不同,React使用合成事件(SyntheticEvent)来封装原生事件,并提供了一种统一的事件处理方式。
React的事件处理机制有以下特点:
- 事件绑定采用驼峰命名:React的事件名使用驼峰命名,而不是原生DOM的全小写。
- 事件处理函数是合成事件:React的事件处理函数是由合成事件包装的,而不是直接绑定在DOM元素上的。
- 事件处理函数中的this指向:React中的事件处理函数中的this默认指向组件实例,而不是触发事件的DOM元素。
- 事件传参:可以通过箭头函数或bind方法来传递额外的参数给事件处理函数。
示例:
class MyComponent extends React.Component { handleClick(event) { console.log('Clicked'); } render() { return <button onClick={this.handleClick}>Click Me</button>; } }
在上述示例中,当按钮被点击时,会调用handleClick方法,并在控制台输出’Clicked’。
React的事件处理机制使得事件处理更加简洁和易于理解,并提供了一种统一的方式来处理事件。
56、 React中的状态管理解决方案
在大型React应用中,有效管理和共享状态是很重要的。以下是一些常用的React状态管理解决方案:
- Context API:React的Context API可以用于在组件树中共享状态。通过创建上下文(Context)并使用Provider和Consumer组件,可以将状态传递给子组件,避免了props层层传递的麻烦。
- Redux:Redux是一个流行的状态管理库,它提供了可预测的状态容器。通过创建一个全局的存储(store)来管理应用的状态,组件可以通过连接(connect)函数来访问和更新状态。
- MobX:MobX是另一个流行的状态管理库,它使用观察者模式和响应式数据流来管理状态。通过使用装饰器或类装饰器的语法,可以轻松地将状态和组件关联起来。
- Recoil:Recoil是由Facebook开发的状态管理库,专注于React状态的简洁性和可扩展性。它提供了一种声明式的方式来定义和使用状态,支持原子化状态和选择性订阅。
- Zustand:Zustand是一个简单且轻量的状态管理库,它使用React的Hook机制来管理状态。通过使用useState和useEffect等Hooks,可以在组件中定义和更新状态。
这些状态管理解决方案都有各自的特点和适用场景,根据项目的需求和开发团队的偏好,选择合适的解决方案来管理React应用的状态。
57、 React中的Fragments的作用和使用方式
Fragments(片段)是一种特殊的React组件,用于在不添加额外节点的情况下,将多个子元素进行分组。
作用:
- 减少不必要的包裹层级:使用Fragments可以避免因为需要包裹多个子元素而增加额外的父节点。
- 提高性能:使用Fragments可以减少渲染树中的节点数量,提高渲染性能。
使用方式:
- 使用空标签语法:
<></>
- 使用<React.Fragment>标签:
<React.Fragment></React.Fragment>
示例:
function MyComponent() { return ( <> <h1>Title</h1> <p>Content</p> </> ); }
在需要分组但不需要额外节点的情况下,可以使用Fragments来提高代码的简洁性和性能。
58、![] == ![],![] == [],结果是什么?为什么?
①、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]
②、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0
③、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())
④、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)
59、什么是闭包,应用场景是什么?
闭包就是能够读取其他函数内部变量的函数,说白了闭包就是个函数,只不过是处于其他函数内部而已。
1.访问函数内部的变量
2.防止函数内部的变量执行完城后,被销毁,使其一直保存在内存中。
60.谈谈你是如何做移动端适配的?
[flex弹性布局]
[百分比]
[用框架搭建页面]
[viewport适配]
[媒体查询media]
[rem+viewport缩放(也成为1px适配)]
61、移动端1像素的解决方案?
- 移动端1像素的解决方案?
- 伪类+transform
- viewport + rem
- border-image
- background-image
- postcss-write-svg
总结
0.5px,相信浏览器肯定是会慢慢支持的,目前而言,如果能用的话,可以hack一下。
对于老项目,建议采用transform+伪类。
新项目可以设置viewport的scale值,这个方法兼容性好。
postcss-write-svg简单易用,仅适合直线,圆角建议用transform+伪类实现
62、弹性盒中的缩放机制是怎样的
弹性盒中的项目设置flex-grow属性定义项目的放大比例,默认值为0,值越大,放大越厉害,且不支持负值; 而flex-shrink属性定义项目的缩小比例,默认值为1,数值越大,缩小越厉害,同样不支持负值;
说说你对TypeScript中泛型的理解及其应用场景?
泛型的本质是参数化类型,通俗的将就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别成为泛型类,泛型接口、泛型方法。
TypeScript 中不建议使用 any 类型,不能保证类型安全,调试时缺乏完整的信息。
TypeScript可以使用泛型来创建可重用的组件。支持当前数据类型,同时也能支持未来的数据类型。扩展灵活。可以在编译时发现你的类型错误,从而保证了类型安全。
使用泛型可以创建泛型函数、泛型接口,泛型类
说说你对TypeScript装饰器的理解及其应用场景
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,是一种在不改变原类和使用继承的情况下,动态地扩展对象功能同样的,本质也不是什么高大上的结构,就是一个普通的函数,@expression 的形式其实是Object.defineProperty的语法糖,expression 求值后必须也是一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入
场景 1.逻辑层消除繁琐的try/catch块,装饰器内统一输出函数日志 2.校验参数或返回值类型
说说reduce方法的作用?自己手动封装一个reduce,写出其核心代码?
reduce方法是JavaScript中的一个高阶函数,它接收两个参数:一个回调函数和一个初始值。它的作用是进行累加操作,将数组中的每一个元素和初始值一起传入回调函数进行计算,得到单个的结果。然后将这个结果作为下一次计算的初始值,将下一个元素作为计算参数,重复调用回调函数进行计算。最终将每次计算得到的结果合成并返回一个单一的结果值。reduce方法可以用于数值、字符串、对象等类型数组,并且可以根据需要编写不同的回调函数来实现不同的功能
redux-thunk 和 redux-saga 的区别?
redux-thunk:通过执行action中的函数实现业务逻辑,没有拓展API
redux-saga:通过定义saga函数进行监控,同时提供一些常用的API
redux-thunk将部分异步处理业务逻辑写在action中,redux-sagasaga则是放在监控的函数中。