面试题

作者: Daeeman | 来源:发表于2020-05-06 23:53 被阅读0次

React组件的渲染流程是什么?

使用 React.createElement或 JSX编写 React组件,实际上所有的 JSX代码最后都会转换成 React.createElement(…), Babel帮助我们完成了这个转换的过程。

createElement函数对 key和 ref等特殊的 props进行处理,并获取 defaultProps对默认 props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个 ReactElement对象(所谓的虚拟 DOM)。

ReactDOM.render将生成好的虚拟 DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实 DOM。

为什么代码中一定要引入React?

JSX只是为 React.createElement(component,props,…children)方法提供的语法糖。

所有的 JSX代码最后都会转换成 React.createElement(…), Babel帮助我们完成了这个转换的过程。

所以使用了 JSX的代码都必须引入 React。

为什么React组件首字母必须大写?
babel在编译时会判断 JSX中组件的首字母,当首字母为小写时,其被认定为原生 DOM标签, createElement的第一个变量被编译为字符串;当首字母为大写时,其被认定为自定义组件, createElement的第一个变量被编译为对象;

React在渲染真实Dom时做了哪些性能优化?

在 IE(8-11)和 Edge浏览器中,一个一个插入无子孙的节点,效率要远高于插入一整个序列化完整的节点树。

React通过 lazyTree,在 IE(8-11)和 Edge中进行单个节点依次渲染节点,而在其他浏览器中则首先将整个大的 DOM结构构建好,然后再整体插入容器。

并且,在单独渲染节点时, React还考虑了 fragment等特殊节点,这些节点则不会一个一个插入渲染。

什么是高阶组件?如何实现?

高阶组件可以看作 React对装饰模式的一种实现,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。

高阶组件( HOC)是 React中的高级技术,用来重用组件逻辑。但高阶组件本身并不是 ReactAPI。它只是一种模式,这种模式是由 React自身的组合性质必然产生的。

function visible(WrappedComponent) {  
    return class extends Component {
    render() {
        const 
{visible, ...props } =this.props;      
    if(visible ===false) 
        return null;      
        return<WrappedComponent {...props}/>;
    
}  
}
}

上面的代码就是一个 HOC的简单应用,函数接收一个组件作为参数,并返回一个新组件,新组建可以接收一个 visible props,根据 visible的值来判断是否渲染Visible。

我们可以通过以下两种方式实现高阶组件:

属性代理
函数返回一个我们自己定义的组件,然后在 render中返回要包裹的组件,这样我们就可以代理所有传入的 props,并且决定如何渲染,实际上 ,这种方式生成的高阶组件就是原组件的父组件,上面的函数 visible就是一个 HOC属性代理的实现方式。

functionproxyHOC(WrappedComponent) {  
    return class extends Component{
    render() {      
    return<WrappedComponent {...this.props} />;    
}  
}
}

对比原生组件增强的项:

可操作所有传入的 props

可操作组件的生命周期

可操作组件的 static方法

获取 refs

反向继承
返回一个组件,继承原组件,在 render中调用原组件的 render。由于继承了原组件,能通过this访问到原组件的 生命周期、props、state、render等,相比属性代理它能操作更多的属性。

function inheritHOC(WrappedComponent){  
return class extends WrappedComponent {
    render() {      
return super.render();    
}  
}
}

对比原生组件增强的项:

可操作所有传入的 props

可操作组件的生命周期

可操作组件的 static方法

获取 refs

可操作 state

可以渲染劫持

HOC在业务场景中有哪些实际应用场景?

HOC可以实现的功能:

组合渲染

条件渲染

操作 props

获取 refs

状态管理

操作 state

渲染劫持

HOC在业务中的实际应用场景:

日志打点

权限控制

双向绑定

表单校验

高阶组件(HOC)和Mixin的异同点是什么?

Mixin和 HOC都可以用来解决 React的代码复用问题。

Mixin 可能会相互依赖,相互耦合,不利于代码维护

不同的 Mixin中的方法可能会相互冲突

Mixin非常多时,组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性

而 HOC的出现可以解决这些问题:

高阶组件就是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合

高阶组件也有可能造成冲突,但我们可以在遵守约定的情况下避免这些行为

高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担

Hook有哪些优势?

减少状态逻辑复用的风险

Hook和 Mixin在用法上有一定的相似之处,但是 Mixin引入的逻辑和状态是可以相互覆盖的,而多个 Hook之间互不影响,这让我们不需要在把一部分精力放在防止避免逻辑复用的冲突上。在不遵守约定的情况下使用 HOC也有可能带来一定冲突,比如 props覆盖等等,使用 Hook则可以避免这些问题。

避免地狱式嵌套

大量使用 HOC的情况下让我们的代码变得嵌套层级非常深,使用 HOC,我们可以实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。

让组件更容易理解

在使用 class组件构建我们的程序时,他们各自拥有自己的状态,业务逻辑的复杂使这些组件变得越来越庞大,各个生命周期中会调用越来越多的逻辑,越来越难以维护。使用 Hook,可以让你更大限度的将公用逻辑抽离,将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割。

使用函数代替class

相比函数,编写一个 class可能需要掌握更多的知识,需要注意的点也越多,比如 this指向、绑定事件等等。另外,计算机理解一个 class比理解一个函数更快。Hooks让你可以在 classes之外使用更多 React的新特性。
————————————————

React生命周期有哪些,16版本生命周期发生了哪些变化?

15生命周期

初始化阶段

constructor 构造函数

getDefaultProps props默认值

getInitialState state默认值

挂载阶段

componentWillMount 组件初始化渲染前调用

render 组件渲染

componentDidMount组件挂载到 DOM后调用

更新阶段

componentWillReceiveProps 组件将要接收新 props前调用

shouldComponentUpdate 组件是否需要更新

componentWillUpdate 组件更新前调用

render 组件渲染

componentDidUpdate 组件更新后调用

卸载阶段

componentWillUnmount 组件卸载前调用

16生命周期

初始化阶段

constructor 构造函数

getDefaultProps props默认值

getInitialState state默认值

挂载阶段

staticgetDerivedStateFromProps(props,state)

render

componentDidMount

getDerivedStateFromProps:组件每次被 rerender的时候,包括在组件构建之后(虚拟 dom之后,实际 dom挂载之前),每次获取新的 props或 state之后;每次接收新的props之后都会返回一个对象作为新的 state,返回null则说明不需要更新 state;配合 componentDidUpdate,可以覆盖 componentWillReceiveProps的所有用法

更新阶段

staticgetDerivedStateFromProps(props,state)

shouldComponentUpdate

render

getSnapshotBeforeUpdate(prevProps,prevState)

componentDidUpdate

getSnapshotBeforeUpdate:触发时间: update发生的时候,在 render之后,在组件 dom渲染之前;返回一个值,作为 componentDidUpdate的第三个参数;配合 componentDidUpdate, 可以覆盖 componentWillUpdate的所有用法

卸载阶段

componentWillUnmount

错误处理

componentDidCatch

React16新的生命周期弃用了 componentWillMount、componentWillReceivePorps,componentWillUpdate新增了 getDerivedStateFromProps、getSnapshotBeforeUpdate来代替弃用的三个钩子函数。

React16并没有删除这三个钩子函数,但是不能和新增的钩子函数混用, React17将会删除这三个钩子函数,新增了对错误的处理( componentDidCatch)

setState是同步的还是异步的?

生命周期和合成事件中

在 React的生命周期和合成事件中, React仍然处于他的更新机制中,这时无论调用多少次 setState,都会不会立即执行更新,而是将要更新的·存入 _pendingStateQueue,将要更新的组件存入 dirtyComponent。

当上一次更新机制执行完毕,以生命周期为例,所有组件,即最顶层组件 didmount后会将批处理标志设置为 false。这时将取出 dirtyComponent中的组件以及 _pendingStateQueue中的 state进行更新。这样就可以确保组件不会被重新渲染多次。

  componentDidMount(){
    this.setState({
         index:this.state.index +1
})
 console.log('state',this.state.index); 
}

