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

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

时间2025-05-03 00:46:08分类IT科技浏览3303
导读:何为 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
typecho自定义页面(如何使用Typecho独立页面添加分类并优化网站SEO) 苹果cms采集参数配置(苹果CMS在线采集接口——解放双手提升效率)