首页IT科技前端面试二面聊什么问题(2023前端二面react面试题(边面边更))

前端面试二面聊什么问题(2023前端二面react面试题(边面边更))

时间2025-08-02 21:49:25分类IT科技浏览4566
导读:何为 JSX JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产 React “元素”,你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX 中。在编译完成之后,JSX 表达式就变成了常规的 JavaS...

何为 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 优化整体优化

虚拟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> ); }

在上述的示例中             ,不会对 Header 和 Footer 组件运行严格模式检查       。但是,ComponentOne 和 ComponentTwo 以及它们的所有后代元素都将进行检查       。

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 更新流程: 这个过程当中涉及的函数:

shouldComponentUpdate: 当组件的 state 或 props 发生改变时       ,都会首先触发这个生命周期函数                    。它会接收两个参数:nextProps, nextState——它们分别代表传入的新 props 和新的 state 值              。拿到这两个值之后                     ,我们就可以通过一些对比逻辑来决定是否有 re-render(重渲染)的必要了。如果该函数的返回值为 false             ,则生命周期终止,反之继续;

注意:此方法仅作为性能优化的方式而存在                    。不要企图依靠此方法来“阻止                    ”渲染                     ,因为这可能会产生 bug                    。应该考虑使用内置的 PureComponent 组件                    ,而不是手动编写 shouldComponentUpdate()

componentWillUpdate:当组件的 state 或 props 发生改变时,会在渲染之前调用 componentWillUpdate。componentWillUpdate 是 React16 废弃的三个生命周期之一              。过去              ,我们可能希望能在这个阶段去收集一些必要的信息(比如更新前的 DOM 信息等等)                    ,现在我们完全可以在 React16 的 getSnapshotBeforeUpdate 中去做这些事; 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                    ,匹配 /login 到 Login,所以需要两个 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                     ,表示组件即将被销毀              。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
网站怎么优化关键词排名的方法(如何通过SEO优化提高网站关键词排名?) django(Django实战(3):Django也可以有scaffold Holbrook 博客园)