如何解决 props 层级过深的问题

  • 应用Context API:提供一种组件之间的状态共享,而不用通过显式组件树逐层传递props;
  • 应用Redux等状态库。

react 实现一个全局的 dialog

import React, { Component } from 'react';import { is, fromJS } from 'immutable';import ReactDOM from 'react-dom';import ReactCSSTransitionGroup from 'react-addons-css-transition-group';import './dialog.css';let defaultState = {  alertStatus:false,  alertTip:"提醒",  closeDialog:function(){},  childs:''}class Dialog extends Component{  state = {    ...defaultState  };  // css动画组件设置为指标组件  FirstChild = props => {    const childrenArray = React.Children.toArray(props.children);    return childrenArray[0] || null;  }  //关上弹窗  open =(options)=>{    options = options || {};    options.alertStatus = true;    var props = options.props || {};    var childs = this.renderChildren(props,options.childrens) || '';    console.log(childs);    this.setState({      ...defaultState,      ...options,      childs    })  }  //敞开弹窗  close(){    this.state.closeDialog();    this.setState({      ...defaultState    })  }  renderChildren(props,childrens) {    //遍历所有子组件    var childs = [];    childrens = childrens || [];    var ps = {        ...props,  //给子组件绑定props        _close:this.close  //给子组件也绑定一个敞开弹窗的事件           };    childrens.forEach((currentItem,index) => {        childs.push(React.createElement(            currentItem,            {                ...ps,                key:index            }        ));    })    return childs;  }  shouldComponentUpdate(nextProps, nextState){    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))  }     render(){    return (      <ReactCSSTransitionGroup        component={this.FirstChild}        transitionName='hide'        transitionEnterTimeout={300}        transitionLeaveTimeout={300}>        <div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>            {this.state.childs}        </div>      </ReactCSSTransitionGroup>    );  }}let div = document.createElement('div');let props = {   };document.body.appendChild(div);let Box = ReactD

子类:

//子类jsximport React, { Component } from 'react';class Child extends Component {    constructor(props){        super(props);        this.state = {date: new Date()};  }  showValue=()=>{    this.props.showValue && this.props.showValue()  }  render() {    return (      <div className="Child">        <div className="content">           Child           <button onClick={this.showValue}>调用父的办法</button>        </div>      </div>    );  }}export default Child;

css:

.dialog-con{    position: fixed;    top: 0;    left: 0;    width: 100%;    height: 100%;    background: rgba(0, 0, 0, 0.3);}

应用状态要留神哪些事件?

要留神以下几点。

  • 不要间接更新状态
  • 状态更新可能是异步的
  • 状态更新要合并。
  • 数据从上向下流动

跨级组件的通信形式?

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

  • 应用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>          )      }}

React 16中新生命周期有哪些

对于 React16 开始利用的新生命周期: 能够看出,React16 自上而下地对生命周期做了另一种维度的解读:

  • Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
  • Pre-commit阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
  • Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。

与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:

  • 挂载过程:

    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • 更新过程:

    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • 卸载过程:

    • componentWillUnmount

React 事件机制

<div onClick={this.handleClick.bind(this)}>点我</div>

React并不是将click事件绑定到了div的实在DOM上,而是在document处监听了所有的事件,当事件产生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的形式不仅仅缩小了内存的耗费,还能在组件挂在销毁时对立订阅和移除事件。

除此之外,冒泡到document上的事件也不是原生的浏览器事件,而是由react本人实现的合成事件(SyntheticEvent)。因而如果不想要是事件冒泡的话应该调用event.preventDefault()办法,而不是调用event.stopProppagation()办法。 JSX 上写的事件并没有绑定在对应的实在 DOM 上,而是通过事件代理的形式,将所有的事件都对立绑定在了 document 上。这样的形式不仅缩小了内存耗费,还能在组件挂载销毁时对立订阅和移除事件。

另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 本人实现的合成事件(SyntheticEvent)。因而咱们如果不想要事件冒泡的话,调用 event.stopPropagation 是有效的,而应该调用 event.preventDefault

实现合成事件的目标如下:

  • 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
  • 对于原生浏览器事件来说,浏览器会给监听器创立一个事件对象。如果你有很多的事件监听,那么就须要调配很多的事件对象,造成高额的内存调配问题。然而对于合成事件来说,有一个事件池专门来治理它们的创立和销毁,当事件须要被应用时,就会从池子中复用对象,事件回调完结后,就会销毁事件对象上的属性,从而便于下次复用事件对象。

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

React setState 调用之后产生了什么?是同步还是异步?

(1)React中setState后产生了什么

在代码中调用setState函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发和谐过程(Reconciliation)。通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个UI界面。

在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染。在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。

如果在短时间内频繁setState。React会将state的扭转压入栈中,在适合的机会,批量更新state和视图,达到进步性能的成果。

