240305Note: fe - React
Opened this issue · 0 comments
React
一、组件基础
1. React 事件机制
<div
onClick={this.handleClick.bind(this)}
>
点我
</div>
react并不是将click事件绑定到div真实的dom上,而是在document处监听了所有的事件。当事件发生并且冒泡在document处时,react将事件内容封装并交给真正的处理函数处理,这样的方式不仅仅减少了内存的消耗,还能在挂件在销毁时统一订阅和移除事件。
除此之外,冒泡到document上的事件也不是原生的浏览器事件,而是由react自己实现的合成事件。因此不想事件冒泡,应该调用event.preventDefault()方法,而不是调用event.stopPropagation()方法。
So,
- react在document处监听了所有事件,当事件发生并冒泡到document,将事件内容封装成合成事件并交给真正的处理函数处理
- 没有将click事件绑定到真是的dom上,所以要用event.preventDefault()阻止事件冒泡,而不是event.stopPropagation()方法
- 好处:
- 减少了内存的消耗
- 让组件挂载销毁时统一订阅和移除事件
- 抹平浏览器之间的兼容性
4. React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代
- 高阶组件HOC是react用于复用组件逻辑的一种高级技巧。HOC自身不是React API的一部分,他是基于React的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。HOC接受一个组件和额外的参数(如果需要),返回一个新的组件。
function withSubscription(WrappedComponent, selectData){
return class extend 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));
- render props是一种在React组件之间使用一个值为函数的prop共享代码的简单技术。render prop是一个用于告知组件需要渲染什么内容的函数prop
-
通常,render props和高阶组件只渲染一个子节点。让Hook来服务这个使用场景更简单,Hook可以帮助减少桥套。
-
hoc、render props、hook都是为了解决代码复用的问题。
-
hoc接收一个组件和一个可选的参数,返回一个组件。通过包装现有的组件来添加新的行为和功能
-
render props接收一个返回react函数,将render的渲染注入到组件内部。父组件通过一个函数prop将渲染逻辑传给子组件,子组件通过函数获取渲染要渲染的内容。
-
hook是react16.8的新特性,可以使用状态useState、生命周期useEffect、上下文useContext
5. 对React-Fiber的理解,它解决了什么问题?
- React 15在渲染是会递归对比虚拟Dom树,再出需要变动的节点,然后同步更新他们,一气呵成。这个过程React会占据浏览器资源,会导致用户触发的事件得不到响应,导致用户感觉卡顿。
- React通过fiber架构让渲染过程可中断,让浏览器及时响应用户交互。
- 此外fiber还可以分批延时对DOM进行操作,避免一次性操作大量的DOM节点
9. React 高阶组件是什么,和普通组件有什么区别,适用什么场景
- 高阶组件,输入为一个组件和一个可选参数,输出一个组件
- 场景
- 代码、状态逻辑复用
- 渲染劫持,权限控制
- 场景
- 普通组件
- 场景
- 描述UI、功能单一、易于理解
- 类组件
- 继承Component,输出组件
- 函数组件
- 输入props,输出组件
- 场景
function hoc(wrappedComponent, optionalData){
return class extends React.Component {
constructor(props){
super(props);
this.state = {
data: optionalData(DataSource, props)
}
}
}
render(){
return <WrappedComponent data={this.state.data} {...this.props}/>;
}
}
const BlogPostWithSubscription = hoc(BlogPost, (DataSource, props)=>DataSource.getBlogPost(props.id));
//普通组件
import React, {Component} from "react";
class MyCompoent extends Component{
render(){
return <div>...</div>
}
}
function MyCompoent(props) {
return <div>...</div>
}
11. 哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?
- 哪些方法
- State
- Props
- force update
- 生命周期方法
- Context变化
- 会做些什么
- 调用render生成虚拟dom
- 用Diff算法对新旧虚拟DOM节点进行对比
- 对新旧两棵树进行深度优先遍历,进行标记对比。更具差异类型和对应的规则更新变化部分的虚拟DOM节点
- 生命周期调用
14. 对有状态组件和无状态组件的理解及使用场景
无状态一般是纯展示性的组件,简单UI。
有状态适用于复杂UI和处理用户交互的组件。
23. React中什么是受控组件和非控组件?
- 受控组件
- 受到state控制
- 用户与组件交互时,通常会触发事件来处理函数,更新组件state
- 非受控组件
- 不受state控制
- 由DOM元素本身管理
- 在非受控组件中,可以使用ref来获取输入框的值
28. 类组件与函数组件有什么异同?
-
不同
- 生命周期
- 在react16.8之前,函数组件没有生命周期
- 代码量,函数更轻量
- 性能,函数更快,但是微不足道
- 初始化状态,类组件在构造函数中进行,函数组件在useState hook来初始化状态
- 生命周期
-
共同
- 用途,都是为了构建用户界面和定义组件行为
- 组件特性,都可以接受输入props
- 组件嵌套,都可以嵌套成组件树
23
数据管理
1. React setState 调用的原理
- 首先调用setState入口函数,根据输入参数不同,分发到不同功能函数中去
ReactComponent.prototype.setState = function(partialState, callback){
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, "setState");
}
}
- enqueueSetState将新的state放进组件的状态队列,调用enqueeUpdate来处理将要更新的实例
enqueueSetState: function(publicInstance, partialState){
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState");
var queue = internalInstance._pendingStateQueue||(internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
}
- 在enqueueUpdate方法引出一个关键的对象batchingStrategy,该对象所具备的isBatchingUpdates属性直接决定了当下是要走更新流程,还是应该排队等待;如果轮到执行,就调用batchUpdates方法发起更新流程。由此可以推测,batchingStrategy或许正是React内部专用于批量更新的对象
function enqueueUpdate(component){
ensureInjected();
if(!batchingStrategy.isBatchingUpdates){
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return
}
dirtyComponents.push(component);
if(component._updateBatchNUmber == null){
component._updateBatchNumber = updateBatchNumber + 1;
}
}
- setState
- enqueueSetState:将新的状态放入组件的状态队列,调用enqueueUpdate来处理要更新的视力
- enqueueUpdate
- isBatchingUpdates,是否批量创建/更新组件
- true: dirtyComponent,更新组件
- false: dirtyComponents,推入组件
2. React setState 调用之后发生了什么?是同步还是异步?
在代码中调用setState,React会将传入的参数对象与组件当前的状态合并,然后触发调和过程,经过调和过程。React会以相对高效的方式根据新的状态React元素树并且着手重新渲染整个UI界面。
React得到元素树之后,React会自动计算出新的树和老树的节点差异,然后根据差异对界面进行最小化重新渲染。在差异计算算法中,React能够精确地知道哪些位置发生了改变以及如何改变,这就保证了按需更新,而不是重新渲染。如果短时间频繁setState,React会将state的改变压入栈,在合适的时机,批量更新state和视图,达到提高性能的效果。
- React将输入和组件当前的状态合并,触发调和过程
- 得到新的元素树
- 计算新老树的差异
- 按需更新
- 如果短时间频繁setState,将state改变压入栈,在合适的时机批量更新state和视图
7. 在React中组件的this.state和setState有什么区别?
this.state通常是用来初始化state,setState是用来修改state值的。如果直接修改state页面是不会更新的。
9. React组件的state和props有什么区别?
- props是传递给函数的形参,state是组件内自己管理的变量
- props是不可以被修改的,react要不好props不能被修改
- state是在组件中创建的,一般在constructor中初始化
三、生命周期
1. React的生命周期有哪些?
- 装载阶段Mount
- constructor 用来创建组件
- render,渲染返回需要渲染的内容
- componentDidMount,挂在完成后执行
- 更新阶段Update
- 给组件创建props、setState、forceUpdate都会重新调用render函数,
- 更新完成后执行componentDidUpdate
- 卸载过程Unmount
- 移除完成执行componentWillUnmount
四、组件通信
1. 父子组件的通信方式?
- 父到子
- props
const Child = props => {
return <p>{props.name}</p>
}
const Parent = () => {
return <Child name="rect"></Child>
}
- 子到父
- props+回调
const Child = props => {
const cb = msg => {
return () => {
props.callback(msg)
}
}
return {
<button onClick={cb("你好")}>你好</button>
}
}
class Parent extends Component {
callback(msg){
console.log(msg)
}
render(){
return <Child callback={this.callback.bind(this)}></Child>
}
}
2. 跨级组件的通信方式?
父组件向子组件的子组件通信,向更深层子组件通信:
- props
- context
const BatteryContext = createContext();
// 子组件的子组件
class GrandChild extends Component {
render(){
return (
<BatteryContext.Consumer>
{
color => <h1
style={{"color": color}}我是红色的:{color}></h1>
}
</BatteryContext.Consumer>
)
}
}
// 子组件
const Child = () => {
return (
<GrandChild/>
)
}
// 父组件
class Parent extends Component {
state = {
color: "red"
}
render(){
const {color} = this.state;
return (
<BatteryContext.Provider value={color}>
<Child></Child>
</BatteryContext.Provider>
)
}
}
3. 非嵌套关系组件的通信方式?
- 事件通信,订阅模式
- redux全局状态管理
- 兄弟之间可以找到共同的父节点通信
5. 组件通信的方式有哪些
- props
- props+回调
- Context
- 订阅模式
- 全局状态管理
五、路由
1. React-Router的实现原理是什么?
- hash模式
- 监听hashchange
- history
- 监听url变化
7. React-Router的路由有几种模式?
- history的BrowserRouter模式
- HashRouter模式
六、Redux
1. 对 Redux 的理解,主要解决什么问题
react是视图层框架,redux是一个用来管理数据状态和UI状态的js应用工具。随着js单页应用变得复杂,js需要更多状态,redux就是为了降低管理难度的。
在redux中,提供了一个叫store的统一仓储库,组件通过dispatch将state直接传入store,不用通过其他组件。并且组件通过subscribe从store获取到state的改变,使用了redux,所有的组件都可以从store获取到所需的state,他们也能从store获取到state的改变。
解决的问题:react-redux的作用是将redux的状态机和react的ui绑定在一起,当你dispatch action改变state的时候,会自动更新页面。
2. Redux 原理及工作流程
工作流程
- view通过dispatch发出action
- store调用reducer,传入state、action,reduce返回新的state
- state有了变化,store调用监听函数,更新view
9. Redux 和 Vuex 有什么区别,它们的共同**
- 区别
- vuex改进了redux的action和reducer函数,以及mutations变化函数取代reducer,无需switch,只需要在对应的mutation函数里改变state值即可
- vuex由于vue自动重新渲染的特性,无需订阅。
- vuex数据流的顺序是view调用store.commit提交对应的请求到store中对应的mutation函数到store改变
- 共同**
- 单一数据源
- 变化可以预测
七、Hooks
1. 对 React Hook 的理解,它的实现原理是什么
hook让组件实现变得更加简洁。
闭包
2. 为什么 useState 要使用数组而不是对象
- 简洁
- 对象需要起别名
3. React Hooks 解决了哪些问题?
- 组件之间复用状态逻辑困难的问题
- 复杂组件难以理解
- class难以理解
八、虚拟DOM
1. 对虚拟 DOM 的理解?虚拟 DOM 主要做了什么?虚拟 DOM 本身是什么?
虚拟DOM是一个React的优化手段,通过在内存中维护一份轻量的DOM副本,实现更高效的渲染和更新机制。
工作流程:
- 状态更新
- 虚拟DOM表示
- 差异计算
- 实际DOM操作
虚拟DOM本身是一个内存中的表示真实DOM结构的js对象树,用于提高渲染的性能和优化DOM操作。
2. React diff 算法的原理是什么?
diff算法是为了找出新旧虚拟DOM之间的差异,以最小化对实际DOM的操作。
算法的核心:
- 同层比较,不跨层级比较
- 节点类型比较,如果节点类型不同,就会将节点和子节点全部替换,不会再深入比较子节点
- 唯一key标识,比较完节点类型,再比较节点属性,如果节点有唯一的key标识,react会尽量复用这个节点
- 递归比较子节点,没有唯一key标识,会递归比较子节点的差异。
- 列表节点优化,处理列表时,会通过判断位移、删除和添加来最小化操作。
九、其他
5. 对 React 和 Vue 的理解,它们的异同
- 相似
- 将重心都放在核心库,将其他功能比如路由和全局状态交给其他库
- 都有自己的构建工具,得到一个最佳实践的项目模板
- 都使用虚拟DOM重绘性能
- 都有props概念,允许组件间的数据传递
- 都鼓励组件化,提高复用性
- 不同
- 数据流
- Vue默认支持数据双向绑定,React提倡单向数据流
- 虚拟DOM
- Vue会根据每个组件的依赖关系,而不会重新渲染整个组件树
- React的全部子组件都会重新渲染,当然可以通过PureComponent/ShouldComponentUpdate这个生命周期方法来控制
- 组件化
- Vue默认使用HTML模板
- React推荐使用JSX
- 数据流
12. 在React中遍历的方法有哪些?
遍历数组,map&&forEach
arr.map((item, index) => {
})
arr.forEach((item, index) => {
})
遍历对象,map for in
for(const key in obj){
if(obj.hasOwnProperty(key){
const value = obj[key]
domArr.push(...)
})
}
Object.entries(obj).map(([key, value], index)=>{
})
19. 对React SSR的理解
服务端渲染是数据与模板组成的html,即html=数据+模板。将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记混合为客户端上完全交互的应用程序。页面没使用服务渲染,当请求页面时,返回的body为空,之后执行js将html结构注入到body里,结合css显示出来
- 好处:
- SEO友好
- 资源都存储在服务器端
- 响应亏,体验好,首屏渲染快
- 局限性
- 服务端压力大
- 开发条件受限
20. 为什么 React 要用 JSX?
react并没有强制使用jsx,jsx类似一种语法糖,代码更为简洁和清晰。