关于react.js:React高频面试题合集二

36次阅读

共计 10139 个字符,预计需要花费 26 分钟才能阅读完成。

什么是虚构 DOM?

虚构 DOM (VDOM)是实在 DOM 在内存中的示意。UI 的示意模式保留在内存中,并与理论的 DOM 同步。这是一个产生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为 和谐。

React 中的状态是什么?它是如何应用的

状态是 React 组件的外围,是数据的起源,必须尽可能简略。基本上状态是确定组件出现和行为的对象。与 props 不同,它们是可变的,并创立动静和交互式组件。能够通过 this.state() 拜访它们。

虚构 DOM 的引入与间接操作原生 DOM 相比,哪一个效率更高,为什么

虚构 DOM 绝对原生的 DOM 不肯定是效率更高,如果只批改一个按钮的文案,那么虚构 DOM 的操作无论如何都不可能比实在的 DOM 操作更快。在首次渲染大量 DOM 时,因为多了一层虚构 DOM 的计算,虚构 DOM 也会比 innerHTML 插入慢。它能保障性能上限,在实在 DOM 操作的时候进行针对性的优化时,还是更快的。所以要依据具体的场景进行探讨。

在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验 / 研发效率。虚构 DOM 不是别的,正是前端开发们为了谋求更好的研发体验和研发效率而发明进去的高阶产物。虚构 DOM 并不一定会带来更好的性能,React 官网也素来没有把虚构 DOM 作为性能层面的卖点对外输入过。** 虚构 DOM 的优越之处在于,它可能在提供更爽、更高效的研发模式(也就是函数式的 UI 编程形式)的同时,依然放弃一个还不错的性能。

为什么 useState 要应用数组而不是对象

useState 的用法:

const [count, setCount] = useState(0)
复制代码

能够看到 useState 返回的是一个数组,那么为什么是返回数组而不是返回对象呢?

这里用到了解构赋值,所以先来看一下 ES6 的解构赋值:

数组的解构赋值
const foo = [1, 2, 3];
const [one, two, three] = foo;
console.log(one);    // 1
console.log(two);    // 2
console.log(three);    // 3
复制代码
对象的解构赋值
const user = {
  id: 888,
  name: "xiaoxin"
};
const {id, name} = user;
console.log(id);    // 888
console.log(name);    // "xiaoxin"
复制代码

看完这两个例子,答案应该就进去了:

  • 如果 useState 返回的是数组,那么使用者能够对数组中的元素命名,代码看起来也比拟洁净
  • 如果 useState 返回的是对象,在解构对象的时候必须要和 useState 外部实现返回的对象同名,想要应用屡次的话,必须得设置别名能力应用返回值

上面来看看如果 useState 返回对象的状况:

// 第一次应用
const {state, setState} = useState(false);
// 第二次应用
const {state: counter, setState: setCounter} = useState(0) 
复制代码

这里能够看到,返回对象的应用形式还是挺麻烦的,更何况理论我的项目中会应用的更频繁。总结:useState 返回的是 array 而不是 object 的起因就是为了 升高应用的复杂度,返回数组的话能够间接依据程序解构,而返回对象的话要想应用屡次就须要定义别名了。

Redux 申请中间件如何解决并发

应用 redux-Saga redux-saga 是一个治理 redux 利用异步操作的中间件,用于代替 redux-thunk 的。它通过创立 Sagas 将所有异步操作逻辑寄存在一个中央进行集中处理,以此将 react 中的同步操作与异步操作辨别开来,以便于前期的治理与保护。redux-saga 如何解决并发:

  • takeEvery

能够让多个 saga 工作并行被 fork 执行。

import {
    fork,
    take
} from "redux-saga/effects"

const takeEvery = (pattern, saga, ...args) => fork(function*() {while (true) {const action = yield take(pattern)
        yield fork(saga, ...args.concat(action))
    }
})
复制代码
  • takeLatest

takeLatest 不容许多个 saga 工作并行地执行。一旦接管到新的发动的 action,它就会勾销后面所有 fork 过的工作(如果这些工作还在执行的话)。
在解决 AJAX 申请的时候,如果只心愿获取最初那个申请的响应,takeLatest 就会十分有用。

import {
    cancel,
    fork,
    take
} from "redux-saga/effects"

const takeLatest = (pattern, saga, ...args) => fork(function*() {
    let lastTask
    while (true) {const action = yield take(pattern)
        if (lastTask) {yield cancel(lastTask) // 如果工作曾经完结,则 cancel 为空操作
        }
        lastTask = yield fork(saga, ...args.concat(action))
    }
})
复制代码

React 组件命名举荐的形式是哪个?

通过援用而不是应用来命名组件 displayName。

应用 displayName 命名组件:

export default React.createClass({displayName: 'TodoApp',  // ...})
复制代码

React 举荐的办法:

export default class TodoApp extends React.Component {// ...}
复制代码

为什么调用 setState 而不是间接扭转 state?

解答

如果您尝试间接扭转组件的状态,React 将无奈得悉它须要从新渲染组件。通过应用 setState() 办法,React 能够更新组件的 UI。

另外,您还能够谈谈如何不保障状态更新是同步的。如果须要基于另一个状态(或属性)更新组件的状态,请向 setState() 传递一个函数,该函数将 state 和 props 作为其两个参数:

this.setState((state, props) => ({counter: state.counter + props.increment}));
复制代码

进一步浏览

  • 正确地应用 State

Redux 中间件是什么?承受几个参数?柯里化函数两端的参数具体是什么?

Redux 的中间件提供的是位于 action 被发动之后,达到 reducer 之前的扩大点,换而言之,本来 view -→> action -> reducer -> store 的数据流加上中间件后变成了 view -> action -> middleware -> reducer -> store,在这一环节能够做一些 ” 副作用 ” 的操作,如异步申请、打印日志等。

applyMiddleware 源码:

export default function applyMiddleware(...middlewares) {return createStore => (...args) => {
        // 利用传入的 createStore 和 reducer 和创立一个 store
        const store = createStore(...args)
        let dispatch = () => {throw new Error()
        }
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        // 让每个 middleware 带着 middlewareAPI 这个参数别离执行一遍
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        // 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
        dispatch = compose(...chain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}
复制代码

从 applyMiddleware 中能够看出∶

  • redux 中间件承受一个对象作为参数,对象的参数上有两个字段 dispatch 和 getState,别离代表着 Redux Store 上的两个同名函数。
  • 柯里化函数两端一个是 middewares,一个是 store.dispatch

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 及其子组件的从新渲染

Redux 怎么实现属性传递,介绍下原理

react-redux 数据传输∶ view–>action–>reducer–>store–>view。看下点击事件的数据是如何通过 redux 传到 view 上:

  • view 上的 AddClick 事件通过 mapDispatchToProps 把数据传到 action —> click:()=>dispatch(ADD)
  • action 的 ADD 传到 reducer 上
  • reducer 传到 store 上 const store = createStore(reducer);
  • store 再通过 mapStateToProps 映射穿到 view 上 text:State.text

代码示例∶

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider, connect} from 'react-redux';
class App extends React.Component{render(){let { text, click, clickR} = this.props;
        return(
            <div>
                <div> 数据: 已有人{text}</div>
                <div onClick={click}> 加人 </div>
                <div onClick={clickR}> 减人 </div>
            </div>
        )
    }
}
const initialState = {text:5}
const reducer = function(state,action){switch(action.type){
        case 'ADD':
            return {text:state.text+1}
        case 'REMOVE':
            return {text:state.text-1}
        default:
            return initialState;
    }
}

let ADD = {type:'ADD'}
let Remove = {type:'REMOVE'}

const store = createStore(reducer);

let mapStateToProps = function (state){
    return{text:state.text}
}

let mapDispatchToProps = function(dispatch){
    return{click:()=>dispatch(ADD),
        clickR:()=>dispatch(Remove)
    }
}

const App1 = connect(mapStateToProps,mapDispatchToProps)(App);

ReactDOM.render(<Provider store = {store}>
        <App1></App1>
    </Provider>,document.getElementById('root')
)
复制代码

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
复制代码

子类:

// 子类 jsx
import 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);
}
复制代码

useEffect 与 useLayoutEffect 的区别

(1)共同点

  • 使用成果: useEffect 与 useLayoutEffect 两者都是用于解决副作用,这些副作用包含扭转 DOM、设置订阅、操作定时器等。在函数组件外部操作副作用是不被容许的,所以须要应用这两个函数去解决。
  • 应用形式: useEffect 与 useLayoutEffect 两者底层的函数签名是完全一致的,都是调用的 mountEffectImpl 办法,在应用上也没什么差别,根本能够间接替换。

(2)不同点

  • 应用场景: useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景;而 useLayoutEffect 会在所有的 DOM 变更之后同步调用,次要用于解决 DOM 操作、调整款式、防止页面闪动等问题。也正因为是同步解决,所以须要防止在 useLayoutEffect 做计算量较大的耗时工作从而造成阻塞。
  • 应用成果: useEffect 是依照程序执行代码的,扭转屏幕像素之后执行(先渲染,后扭转 DOM),当扭转屏幕内容时可能会产生闪动;useLayoutEffect 是扭转屏幕像素之前就执行了(会推延页面显示的事件,先扭转 DOM 后渲染),不会产生闪动。useLayoutEffect 总是比 useEffect 先执行。

在将来的趋势上,两个 API 是会长期共存的,临时没有删减合并的打算,须要开发者依据场景去自行抉择。React 团队的倡议十分实用,如果切实分不清,先用 useEffect,个别问题不大;如果页面有异样,再间接替换为 useLayoutEffect 即可。

React Hook 的应用限度有哪些?

React Hooks 的限度次要有两条:

  • 不要在循环、条件或嵌套函数中调用 Hook;
  • 在 React 的函数组件中调用 Hook。

那为什么会有这样的限度呢?Hooks 的设计初衷是为了改良 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。

  • 组件之间难以复用状态逻辑。过来常见的解决方案是高阶组件、render props 及状态治理框架。
  • 简单的组件变得难以了解。生命周期函数与业务逻辑耦合太深,导致关联局部难以拆分。
  • 人和机器都很容易混同类。常见的有 this 的问题,但在 React 团队中还有类难以优化的问题,心愿在编译优化层面做出一些改良。

这三个问题在肯定水平上妨碍了 React 的后续倒退,所以为了解决这三个问题,Hooks 基于函数组件 开始设计。然而第三个问题决定了 Hooks 只反对函数组件。

那为什么不要在循环、条件或嵌套函数中调用 Hook 呢?因为 Hooks 的设计是基于数组实现。在调用时按程序退出数组中,如果应用循环、条件或嵌套函数很有可能导致数组取值错位,执行谬误的 Hook。当然,本质上 React 的源码里不是数组,是链表。

这些限度会在编码上造成肯定水平的心智累赘,老手可能会写错,为了防止这样的状况,能够引入 ESLint 的 Hooks 查看插件进行预防。

React 设计思路,它的理念是什么?

(1)编写简略直观的代码

React 最大的价值不是高性能的虚构 DOM、封装的事件机制、服务器端渲染,而是申明式的直观的编码方式。react 文档第一条就是申明式,React 使创立交互式 UI 变得轻而易举。为利用的每一个状态设计简洁的视图,当数据扭转时 React 能无效地更新并正确地渲染组件。以申明式编写 UI,能够让代码更加牢靠,且不便调试。

(2)简化可复用的组件

React 框架外面应用了简化的组件模型,但更彻底地应用了组件化的概念。React 将整个 UI 上的每一个功能模块定义成组件,而后将小的组件通过组合或者嵌套的形式形成更大的组件。React 的组件具备如下的个性∶

  • 可组合:简略组件能够组合为简单的组件
  • 可重用:每个组件都是独立的,能够被多个组件应用
  • 可保护:和组件相干的逻辑和 UI 都封装在了组件的外部,不便保护
  • 可测试:因为组件的独立性,测试组件就变得不便很多。

(3) Virtual DOM

实在页面对应一个 DOM 树。在传统页面的开发模式中,每次须要更新页面时,都要手动操作 DOM 来进行更新。DOM 操作十分低廉。在前端开发中,性能耗费最大的就是 DOM 操作,而且这部分代码会让整体我的项目的代码变得难 以保护。React 把实在 DOM 树转换成 JavaScript 对象树,也就是 Virtual DOM,每次数据更新后,从新计算 Virtual DOM,并和上一次生成的 Virtual DOM 做比照,对发生变化的局部做批量更新。React 也提供了直观的 shouldComponentUpdate 生命周期回调,来缩小数据变动后不必要的 Virtual DOM 比照过程,以保障性能。

(4)函数式编程

React 把过来一直反复构建 UI 的过程形象成了组件,且在给定参数的状况下约定渲染对应的 UI 界面。React 能充分利用很多函数式办法去缩小冗余代码。此外,因为它自身就是简略函数,所以易于测试。

(5)一次学习,随处编写

无论当初正在应用什么技术栈,都能够随时引入 React 来开发新个性,而不须要重写现有代码。

React 还能够应用 Node 进行服务器渲染,或应用 React Native 开发原生挪动利用。因为 React 组件能够映射为对应的原生控件。在输入的时候,是输入 Web DOM,还是 Android 控件,还是 iOS 控件,就由平台自身决定了。所以,react 很不便和其余平台集成

react 中 key 的作用

简略的说:key 是虚构 DOM 中的一种标识,在更新显示是 key 起到了极其重要的作用

简单的说:当状态中的数据产生扭转的时候,react 会依据【新数据】生成【新的虚构 DOM】,随后 react 进行【新虚构 DOM】和【旧的虚构 DOM】的 diff 比拟,而在这个比拟过程中 key 就是起到是要害中用

怎么用 React.createElement 重写上面的代码

Question:

const element = (
  <h1 className="greeting">
    Hello, rdhub.cn!
  </h1>
);

Answer:

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, rdhub.cn!'
);

React- Router 有几种模式?

有以下几种模式。
HashRouter,通过散列实现,路由要带 #。
BrowerRouter,利用 HTML5 中 history API 实现,须要服务器端反对,兼容性不是很好。

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 都异步更新的。

正文完
 0