翻到OneNote乱七八糟的面试题截图,随手整理一下整了一天,手已软···
- 有更好看的版面在此处!!!点我 !!!
什么是虚拟 DOM?
- 虚拟 DOM(VDOM)是真实 DOM 在内存中的表示,是 UI 的表示形式,同时和实际 DOM 同步。这是发生在渲染函数被调用和DOM 元素在屏幕上显示之间的过程,整个过程被称为调和。
为什么虚拟 DOM 会提高性能?
-
虚拟 DOM 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
-
用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中,当状态变更的时候,重新构造一颗新的对象树,然后用新的树和旧的树进行比较,记录两棵树的差异,把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
react diff 原理?
- 把树形结构按层分解,只比较同级元素。
- 把列表结构的每个单元添加唯一的 key 属性,方便比较。
- react 只会匹配相同的 class 名的 component。
- 合并操作,调用 component 的 setState 方法的时候,react 将其标记到 dirty。到每一个事件循环结束,react 检查所有标记 dirty 的 component 重新绘制。
- 选择性子树渲染,开发人员可以重写 shouldComponentUpdate,提高 diff 性能。
react 构建组件的三种方式?
- React.createClass()
- ES6 clss
- 无状态函数 function
如何 React.createElement?
const element=React.createElement(
'h1',
{className:'greeting'},
'Hello,world!'
)
类组件(Class)和函数组件(Function)有什么区别?
- 类组件允许使用其它特性,如组件自身的状态 state 和生命周期钩子,还能使组件直接访问 store 并维持状态。
- 当组件只接收 props 组件渲染页面时,就是一个"无状态组件(Stateless)",可以使用一个纯函数来创建这样的组件,这就是函数组件,这种组件也被称为"哑组件(Dumb)“或"展示组件”。
- 通过 function 定义组件的写法更加 js,代码结构简单,代码量小,没有 this 指向问题
什么是纯函数?
- 纯函数是不依赖并且不会在其作用域之外修改变量状态的函数。本质上,纯函数始终在给定向同参数的情况下返回相同的结果。
(组件的)状态(State)和属性(props)之间有何不同?
- State 是一个数据结构,用于组件挂载时所需数据的默认值,是组件自己管理数据,控制自己的状态,可变。
- Props(properties)是组件的配置,是外部传入的数据参数,Props 由父组件传递给子组件,并且就子组件而言,props 是不可变的。组件虽然不能改变自身的 props,但是可以把其子组件的 props 放在一起统一管理。Props 也不仅仅是数据,回调函数也可以通过 Props 传递。
- props 和 state 是普通的 JS 对象,它们都包含影响渲染输出的信息,没有 state 的叫做无状态组件,有 state 的叫做有状态组件,多用 props,少用 state,也就是多写无状态组件。
在构造函数中,调用 super(props)的目的/作用是什么?
- 在 super()被调用之前,子类构造函数是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super(),ES6 一样。
- 将参数 props 传递 给 super()的原因则是:便于在子类中能在 constructor 访问 this.props,通过 this.props 来获取传入的 props。
为什么类方法需要绑定到类实例?
- 在 JS 中,this 值会根据当前上下文变化,在 React 类组件方法中,开发人员通常希望 this 引用组件的当前实例,因此非常有必要把这些方法绑定到实例,通常这是在构造函数中完成的。
有几种为事件处理函数绑定 this 指向方法?
- 组件初始化时,通过 class 类的方法在 constructor 里绑定,好处只会执行一次。
this.state = {};
this.keyUpHandle = this.keyUpHandle.bind(this);
- 方法调用时绑定,每一次数据改变,方法重新调用就会执行。
<input onKeyUp = { this.nickNameKeyUpHandle.bind(this) }>
nickNameKeyUpHandle(e){
console.group("在方法调用的时候绑定");
console.log("e.target.value");
console.log("this");
console.groupEnd();
}
- render 里,用箭头函数绑定,因为箭头函数的 this 指向箭头函数定义的时候其所处作用域的 this,render 函数 this 始终指向组件实例,所以箭头函数 this 也指向组件实例,但是,每次数据改变页面刷新都会重新绑定。
<input onKeyUp = { e => this.skillKeyUpHandle(e) }>
skillKeyUpHandle(e){
console.group("箭头函数改变this指向");
console.log("e.target.value");
console.log("this");
console.groupEnd();
}
有几种常用方法可以避免在 react 中绑定方法?
- 将事件处理程序定义为内联箭头函数
class SubmitButton extends React.Component{
constructor(props){
super(props);
this.state={
isFormSubmitted:false
}
}
render(){
return(
<button onClick={()=>{
this.setState({isFormSubmitted:true});
}}>Submit</button>
)
}
}
- 使用箭头函数来定义方法
class SubmitButton extends React.Component{
state={
isFormSubmitted:false
}
handleSubmit =()=>{
this.setState({
isFormSubmitted:true
})
}
render(){
return(
<button onClick={this.handleSubmit}>Submit</button>
)
}
}
- 使用带有 Hooks 的函数组件
const SubmitButton=()=>{
const [isFormSubmitted,setIsFormSubmitted]=useState(false);
return(
<button onClick={()=>{
setIsFormSubmitted(true)
}}>Submit</button>
)
}
什么是受控组件?
- 在 HTML 中,类似<input>、<textarea>和<select>这样的表单元素会维护自身的状态,并基于用户的输入来更新,当用户提交表单时,前面提到的元素的值将随表单一起被发送,但在 react 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值,并且每次调用回调函数时,如 onChange 会更新 state,重新渲染组件。一个输入表单元素,它的值通过 react 这种方式来控制,这样的元素就被称为"受控元素"。
受控组件和非受控组件区别是什么?
-
受控组件是 react 控制中的组件,并且是表单数据真实的唯一来源。
-
非受控组件是由 DOM 处理表单数据的地方,而不在 react 组件中。
- 尽管非受控组件通常更易于实现,因为只需使用 refs 即可从 DOM 中获取值,但通常建议优先选择受控组件,主要原因是受控组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式。
什么是 React Hooks?
- Hooks是 react16.8 中的新增内容,它们允许在不编写类的情况下使用 state 和其他 react 特性。使用 Hooks 可以从组件中提取有状态逻辑,这样就可以独立地测试和重用它。Hooks允许咱在不改变组件层次结构的情况下重用有状态逻辑,这使得在许多组件之间或者与社区共享 Hooks 变得很容易。
使用 React Hooks 好处是啥?
-
首先,Hooks通常支持提取和重用跨多个组件通用的有状态逻辑,而无需承担高阶组件或渲染 props 的负担,Hooks可以轻松地操作函数组件的状态,而不需要将它们转换为类组件。
-
Hooks 在类中不起作用,通过使用它们,咱们可以完全避免使用生命周期,例如 componentDidMount、componentDidUpdate、componentWillUnmount,相反去使用像 useEffect 这样的内置钩子。
-
useState、useEffect、useLocation、useParams 等,useLocation 可以获取当前的 url 地址数据,useParams 是路由新增的 hook 方法可以直接获取到当前的 params 参数。
什么是 useState,useEffect?
- useState 可以在 function 定义的组件中设置局部状态,返回两个值:局部状态的名字和改变状态的方法。改变数据或者使用数据直接使用 useState,数组。
const {useState}=React;
function Counter(){
const [count,setCount]=useState(0);
}
- useEffect 副作用,可以在 function 定义的组件中模拟生命周期,参数一是回调函数,参数二是依赖项,当参数二中的数据改变之后会触发第一个回调函数,如果参数二为空数组表示组件初始化的时候只执行一次
useEffect(()=>{
},[...])
Hooks 会取代 render props 和高阶组件吗?
- 通常,render、props 和高阶组件仅渲染一个子组件,React 团队认为,Hooks 是服务此用例的更简单方法。
- 这两种模式仍然有一席之地(例如,一个虚拟的 scroller 组件可能有一个 renderItem prop,或者一个可视化的容器组件可能有它自己的 DOM 结构)。但在大多数情况下,Hooks 就足够了,可以帮助减少树中的嵌套。
这三个点(…)在 React 是干嘛用的?
- 对于创建具有现有对象的大多数属性的新对象非常方便,更新 state 经常这么做
为什么建议传递给 setState 的参数是一个 callback 而不是一个对象?
- 因为 this.props 和 this.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。
为什么不直接更新 state 呢?
- 如果试图直接更新 state,则不会重新渲染组件,需要使用 setState()方法来更新 state,它调度对组件 state 对象的更新,当 state 改变时,组件通过重新渲染来响应。
当调用 setState 时,React render 是如何工作的?
-
咱们可以将"render"分为两个步骤:
-
虚拟 DOM 渲染:当 render 方法被调用时,它返回一个新组件的虚拟 DOM 结构,当调用 setState()时,render 会被再次调用,因为默认情况下 shouldComponentUpdate 总是返回 true,所以默认情况下 react 是没有优化的。
-
原生 DOM 渲染:react 只会在虚拟 DOM 中修改真实 DOM 节点,而且修改的次数非常少–这是很棒的 react 特性,它优化了真实 DOM 的变化,使 React 变得更快。
-
调用 setState 之后发生了什么?
-
在代码中调用 setState 函数之后,react 会将传入的参数对象和组件当前的状态合并,然后触发所谓的调和过程。
-
经过调和过程,react 会以相对高效的方式根据新的状态构建 react 元素树并且着手重新渲染整个 UI 界面。
-
在 react 得到元素树之后,react 会自动计算出新树和老树的节点差异,然后根据差异对界面进行最小化重渲染,在差异计算算法中,react 能够相对精确地知道哪些位置发生了改变,以及应该如何改变,这就保证了按需更新而不是全部重新渲染。
如果需要根据以前的状态重新设置状态,怎么写比较好?
- 将一个函数传递给 setState,该函数接收上一个 state 的值和当前的 props,并返回一个新的状态。
this.setState((prevState,props)=>{
return{
streak:prevState.streak+props.count
}
})
react 中 refs 是干嘛用的?
- Refs提供了一种访问在 render 方法中创建 DOM 节点或者 React 元素的方法,在典型的数据流中,父子组件交互的唯一方式是 props,先要修改子组件,需要使用新的 props 重新渲染,凡是有例外,某些情况下咱们需要在典型数据流外,强制修改子代,这个时候可以使用 refs,在组件添加一个 ref 属性来用,该属性的值是一个回调函数,接收作为其第一个参数的底层 DOM 元素或组件的挂载实例。
<form onSumbit={this.handleSubmit}>
<input type="text" ref={(input)=>this.input=input} />
- Input 有一个ref 属性,它的值是一个函数,该函数接收输入的实际 DOM 元素,然后将其放在实例上,这样就可以在 handleSumbmit 内部访问它。
handelSubmit=()=>{
console.log('Input Value:',this.input.value)
}
- 经常被误解,只有在类组件才能使用 refs,但 refs 也可以通过利用 js 中的闭包和函数组件一起使用。
function CustomForm({handleSumit}){
let inputElement
return(
<form onSubmit={()=>handleSubmit(inputElement.value)}>
<input type="text"
ref={(input)=>inputElement=input} />
<button type="submit">Submit</button>
</form>
)
}
react 中 keys 是什么作用?
- Keys 是 react 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
render(){
return(
<ul>
{this.state.todoItems.map(({item,key})=>{
return <li key={key}>{item}</li>>
})}
</ur>
)
}
- 在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。
- 在 React Diff 算法中 react 会借助元素的 key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。
- React 还需要借助 Key 值来判断元素和本地状态的关联关系,因此,绝不可忽视转换函数中 Key 的重要性。
React 组件生命周期有哪些不同阶段?
- 在组件生命周期有四个不同阶段:
-
组件初始化阶段,Initialization,该阶段组件设置一些默认属性和初始化状态 state。
-
挂载阶段,Mounting,该阶段三个过程:将要挂载(componentWillMount)->render 渲染(此阶段 render 执行一次)->挂载完成(componentDidMount)。
-
更新阶段,Updating,该阶段中,组件以两种方式更新:属性更新和数据更新。发送新的 props 和 state 状态,当组件接收之后会判断组件是否需要更新,更新阶段只有修改了属性或者数据,都会重新渲染。在 render 调用 this.setState 就会死循环。(shouldComponentUpdata,ComponentWillUpdata 和 ComponentDidUpdata)
-
卸载阶段,componentWillUnmount。
React 组件生命周期方法有哪些?
- componentWillMount:渲染之前执行,用于根组件的 App 级配置
- componentDidMount:在第一次渲染之后执行,可以在这里做ajax 请求、dom 的操作和状态更新以及事件监听器
- componentWillReceiveProps:初始化 render 时不会执行,会在组件接收到新的状态(props)时被触发,一般用于父组件状态更新时子组件的重新渲染
- shouldComponentUpdata:确定是否更新组件,默认情况下返回 true,如果确定在 state 或 props 更新后组件不需要再重新渲染,返回 false,这是一个提高性能的好方法
- ComponentWillUpdata:在 shouldComponentUpdata 返回 true 确定要更新组件之前执行
- ComponentDidUpdata:主要用于更新 DOM 以响应 props 或 state 更改
- componentWillUnmount:用于取消任何的网络请求,或删除与组件关联的所有事件监听器
shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)
- shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom,因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中写出更优化的 dom diff 算法,可以极大的提高性能。
应该在 React 组件的何处发起 ajax 请求
- 在 react 组件中,应该在 componentDidMount 中发网络请求,这个方法会在组件第一次挂载时执行,在组件的生命周期中仅会执行一次。最重要的是,你不能保证组件挂载之前 ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用,在 componentDidMount 中发网络请求将保证这有一个组件可以更新了。
描述事件在 React 中的处理方式,(在 react 中如何处理事件)?
-
为了解决浏览器的兼容性问题,SyntheticEvent 实例将被传递给事件处理函数。SyntheticEvent 是 react 跨浏览器的浏览器原生事件包装器,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation()和 preventDefault()。
-
react 实际上并不将事件附加到子节点本身,react 使用单个事件侦听器侦听顶层所有事件,这对性能有好处,也意味着 react 在更新 dom 时不需要跟踪事件监听器。
React 组件的划分业务组件技术组件?
-
根据组件的职责通常把组件分为UI 组件和容器组件。
-
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
-
两者通过React-Redux提供connect方法联系起来。
展示组件(Presentational component)和容器组件(Container component)之间有何不同?
-
展示组件关心组件看起来是什么,展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只会关心 UI 状态而不是数据的状态。
-
容器组件关心组件是如何运作的,容器组件会为展示组件或者其他容器组件提供数据和行为,他们会调用 Flux actions,并将其作为回调提供给展示组件,容器组件经常是有状态的,因为他们是(其它组件的)数据源。
描述 Flux 与 MVC?
-
传统的 MVC 模式在分离数据(Model)、UI(View)和逻辑(Controller)方面工作得很好,但是 MVC 架构经常遇到两个主要问题:
- 数据流不够清晰:跨视图发生的级联更新常常会导致混乱的事件网络,难于调试
- 缺乏数据完整性:模型数据可以在任何地方发生突变,从而在整个 UI 中产生不可预测的结果
-
使用Flux 模式的复杂用户界面
- 不再遭受级联更新
- 任何给定的 react 组件都能够根据 store 提供的数据重建其状态
- Flux 模式还通过限制对共享数据的直接访问来加强数据完整性
简述 Flux 思想?
-
Flux 的最大特点,就是数据的"单向流动"
- 用户访问 view
- view 发出用户的 action
- dispatch 收到 action,要求 store 进行相应的更新
- store 更新后,发出一个"change"事件
- view 收到"change"事件,更新页面
什么是高阶组件(Higher Order Component)?
- 高阶组件是一个以组件为参数并返回一个新组件的函数。最常见的可能是 Redux 的connect 函数,connect 接收一个 function 作为参数,返回值表示当前组件中可以使用到的 redux 里面的数据。除了简单分享工具库,HOC 最好的方式是共享 React 组件之间的行为,如果你发现在不同的地方写了大量代码来做同一件事时,就应考虑将代码重构为可重用的 HOC。基本上,这是一个模式,是从 React 的组合特性中衍生出来的,称其为纯组件,因为它们可以接受任何动态提供的子组件,但不会修改或复制输入组件中的任何行为。
const EnhancedComponent=higherOrderComponent(WrappedComponent);
-
HOC 可用于以下许多用例:
-
代码重用、逻辑和引导抽象
-
渲染劫持
-
state 抽象和操作
-
props 处理
-
了解 redux 么,说一下 redux 吧?
-
redux 是一个应用数据流框架,主要解决了组件间状态共享的问题,原理是集中式管理,可以让数据更可控,react 中所有数据处理都在 redux 中进行,三个核心方法,action、store 和 reducer。
-
在 redux 中遵循的原则:
-
一个项目只能有一颗数据树。
-
所有的数据改变都需要在 reducer 中进行。
-
所有的 action 都是简单的对象。
-
store 用来存储数据
-
-
redux 工作流程是:view 调用 store 的 dispatch 接收 action 传入的 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据。
-
Flux 也是用来进行数据操作的,有四个组成部分,action、dispatch、view 和 store。
-
Flux 工作流程是:view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。
-
Redux 和 Flux 很像,主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中 dispatch 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store、redux 把 store 和 dispatcher 合并,结构更加清晰简单。
-
新增 state,对状态的管理更加明确,通过 redux 流程更加规范,减少手动编码量,提高了编码效率,同时缺点是当数据更新时,有时候组件不需要,但也要重新绘制,有些影响效率,一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们。
redux 有什么缺点?
- 一个组件所需要的数据,必须由父组件传过来,而不能像 flux 中直接从 store 中取。
- 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的 shouldComponentUpdate 进行判断。
React 中的 StrictMode(严格模式)是什么?
-
react 的 StrictMode 是一种辅助组件,可以帮助咱们编写更好的 react 组件,可以使用包装一组组件,并且可以帮咱们以下检查:
- 验证内部组件是否遵循某些推荐做法,如果没有,会在控制台给出警告
- 验证是否使用的已经废弃的方法,如果有,会在控制台给出警告
- 通过识别潜在的风险预防一些副作用
什么是 prop drilling,如何避免?
-
在构建 react 应用程序时,在多层嵌套组件来使用另一个嵌套组件提供的数据,最简单的方法是将一个 prop 从每个组件一层层的传递下去,从源组件传递到深层嵌套组件,这就叫 prop drilling。
-
prop drilling 的主要缺点是原本不需要数据的组件变得不必要的复杂,并且难以维护。
-
为了避免 prop drilling,一种常用的方法是使用 React Context,通过定义提供数据的 Provider 组件,并允许嵌套的组件通过 Consumer 组件或 useContext Hook 使用上下文数据。通过 Provider 组件可以把 store 也就是整个 redux 和实例绑定。
什么是 React Context?
- Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
如何避免组件的重新渲染?
-
React 中最常见的问题之一是组件不必要的重新渲染,React 提供了两个方法,非常有用:
- React.memo():这可以防止不必要地重新渲染函数组件
- PureComponent:这可以防止不必要地重新渲染类组件
-
这两种方法都依赖于对传递组件的 props 的浅比较,如果 props 没有改变,那么组件将不会重新渲染,虽然这两种工具都非常有用,但是浅比较会带来额外的性能损失,因此如果使用不当,这两种方法都会对性能产生负面影响。
-
通过使用 React Profiler,可以在使用这些方法前后对性能进行测量,从而确保通过进行给定的更改来实际改进性能
什么是 React Fiber?
-
Fiber 是 React16 中新的协调引擎或重新实现核心算法,它的主要目标是支持虚拟 DOM 的增量渲染。
-
React Fiber 的目标是增强其在动画、布局和手势等领域的适用性,并为不同类型的更新分配优先级,以及新的并发原语。它的主要特性是增量渲染:能够将渲染工作分割成块,并将其分散到多个帧中。
在 React 中使用构造函数和 getInitialState 有什么区别?
- 构造函数和 getInitialState 之间的区别就是 ES6 和 ES5 本身的区别,在使用 ES6 类时,应该在构造函数中初始化 state,并在使用 React.createClass 时定义 getInitialState 方法。
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={/*initial state*/};
}
}
等价于
const MyComponent==React.createClass({
getInitialState(){
return {/*initial state*/}
}
})
如何有条件地向 React 组件添加属性?
- 对于某些属性 react 非常聪明,如果传递给它的值是虚值,可以省略该属性,例如:
const InputComponent=React.createClass({
render:function(){
const required=true;
const disabled=false;
return(
<input type="text" disabled={disabled} required={required} />
)
}
})
渲染结果:
<input type="text" required />
- 另一种可能的方法是:
const condition = true;
const component=(
<div
value="foo"
{...(condition&&{disabled:true})}>
)
createElement和cloneElement有什么区别?
- React.createElement():JSX语法就是用React.createElement()来构建react元素的,它接收三个参数:
- 第一个参数可以是一个标签名,如div、span或者react组件;
- 第二个参数为传入的属性;
- 第三个以及之后的参数,皆为组件的子组件
React.createElement(
type,
[props],
[...children]
)
- React.cloneElement()与React.createElement()相似,不同的是它传入的第一个参数是一个react元素,而不是标签名或组件。新添加的属性会并入原有的属性,传入到返回的新元素中,而旧的子元素将被替换
React.cloneElement(
element,
[props],
[...children]
)
React项目用过什么脚手架?
- craet-react-app、Yeoman等
没想好题目…自定义属性.?
- 当应用程序在开发者模式下运行时,react将自动检查咱们在组件设置的所由于性能影响而禁用它。有props,以确保它们具有正确的数据类型。对于不正确的类型,开发模式下会在控制台中生成警告消息,而在生产模式中由于性能影响而禁用它,强制的props用isRequired定义的。
e.g: 一组预定义的prop类型:
React.PropTypes.string
React.PropTypes.number
React.PropTypes.func
React.PropTypes.node
React.PropTypes.bool
例如,我们为用户组件定义了如下的propTypes
import ProTypes from 'prop-types';
Class User extends React.Component{
render(){
return(
<h1>Welcome,{this.props.name}</h1>
<h2>Age,{this.props.age}</h2>
)
}
}
User.propTypes={
name:PropTypes.string.isRequired,
age:PropTypes.number.isRequired
}