什么是 React Fiber?

Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构DOM的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染:可能将渲染工作宰割成块,并将其扩散到多个帧中。

Redux 原理及工作流程

(1)原理 Redux源码次要分为以下几个模块文件

  • compose.js 提供从右到左进行函数式编程
  • createStore.js 提供作为生成惟一store的函数
  • combineReducers.js 提供合并多个reducer的函数,保障store的唯一性
  • bindActionCreators.js 能够让开发者在不间接接触dispacth的前提下进行更改state的操作
  • applyMiddleware.js 这个办法通过中间件来加强dispatch的性能
const actionTypes = {    ADD: 'ADD',    CHANGEINFO: 'CHANGEINFO',}const initState = {    info: '初始化',}export default function initReducer(state=initState, action) {    switch(action.type) {        case actionTypes.CHANGEINFO:            return {                ...state,                info: action.preload.info || '',            }        default:            return { ...state };    }}export default function createStore(reducer, initialState, middleFunc) {    if (initialState && typeof initialState === 'function') {        middleFunc = initialState;        initialState = undefined;    }    let currentState = initialState;    const listeners = [];    if (middleFunc && typeof middleFunc === 'function') {        // 封装dispatch         return middleFunc(createStore)(reducer, initialState);    }    const getState = () => {        return currentState;    }    const dispatch = (action) => {        currentState = reducer(currentState, action);        listeners.forEach(listener => {            listener();        })    }    const subscribe = (listener) => {        listeners.push(listener);    }    return {        getState,        dispatch,        subscribe    }}

(2)工作流程

  • const store= createStore(fn)生成数据;
  • action: {type: Symble('action01), payload:'payload' }定义行为;
  • dispatch发动action:store.dispatch(doSomething('action001'));
  • reducer:解决action,返回新的state;

艰深点解释:

  • 首先,用户(通过View)收回Action,收回形式就用到了dispatch办法
  • 而后,Store主动调用Reducer,并且传入两个参数:以后State和收到的Action,Reducer会返回新的State
  • State—旦有变动,Store就会调用监听函数,来更新View

以 store 为外围,能够把它看成数据存储核心,然而他要更改数据的时候不能间接批改,数据批改更新的角色由Reducers来负责,store只做存储,中间人,当Reducers的更新实现当前会通过store的订阅来告诉react component,组件把新的状态从新获取渲染,组件中也能被动发送action,创立action后这个动作是不会执行的,所以要dispatch这个action,让store通过reducers去做更新React Component 就是react的每个组件。

高阶组件

高阶函数:如果一个函数承受一个或多个函数作为参数或者返回一个函数就可称之为高阶函数

高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件

react 中的高阶组件

React 中的高阶组件次要有两种模式:属性代理反向继承

属性代理 Proxy

  • 操作 props
  • 抽离 state
  • 通过 ref 拜访到组件实例
  • 用其余元素包裹传入的组件 WrappedComponent

反向继承

会发现其属性代理和反向继承的实现有些相似的中央,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent

反向继承能够用来做什么:

1.操作 state

高阶组件中能够读取、编辑和删除WrappedComponent组件实例中的state。甚至能够减少更多的state项,然而十分不倡议这么做因为这可能会导致state难以保护及治理。

function withLogging(WrappedComponent) {        return class extends WrappedComponent {            render() {                return (                    <div>;                        <h2>;Debugger Component Logging...<h2>;                        <p>;state:<p>;                        <pre>;{JSON.stringify(this.state, null, 4)}<pre>;                        <p>props:<p>;                        <pre>{JSON.stringify(this.props, null, 4)}<pre>;                        {super.render()}                    <div>;                );            }        };    }

2.渲染劫持(Render Highjacking)

条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。

批改由 render() 输入的 React 元素树

说说你用react有什么坑点?

1. JSX做表达式判断时候,须要强转为boolean类型

如果不应用 !!b 进行强转数据类型,会在页面外面输入 0
render() {  const b = 0;  return <div>    {      !!b && <div>这是一段文本</div>    }  </div>}

2. 尽量不要在 componentWillReviceProps 里应用 setState,如果肯定要应用,那么须要判断完结条件,不然会呈现有限重渲染,导致页面解体

3. 给组件增加ref时候,尽量不要应用匿名函数,因为当组件更新的时候,匿名函数会被当做新的prop解决,让ref属性承受到新函数的时候,react外部会先清空ref,也就是会以null为回调参数先执行一次ref这个props,而后在以该组件的实例执行一次ref,所以用匿名函数做ref的时候,有的时候去ref赋值后的属性会取到null

4. 遍历子节点的时候,不要用 index 作为组件的 key 进行传入

参考:前端react面试题具体解答

React-Router的实现原理是什么?

客户端路由实现的思维:

  • 基于 hash 的路由:通过监听hashchange事件,感知 hash 的变动

    • 扭转 hash 能够间接通过 location.hash=xxx
  • 基于 H5 history 路由:

    • 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时可能利用 history.go() 等 API
    • 监听 url 的变动能够通过自定义事件触发实现

react-router 实现的思维:

  • 基于 history 库来实现上述不同的客户端路由实现思维,并且可能保留历史记录等,磨平浏览器差别,下层无感知
  • 通过保护的列表,在每次 URL 发生变化的回收,通过配置的 路由门路,匹配到对应的 Component,并且 render

什么是状态晋升

应用 react 常常会遇到几个组件须要共用状态数据的状况。这种状况下,咱们最好将这部分共享的状态晋升至他们最近的父组件当中进行治理。咱们来看一下具体如何操作吧。

import React from 'react'class Child_1 extends React.Component{    constructor(props){        super(props)    }    render(){        return (            <div>                <h1>{this.props.value+2}</h1>            </div>         )    }}class Child_2 extends React.Component{    constructor(props){        super(props)    }    render(){        return (            <div>                <h1>{this.props.value+1}</h1>            </div>         )    }}class Three extends React.Component {    constructor(props){        super(props)        this.state = {            txt:"牛逼"        }        this.handleChange = this.handleChange.bind(this)    }    handleChange(e){        this.setState({            txt:e.target.value        })    }    render(){       return (            <div>                <input type="text" value={this.state.txt} onChange={this.handleChange}/>                <p>{this.state.txt}</p>                <Child_1 value={this.state.txt}/>                <Child_2 value={this.state.txt}/>            </div>       )    }}export default Three

在React中组件的this.state和setState有什么区别?

this.state通常是用来初始化state的,this.setState是用来批改state值的。如果初始化了state之后再应用this.state,之前的state会被笼罩掉,如果应用this.setState,只会替换掉相应的state值。所以,如果想要批改state的值,就须要应用setState,而不能间接批改state,间接批改state之后页面是不会更新的。

React和vue.js的相似性和差异性是什么?

相似性如下。
(1)都是用于创立UI的 JavaScript库。
(2)都是疾速和轻量级的代码库(这里指 React外围库)。
(3)都有基于组件的架构。
(4)都应用虚构DOM。
(5)都能够放在独自的HTML文件中,或者放在 Webpack设置的一个更简单的模块中。
(6)都有独立但罕用的路由器和状态治理库。
它们最大的区别在于 Vue. js通常应用HTML模板文件,而 React齐全应用 JavaScript创立虚构DOM。 Vue. js还具备对于“可变状态”的“ reactivity”的从新渲染的自动化检测零碎。

React 中的key是什么?为什么它们很重要?

key能够帮忙 React跟踪循环创立列表中的虚构DOM元素,理解哪些元素已更改、增加或删除。
每个绑定key的虚构DOM元素,在兄弟元素之间都是举世无双的。在 React的和解过程中,比拟新的虛拟DOM树与上一个虛拟DOM树之间的差别,并映射到页面中。key使 React解决列表中虛拟DOM时更加高效,因为 React能够应用虛拟DOM上的key属性,疾速理解元素是新的、须要删除的,还是批改过的。如果没有key,Rat就不晓得列表中虚构DOM元素与页面中的哪个元素绝对应。所以在创立列表的时候,不要疏忽key。

React 16.X 中 props 扭转后在哪个生命周期中解决

在getDerivedStateFromProps中进行解决。

这个生命周期函数是为了代替componentWillReceiveProps存在的,所以在须要应用componentWillReceiveProps时,就能够思考应用getDerivedStateFromProps来进行代替。

两者的参数是不雷同的,而getDerivedStateFromProps是一个动态函数,也就是这个函数不能通过this拜访到class的属性,也并不举荐间接拜访属性。而是应该通过参数提供的nextProps以及prevState来进行判断,依据新传入的props来映射到state。

须要留神的是,如果props传入的内容不须要影响到你的state,那么就须要返回一个null,这个返回值是必须的,所以尽量将其写到函数的开端:

static getDerivedStateFromProps(nextProps, prevState) {    const {type} = nextProps;    // 当传入的type发生变化的时候,更新state    if (type !== prevState.type) {        return {            type,        };    }    // 否则,对于state不进行任何操作    return null;}

react性能优化是哪个周期函数

shouldComponentUpdate 这个办法用来判断是否须要调用render办法从新描述dom。因为dom的描述十分耗费性能,如果咱们能在shouldComponentUpdate方法中可能写出更优化的dom diff算法,能够极大的进步性能

跨级组件的通信形式?

父组件向子组件的子组件通信,向更深层子组件通信:

  • 应用props,利用两头组件层层传递,然而如果父组件构造较深,那么两头每一层组件都要去传递props,减少了复杂度,并且这些props并不是两头组件本人须要的。
  • 应用context,context相当于一个大容器,能够把要通信的内容放在这个容器中,这样不论嵌套多深,都能够随便取用,对于逾越多层的全局数据能够应用context实现。
// context形式实现跨级组件通信 // Context 设计目标是为了共享那些对于一个组件树而言是“全局”的数据const BatteryContext = createContext();//  子组件的子组件 class GrandChild extends Component {    render(){        return (            <BatteryContext.Consumer>                {                    color => <h1 style={{"color":color}}>我是红色的:{color}</h1>                }            </BatteryContext.Consumer>        )    }}//  子组件const Child = () =>{    return (        <GrandChild/>    )}// 父组件class Parent extends Component {      state = {          color:"red"      }      render(){          const {color} = this.state          return (          <BatteryContext.Provider value={color}>              <Child></Child>          </BatteryContext.Provider>          )      }}

什么是受控组件和非受控组件

  • 受状态管制的组件,必须要有onChange办法,否则不能应用 受控组件能够赋予默认值(官网举荐应用 受控组件) 实现双向数据绑定
class Input extends Component{    constructor(){        super();        this.state = {val:'100'}    }    handleChange = (e) =>{ //e是事件源        let val = e.target.value;        this.setState({val});    };    render(){        return (<div>            <input type="text" value={this.state.val} onChange={this.handleChange}/>            {this.state.val}        </div>)    }}
  • 非受控也就意味着我能够不须要设置它的state属性,而通过ref来操作实在的DOM
class Sum extends Component{    constructor(){        super();        this.state =  {result:''}    }    //通过ref设置的属性 能够通过this.refs获取到对应的dom元素    handleChange = () =>{        let result = this.refs.a.value + this.b.value;        this.setState({result});    };    render(){        return (            <div onChange={this.handleChange}>                <input type="number" ref="a"/>                {/*x代表的实在的dom,把元素挂载在了以后实例上*/}                <input type="number" ref={(x)=>{                    this.b = x;                }}/>                {this.state.result}            </div>        )    }}

在 React中元素( element)和组件( component)有什么区别?

简略地说,在 React中元素(虛拟DOM)形容了你在屏幕上看到的DOM元素。
换个说法就是,在 React中元素是页面中DOM元素的对象示意形式。在 React中组件是一个函数或一个类,它能够承受输出并返回一个元素。
留神:工作中,为了进步开发效率,通常应用JSX语法示意 React元素(虚构DOM)。在编译的时候,把它转化成一个 React. createElement调用办法。

介绍一下react

  1. 以前咱们没有jquery的时候,咱们大略的流程是从后端通过ajax获取到数据而后应用jquery生成dom后果而后更新到页面当中,然而随着业务倒退,咱们的我的项目可能会越来越简单,咱们每次申请到数据,或则数据有更改的时候,咱们又须要从新组装一次dom构造,而后更新页面,这样咱们手动同步dom和数据的老本就越来越高,而且频繁的操作dom,也使我咱们页面的性能缓缓的升高。
  2. 这个时候mvvm呈现了,mvvm的双向数据绑定能够让咱们在数据批改的同时同步dom的更新,dom的更新也能够间接同步咱们数据的更改,这个特定能够大大降低咱们手动去保护dom更新的老本,mvvm为react的个性之一,尽管react属于单项数据流,须要咱们手动实现双向数据绑定。
  3. 有了mvvm还不够,因为如果每次有数据做了更改,而后咱们都全量更新dom构造的话,也没方法解决咱们频繁操作dom构造(升高了页面性能)的问题,为了解决这个问题,react外部实现了一套虚构dom构造,也就是用js实现的一套dom构造,他的作用是讲实在dom在js中做一套缓存,每次有数据更改的时候,react外部先应用算法,也就是鼎鼎有名的diff算法对dom构造进行比照,找到那些咱们须要新增、更新、删除的dom节点,而后一次性对实在DOM进行更新,这样就大大降低了操作dom的次数。 那么diff算法是怎么运作的呢,首先,diff针对类型不同的节点,会间接断定原来节点须要卸载并且用新的节点来装载卸载的节点的地位;针对于节点类型雷同的节点,会比照这个节点的所有属性,如果节点的所有属性雷同,那么断定这个节点不须要更新,如果节点属性不雷同,那么会断定这个节点须要更新,react会更新并重渲染这个节点。
  4. react设计之初是次要负责UI层的渲染,尽管每个组件有本人的state,state示意组件的状态,当状态须要变动的时候,须要应用setState更新咱们的组件,然而,咱们想通过一个组件重渲染它的兄弟组件,咱们就须要将组件的状态晋升到父组件当中,让父组件的状态来管制这两个组件的重渲染,当咱们组件的档次越来越深的时候,状态须要始终往下传,无疑加大了咱们代码的复杂度,咱们须要一个状态管理中心,来帮咱们治理咱们状态state。
  5. 这个时候,redux呈现了,咱们能够将所有的state交给redux去治理,当咱们的某一个state有变动的时候,依赖到这个state的组件就会进行一次重渲染,这样就解决了咱们的咱们须要始终把state往下传的问题。redux有action、reducer的概念,action为惟一批改state的起源,reducer为惟一确定state如何变动的入口,这使得redux的数据流十分标准,同时也暴露出了redux代码的简单,原本那么简略的性能,却须要实现那么多的代码。
  6. 起初,社区就呈现了另外一套解决方案,也就是mobx,它推崇代码简洁易懂,只须要定义一个可观测的对象,而后哪个组价应用到这个可观测的对象,并且这个对象的数据有更改,那么这个组件就会重渲染,而且mobx外部也做好了是否重渲染组件的生命周期shouldUpdateComponent,不倡议开发者进行更改,这使得咱们应用mobx开发我的项目的时候能够简略疾速的实现很多性能,连redux的作者也举荐应用mobx进行我的项目开发。然而,随着我的项目的一直变大,mobx也一直暴露出了它的毛病,就是数据流太随便,出了bug之后不好追溯数据的流向,这个毛病正好体现出了redux的长处所在,所以针对于小我的项目来说,社区举荐应用mobx,对大我的项目举荐应用redux

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. Children. map( props. children,( )=>)而不是props. children. map ( ( ) => )?

因为不能保障 props. children将是一个数组。
以上面的代码为例。

<Parent>    <h1>有课前端网</h1></Parent>

在父组件外部,如果尝试应用 props.children. map映射子对象,则会抛出谬误,因为props. children是一个对象,而不是一个数组。
如果有多个子元素, React会使 props.children成为一个数组,如下所示。

<Parent>  <h1>有课前端网</h1>  <h2>前端技术学习平台</h2></Parent>;//不倡议应用如下形式,在这个案例中会抛出谬误。class Parent extends Component {  render() {    return <div> {this.props.children.map((obj) => obj)}</div>;  }}

倡议应用如下形式,防止在上一个案例中抛出谬误。

class Parent extends Component {  render() {    return <div> {React.Children.map(this.props.children, (obj) => obj)}</div>;  }}

React-Router 4的Switch有什么用?

Switch 通常被用来包裹 Route,用于渲染与门路匹配的第一个子 <Route><Redirect>,它外面不能放其余元素。

如果不加 <Switch>

import { Route } from 'react-router-dom'<Route path="/" component={Home}></Route><Route path="/login" component={Login}></Route>

Route 组件的 path 属性用于匹配门路,因为须要匹配 /Home,匹配 /loginLogin,所以须要两个 Route,然而不能这么写。这样写的话,当 URL 的 path 为 “/login” 时,<Route path="/" /><Route path="/login" /> 都会被匹配,因而页面会展现 Home 和 Login 两个组件。这时就须要借助 <Switch> 来做到只显示一个匹配组件:

import { Switch, Route} from 'react-router-dom'<Switch>    <Route path="/" component={Home}></Route>    <Route path="/login" component={Login}></Route></Switch>

此时,再拜访 “/login” 门路时,却只显示了 Home 组件。这是就用到了exact属性,它的作用就是准确匹配门路,常常与<Switch> 联结应用。只有当 URL 和该 <Route> 的 path 属性完全一致的状况下能力匹配上:

import { Switch, Route} from 'react-router-dom'<Switch>   <Route exact path="/" component={Home}></Route>   <Route exact path="/login" component={Login}></Route></Switch>

diff算法?

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

React 性能优化

  • shouldCompoentUpdate
  • pureComponent 自带shouldCompoentUpdate的浅比拟优化
  • 联合Immutable.js达到最优