state 和 props 触发更新的生命周期别离有什么区别?

state 更新流程: 这个过程当中波及的函数:

  1. shouldComponentUpdate: 当组件的 state 或 props 产生扭转时,都会首先触发这个生命周期函数。它会接管两个参数:nextProps, nextState——它们别离代表传入的新 props 和新的 state 值。拿到这两个值之后,咱们就能够通过一些比照逻辑来决定是否有 re-render(重渲染)的必要了。如果该函数的返回值为 false,则生命周期终止,反之持续;
留神:此办法仅作为性能优化的形式而存在。不要希图依附此办法来“阻止”渲染,因为这可能会产生 bug。应该思考应用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()
  1. componentWillUpdate:当组件的 state 或 props 产生扭转时,会在渲染之前调用 componentWillUpdate。componentWillUpdate 是 React16 废除的三个生命周期之一。过来,咱们可能心愿能在这个阶段去收集一些必要的信息(比方更新前的 DOM 信息等等),当初咱们齐全能够在 React16 的 getSnapshotBeforeUpdate 中去做这些事;
  2. componentDidUpdate:componentDidUpdate() 会在UI更新后会被立刻调用。它接管 prevProps(上一次的 props 值)作为入参,也就是说在此处咱们依然能够进行 props 值比照(再次阐明 componentWillUpdate 的确鸡肋哈)。

props 更新流程: 绝对于 state 更新,props 更新后惟一的区别是减少了对 componentWillReceiveProps 的调用。对于 componentWillReceiveProps,须要晓得这些事件:

  • componentWillReceiveProps:它在Component承受到新的 props 时被触发。componentWillReceiveProps 会接管一个名为 nextProps 的参数(对应新的 props 值)。该生命周期是 React16 废除掉的三个生命周期之一。在它被废除前,能够用它来比拟 this.props 和 nextProps 来从新setState。在 React16 中,用一个相似的新生命周期 getDerivedStateFromProps 来代替它。

React组件的state和props有什么区别?

(1)props

props是一个从内部传进组件的参数,次要作为就是从父组件向子组件传递数据,它具备可读性和不变性,只能通过内部组件被动传入新的props来从新渲染子组件,否则子组件的props以及展示模式不会扭转。

(2)state

state的次要作用是用于组件保留、管制以及批改本人的状态,它只能在constructor中初始化,它算是组件的公有属性,不可通过内部拜访和批改,只能通过组件外部的this.setState来批改,批改state属性会导致组件的从新渲染。

(3)区别

  • props 是传递给组件的(相似于函数的形参),而state 是在组件内被组件本人治理的(相似于在一个函数内申明的变量)。
  • props 是不可批改的,所有 React 组件都必须像纯函数一样爱护它们的 props 不被更改。
  • state 是在组件中创立的,个别在 constructor中初始化 state。state 是多变的、能够批改,每次setState都异步更新的。

React中什么是受控组件和非控组件?

(1)受控组件 在应用表单来收集用户输出时,例如<input><select><textearea>等元素都要绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,更新组件的state。这种组件在React中被称为受控组件,在受控组件中,组件渲染出的状态与它的value或checked属性绝对应,react通过这种形式打消了组件的部分状态,使整个状态可控。react官网举荐应用受控表单组件。

受控组件更新state的流程:

  • 能够通过初始state中设置表单的默认值
  • 每当表单的值发生变化时,调用onChange事件处理器
  • 事件处理器通过事件对象e拿到扭转后的状态,并更新组件的state
  • 一旦通过setState办法更新state,就会触发视图的从新渲染,实现表单组件的更新

受控组件缺点: 表单元素的值都是由React组件进行治理,当有多个输入框,或者多个这种组件时,如果想同时获取到全副的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种状况,呈现了非受控组件。

(2)非受控组件 如果一个表单组件没有value props(单选和复选按钮对应的是checked props)时,就能够称为非受控组件。在非受控组件中,能够应用一个ref来从DOM取得表单值。而不是为每个状态更新编写一个事件处理程序。

React官网的解释:

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你能够应用 ref来从 DOM 节点中获取表单数据。
因为非受控组件将实在数据贮存在 DOM 节点中,所以在应用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码好看性,并且心愿疾速编写代码,应用非受控组件往往能够缩小你的代码量。否则,你应该应用受控组件。

例如,上面的代码在非受控组件中接管单个属性:

class NameForm extends React.Component {  constructor(props) {    super(props);    this.handleSubmit = this.handleSubmit.bind(this);  }  handleSubmit(event) {    alert('A name was submitted: ' + this.input.value);    event.preventDefault();  }  render() {    return (      <form onSubmit={this.handleSubmit}>        <label>          Name:          <input type="text" ref={(input) => this.input = input} />        </label>        <input type="submit" value="Submit" />      </form>    );  }}

总结: 页面中所有输出类的DOM如果是现用现取的称为非受控组件,而通过setState将输出的值保护到了state中,须要时再从state中取出,这里的数据就受到了state的管制,称为受控组件。

如何防止反复发动ajax获取数据?

  • 数据放在redux外面

在应用 React Router时,如何获取以后页面的路由或浏览器中地址栏中的地址?

在以后组件的 props中,蕴含 location属性对象,蕴含以后页面路由地址信息,在 match中存储以后路由的参数等数据信息。能够间接通过 this .props应用它们。

connect原理

  • 首先connect之所以会胜利,是因为Provider组件:
  • 在原利用组件上包裹一层,使原来整个利用成为Provider的子组件 接管Reduxstore作为props,通过context对象传递给子孙组件上的connect
connect做了些什么。它真正连贯 ReduxReact,它包在咱们的容器组件的外一层,它接管下面 Provider 提供的 store 外面的statedispatch,传给一个构造函数,返回一个对象,以属性模式传给咱们的容器组件
  • connect是一个高阶函数,首先传入mapStateToPropsmapDispatchToProps,而后返回一个生产Component的函数(wrapWithConnect),而后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个通过包裹的Connect组件,

该组件具备如下特点

  • 通过props.store获取先人Componentstore props包含statePropsdispatchPropsparentProps,合并在一起失去nextState,作为props传给真正的Component componentDidMount时,增加事件this.store.subscribe(this.handleChange),实现页面交互
  • shouldComponentUpdate时判断是否有防止进行渲染,晋升页面性能,并失去nextState componentWillUnmount时移除注册的事件this.handleChange
