关于react.js:2023前端二面react面试题边面边更

45次阅读

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

何为 JSX

JSX 是 JavaScript 语法的一种语法扩大,并领有 JavaScript 的全副性能。JSX 生产 React “ 元素 ”,你能够将任何的 JavaScript 表达式封装在花括号里,而后将其嵌入到 JSX 中。在编译实现之后,JSX 表达式就变成了惯例的 JavaScript 对象,这意味着你能够在 if 语句和 for 循环外部应用 JSX,将它赋值给变量,承受它作为参数,并从函数中返回它。

调用 setState 之后产生了什么

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

  • 在 setState 的时候,React 会为以后节点创立一个 updateQueue 的更新列队。
  • 而后会触发 reconciliation 过程,在这个过程中,会应用名为 Fiber 的调度算法,开始生成新的 Fiber 树,Fiber 算法的最大特点是能够做到异步可中断的执行。
  • 而后 React Scheduler 会依据优先级高下,先执行优先级高的节点,具体是执行 doWork 办法。
  • 在 doWork 办法中,React 会执行一遍 updateQueue 中的办法,以取得新的节点。而后比照新旧节点,为老节点打上 更新、插入、替换 等 Tag。
  • 以后节点 doWork 实现后,会执行 performUnitOfWork 办法取得新节点,而后再反复下面的过程。
  • 当所有节点都 doWork 实现后,会触发 commitRoot 办法,React 进入 commit 阶段。
  • 在 commit 阶段中,React 会依据后面为各个节点打的 Tag,一次性更新整个 dom 元素

vue 或者 react 优化整体优化

  1. 虚构 dom

为什么虚构 dom 会进步性能?(必考)

虚构 dom 相当于在 js 和实在 dom 两头加了一个缓存,利用 dom diff 算法防止了没有必要的 dom 操作,从而进步性能。

用 JavaScript 对象构造示意 DOM 树的构造;而后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,从新结构一棵新的对象树。而后用新的树和旧的树进行比拟,记录两棵树差别把 2 所记录的差别利用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

React 的严格模式如何应用,有什么用途?

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后辈元素触发额定的检查和正告。
能够为应用程序的任何局部启用严格模式。例如:

import React from 'react';
function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>        
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>      
      <Footer />
    </div>
  );
}

在上述的示例中,不会对 HeaderFooter 组件运行严格模式查看。然而,ComponentOneComponentTwo 以及它们的所有后辈元素都将进行查看。

StrictMode 目前有助于:

  • 辨认不平安的生命周期
  • 对于应用过期字符串 ref API 的正告
  • 对于应用废除的 findDOMNode 办法的正告
  • 检测意外的副作用
  • 检测过期的 context API

hooks 父子传值

父传子
在父组件中用 useState 申明数据
 const [data, setData] = useState(false)

把数据传递给子组件
<Child data={data} />

子组件接管
export default function (props) {const { data} = props
    console.log(data)
}
子传父
子传父能够通过事件办法传值,和父传子有点相似。在父组件中用 useState 申明数据
 const [data, setData] = useState(false)

把更新数据的函数传递给子组件
<Child setData={setData} />

子组件中触发函数更新数据,就会间接传递给父组件
export default function (props) {const { setData} = props
    setData(true)
}
如果存在多个层级的数据传递,也可按照此办法顺次传递

// 多层级用 useContext
const User = () => {
 // 间接获取,不必回调
 const {user, setUser} = useContext(UserContext);
 return <Avatar user={user} setUser={setUser} />;
};

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

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

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 要用 JSX?

JSX 是一个 JavaScript 的语法扩大,或者说是一个相似于 XML 的 ECMAScript 语法扩大。它自身没有太多的语法定义,也不冀望引入更多的规范。

其实 React 自身并不强制应用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于应用 React.createElement 函数。代码如下:

