React面试百题通
小编比较追求原理可能有些枯燥,乏味还望读者见谅,但是看一下总对技术提高有好处。
Virtual Dom和Real Dom的区别分析
-
减少dom操作
操作dom消耗是非常大的,虽然有可能读者没有感觉到,但是你如果想象一下你有10000个dom节点,通过浏览器提供api就需要操作10000次,但是虚拟dom只需要一次
-
更新区域最小化
虚拟dom会通过diff算法,对比前后两次dom树的区别,从而更新最小范围的dom元素
-
在某种情况下虚拟dom性能比真实dom快
React的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PojQdqb0-1617296391159)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210320104824319.png)]
这是React 16.4 + 版本的生命周期函数,在react16前还存在三个方法
-
componentWillMount
-
componentWillReceiveProps
-
componentWillUpdate
这三个生命周期在React16后被遗弃了
react的声明周期函数从宏观方面分为挂载时,更新时,卸载时
挂载阶段触发的生命周期函数:
-
constructor: 构造函数 初始化状态和属性
-
getDerivedStateFromProps:: 根据字面意思理解可以看出这个方法就是从props获取state,方法功能就是可以根据自己的意愿,控制从props获取的值
例如:
static getDerivedStateFromProps (props, state) { if(typeof props.list === 'object') { // 这里判断如果props的list是Object 则返回否则返回空 意义是说如果List不是对象则不传给子组件 return { list: props.list, } }else { return null; } }
-
render: 渲染Dom节点
-
componentDidMount:
什么是虚拟dom(virtual dom)
虚拟dom
:简答说就是用js对象来表示dom结构
例如:
<div id='app'>
<span id='child'>1</span>
</div>
//转换成js对象
const vnode = {
tag:'div',
attrs:{id:'app'},
children:[{ tag:'span',attrs:{id:'child'},children:['1']}]
}
React中Context有什么作用
React中Context是组件之间用来传递数据的方法,并且这个传递还可以是跨层级的。
React中的Diff算法
传统的diff算法
: 传统diff算法对比两颗树形结构,需要对比全部节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lewK1J4k-1617296391160)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210328075629753.png)]
这样时间复杂度就已经达到O(n2)
了,对比后还要逐一移动元素或者处理元素又是一个O(n)
时间复杂度就达到O(n3)
react diff算法
: react基于三个优化把时间复杂度降到O(n)
Tree diff 优化
在WEBUI在跨层级移动比较少,可以忽略不计,首先对virtual dom 进行层级比较,并且只比较父节点,如果父节点不在,则直接删除整个节点包括子节点,这样循环的时间复杂度为O(n)
,如果出现跨层级则对节点进行删除,重建,尽量避免跨层级因为开销比较大
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aN7uAZAx-1617296391162)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210328085541698.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tqbnBj0S-1617296391164)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210328090953029.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hrD6Gg5Y-1617296391165)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210328091250780.png)]
Component diff 优化
首先也是层级比较
- 如果是同一类型的组件,组件内的元素又是一个virtual Dom 树继续比较
- 如果不是同一类型的组件,则判断为
dirty component
,直接替换整个组件以及所有的子节点
Element diff优化
同一层级比较,diff提供三种操作:删除、插入、移动
- 当元素不在层级中则
插入
- 当元素已经改变无法复用则
删除
再创建
- 如果元素只是改变位置则
移动位置
,移动可以根据key
加以区分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVXNoP0r-1617296391166)(C:%5CUsers%5Clin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20210328092155312.png)]
Hoc又称高阶组件
高阶组件
:首先要知道什么是高阶组件,简单说就是一个函数接收一个组件
做为参数,并且返回一个组件
这样的函数就是高阶组件。
那么有什么用呢?
高阶组件的应用主要在:功能增强、添加行为、添加逻辑等
简单的高阶组件:
// 接收一个组件作为参数
const Hoc = (WrappedCompoent)=> {
return (props) => { //返回一个组件 注意这里写成了函数组件 如果是class则 class 组件名 extends Compoent 同理
return (
<div className={'hocWrap'}>
<WrappedCompoent {...props}></WrappedCompoent>
</div>
)
}
}
高阶组件实现方式:
1.属性代理(props proxy)
属性代理主要: 操作props 、 组件与其他元素组合、抽离State
class SayGoodBye extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={'name'}>
父组件:{this.props.greet}
</div>
)
}
}
const PropsProxy = function (WrappedCompoent) {
//返回一个组件
return class InnerCompoent extends Component{
render() {
const props = {
...this.props,
greet: '改变后的props'
}
return <div>
<WrappedCompoent {...props} ></WrappedCompoent>
</div>
}
}
}
2.反向代理(inheritance inversion)
反向代理主要作用有: 渲染劫持、 操作dom
简单的例子:
const inheritance = function (WrappedCompoent) {
//返回一个组件
return class extends WrappedCompoent { //这里继承了作为的函数的组 因此只需调用WrappedCompoent的render方法即可
render() {
return <div>
{super.render()};
<div>这里写上需要添加的东西</div>
</div>
}
}
}
渲染劫持、操作dom
class SayGoodBye extends Component {
constructor(props) {
super(props);
this.state = {
greet: 'goodBye'
}
}
render() {
return (
<div className={'name'}>
父组件:{this.state.greet}
</div>
)
}
}
const inheritance = function (WrappedCompoent) {
//返回一个组件
return class extends WrappedCompoent { //这里继承了作为的函数的组 因此只需调用WrappedCompoent的render方法即可
handleAlterState = ()=> {
this.setState({
greet: 'hello'
})
}
render() {
return <div>
{
this.state.greet === 'goodBye' ? super.render(): ''
}
<div className={'test'}>渲染劫持</div>
<button onClick={this.handleAlterState}>点我修改状态</button>
<pre>当前greet值为 :{this.state.greet}<br/>
点击按钮后,修改state,随之父组件渲染消失
</pre>
</div>
}
}
}
jsx原理
jsx
是一种编写语法也是一种文件格式,混合了html和javascript的代码,但是浏览器不认识这样的代码,浏览器只认识javascript的代码,从而React创建就必须把转化成浏览器能够认识的代码,这个过程称之为“转译”
React转译原理:
其实jsx文件都是通过调用函数React.createElement
这个方法
React.createElement(type,attribues,[children])
以此类推
参数:
type: string,
attribues: Object,
children: Array
例如:
<div className='box1'>
hello world!
</div>
转译后
React.createElement(
'div',
{ className: 'box1' },
'hello world!!'
);
再如
<div className='box1'>
hello world!
<p className='txt1'>test1</p>
文字
</div>
转译后
React.createElement(
'div',
{ className: 'box1' },
'hello world!!', //第一个元素
React.createElement(//第二个元素
'p',
{ className: 'txt1' },
'text',
),
'文字', //第三个元素
);
以此类推下去,如果是第二个元素内又插入元素
就是在第二次CreateElement的children多加上一个,有兴趣可以去babel上翻译看看babeljs跳转连接
什么是props和state,他们之间 有什么区别
react最主要的特性就是组件,可以将一个应用分解成多个组件,而组件中的数据流就是由Props和state控制。
props
是一种react组件的一种属性,或者也可以说是一种组件之间传递数据的一种方式,而属性的值是由父组件
由决决定的,并且最重要的特性props不可变
state
:是react组件内部数据集,或者说是react私有数据也是可以的,state是可变的并且私有的,每个组件都有自己的state,并且改变后组件将根据新的状态重新渲染,除此之外state可以由外部决定例如 键盘输入、服务器获取数据等
总结一句话: props是不可变,数据由自己决定, state是可以变的,数据可以由外部来决定
ct最主要的特性就是组件,可以将一个应用分解成多个组件,而组件中的数据流就是由Props和state控制。
props
是一种react组件的一种属性,或者也可以说是一种组件之间传递数据的一种方式,而属性的值是由父组件
由决决定的,并且最重要的特性props不可变
state
:是react组件内部数据集,或者说是react私有数据也是可以的,state是可变的并且私有的,每个组件都有自己的state,并且改变后组件将根据新的状态重新渲染,除此之外state可以由外部决定例如 键盘输入、服务器获取数据等
总结一句话: props是不可变,数据由自己决定, state是可以变的,数据可以由外部来决定