(2)setState 是同步还是异步的

如果所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,屡次setState),都从新vnode diff + dom批改,这对性能来说是极为不好的。如果是异步,则能够把一个同步代码中的多个setState合并成一次组件更新。所以默认是异步的,然而在一些状况下是同步的。

setState 并不是单纯同步/异步的,它的体现会因调用场景的不同而不同。在源码中,通过 isBatchingUpdates 来判断setState 是先存进 state 队列还是间接更新,如果值为 true 则执行异步操作,为 false 则间接更新。

  • 异步: 在 React 能够管制的中央,就为 true,比方在 React 生命周期事件和合成事件中,都会走合并操作,提早更新的策略。
  • 同步: 在 React 无法控制的中央,比方原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。

个别认为,做异步设计是为了性能优化、缩小渲染次数:

  • setState设计为异步,能够显著的晋升性能。如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面从新渲染,这样效率是很低的;最好的方法应该是获取到多个更新,之后进行批量更新;
  • 如果同步更新了state,然而还没有执行render函数,那么stateprops不能放弃同步。stateprops不能放弃一致性,会在开发中产生很多的问题;

react16版本的reconciliation阶段和commit阶段是什么

  • reconciliation阶段蕴含的次要工作是对current tree 和 new tree 做diff计算,找出变动局部。进行遍历、比照等是能够中断,歇一会儿接着再来。
  • commit阶段是对上一阶段获取到的变动局部利用到实在的DOM树中,是一系列的DOM操作。不仅要保护更简单的DOM状态,而且中断后再持续,会对用户体验造成影响。在广泛的利用场景下,此阶段的耗时比diff计算等耗时绝对短。

用户不同权限 能够查看不同的页面 如何实现?

  1. Js形式
    依据用户权限类型,把菜单配置成json, 没有权限的间接不显示
  2. react-router 形式 在route 标签上 增加onEnter事件,进入路由之前替换到首页
<Route path="/home" component={App} onEnter={(nexState,replace)=>{      if(nexState.location.pathname!=='/'){         var  sid = UtilsMoudle.getSidFromUrl(nexState);         if(!sid){            replace("/")         }else{            console.log(sid);         }      }    }}>
  1. 本人封装一个privateRouter组件 外面判断是否有权限,有的话返回
    <Route path={path} component={component} exact={exact}/>
    没有权限的话component 返回一个提示信息的组件。
  2. 扩大一下,如果是依据用权限来判断是否暗藏组件该怎么做呢?
    react 能够应用高阶组件,在高阶组件外面判断是否有权限,而后判断是否返回组件,无权限返回null
    vue 能够应用自定义指令,如果没有权限移除组件
// 须要在入口处增加自定义权限指令v-auth,显示可操作组件Vue.directive('auth', {    bind: function (el, binding, vnode) {        // 用户权限表        const rules = auths        for (let i = 0; i < rules.length; i++) {            const item = rules[i]            if(!binding.value || (binding.value == item.auth)){                // 权限容许则显示组件                return true            }        }        // 移除组件        el.parentNode.removeChild(el)    }})// 应用<template>  <div>    <Button v-auth="admin_user_add">增加用户</Button>    <Button v-auth="admin_user_del">删除用户</Button>    <Button v-auth="admin_user_edit">编辑用户</Button>  </div></template>

Redux中的connect有什么作用

connect负责连贯React和Redux

(1)获取state

connect 通过 context获取 Provider 中的 store,通过 store.getState() 获取整个store tree 上所有state

(2)包装原组件

将state和action通过props的形式传入到原组件外部 wrapWithConnect 返回—个 ReactComponent 对 象 Connect,Connect 重 新 render 内部传入的原组件 WrappedComponent ,并把 connect 中传入的 mapStateToProps,mapDispatchToProps与组件上原有的 props合并后,通过属性的形式传给WrappedComponent

(3)监听store tree变动

connect缓存了store tree中state的状态,通过以后state状态 和变更前 state 状态进行比拟,从而确定是否调用 this.setState()办法触发Connect及其子组件的从新渲染

React中有应用过getDefaultProps吗?它有什么作用?

通过实现组件的getDefaultProps,对属性设置默认值(ES5的写法):

var ShowTitle = React.createClass({  getDefaultProps:function(){    return{      title : "React"    }  },  render : function(){    return <h1>{this.props.title}</h1>  }});

fetch封装

