前端一面高频react面试题(持续更新中)
如何避免组件的重新渲染?
React 中最常见的问题之一是组件不必要地重新渲染。React 提供了两个方法,在这些情况下非常有用:
-
React.memo()
:这可以防止不必要地重新渲染函数组件 -
PureComponent
:这可以防止不必要地重新渲染类组件
这两种方法都依赖于对传递给组件的props
的浅比较,如果props
没有改变,那么组件将不会重新渲染。虽然这两种工具都非常有用,但是浅比较会带来额外的性能损失,因此如果使用不当,这两种方法都会对性能产生负面影响。
通过使用 React Profiler,可以在使用这些方法前后对性能进行测量,从而确保通过进行给定的更改来实际改进性能。
React 性能优化
- shouldCompoentUpdate
- pureComponent 自带shouldCompoentUpdate的浅比较优化
- 结合Immutable.js达到最优
说说你用react有什么坑点?
1. JSX做表达式判断时候,需要强转为boolean类型
如果不使用
!!b
进行强转数据类型,会在页面里面输出0
。
render() {
const b = 0;
return <div>
{
!!b && <div>这是一段文本</div>
}
</div>
}
2. 尽量不要在 componentWillReviceProps
里使用 setState,如果一定要使用,那么需要判断结束条件,不然会出现无限重渲染,导致页面崩溃
3. 给组件添加ref时候,尽量不要使用匿名函数,因为当组件更新的时候,匿名函数会被当做新的prop处理,让ref属性接受到新函数的时候,react内部会先清空ref,也就是会以null为回调参数先执行一次ref这个props,然后在以该组件的实例执行一次ref,所以用匿名函数做ref的时候,有的时候去ref赋值后的属性会取到null
4. 遍历子节点的时候,不要用 index 作为组件的 key 进行传入
组件是什么?类是什么?类变编译成什么
- 组件指的是页面的一部分,本质就是一个类,最本质就是一个构造函数
- 类编译成构造函数
React 中 refs 的作用是什么
Refs
是React
提供给我们的安全访问DOM
元素或者某个组件实例的句柄- 可以为元素添加
ref
属性然后在回调函数中接受该元素在DOM
树中的句柄,该值会作为回调函数的第一个参数返回
在 Reducer文件里,对于返回的结果,要注意哪些问题?
在 Reducer文件里,对于返回的结果,必须要使用 Object.assign ( )来复制一份新的 state,否则页面不会跟着数据刷新。
return Object.assign({}, state, {
type: action.type,
shouldNotPaint: true,
});
setState方法的第二个参数有什么用?使用它的目的是什么?
它是一个回调函数,当 setState方法执行结束并重新渲染该组件时调用它。在工作中,更好的方式是使用 React组件生命周期之——“存在期”的生命周期方法,而不是依赖这个回调函数。
export class App extends Component {
constructor(props) {
super(props);
this.state = {
username: "雨夜清荷",
};
}
render() {
return <div> {this.state.username}</div>;
}
componentDidMount() {
this.setstate(
{
username: "有课前端网",
},
() => console.log("re-rendered success. ")
);
}
}
shouldComponentUpdate有什么用?为什么它很重要?
组件状态数据或者属性数据发生更新的时候,组件会进入存在期,视图会渲染更新。在生命周期方法 should ComponentUpdate中,允许选择退出某些组件(和它们的子组件)的和解过程。
和解的最终目标是根据新的状态,以最有效的方式更新用户界面。如果我们知道用户界面的某一部分不会改变,那么没有理由让 React弄清楚它是否应该更新渲染。通过在 shouldComponentUpdate方法中返回 false, React将让当前组件及其所有子组件保持与当前组件状态相同。
传入 setstate函数的第二个参数的作用是什么?
第二个参数是一个函数,该函数会在 setState函数调用完成并且组件开始重渲染时调用,可以用该函数来监听渲染是否完成。
this.setstate(
{
username: "有课前端网",
},
() => console.log("re-rendered success. ")
);
setState 是同步的还是异步的
有时表现出同步,有时表现出异步
- setState 只有在 React 自身的合成事件和钩子函数中是异步的,在原生事件和 setTimeout 中都是同步的
- setState 的异步并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的异步。当然可以通过 setState 的第二个参数中的 callback 拿到更新后的结果
- setState 的批量更新优化也是建立在异步(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在异步中如果对同一个值进行多次 setState,setState 的批量更新策略会对其进行覆盖,去最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新
- 合成事件中是异步
- 钩子函数中的是异步
- 原生事件中是同步
- setTimeout中是同步
说说 React组件开发中关于作用域的常见问题。
在 EMAScript5语法规范中,关于作用域的常见问题如下。
(1)在map等方法的回调函数中,要绑定作用域this(通过bind方法)。
(2)父组件传递给子组件方法的作用域是父组件实例化对象,无法改变。
(3)组件事件回调函数方法的作用域是组件实例化对象(绑定父组件提供的方法就是父组件实例化对象),无法改变。
在 EMAScript6语法规范中,关于作用域的常见问题如下。
(1)当使用箭头函数作为map等方法的回调函数时,箭头函数的作用域是当前组件的实例化对象(即箭头函数的作用域是定义时的作用域),无须绑定作用域。
(2)事件回调函数要绑定组件作用域。
(3)父组件传递方法要绑定父组件作用域。
总之,在 EMAScript6语法规范中,组件方法的作用域是可以改变的。
概述一下 React中的事件处理逻辑。
为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。
这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外, React并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理(基于事件委托原理)。
这样 React在更新DOM时就不需要考虑如何处理附着在DOM上的事件监听器,最终达到优化性能的目的。
redux中间件
中间件提供第三方插件的模式,自定义拦截
action
->reducer
的过程。变为action
->middlewares
->reducer
。这种机制可以让我们改变数据流,实现如异步action
,action
过滤,日志输出,异常报告等功能
redux-logger
:提供日志输出redux-thunk
:处理异步操作redux-promise
:处理异步操作,actionCreator
的返回值是promise
为什么虚拟dom会提高性能
虚拟
dom
相当于在js
和真实dom
中间加了一个缓存,利用dom diff
算法避免了没有必要的dom
操作,从而提高性能
具体实现步骤如下
- 用
JavaScript
对象结构表示 DOM 树的结构;然后用这个树构建一个真正的DOM
树,插到文档当中 - 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的
DOM
树上,视图就更新
虚拟DOM一定会提高性能吗?
很多人认为虚拟DOM一定会提高性能,一定会更快,其实这个说法有点片面,因为虚拟DOM虽然会减少DOM操作,但也无法避免DOM操作
- 它的优势是在于diff算法和批量处理策略,将所有的DOM操作搜集起来,一次性去改变真实的DOM,但在首次渲染上,虚拟DOM会多了一层计算,消耗一些性能,所以有可能会比html渲染的要慢
- 注意,虚拟DOM实际上是给我们找了一条最短,最近的路径,并不是说比DOM操作的更快,而是路径最简单
在 ReactNative中,如何解决8081端口号被占用而提示无法访问的问题?
在运行 react-native start时添加参数port 8082;在 package.json中修改“scripts”中的参数,添加端口号;修改项目下的 node_modules \react-native\local- cli\server\server.js文件配置中的 default端口值。
组件更新有几种方法
-
this.setState() 修改状态的时候 会更新组件
-
this.forceUpdate() 强制更新
-
组件件render之后,子组件使用到父组件中状态,导致子组件的props属性发生改变的时候 也会触发子组件的更新
当渲染一个列表时,何为 key?设置 key 的目的是什么
Keys 会有助于 React 识别哪些 items
改变了,被添加了或者被移除了。Keys 应该被赋予数组内的元素以赋予(DOM)元素一个稳定的标识,选择一个 key 的最佳方法是使用一个字符串,该字符串能惟一地标识一个列表项。很多时候你会使用数据中的 IDs 作为 keys,当你没有稳定的 IDs 用于被渲染的 items
时,可以使用项目索引作为渲染项的 key,但这种方式并不推荐,如果 items
可以重新排序,就会导致 re-render
变慢。
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只能在组件顶层使用,不可在分支语句中使用。、
vue 或者react 优化整体优化
- 虚拟dom
为什么虚拟 dom 会提高性能?(必考)
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
如何告诉 React 它应该编译生产环境版
通常情况下我们会使用
Webpack
的DefinePlugin
方法来将NODE_ENV
变量值设置为production
。编译版本中React
会忽略propType
验证以及其他的告警信息,同时还会降低代码库的大小,React
使用了Uglify
插件来移除生产环境下不必要的注释等信息