关于react.js:校招前端一面经典react面试题附答案

2次阅读

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

React.forwardRef 是什么?它有什么作用?

React.forwardRef 会创立一个 React 组件,这个组件可能将其承受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特地有用:

  • 转发 refs 到 DOM 组件
  • 在高阶组件中转发 refs

跨级组件的通信形式?

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

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

如果用索引值作为 key 会呈现什么样的问题

  • 若对数据进行逆序增加,逆序删除等毁坏程序的操作

    则会产生没有必要的实在 DOM 更新,界面想过看不出区别,然而效劳低,性能不好

  • 如果构造中还蕴含输出类的 DOM

    会产生谬误的 DOM 更新 ===》界面会有问题

如果不存在对数据的逆序增加 逆序删除等毁坏程序操作,仅用于渲染展现,用 index 作为 key 也没有问题

如何通知 React 它应该编译生产环境版

通常状况下咱们会应用 WebpackDefinePlugin 办法来将 NODE_ENV 变量值设置为 production。编译版本中 React会疏忽 propType 验证以及其余的告警信息,同时还会升高代码库的大小,React 应用了 Uglify 插件来移除生产环境下不必要的正文等信息

React 实现的挪动利用中,如果呈现卡顿,有哪些能够思考的优化计划

  • 减少 shouldComponentUpdate 钩子对新旧 props 进行比拟,如果值雷同则阻止更新,防止不必要的渲染,或者应用 PureReactComponent 代替 Component,其外部曾经封装了shouldComponentUpdate 的浅比拟逻辑
  • 对于列表或其余构造雷同的节点,为其中的每一项减少惟一 key 属性,以不便 Reactdiff算法中对该节点的复用,缩小节点的创立和删除操作
  • render函数中缩小相似 onClick={() => {doSomething()}} 的写法,每次调用 render 函数时均会创立一个新的函数,即便内容没有产生任何变动,也会导致节点没必要的重渲染,倡议将函数保留在组件的成员对象中,这样只会创立一次
  • 组件的 props 如果须要通过一系列运算后能力拿到最终后果,则能够思考应用 reselect 库对后果进行缓存,如果 props 值未发生变化,则后果间接从缓存中拿,防止昂扬的运算代价
  • webpack-bundle-analyzer剖析以后页面的依赖包,是否存在不合理性,如果存在,找到优化点并进行优化

Redux 实现原理解析

为什么要用 redux

React 中,数据在组件中是单向流动的,数据从一个方向父组件流向子组件(通过 props), 所以,两个非父子组件之间通信就绝对麻烦,redux 的呈现就是为了解决 state 外面的数据问题

Redux 设计理念

Redux是将整个利用状态存储到一个中央上称为 store, 外面保留着一个状态树store tree, 组件能够派发(dispatch) 行为 (action) 给store, 而不是间接告诉其余组件,组件外部通过订阅 store 中的状态 state 来刷新本人的视图

Redux 三大准则

  • 惟一数据源

整个利用的 state 都被存储到一个状态树外面,并且这个状态树,只存在于惟一的 store 中

  • 放弃只读状态

state是只读的,惟一扭转 state 的办法就是触发 actionaction 是一个用于形容以产生工夫的一般对象

  • 数据扭转只能通过纯函数来执行

应用纯函数来执行批改,为了形容 action 如何扭转 state 的,你须要编写reducers

Redux 源码

let createStore = (reducer) => {
    let state;
    // 获取状态对象
    // 寄存所有的监听函数
    let listeners = [];
    let getState = () => state;
    // 提供一个办法供内部调用派发 action
    let dispath = (action) => {
        // 调用管理员 reducer 失去新的 state
        state = reducer(state, action);
        // 执行所有的监听函数
        listeners.forEach((l) => l())
    }
    // 订阅状态变动事件,当状态扭转产生之后执行监听函数
    let subscribe = (listener) => {listeners.push(listener);
    }
    dispath();
    return {
        getState,
        dispath,
        subscribe
    }
}
let combineReducers=(renducers)=>{
    // 传入一个 renducers 治理组,返回的是一个 renducer
    return function(state={},action={}){let newState={};
        for(var attr in renducers){newState[attr]=renducers[attr](state[attr],action)

        }
        return newState;
    }
}
export {createStore,combineReducers};

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

createElement 与 cloneElement 的区别是什么

createElement 函数是 JSX 编译之后应用的创立 React Element 的函数,而 cloneElement 则是用于复制某个元素并传入新的 Props

受控组件、非受控组件

  • 受控组件就是扭转受控于数据的变动,数据变了页面也变了。受控组件更适合,数据驱动是 react 外围
  • 非受控组件不是通过数据管制页面内容

ref 是一个函数又有什么益处?

  • 不便 react 销毁组件、从新渲染的时候去清空 refs 的货色,避免内存泄露