npm install whatwg-fetch --save  // 适配其余浏览器npm install es6-promiseexport const handleResponse = (response) => {  if (response.status === 403 || response.status === 401) {    const oauthurl = response.headers.get('locationUrl');    if (!_.isEmpty(oauthUrl)) {      window.location.href = oauthurl;      return;    }  }  if (!response.ok) {    return getErrorMessage(response).then(errorMessage => apiError(response.status, errorMessage));  }  if (isJson(response)) {    return response.json();  }  if (isText(response)) {    return response.text();  }  return response.blob();};const httpRequest = {  request: ({    method, headers, body, path, query,  }) => {    const options = {};    let url = path;    if (method) {      options.method = method;    }    if (headers) {      options.headers = {...options.headers,...headers};    }    if (body) {      options.body = body;    }    if (query) {      const params = Object.keys(query)        .map(k => `${k}=${query[k]}`)        .join('&');      url = url.concat(`?${params}`);    }    return fetch(url, Object.assign({}, options, { credentials: 'same-origin' })).then(handleResponse);  },};export default httpRequest;

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中的Portal是什么?

Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的形式。
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或碎片。
第二个参数(container)则是一个 DOM 元素。

ReactDOM.createPortal(child, container)

生命周期调用办法的程序是什么?

React生命周期分为三大周期,11个阶段,生命周期办法调用程序别离如下。
(1)在创立期的五大阶段,调用办法的程序如下。

  • getDetaultProps:定义默认属性数据。
  • getInitialState:初始化默认状态数据。
  • component WillMount:组件行将被构建。
  • render:渲染组件。
  • componentDidMount:组件构建实现

(2)在存在期的五大阶段,调用办法的程序如下。

  • componentWillReceiveProps:组件行将接管新的属性数据。
  • shouldComponentUpdate:判断组件是否应该更新。
  • componnent WillUpdate:组件行将更新。
  • render:渲染组件。
  • componentDidUpdate:组件更新实现。

(3)在销毁期的一个阶段,调用办法 componentWillUnmount,示意组件行将被销毀。

在 Reducer文件里,对于返回的后果,要留神哪些问题?

在 Reducer文件里,对于返回的后果,必须要应用 Object.assign ( )来复制一份新的 state,否则页面不会跟着数据刷新。

return Object.assign({}, state, {  type: action.type,  shouldNotPaint: true,});

什么是 React Context?

Context 通过组件树提供了一个传递数据的办法,从而防止了在每一个层级手动的传递 props 属性。

对虚构 DOM 的了解?虚构 DOM 次要做了什么?虚构 DOM 自身是什么?

从实质上来说,Virtual Dom是一个JavaScript对象,通过对象的形式来示意DOM构造。将页面的状态形象为JS对象的模式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将屡次DOM批改的后果一次性的更新到页面上,从而无效的缩小页面渲染的次数,缩小批改DOM的重绘重排次数,进步渲染性能。

虚构DOM是对DOM的形象,这个对象是更加轻量级的对DOM的形容。它设计的最后目标,就是更好的跨平台,比方node.js就没有DOM,如果想实现SSR,那么一个形式就是借助虚构dom,因为虚构dom自身是js对象。 在代码渲染到页面之前,vue或者react会把代码转换成一个对象(虚构DOM)。以对象的模式来形容实在dom构造,最终渲染到页面。在每次数据发生变化前,虚构dom都会缓存一份,变动之时,当初的虚构dom会与缓存的虚构dom进行比拟。在vue或者react外部封装了diff算法,通过这个算法来进行比拟,渲染时批改扭转的变动,原先没有产生扭转的通过原先的数据进行渲染。

另外古代前端框架的一个根本要求就是毋庸手动操作DOM,一方面是因为手动操作DOM无奈保障程序性能,多人合作的我的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作能够大大提高开发效率。

为什么要用 Virtual DOM:

(1)保障性能上限,在不进行手动优化的状况下,提供过得去的性能

上面比照一下批改DOM时实在DOM操作和Virtual DOM的过程,来看一下它们重排重绘的性能耗费∶

  • 实在DOM∶ 生成HTML字符串+ 重建所有的DOM元素
  • Virtual DOM∶ 生成vNode+ DOMDiff+必要的DOM更新

Virtual DOM的更新DOM的筹备工作消耗更多的工夫,也就是JS层面,相比于更多的DOM操作它的生产是极其便宜的。尤雨溪在社区论坛中说道∶ 框架给你的保障是,你不须要手动优化的状况下,我仍然能够给你提供过得去的性能。 (2)跨平台 Virtual DOM实质上是JavaScript的对象,它能够很不便的跨平台操作,比方服务端渲染、uniapp等。

哪些办法会触发 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 组件增加属性?

对于某些属性,React 十分聪慧,如果传递给它的值是虚值,能够省略该属性。例如:

var InputComponent = React.createClass({  render: function () {    var required = true;    var disabled = false;    return <input type="text" disabled={disabled} required={required} />;  },});

渲染后果:

<input type="text" required>

另一种可能的办法是:

var condition = true;var component = <div value="foo" {...(condition && { disabled: true })} />;