集锦3

72 阅读7分钟

7、 为什么不能直接使用this.state改变数据?

React中不能直接修改state,因为并不会重新出发render。需要使用setState()方法。状态改变时,组件通过重新渲染做出响应。

setState通过一个队列机制来实现state更新。当执行setState时,会将需要更新的state合并后放入状态队列,而不会立刻更新this.state。队列机制可以高效的批量更新state,如果不通过setState而直接修改this.state,那么该state将不会被放入状态队列中,当下次调用setState并对状态队列进行合并时,将会忽略之前直接被修改的state,而造成无法预知的错误。

8、 React中如果绑定事件使用匿名函数有什么影响?

class Demo{
    render(){
        return(<button onClick={(e)=>{console.log("点击按钮")}}>
    按钮          
            </button>)
    }
}

这样的写法,因为使用的是匿名函数,所以组件每次都会被认为是一个新的props,不会使用缓存优化,在性能上会有一定的损耗。

9 、React的事件代理机制和原生事件绑定混用会有什么问题?

在我们平常的开发中应该尽可能的避免React的事件代理机制和原生事件绑定混用。

React 的合成事件层,并没有事件绑定到DOM元素上,所以使用e.stopRpopagation()来阻止原生DOM的冒泡的行为是不行的。阻止React事件冒泡的行为只能用于React合成事件系统,但是在原生事件中的组织冒泡行为,却可以阻止React合并事件的传播。

10、React的事件代理机制和原生事件绑定有什么区别?

  • 事件传播与阻止事件的传播:React的合成事件并没有实际事件捕获,只支持了事件冒泡。阻止事件传播React做了兼容性处理,只需要e.preventDefault()即可,原生存在兼容性问题。
  • 事件类型:React是原生事件类型的一个子集(React只是实现了DOM level3的事件接口,有些事件React并没有实现,比如window的resize事件。)阻止React事件冒泡的行为只能用于React合并事件系统,但是在原生事件中的组织冒泡行为,却可以阻止React合成事件的传播。
  • 事件的绑定方式:原生事件系统中支持多种不同的绑定事件的方式,React中只有一种。
  • 事件对象:原生中存在IE的兼容性问题,React做了兼容处理。

11、React中为什么要给组件设置key?

在开发过程中,我们需要保证某个元素的key在其同级元素中具有唯一性。

在React Diff算法中React会借助元素的Key值来判断该元素是新创建的还是被移动而来的元素,从而减少不必要的元素重新渲染。此外,React还需要借助Key值来判断元素与本都状态的关联关系。

12、setState之后发生了什么?

简述:React利用状态队列机制实现了setState的“异步”更新,避免频繁的重复更新state。首先将新的state合并到状态更新队列分钟,然后根据更新队列和shouldComponentUpdate的状态来判断是否需要更新组件。

深层

  • enqueueSetState将state放入队列分钟,并调用enqueueUpdate处理要更新的Component。

  • 如果组件当前正处理update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。

  • batchedUpdates发起一次transaction.perform()事务

  • 开始执行事务初始化,运行,结束三个阶段

    • 初始化:事务初始化阶段没有注册方法,故无方法要执行
    • 运行:执行setState时传入的callback方法
    • 结束:更新isBachingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法,FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的 dirtyComponents,调用updateComponent 刷新组件,并执行它的 pendingCallbacks, 也就是 setState 中设置的 callback。

13、在shouldComponentUpdate或componentWillUpdate中使用setState会发生什么?

当调用setState时,会将新的state合并到状态更新队列中,并对paratialState以及pendingStateQueue更新队列进行合并操作。最终通过enquueUpdate执行state更新。如果在 shouldComponentUpdate 或 componentWillUpdate 中使用 setState,会使得 state 队列( pendingStateQueue)不为 null,从而调用 updateComponent 方法,updateComponent 中会继续调用 shouldComponentUpdate 和 componentWillUpdate,因此造成死循环。

14、React中的ref有什么用?

使用refs获取组件被调用时会新建一个该组建的实例。refs会指向这个实例,可以是一个回调函数,回调函数会在组件被挂载之后立即执行。

如果吧refs放到原生DOM组件的input中,我们就可以通过refs得到DOM节点;如果把refs放到React组件中,那得到的是组件的实例,因此就可以调用实例的方法(如果想访问该组件的真实DOM,那么可以用React.findDomNode来找到DOM节点,但不建议使用次方法。)

refs无法用于无状态组件,无状态组件挂载时只是方法调用,没有新建实例。在v16.8之后,可以使用hooks中的useRef。

15、React 的虚拟dom是怎么实现的?

React是把真实DOM树转化为JS对象树,也就是Vitual DOM。每次数据更新后,重新计算VM。并和上一次生成的VM树进行对比,对发生变化的部分进行批量更新。除了性能之外VM的实现最大的好处在于和其他平台的集成。例如:

<button class="btn">
    <span>this is button</span>
</button>

在转化为VM之后就变成:

{
    type:'button',
    props:{
        className:"btn",
        props:{
            type:"text",
            children:"this is button"
        }
    }
}

16、为什么React的VM可以提高性能?

因为VM并不是真实的操作DOM,通过diff算法可以避免一些不必要的DOM操作,从而提高了性能。

17、React中的VM一定会提高性能吗?

不一定。因为VM只是通过diff算法避免了一些不需要变更的DOM操作,最终还是要操作DOM的,并且diff 的过程也是有成本的。

对于某些场景,比如都是需要变更DOM的操作,因为VM还会有额外的diff算法的成本在里面,所以VM的方式并不会提高性能,甚至比原生DOM要慢。

框架的意义在于为你掩盖底层的DOM操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。

没有任何框架可以比纯手动的优化DOM操作更快,因为框架的DOM操作层需要应对任何上层API可能产生的操作,它的实现必须是普适的。

18、 React Kooks带来了什么便利?

在没有hooks之前,我们使用函数定义的组件中,不能使用React的state、各种生命周期钩子类组件的特性。在React16.8之后,推出了新功能:Hooks,通过hooks我们可以在函数定义的组件中使用类组件的特性。

好处:

  • 跨组件复用:其实render、props/HOC也是为了复用,相比于它们,Hooks作为官方 底层的API,最为轻量,而且改造成本小,不会影响原来的组件层次结构和传说中的嵌套地狱

  • 相比而言,类组件的实现更为复杂:

    • 不同的生命周期会使逻辑变得分散且混乱,不易维护和管理
    • 时刻需要关注this的指向问题
    • 代码复用代价高,高阶组件的使用经常会使整个组件树变得臃肿
  • 状态与UI隔离:正是由于Hooks的特性,状态逻辑会变成更小的粒度,并且极容易被抽象成一个自定义的Hooks,组件中的状态和UI变得更为清晰和隔离

注意

  • 避免在循环、条件判断、嵌套函数 中调用hooks,保证调用顺序的稳定
  • 不能在useEffect中使用useState,React会报错提示
  • 类组件不会替换或废弃,不需要强制改造类组件,两种方式能并存。