class Hello extends React.Component {render() {
    return React.createElement(
        'div',
        null, 
        `Hello ${this.props.toWhat}`
      );
  }
}
ReactDOM.render(React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

而 JSX 更像是一种语法糖,通过相似 XML 的形容形式,刻画函数对象。在采纳 JSX 之后,这段代码会这样写:

class Hello extends React.Component {render() {return <div>Hello {this.props.toWhat}</div>;
  }
}
ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

通过比照,能够清晰地发现,代码变得更为简洁,而且代码构造档次更为清晰。

因为 React 须要将组件转化为虚构 DOM 树,所以在编写代码时,实际上是在手写一棵构造树。而XML 在树结构的形容上天生具备可读性强的劣势。

但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会应用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。

总结: JSX 是一个 JavaScript 的语法扩大,构造相似 XML。JSX 次要用于申明 React 元素,但 React 中并不强制应用 JSX。即便应用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。

React 团队并不想引入 JavaScript 自身以外的开发体系。而是心愿通过正当的关注点拆散放弃组件开发的纯正性。

react 父子传值

父传子——在调用子组件上绑定,子组件中获取 this.props
子传父——援用子组件的时候传过来一个办法,子组件通过 this.props.methed()传过来参数

connection

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 长久化本地数据存储的简略利用。

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

为什么 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 的起因就是为了 升高应用的复杂度,返回数组的话能够间接依据程序解构,而返回对象的话要想应用屡次就须要定义别名了。

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>

react 最新版本解决了什么问题,减少了哪些货色

React 16.x 的三大新个性 Time Slicing、Suspense、hooks

  • Time Slicing(解决 CPU 速度问题)使得在执行工作的期间能够随时暂停,跑去干别的事件,这个个性使得 react 能在性能极其差的机器跑时,依然放弃有良好的性能
  • Suspense(解决网络 IO 问题) 和 lazy 配合,实现异步加载组件。能暂停以后组件的渲染,当实现某件事当前再持续渲染,解决从 react 出世到当初都存在的「异步副作用」的问题,而且解决得非的优雅,应用的是 T 异步然而同步的写法,这是最好的解决异步问题的形式
  • 提供了一个 内置函数 componentDidCatch,当有谬误产生时,能够敌对地展现 fallback 组件; 能够捕捉到它的子元素(包含嵌套子元素)抛出的异样; 能够复用谬误组件。

(1)React16.8 退出 hooks,让 React 函数式组件更加灵便,hooks 之前,React 存在很多问题:

  • 在组件间复用状态逻辑很难
  • 简单组件变得难以了解,高阶组件和函数组件的嵌套过深。
  • class 组件的 this 指向问题
  • 难以记忆的生命周期

hooks 很好的解决了上述问题,hooks 提供了很多办法

  • useState 返回有状态值,以及更新这个状态值的函数
  • useEffect 承受蕴含命令式,可能有副作用代码的函数。
  • useContext 承受上下文对象(从 React.createContext 返回的值)并返回以后上下文值,
  • useReducer useState 的代替计划。承受类型为(state,action)=> newState 的 reducer,并返回与 dispatch 办法配对的以后状态。
  • useCalLback 返回一个回顾的 memoized 版本,该版本仅在其中一个输出产生更改时才会更改。纯函数的输入输出确定性 o useMemo 纯的一个记忆函数 o useRef 返回一个可变的 ref 对象,其 Current 属性被初始化为传递的参数,返回的 ref 对象在组件的整个生命周期内放弃不变。
  • useImperativeMethods 自定义应用 ref 时公开给父组件的实例值
  • useMutationEffect 更新兄弟组件之前,它在 React 执行其 DOM 扭转的同一阶段同步触发
  • useLayoutEffect DOM 扭转后同步触发。应用它来从 DOM 读取布局并同步从新渲染

(2)React16.9

  • 重命名 Unsafe 的生命周期办法。新的 UNSAFE_前缀将有助于在代码 review 和 debug 期间,使这些有问题的字样更突出
  • 废除 javascrip: 模式的 URL。以 javascript: 结尾的 URL 非常容易蒙受攻打,造成安全漏洞。
  • 废除 ”Factory” 组件。工厂组件会导致 React 变大且变慢。
  • act()也反对异步函数,并且你能够在调用它时应用 await。
  • 应用 <React.ProfiLer> 进行性能评估。在较大的利用中追踪性能回归可能会很不便

(3)React16.13.0

  • 反对在渲染期间调用 setState,但仅实用于同一组件
  • 可检测抵触的款式规定并记录正告
  • 废除 unstable_createPortal,应用 CreatePortal
  • 将组件堆栈增加到其开发正告中,使开发人员可能隔离 bug 并调试其程序,这能够分明地阐明问题所在,并更快地定位和修复谬误。

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

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

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

对 React 中 Fragment 的了解,它的应用场景是什么?

在 React 中,组件返回的元素只能有一个根元素。为了不增加多余的 DOM 节点,咱们能够应用 Fragment 标签来包裹所有的元素,Fragment 标签不会渲染出任何元素。React 官网对 Fragment 的解释:

React 中的一个常见模式是一个组件返回多个元素。Fragments 容许你将子列表分组,而无需向 DOM 增加额定节点。

import React, {Component, Fragment} from 'react'

// 个别模式
render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}
// 也能够写成以下模式
render() {
  return (
    <>
      <ChildA />
      <ChildB />
      <ChildC />
    </>
  );
}

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 中 constructor 和 getInitialState 的区别?

两者都是用来初始化 state 的。前者是 ES6 中的语法,后者是 ES5 中的语法,新版本的 React 中曾经废除了该办法。

getInitialState 是 ES5 中的办法,如果应用 createClass 办法创立一个 Component 组件,能够主动调用它的 getInitialState 办法来获取初始化的 State 对象,

var APP = React.creatClass ({getInitialState() {
    return { 
        userName: 'hi',
        userId: 0
     };
 }
})

React 在 ES6 的实现中去掉了 getInitialState 这个 hook 函数,规定 state 在 constructor 中实现,如下:

Class App extends React.Component{constructor(props){super(props);
      this.state={};}
  }

react router

import React from 'react'
import {render} from 'react-dom'
import {browserHistory, Router, Route, IndexRoute} from 'react-router'

import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'

render(<Router history={browserHistory}>  // history 路由
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='about' component={About} />
      <Route path='features' component={Features} />
    </Route>
  </Router>,
  document.getElementById('app')
)
render(<Router history={browserHistory} routes={routes} />,
  document.getElementById('app')
)

React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件能够拦挡正在产生的跳转,或在来到 route 前提醒用户。routerWillLeave 返回值有以下两种:

return false 勾销此次跳转
return 返回提示信息,在来到 route 前提醒用户进行确认。

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

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

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

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

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

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

正文完
 0