所以,如上面的代码,当我们在执行 setState后立即去获取 state,这时是获取不到更新后的 state的,因为处于 React的批处理机制中, state被暂存起来,待批处理机制完成之后,统一进行更新。

所以。setState本身并不是异步的,而是 React的批处理机制给人一种异步的假象。

异步代码和原生事件中

  componentDidMount(){
     setTimeout(()=>{
         console.log('调用setState');
        this.setState({ index:this.state.index +1})
         console.log('state',this.stateindex);    },0);  
}

如上面的代码,当我们在异步代码中调用 setState时,根据 JavaScript的异步机制,会将异步代码先暂存,等所有同步代码执行完毕后在执行,这时 React的批处理机制已经走完,处理标志设被设置为 false,这时再调用 setState即可立即执行更新,拿到更新后的结果。

在原生事件中调用 setState并不会出发 React的批处理机制,所以立即能拿到最新结果。

最佳实践

setState的第二个参数接收一个函数,该函数会在 React的批处理机制完成之后调用,所以你想在调用 setState后立即获取更新后的值,请在该回调函数中获取。

this.setState({ index: this.state.index + 1 }, () => {
 console.log(this.state.index);    
})

为什么有时连续多次setState只有一次生效?

例如下面的代码,两次打印出的结果是相同的:

  componentDidMount(){    
    this.setState({ index:this.state.index + 1 }, () => {
      console.log(this.state.index);    
})    
this.setState({ index: this.state.index + 1 }, () => 
{
      console.log(this.state.index);    
}) 
}

原因就是 React会批处理机制中存储的多个 setState进行合并,来看下 React源码中的 _assign函数,类似于 Object的 assign:

 _assign(nextState, typeof partial 'function'? partial.call(inst nextState, props context
) : partial);

如果传入的是对象,很明显会被合并成一次,所以上面的代码两次打印的结果是相同的:

Object.assign(
  nextState, 
    {index: state.index+1},  
    {index: state.index+ 1}
)

注意, assign函数中对函数做了特殊处理,处理第一个参数传入的是函数,函数的参数 preState是前一次合并后的结果,所以计算结果是准确的:

  componentDidMount() {    this.setState((state, props) => ({ 
   index: state.index + 1  
}),() => {
      console.log(this.state.index);   
}) 
this.setState((state, props) => ({
     index state.index + 1    
}),() => {
      console.log(this.state.index);   
})  
}

相关文章

  • 面试材料

    面试经验 面试题1 面试题2 面试题3 面试题4 面试题5 面试题6――数据结构 面试题7――网络 面试题8――汇...

  • 高阶面试题

    webpack面试题 面试题:webpack插件 Git面试题 面试题:git常用命令 面试题:解决冲突 面试题:...

  • this的指向的面试题

    面试题1 面试题2 面试题3 面试题4

  • 面试所涉及的问题

    面试题参考1 : 面试题 面试题参考2 : 内存管理 面试题参考3 :面试题 ...

  • Android超实用最全面试大纲(三)

    文章目录: ANR面试题 OOM面试题 Bitmap面试题 UI卡顿面试题 内存泄漏面试题 内存管理面试题 一、A...

  • Android最全面试大纲(三)

    文章目录: ANR面试题 OOM面试题 Bitmap面试题 UI卡顿面试题 内存泄漏面试题 内存管理面试题 一、A...

  • 2022年web前端面试题

    web前端面试题分为:html/css面试题、javascript面试题、vue面试题、性能优化面试题、网络方面面...

  • ios面试题

    初级面试题 中级面试题 高级面试题 swift篇

  • Android超实用最全面试大纲(四)

    文章目录: 冷启动和热启动面试题 其他优化面试题 架构模式面试题 插件化面试题 热更新面试题 进程保活面试题 Li...

  • Android最全面试大纲(四)

    文章目录: 冷启动和热启动面试题 其他优化面试题 架构模式面试题 插件化面试题 热更新面试题 进程保活面试题 Li...

网友评论

      本文标题:面试题

      本文链接:https://www.haomeiwen.com/subject/yrenghtx.html