因为connect的源码过长,咱们只看次要逻辑
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {  return function wrapWithConnect(WrappedComponent) {    class Connect extends Component {      constructor(props, context) {        // 从先人Component处取得store        this.store = props.store || context.store        this.stateProps = computeStateProps(this.store, props)        this.dispatchProps = computeDispatchProps(this.store, props)        this.state = { storeState: null }        // 对stateProps、dispatchProps、parentProps进行合并        this.updateState()      }      shouldComponentUpdate(nextProps, nextState) {        // 进行判断,当数据产生扭转时,Component从新渲染        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {          this.updateState(nextProps)            return true          }        }        componentDidMount() {          // 扭转Component的state          this.store.subscribe(() = {            this.setState({              storeState: this.store.getState()            })          })        }        render() {          // 生成包裹组件Connect          return (            <WrappedComponent {...this.nextState} />          )        }      }      Connect.contextTypes = {        store: storeShape      }      return Connect;    }  }

参考 前端进阶面试题具体解答

redux 有什么毛病

  • 一个组件所须要的数据,必须由父组件传过来,而不能像 flux 中间接从 store 取
  • 当一个组件相干数据更新时,即便父组件不须要用到这个组件,父组件还是会从新 render,可能会有效率影响,或者须要写简单的 shouldComponentUpdate 进行判断

react 的虚构dom是怎么实现的

首先说说为什么要应用Virturl DOM,因为操作实在DOM的消耗的性能代价太高,所以react外部应用js实现了一套dom构造,在每次操作在和实在dom之前,应用实现好的diff算法,对虚构dom进行比拟,递归找出有变动的dom节点,而后对其进行更新操作。为了实现虚构DOM,咱们须要把每一种节点类型形象成对象,每一种节点类型有本人的属性,也就是prop,每次进行diff的时候,react会先比拟该节点类型,如果节点类型不一样,那么react会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟prop是否有更新,如果有prop不一样,那么react会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点

哪些办法会触发 React 从新渲染?从新渲染 render 会做些什么?

(1)哪些办法会触发 react 从新渲染?

  • setState()办法被调用

setState 是 React 中最罕用的命令,通常状况下,执行 setState 会触发 render。然而这里有个点值得关注,执行 setState 的时候不肯定会从新渲染。当 setState 传入 null 时,并不会触发 render。

class App extends React.Component {  state = {    a: 1  };  render() {    console.log("render");    return (      <React.Fragement>        <p>{this.state.a}</p>        <button          onClick={() => {            this.setState({ a: 1 }); // 这里并没有扭转 a 的值          }}        >          Click me        </button>        <button onClick={() => this.setState(null)}>setState null</button>        <Child />      </React.Fragement>    );  }}
  • 父组件从新渲染

只有父组件从新渲染了,即便传入子组件的 props 未发生变化,那么子组件也会从新渲染,进而触发 render

(2)从新渲染 render 会做些什么?

  • 会对新旧 VNode 进行比照,也就是咱们所说的Diff算法。
  • 对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行比照,如果有差别就放到一个对象外面
  • 遍历差别对象,依据差别的类型,依据对应对规定更新VNode

React 的解决 render 的根本思维模式是每次一有变动就会去从新渲染整个利用。在 Virtual DOM 没有呈现之前,最简略的办法就是间接调用 innerHTML。Virtual DOM厉害的中央并不是说它比间接操作 DOM 快,而是说不论数据怎么变,都会尽量以最小的代价去更新 DOM。React 将 render 函数返回的虚构 DOM 树与老的进行比拟,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特地是在顶层 setState 一个渺小的批改,默认会去遍历整棵树。只管 React 应用高度优化的 Diff 算法,然而这个过程依然会损耗性能.

为什么React并不举荐优先思考应用Context?

  • Context目前还处于试验阶段,可能会在前面的发行版本中有很大的变动,事实上这种状况曾经产生了,所以为了防止给今后降级带来大的影响和麻烦,不倡议在app中应用context。
  • 只管不倡议在app中应用context,然而独有组件而言,因为影响范畴小于app,如果能够做到高内聚,不毁坏组件树之间的依赖关系,能够思考应用context
  • 对于组件之间的数据通信或者状态治理,无效应用props或者state解决,而后再思考应用第三方的成熟库进行解决,以上的办法都不是最佳的计划的时候,在思考context。
  • context的更新须要通过setState()触发,然而这并不是很牢靠的,Context反对跨组件的拜访,然而如果两头的子组件通过一些办法不影响更新,比方 shouldComponentUpdate() 返回false 那么不能保障Context的更新肯定能够应用Context的子组件,因而,Context的可靠性须要关注

react hooks,它带来了那些便当

  • 代码逻辑聚合,逻辑复用
  • HOC嵌套天堂
  • 代替class
React 中通常应用 类定义 或者 函数定义 创立组件:

在类定义中,咱们能够应用到许多 React 个性,例如 state、 各种组件生命周期钩子等,然而在函数定义中,咱们却无能为力,因而 React 16.8 版本推出了一个新性能 (React Hooks),通过它,能够更好的在函数定义组件中应用 React 个性。

益处:

  1. 跨组件复用: 其实 render props / HOC 也是为了复用,相比于它们,Hooks 作为官网的底层 API,最为轻量,而且革新老本小,不会影响原来的组件层次结构和传说中的嵌套天堂;
  2. 类定义更为简单
  3. 不同的生命周期会使逻辑变得扩散且凌乱,不易保护和治理;
  • 时刻须要关注this的指向问题;
  • 代码复用代价高,高阶组件的应用常常会使整个组件树变得臃肿;
  • 状态与UI隔离: 正是因为 Hooks 的个性,状态逻辑会变成更小的粒度,并且极容易被形象成一个自定义 Hooks,组件中的状态和 UI 变得更为清晰和隔离。

留神:

  • 防止在 循环/条件判断/嵌套函数 中调用 hooks,保障调用程序的稳固;
  • 只有 函数定义组件 和 hooks 能够调用 hooks,防止在 类组件 或者 一般函数 中调用;
  • 不能在useEffect中应用useState,React 会报错提醒;
  • 类组件不会被替换或废除,不须要强制革新类组件,两种形式能并存;

重要钩子

  1. 状态钩子 (useState): 用于定义组件的 State,其到类定义中this.state的性能;
// useState 只承受一个参数: 初始状态// 返回的是组件名和更改该组件对应的函数const [flag, setFlag] = useState(true);// 批改状态setFlag(false)// 下面的代码映射到类定义中:this.state = {    flag: true    }const flag = this.state.flagconst setFlag = (bool) => {    this.setState({        flag: bool,    })}
  1. 生命周期钩子 (useEffect):
类定义中有许多生命周期函数,而在 React Hooks 中也提供了一个相应的函数 (useEffect),这里能够看做componentDidMount、componentDidUpdate和componentWillUnmount的联合。

useEffect(callback, [source])承受两个参数

  • callback: 钩子回调函数;
  • source: 设置触发条件,仅当 source 产生扭转时才会触发;
  • useEffect钩子在没有传入[source]参数时,默认在每次 render 时都会优先调用上次保留的回调中返回的函数,后再从新调用回调;
useEffect(() => {    // 组件挂载后执行事件绑定    console.log('on')    addEventListener()    // 组件 update 时会执行事件解绑    return () => {        console.log('off')        removeEventListener()    }}, [source]);// 每次 source 产生扭转时,执行后果(以类定义的生命周期,便于大家了解):// --- DidMount ---// 'on'// --- DidUpdate ---// 'off'// 'on'// --- DidUpdate ---// 'off'// 'on'// --- WillUnmount --- // 'off'

通过第二个参数,咱们便可模拟出几个罕用的生命周期:

  • componentDidMount: 传入[]时,就只会在初始化时调用一次
const useMount = (fn) => useEffect(fn, [])
  • componentWillUnmount: 传入[],回调中的返回的函数也只会被最终执行一次
const useUnmount = (fn) => useEffect(() => fn, [])
  • mounted: 能够应用 useState 封装成一个高度可复用的 mounted 状态;
const useMounted = () => {    const [mounted, setMounted] = useState(false);    useEffect(() => {        !mounted && setMounted(true);        return () => setMounted(false);    }, []);    return mounted;}
  • componentDidUpdate: useEffect每次均会执行,其实就是排除了 DidMount 后即可;
const mounted = useMounted() useEffect(() => {    mounted && fn()})
  1. 其它内置钩子:
  2. useContext: 获取 context 对象
  • useReducer: 相似于 Redux 思维的实现,但其并不足以代替 Redux,能够了解成一个组件外部的 redux:

    • 并不是长久化存储,会随着组件被销毁而销毁;
    • 属于组件外部,各个组件是互相隔离的,单纯用它并无奈共享数据;
    • 配合useContext`的全局性,能够实现一个轻量级的 Redux;(easy-peasy)
  • useCallback: 缓存回调函数,防止传入的回调每次都是新的函数实例而导致依赖组件从新渲染,具备性能优化的成果;
  • useMemo: 用于缓存传入的 props,防止依赖的组件每次都从新渲染;
  • useRef: 获取组件的实在节点;
  • useLayoutEffect

    • DOM更新同步钩子。用法与useEffect相似,只是区别于执行工夫点的不同
    • useEffect属于异步执行,并不会期待 DOM 真正渲染后执行,而useLayoutEffect则会真正渲染后才触发;
    • 能够获取更新后的 state;
  • 自定义钩子(useXxxxx): 基于 Hooks 能够援用其它 Hooks 这个个性,咱们能够编写自定义钩子,如下面的useMounted。又例如,咱们须要每个页面自定义题目:
function useTitle(title) {  useEffect(    () => {      document.title = title;    });}// 应用:function Home() {    const title = '我是首页'    useTitle(title)    return (        <div>{title}</div>    )}

React中发动网络申请应该在哪个生命周期中进行?为什么?

对于异步申请,最好放在componentDidMount中去操作,对于同步的状态扭转,能够放在componentWillMount中,个别用的比拟少。

如果认为在componentWillMount里发动申请能提前取得后果,这种想法其实是谬误的,通常componentWillMount比componentDidMount早不了多少微秒,网络上任何一点提早,这一点差别都可忽略不计。

react的生命周期: constructor() -> componentWillMount() -> render() -> componentDidMount()

下面这些办法的调用是有秩序的,由上而下顺次调用。

  • constructor被调用是在组件筹备要挂载的最开始,此时组件尚未挂载到网页上。
  • componentWillMount办法的调用在constructor之后,在render之前,在这办法里的代码调用setState办法不会触发从新render,所以它个别不会用来作加载数据之用。
  • componentDidMount办法中的代码,是在组件曾经齐全挂载到网页上才会调用被执行,所以能够保证数据的加载。此外,在这办法中调用setState办法,会触发从新渲染。所以,官网设计这个办法就是用来加载内部数据用的,或解决其余的副作用代码。与组件上的数据无关的加载,也能够在constructor里做,但constructor是做组件state初绐化工作,并不是做加载数据这工作的,constructor里也不能setState,还有加载的工夫太长或者出错,页面就无奈加载进去。所以有副作用的代码都会集中在componentDidMount办法里。

总结:

  • 跟服务器端渲染(同构)有关系,如果在componentWillMount外面获取数据,fetch data会执行两次,一次在服务器端一次在客户端。在componentDidMount中能够解决这个问题,componentWillMount同样也会render两次。
  • 在componentWillMount中fetch data,数据肯定在render后能力达到,如果遗记了设置初始状态,用户体验不好。
  • react16.0当前,componentWillMount可能会被执行屡次。

diff算法?

  • 把树形构造依照层级合成,只比拟同级元素。
  • 给列表构造的每个单元增加惟一的key属性,不便比拟。
  • React 只会匹配雷同 classcomponent(这外面的class指的是组件的名字)
  • 合并操作,调用 componentsetState 办法的时候, React 将其标记为 - dirty.到每一个事件循环完结, React 查看所有标记 dirtycomponent从新绘制.
  • 抉择性子树渲染。开发人员能够重写shouldComponentUpdate进步diff的性能

Component, Element, Instance 之间有什么区别和分割?

  • 元素: 一个元素element是一个一般对象(plain object),形容了对于一个DOM节点或者其余组件component,你想让它在屏幕上出现成什么样子。元素element能够在它的属性props中蕴含其余元素(译注:用于造成元素树)。创立一个React元素element老本很低。元素element创立之后是不可变的。
  • 组件: 一个组件component能够通过多种形式申明。能够是带有一个render()办法的类,简略点也能够定义为一个函数。这两种状况下,它都把属性props作为输出,把返回的一棵元素树作为输入。
  • 实例: 一个实例instance是你在所写的组件类component class中应用关键字this所指向的货色(译注:组件实例)。它用来存储本地状态和响应生命周期事件很有用。

函数式组件(Functional component)基本没有实例instance。类组件(Class component)有实例instance,然而永远也不须要间接创立一个组件的实例,因为React帮咱们做了这些。

React中refs的作用是什么?有哪些利用场景?

Refs 提供了一种形式,用于拜访在 render 办法中创立的 React 元素或 DOM 节点。Refs 应该审慎应用,如下场景应用 Refs 比拟适宜:

  • 解决焦点、文本抉择或者媒体的管制
  • 触发必要的动画
  • 集成第三方 DOM 库

Refs 是应用 React.createRef() 办法创立的,他通过 ref 属性附加到 React 元素上。
要在整个组件中应用 Refs,须要将 ref 在构造函数中调配给其实例属性:

class MyComponent extends React.Component {  constructor(props) {    super(props)    this.myRef = React.createRef()  }  render() {    return <div ref={this.myRef} />  }}

如何用 React构建( build)生产模式?

通常,应用 Webpack的 DefinePlugin办法将 NODE ENV设置为 production。这将剥离 propType验证和额定的正告。除此之外,还能够缩小代码,因为 React应用 Uglify的dead-code来打消开发代码和正文,这将大大减少包占用的空间。

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

为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。
这里的合成事件提供了与原生事件雷同的接口,不过它们屏蔽了底层浏览器的细节差别,保障了行为的一致性。另外, React并没有间接将事件附着到子元素上,而是以繁多事件监听器的形式将所有的事件发送到顶层进行解决(基于事件委托原理)。
这样 React在更新DOM时就不须要思考如何解决附着在DOM上的事件监听器,最终达到优化性能的目标。

说说 React组件开发中对于作用域的常见问题。

在 EMAScript5语法标准中,对于作用域的常见问题如下。
(1)在map等办法的回调函数中,要绑定作用域this(通过bind办法)。
(2)父组件传递给子组件办法的作用域是父组件实例化对象,无奈扭转。
(3)组件事件回调函数办法的作用域是组件实例化对象(绑定父组件提供的办法就是父组件实例化对象),无奈扭转。
在 EMAScript6语法标准中,对于作用域的常见问题如下。
(1)当应用箭头函数作为map等办法的回调函数时,箭头函数的作用域是以后组件的实例化对象(即箭头函数的作用域是定义时的作用域),毋庸绑定作用域。
(2)事件回调函数要绑定组件作用域。
(3)父组件传递办法要绑定父组件作用域。
总之,在 EMAScript6语法标准中,组件办法的作用域是能够扭转的。

React中能够在render拜访refs吗?为什么?

<>  <span id="name" ref={this.spanRef}>{this.state.title}</span>  <span>{     this.spanRef.current ? '有值' : '无值'  }</span></>

不能够,render 阶段 DOM 还没有生成,无奈获取 DOM。DOM 的获取须要在 pre-commit 阶段和 commit 阶段:

refs的作用是什么,你在什么样的业务场景下应用refs

  • 操作DOM,为什么操作DOM?
  • 场景

    • 图片渲染好后,操作图片宽高。比方做个放大镜性能