React Portal 有哪些应用场景

  • 在以前,react 中所有的组件都会位于 #app 下,而应用 Portals 提供了一种脱离 #app 的组件
  • 因而 Portals 适宜脱离文档流(out of flow) 的组件,特地是 position: absolute 与 position: fixed 的组件。比方模态框,告诉,正告,goTop 等。

以下是官网一个模态框的示例,能够在以下地址中测试成果

<html>
  <body>
    <div id="app"></div>
    <div id="modal"></div>
    <div id="gotop"></div>
    <div id="alert"></div>
  </body>
</html>
const modalRoot = document.getElementById('modal');

class Modal extends React.Component {constructor(props) {super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

React Hooks 当中的 useEffect 是如何辨别生命周期钩子的

useEffect 能够看成是 componentDidMountcomponentDidUpdatecomponentWillUnmount三者的联合。useEffect(callback,)接管两个参数,调用形式如下

useEffect(() => {console.log('mounted');

   return () => {console.log('willUnmount');
   }
 }, );

生命周期函数的调用次要是通过第二个参数来进行管制,有如下几种状况:

  • 参数不传时,则每次都会优先调用上次保留的函数中返回的那个函数,而后再调用内部那个函数;
  • 参数传 [] 时,则内部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;
  • 参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用内部的函数。

哪个生命周期发送 ajax

  • componentWillMount 在新版本 react 中曾经被废除了
  • 在做 ssr 我的项目时候,componentWillMount 要做服务端数据的获取,不能被占用
  • 所以在 componentDidMount 中申请

setState

在理解 setState 之前,咱们先来简略理解下 React 一个包装构造: Transaction:

事务 (Transaction)

是 React 中的一个调用构造,用于包装一个办法,构造为: initialize – perform(method) – close。通过事务,能够对立治理一个办法的开始与完结;处于事务流中,示意过程正在执行一些操作

  • setState: React 中用于批改状态,更新视图。它具备以下特点:

异步与同步: setState 并不是单纯的异步或同步,这其实与调用时的环境相干:

  • 合成事件 生命周期钩子 (除 componentDidUpdate) 中,setState 是 ” 异步 ” 的;

    • 起因: 因为在 setState 的实现中,有一个判断: 当更新策略正在事务流的执行中时,该组件更新会被推入 dirtyComponents 队列中期待执行;否则,开始执行 batchedUpdates 队列更新;

      • 在生命周期钩子调用中,更新策略都处于更新之前,组件仍处于事务流中,而 componentDidUpdate 是在更新之后,此时组件曾经不在事务流中了,因而则会同步执行;
      • 在合成事件中,React 是基于 事务流实现的事件委托机制 实现,也是处于事务流中;
    • 问题: 无奈在 setState 后马上从 this.state 上获取更新后的值。
    • 解决: 如果须要马上同步去获取新值,setState 其实是能够传入第二个参数的。setState(updater, callback),在回调中即可获取最新值;
  • 原生事件 和 setTimeout 中,setState 是同步的,能够马上获取更新后的值;

    • 起因: 原生事件是浏览器自身的实现,与事务流无关,天然是同步;而 setTimeout 是搁置于定时器线程中延后执行,此时事务流已完结,因而也是同步;
  • 批量更新 : 在 合成事件 和 生命周期钩子 中,setState 更新队列时,存储的是 合并状态(Object.assign)。因而后面设置的 key 值会被前面所笼罩,最终只会执行一次更新;
  • 函数式 : 因为 Fiber 及 合并 的问题,官网举荐能够传入 函数 的模式。setState(fn),在 fn 中返回新的 state 对象即可,例如 this.setState((state, props) => newState);

    • 应用函数式,能够用于防止 setState 的批量更新的逻辑,传入的函数将会被 顺序调用;

注意事项:

  • setState 合并,在 合成事件 和 生命周期钩子 中屡次间断调用会被优化为一次;
  • 当组件已被销毁,如果再次调用 setState,React 会报错正告,通常有两种解决办法

