关于前端:前端一面必会react面试题边面边更

3次阅读

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

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 不能放弃一致性,会在开发中产生很多的问题;

为什么应用 jsx 的组件中没有看到应用 react 却须要引入 react?

实质上来说 JSX 是 React.createElement(component, props, ...children) 办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel 会把组件转换为 CreateElement 模式。在 React 17 之后,就不再须要引入,因为 babel 曾经能够帮咱们主动引入 react。

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

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

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

React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代

这三者是目前 react 解决代码复用的次要形式:

  • 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
  • render props 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
  • 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。

(1)HOC 官网解释∶

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。

简言之,HOC 是一种组件的设计模式,HOC 承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。

// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {constructor(props) {super(props);
      this.state = {data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑解决
    render() {
      // ... 并应用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };

// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id));

HOC 的优缺点∶

  • 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
  • 毛病∶ hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩

(2)Render props 官网解释∶

“render prop” 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术

具备 render prop 的组件承受一个返回 React 元素的函数,将 render 的渲染逻辑注入到组件外部。在这里,”render” 的命名能够是任何其余无效的标识符。

// DataProvider 组件外部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {name: 'Tom'}

    render() {
    return (
        <div>
          <p> 共享数据组件本人外部的渲染逻辑 </p>
          {this.props.render(this.state) }      </div>
    );
  }
}

// 调用形式
<DataProvider render={data => (<h1>Hello {data.name}</h1>
)}/>

由此能够看到,render props 的优缺点也很显著∶

  • 长处:数据共享、代码复用,将组件内的 state 作为 props 传递给调用者,将渲染逻辑交给调用者。
  • 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅

(3)Hooks 官网解释∶

Hook 是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义 hook,能够复用代码逻辑。

// 自定义一个获取订阅数据的 hook
function useSubscription() {const data = DataSource.getComments();
  return [data];
}
// 
function CommentList(props) {const {data} = props;
  const [subData] = useSubscription();
    ...
}
// 应用
<CommentList data='hello' />

以上能够看出,hook 解决了 hoc 的 prop 笼罩的问题,同时应用的形式解决了 render props 的嵌套天堂的问题。hook 的长处如下∶

  • 应用直观;
  • 解决 hoc 的 prop 重名问题;
  • 解决 render props 因共享数据 而呈现嵌套天堂的问题;
  • 能在 return 之外应用数据的问题。

须要留神的是:hook 只能在组件顶层应用,不可在分支语句中应用。、

React key 是干嘛用的 为什么要加?key 次要是解决哪一类问题的

Keys 是 React 用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识。在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。

在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染此外,React 还须要借助 Key 值来判断元素与本地状态的关联关系。

注意事项:

  • key 值肯定要和具体的元素—一对应;
  • 尽量不要用数组的 index 去作为 key;
  • 不要在 render 的时候用随机数或者其余操作给元素加上不稳固的 key,这样造成的性能开销比不加 key 的状况下更蹩脚。

setState 之后 产生了什么?

  • (1)代码中调用 setState 函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发所谓的和谐过程(Reconciliation)。
  • (2)通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面;
  • (3)在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染;
  • (4)在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。

setState 的调用会引起 React 的更新生命周期的 4 个函数执行。

shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

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

什么是 React Fiber?

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

react 生命周期

初始化阶段:

  • getDefaultProps: 获取实例的默认属性
  • getInitialState: 获取每个实例的初始化状态
  • componentWillMount:组件行将被装载、渲染到页面上
  • render: 组件在这里生成虚构的 DOM 节点
  • componentDidMount: 组件真正在被装载之后

运行中状态:

  • componentWillReceiveProps: 组件将要接管到属性的时候调用
  • shouldComponentUpdate: 组件承受到新属性或者新状态的时候(能够返回 false,接收数据后不更新,阻止 render 调用,前面的函数不会被继续执行了)
  • componentWillUpdate: 组件行将更新不能批改属性和状态
  • render: 组件从新描述
  • componentDidUpdate: 组件曾经更新

销毁阶段:

  • componentWillUnmount: 组件行将销毁

shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)

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

在 react17 会删除以下三个生命周期
componentWillMount,componentWillReceiveProps,componentWillUpdate

对 React 的插槽 (Portals) 的了解,如何应用,有哪些应用场景

React 官网对 Portals 的定义:

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划

Portals 是 React 16 提供的官网解决方案,使得组件能够脱离父组件层级挂载在 DOM 树的任何地位。艰深来讲,就是咱们 render 一个组件,但这个组件的 DOM 构造并不在本组件内。

Portals 语法如下:

ReactDOM.createPortal(child, container);
  • 第一个参数 child 是可渲染的 React 子项,比方元素,字符串或者片段等;
  • 第二个参数 container 是一个 DOM 元素。

个别状况下,组件的 render 函数返回的元素会被挂载在它的父级组件上:

import DemoComponent from './DemoComponent';
render() {
  // DemoComponent 元素会被挂载在 id 为 parent 的 div 的元素上
  return (
    <div id="parent">
        <DemoComponent />
    </div>
  );
}

然而,有些元素须要被挂载在更高层级的地位。最典型的利用场景:当父组件具备 overflow: hidden 或者 z-index 的款式设置时,组件有可能被其余元素遮挡,这时就能够思考要不要应用 Portal 使组件的挂载脱离父组件。例如:对话框,模态窗。

import DemoComponent from './DemoComponent';
render() {
  // DemoComponent 元素会被挂载在 id 为 parent 的 div 的元素上
  return (
    <div id="parent">
        <DemoComponent />
    </div>
  );
}

React.Children.map 和 js 的 map 有什么区别?

JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行解决,而 React.Children.map 中的 map 能够解决 React.Children 为 null 或者 undefined 的状况。

对 React context 的了解

在 React 中,数据传递个别应用 props 传递数据,维持单向数据流,这样能够让组件之间的关系变得简略且可预测,然而单项数据流在某些场景中并不实用。单纯一对的父子组件传递并无问题,但要是组件之间层层依赖深刻,props 就须要层层传递显然,这样做太繁琐了。

Context 提供了一种在组件之间共享此类值的形式,而不用显式地通过组件树的逐层传递 props。

能够把 context 当做是特定一个组件树内共享的 store,用来做数据传递。简略说就是,当你不想在组件树中通过逐层传递 props 或者 state 的形式来传递数据时,能够应用 Context 来实现跨层级的组件数据传递。

JS 的代码块在执行期间,会创立一个相应的作用域链,这个作用域链记录着运行时 JS 代码块执行期间所能拜访的流动对象,包含变量和函数,JS 程序通过作用域链拜访到代码块外部或者内部的变量和函数。

如果以 JS 的作用域链作为类比,React 组件提供的 Context 对象其实就好比一个提供给子组件拜访的作用域,而 Context 对象的属性能够看成作用域上的流动对象。因为组件 的 Context 由其父节点链上所有组件通 过 getChildContext()返回的 Context 对象组合而成,所以,组件通过 Context 是能够拜访到其父组件链上所有节点组件提供的 Context 的属性。

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} />
  }
}

因为函数组件没有实例,因而不能在函数组件上间接应用 ref

function MyFunctionalComponent() {return <input />;}
class Parent extends React.Component {constructor(props) {super(props);
    this.textInput = React.createRef();}
  render() {
    // 这将不会工作!return (<MyFunctionalComponent ref={this.textInput} />
    );
  }
}

但能够通过闭合的帮忙在函数组件外部进行应用 Refs:

function CustomTextInput(props) {
  // 这里必须申明 textInput,这样 ref 回调才能够援用它
  let textInput = null;
  function handleClick() {textInput.focus();
  }
  return (
    <div>
      <input
        type="text"
        ref={(input) => {textInput = input;}} />      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

留神:

  • 不应该适度的应用 Refs
  • ref 的返回值取决于节点的类型:

    • ref 属性被用于一个一般的 HTML 元素时,React.createRef() 将接管底层 DOM 元素作为他的 current 属性以创立 ref
    • ref 属性被用于一个自定义的类组件时,ref 对象将接管该组件已挂载的实例作为他的 current
  • 当在父组件中须要拜访子组件中的 ref 时可应用传递 Refs 或回调 Refs。

在 React 中遍历的办法有哪些?

(1)遍历数组:map && forEach

import React from 'react';

class App extends React.Component {render() {let arr = ['a', 'b', 'c', 'd'];
    return (
      <ul>
        {arr.map((item, index) => {return <li key={index}>{item}</li>
          })
        }
      </ul>
    )
  }
}

class App extends React.Component {render() {let arr = ['a', 'b', 'c', 'd'];
    return (
      <ul>
        {arr.forEach((item, index) => {return <li key={index}>{item}</li>
          })
        }
      </ul>
    )
  }
}

(2)遍历对象:map && for in

class App extends React.Component {render() {
    let obj = {
      a: 1,
      b: 2,
      c: 3
    }
    return (
      <ul>
        {(() => {let domArr = [];
            for(const key in obj) {if(obj.hasOwnProperty(key)) {const value = obj[key]
                domArr.push(<li key={key}>{value}</li>)
              }
            }
            return domArr;
          })()}
      </ul>
    )
  }
}

// Object.entries() 把对象转换成数组
class App extends React.Component {render() {
    let obj = {
      a: 1,
      b: 2,
      c: 3
    }
    return (
      <ul>
        {Object.entries(obj).map(([key, value], index) => {// item 是一个数组,把 item 解构,写法是[key, value]
            return <li key={key}>{value}</li>
          }) 
        }
      </ul>
    )
  }
}

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

该函数会在 setState 函数调用实现并且组件开始重渲染的时候被调用,咱们能够用该函数来监听渲染是否实现:

this.setState({ username: 'tylermcginnis33'},
  () => console.log('setState has finished and the component has re-rendered.')
)
this.setState((prevState, props) => {
  return {streak: prevState.streak + props.count}
})

class 类的 key 改了,会产生什么,会执行哪些周期函数?

在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染。此外,React 还须要借助 Key 值来判断元素与本地状态的关联关系,因而咱们绝不可漠视转换函数中 Key 的重要性。

答:componentWillMount componentDidMount render

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

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

React 的 Fiber 工作原理,解决了什么问题

  • React Fiber 是一种基于浏览器的单线程调度算法。

React Fiber 用相似 requestIdleCallback 的机制来做异步 diff。然而之前数据结构不反对这样的实现异步 diff,于是 React 实现了一个相似链表的数据结构,将原来的 递归 diff 变成了当初的 遍历 diff,这样就能做到异步可更新了

React 数据长久化有什么实际吗?

封装数据长久化组件:

let storage={
    // 减少
    set(key, value){localStorage.setItem(key, JSON.stringify(value));
    },
    // 获取
    get(key){return JSON.parse(localStorage.getItem(key));
    },
    // 删除
    remove(key){localStorage.removeItem(key);
    }
};
export default Storage;

在 React 我的项目中,通过 redux 存储全局数据时,会有一个问题,如果用户刷新了网页,那么通过 redux 存储的全局数据就会被全副清空,比方登录信息等。这时就会有全局数据长久化存储的需要。首先想到的就是 localStorage,localStorage 是没有工夫限度的数据存储,能够通过它来实现数据的长久化存储。

然而在曾经应用 redux 来治理和存储全局数据的根底上,再去应用 localStorage 来读写数据,这样不仅是工作量微小,还容易出错。那么有没有联合 redux 来达到持久数据存储性能的框架呢?当然,它就是redux-persist。redux-persist 会将 redux 的 store 中的数据缓存到浏览器的 localStorage 中。其应用步骤如下:

(1)首先要装置 redux-persist:

npm i redux-persist

(2)对于 reducer 和 action 的解决不变,只需批改 store 的生成代码,批改如下:

import {createStore} from 'redux'
import reducers from '../reducers/index'
import {persistStore, persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const persistConfig = {
    key: 'root',
    storage: storage,
    stateReconciler: autoMergeLevel2 // 查看 'Merge Process' 局部的具体情况
};
const myPersistReducer = persistReducer(persistConfig, reducers)
const store = createStore(myPersistReducer)
export const persistor = persistStore(store)
export default store

(3)在 index.js 中,将 PersistGate 标签作为网页内容的父标签:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import store from './redux/store/store'
import {persistor} from './redux/store/store'
import {PersistGate} from 'redux-persist/lib/integration/react';
ReactDOM.render(<Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
                {/* 网页内容 */}            </PersistGate>
        </Provider>, document.getElementById('root'));

这就实现了通过 redux-persist 实现 React 长久化本地数据存储的简略利用。

React 中 props.children 和 React.Children 的区别

在 React 中,当波及组件嵌套,在父组件中应用 props.children 把所有子组件显示进去。如下:

function ParentComponent(props){
    return (
        <div>
            {props.children}        </div>
    )
}

如果想把父组件中的属性传给所有的子组件,须要应用 React.Children 办法。

比方,把几个 Radio 组合起来,合成一个 RadioGroup,这就要求所有的 Radio 具备同样的 name 属性值。能够这样:把 Radio 看做子组件,RadioGroup 看做父组件,name 的属性值在 RadioGroup 这个父组件中设置。

首先是子组件:

// 子组件
function RadioOption(props) {
  return (
    <label>
      <input type="radio" value={props.value} name={props.name} />
      {props.label}    </label>
  )
}

而后是父组件,不仅须要把它所有的子组件显示进去,还须要为每个子组件赋上 name 属性和值:

// 父组件用,props 是指父组件的 props
function renderChildren(props) {

  // 遍历所有子组件
  return React.Children.map(props.children, child => {if (child.type === RadioOption)
      return React.cloneElement(child, {
        // 把父组件的 props.name 赋值给每个子组件
        name: props.name
      })
    else
      return child
  })
}
// 父组件
function RadioGroup(props) {
  return (
    <div>
      {renderChildren(props)}    </div>
  )
}
function App() {
  return (
    <RadioGroup name="hello">
      <RadioOption label="选项一" value="1" />
      <RadioOption label="选项二" value="2" />
      <RadioOption label="选项三" value="3" />
    </RadioGroup>
  )
}
export default App;

以上,React.Children.map让咱们对父组件的所有子组件又更灵便的管制。

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

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

正文完
 0