React中stateprops分别是什么?

props是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的props来重新渲染子组件,否则子组件的props以及展现形式不会改变。
state的主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。

React的生命周期有哪些?

  • 组件的初始化阶段:
    • constructor(): 用于绑定事件以及初始化state(可以通过"fork"props的方式给state赋值)
    • componentWillMount(): 只会在服务端渲染时被调用,你可以在这里同步操作state
    • render(): 这个函数是用来渲染DOM没有错。但它只能用来渲染DOM,请保证它的纯粹性。如果有操作DOM或者和浏览器打交道的一系列操作,请在下一步骤componentDidMount中进行
    • componentDidMount(): 如果你有第三方操作DOM的类库需要初始化(类似于jQueryBootstrap的一些组件)操作DOM、或者请求异步数据,都应该放在这个步骤中做
  • 组件更新阶段:
    • componentWillReceiveProps(nextProps): 在这里你可以拿到即将改变的状态,可以在这一步中通过setState方法设置state
    • shouldComponentUpdate(nextProps, nextState): 这一步骤非常重要,它的返回值决定了接下来的生命周期函数是否会被调用,默认返回true,即都会被调用;你也可以重写这个函数使它返回false。
    • componentWillUpdate(): 我也不知道这个声明周期函数的意义在哪里,在这个函数内你不能调用setState改变组件状态
    • render()
    • componentDidUpdate(): 和componentDidMount类似,在这里执行DOM操作以及发起网络请求
  • 组件析构阶段:
    • componentWillUnmount(): 主要用于执行一些清理工作,比如取消网络请求,清楚多余的DOM元素等

组件的Render函数在何时被调用

如果单纯、侠义的回答这个问题,毫无疑问Render是在组件 state 发生改变时候被调用。无论是通过 setState 函数改变组件自身的state值,还是继承的 props 属性发生改变都会造成render函数被调用,即使改变的前后值都是一样的。

React组件中存在两类DOM,一类是众所周知的Virtual DOM,相信大家也耳熟能详了;另一类就是浏览器中的真实DOM(Real DOM/Native DOM)。React的Render函数被调用之后,React立即根据props或者state重新创建了一颗Virtual DOM Tree,虽然每一次调用时都重新创建,但因为在内存中创建DOM树其实是非常快且不影响性能的,所以这一步的开销并不大。而Virtual DOM的更新并不意味这Real DOM的更新,接下来的事情也是大家知道的,React采用算法将Virtual DOMReal DOM进行对比,找出需要更新的最小步骤,此时Real DOM才可能发生修改。

每一次的state更改都会使得render函数被调用,但页面的DOM不一定会发生修改

this.props.children是什么?

它表示组件的所有子节点,值有三种可能:如果当前组件没有子节点,它就是 undefined;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array。 React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object

简单介绍下react的diff

传统 diff 算法的复杂度为 O(n^3),显然这是无法满足性能要求的。React 通过制定大胆的策略,将 O(n^3) 复杂度的问题转换成 O(n) 复杂度的问题。
react的diff 策略:

  • Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。
  • 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
  • 对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。

基于以上三个前提策略,React 分别对 tree diffcomponent diff 以及 element diff 进行算法优化,事实也证明这三个前提策略是合理且准确的,它保证了整体界面构建的性能。

  • tree diff
    基于策略一,React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较。

既然 DOM 节点跨层级的移动操作少到可以忽略不计,针对这一现象,React 通过 updateDepth 对 Virtual DOM 树进行层级控制,只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。

  • component diff
    • 如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
    • 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
    • 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。
  • element diff
    允许开发者对同一层级的同组子节点,添加唯一 key 进行区分

更多深入讲解可以参考这篇文章,讲的非常好React 源码剖析系列 - 不可思议的 react diff

调用 setState 之后发生了什么?

在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

React 中 Element 与 Component 的区别是?

简单而言,React Element 是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 就是利用 JSX 构建的声明式代码片然后被转化为createElement的调用组合。而 React Component 则是可以接收参数输入并且返回某个 React Element 的函数或者类。

在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?

在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。

React组件还有哪些具体的设计模式?

  • Higher-Order Components (HOC高阶组件)
    通过函数向现有组件类添加逻辑,就是高阶组件。
  • Container Components
    我们把数据逻辑部分分离出来成为独立的组件,这类组件就是Container Components,而展现部分组件则是Presentational Components
  • Stateless Components
    自己不维护状态而是依靠外部传入的状态

React 中 refs 的作用是什么?

Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加ref属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回

Controlled Component 与 Uncontrolled Component 之间的区别是什么?

React 的核心组成之一就是能够维持内部状态的自治组件,不过当我们引入原生的HTML表单元素时(input,select,textarea 等),我们是否应该将所有的数据托管到 React 组件中还是将其仍然保留在 DOM 元素中呢?这个问题的答案就是受控组件与非受控组件的定义分割。受控组件(Controlled Component)代指那些交由 React 控制并且所有的表单数据统一存放的组件。譬如下面这段代码中username变量值并没有存放到DOM元素中,而是存放在组件状态数据中。任何时候我们需要改变username变量值时,我们应当调用setState函数进行修改。
而非受控组件(Uncontrolled Component)则是由DOM存放表单数据,并非存放在 React 组件中。我们可以使用 refs 来操控DOM元素。

在生命周期中的哪一步你应该发起 AJAX 请求?

我们应当将AJAX 请求放到 componentDidMount 函数中执行,主要原因有下:

  • React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount。如果我们将 AJAX 请求放到 componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。
  • 如果我们将 AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题。

概述下 React 中的事件处理逻辑

为了解决跨浏览器兼容性问题,React 会将浏览器原生事件(Browser Native Event)封装为合成事件(SyntheticEvent)传入设置的事件处理器中。这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外有意思的是,React 并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理。这样 React 在更新 DOM 的时候就不需要考虑如何去处理附着在 DOM 上的事件监听器,最终达到优化性能的目的。

传入 setState 函数的第二个参数的作用是什么?

该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成