    • 将数据挂载到内部,通过 props 传入,如放到 Redux 或 父级中;
    • 在组件外部保护一个状态量 (isUnmounted),componentWillUnmount 中标记为 true,在 setState 前进行判断;

key 的作用

是给每一个 vnode 的惟一 id, 能够依附 key, 更精确, 更快的拿到 oldVnode 中对应的 vnode 节点

<!-- 更新前 -->
<div>
  <p key="ka">ka</p>
  <h3 key="song">song</he>
</div>

<!-- 更新后 -->
<div>
  <h3 key="song">song</h3>
  <p key="ka">ka</p>
</div>

如果没有 key,React 会认为 div 的第一个子节点由 p 变成 h3,第二个子节点由 h3 变成 p,则会销毁这两个节点并从新结构。

然而当咱们用 key 指明了节点前后对应关系后,React 晓得 key === "ka" 的 p 更新后还在,所以能够复用该节点,只须要替换程序。

key 是 React 用来追踪哪些列表元素被批改、被增加或者被移除的辅助标记。

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

约束性组件(controlled component)与非约束性组件(uncontrolled component)有什么区别?

在 React 中,组件负责管制和治理本人的状态。
如果将 HTML 中的表单元素(input、select、textarea 等)增加到组件中,当用户与表单产生交互时,就波及表单数据存储问题。依据表单数据的存储地位,将组件分成约東性组件和非约東性组件。
约束性组件(controlled component)就是由 React 管制的组件,也就是说,表单元素的数据存储在组件外部的状态中,表单到底出现什么由组件决定。
如下所示,username 没有存储在 DOM 元素内,而是存储在组件的状态中。每次要更新 username 时,就要调用 setState 更新状态;每次要获取 username 的值,就要获取组件状态值。

class App extends Component {
  // 初始化状态
  constructor(props) {super(props);
    this.state = {username: "有课前端网",};
  }
  // 查看后果
  showResult() {
    // 获取数据就是获取状态值
    console.log(this.state.username);
  }
  changeUsername(e) {
    // 原生办法获取
    var value = e.target.value;
    // 更新前,能够进行脏值检测
    // 更新状态
    this.setState({username: value,});
  }
  // 渲染组件
  render() {
    // 返回虚构 DOM
    return (
      <div>
        <p>
          {/* 输入框绑定 va1ue*/}
          <input type="text" onChange={this.changeUsername.bind(this)} value={this.state.username} />
        </p>
        <p>
          <button onClick={this.showResult.bind(this)}> 查看后果 </button>
        </p>
      </div>
    );
  }
}

非约束性组件(uncontrolled component)就是指表单元素的数据交由元素本身存储并解决,而不是通过 React 组件。表单如何出现由表单元素本身决定。
如下所示,表单的值并没有存储在组件的状态中,而是存储在表单元素中,当要批改表单数据时,间接输出表单即可。有时也能够获取元素,再手动批改它的值。当要获取表单数据时,要首先获取表单元素,而后通过表单元素获取元素的值。
留神:为了不便在组件中获取表单元素,通常为元素设置 ref 属性,在组件外部通过 refs 属性获取对应的 DOM 元素。

class App extends Component {
  // 查看后果
  showResult() {
    // 获取值
    console.log(this.refs.username.value);
    // 批改值,就是批改元素本身的值
    this.refs.username.value = "业余前端学习平台";
    // 渲染组件
    // 返回虚构 DOM
    return (
      <div>
        <p>
          {/* 非约束性组件中,表单元素通过 defaultvalue 定义 */}
          <input type="text" ref="username" defaultvalue="有课前端网" />
        </p>
        <p>
          <button onClick={this.showResult.bind(this)}> 查看后果 </button>
        </p>
      </div>
    );
  }
}

尽管非约東性组件通常更容易实现,能够通过 refs 间接获取 DOM 元素,并获取其值,然而 React 倡议应用约束性组件。次要起因是,约東性组件反对即时字段验证,容许有条件地禁用 / 启用按钮,强制输出格局等。

组件是什么?类是什么?类变编译成什么

  • 组件指的是页面的一部分,实质就是一个类,最实质就是一个构造函数
  • 类编译成构造函数

形容 Flux 与 MVC?

传统的 MVC 模式在拆散数据 (Model)、UI(View 和逻辑(Controller) 方面工作得很好,然而 MVC 架构常常遇到两个次要问题:
数据流不够清晰 : 跨视图产生的级联更新经常会导致凌乱的事件网络,难于调试。
不足数据完整性 : 模型数据能够在任何中央产生渐变,从而在整个 UI 中产生不可预测的后果。
应用 Flux 模式的简单用户界面不再蒙受级联更新,任何给定的 React 组件都可能依据 store 提供的数据重建其状态。Flux 模式还通过限度对共享数据的间接拜访来增强数据完整性。

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

要留神以下几点。

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

在 React 中如何处理事件

为了解决跨浏览器的兼容性问题,SyntheticEvent 实例将被传递给你的事件处理函数,SyntheticEvent是 React 跨浏览器的浏览器原生事件包装器,它还领有和浏览器原生事件雷同的接口,包含 stopPropagation()preventDefault()
比拟乏味的是,React 实际上并不将事件附加到子节点自身。React 应用单个事件侦听器侦听顶层的所有事件。这对性能有益处,也意味着 React 在更新 DOM 时不须要跟踪事件监听器。

diff 算法?

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

redux 有什么毛病

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