本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠))
喜欢的话点个赞,谢谢!
1. 约束性组件与非约束性组件
1.1. 非约束性组件
非约束性组件其实就是不能控制状态的组件,比如:
<input type="text" defaultValue="123" onChange={handleChange}>
在这里我们可以输入value值,也可以根据输入的值在change回调里面处理逻辑,但是我们无法控制value值
1.2. 约束性组件
约束性组件就是我们可以直接控制组件的value值或者显示结果的组件,比如:
<input type="text" value={this.state.value} onChange={handleChange}>
在这里我们可以直接控制value值的显示,可以在第三方逻辑中处理value值然后修改显示结果
2. 高阶组件 HOC
高阶组件本质是一个函数。它可以接收一个组件作为参数,然后返回处理完逻辑处理结果之后返回一个新的组件。
高阶组件通常用于处理一些比如路由守卫封装或者表单组件的逻辑复用封装,比如封装一个弹出窗:
显示页面:
import ModalFn from "../../Component/index.tsx"
import { Modal } from 'antd';
const ModalExtend = ModalFn(Modal)
function Test() {
return <div>
<ModalExtend />
</div>
}
export default Test
封装逻辑:
import { Button } from 'antd';
import React from 'react';
export default function ModalFn(Modal) {
return class ModalExtend extends React.PureComponent {
state = {
isModalOpen: false
}
//显示弹出窗
handleShowModal = () => {
this.setState({
isModalOpen: true
})
}
//确定按钮
handleOk = () => {
}
//取消按钮
handleCancel = () => {
this.setState({
isModalOpen: false
})
}
render(): React.ReactNode {
const ModalProps = {
open: this.state.isModalOpen,
onOk: this.handleOk,
onCancel: this.handleCancel,
title: "Basic Modal"
}
return (
<>
<Button type="primary" onClick={this.handleShowModal}>
Open Modal
</Button>
<Modal {...ModalProps} />
</>
)
}
}
}
展示效果:
不过我们通常不会这么做,我们做antd的二次封装只要传递参数即可,只不过一般的二次封装的思路都是学习自高阶组件
3. React路由
现在前端的项目一般都是SPA单页面应用,不再是以前多个页面多套HTML代码项目了,应用内的跳转不需要刷新页面就能完成页面跳转靠的就是路由系统
React路由系统分为BrowerRouter路由和HashRouter路由,分别表现为:
- BrowerRouter路由: 就像平常网站www.baidu.com/test 这就是一个路由,在服务器渲染的时候需要后端做映射
- HashRouter 路由: 比BrowerRouter多出了一个#符号,使用URL的哈希值实现,比如www.baidu.com/#/test,不需要后端做URL映射
可能会问到路由拦截(也就是路由守卫)的问题,这里不详细介绍,可以看看我之前发的React路由文章
4. React diff算法和虚拟DOM
diff算法是React实现组件差异化更新的核心逻辑之一,它与虚拟DOM是相辅相成的存在,正因为有了虚拟DOM才有diff的可能性
虚拟DOM本质上是JS到DOM之间的一个映射缓存,它在形态上表现为一个能够描述DOM树结构和属性信息的JS对象,如下图所示:
function Test(){
return(
<div className='aaa'>
React
</div>
)
}
diff算法: diff算法的本身是VirtualDOM 在render的时候差量更新时需要进行比对需要更新哪些节点的一个产物,diff是递归更新虚拟DOM树的,一旦开始不可以暂停打断
更详细的diff算法和解释可以看看我之前发的React diff算法与虚拟DOM
5. React 性能优化
React性能优化一般有两种情况,类组件性能优化和函数组件性能优化,更多详细内容可以看看我之前写的React性能优化专题
5.1. 类组件:
PurComponent : 类组件可以继承React.PurComponent 组件,PurComponent组件在shouldComponentUpdate里面对组件state、props更新渲染做了浅比较
Immutable.js : Immutable 是Facebook推出的用来给数据做持久性优化的库,配合PurComponent使用的话可以进行组件更新渲染的深层次比较,避免组件无意义的二次渲染
5.2. 函数组件
React.memo:
React.memo 的使用方式相比immutable更加简单一些.它本质上是一个高阶组件,直接包裹住要声明的函数组件即可,内部也是浅比较,与PurComponent类似
useMemo:
React.memo的特性和用法,缺点很明显,无法感知组件内部的state,还有就是不能控制单一的某一段逻辑,所以官方建议使用useMemo加强深比较的能力,useMemo用法就是传递2个参数:第一个是需要渲染的内容,第二个参数是一个状态,根据这个状态是否变化来决定这段内容的更新与否
6. React 数据持久化
有一些面试官不会直接问状态管理容器redux、mobx之类的,会直接问你了解React数据持久化么?
一般的第三方数据持久化库都是用localStorage 或 AsyncStorage来进行存储数据的,我这里以redux-persist + @reduxjs/toolkit为例:
安装:
yarn add redux-persist
yarn add react-redux
yarn add @reduxjs/toolkit
配置src/store.ts
import { configureStore, combineReducers } from '@reduxjs/toolkit'
import HomeReducer from './models/home'
import { persistStore, persistReducer } from 'redux-persist'
// 选择持久化存储引擎,如 localStorage 或 AsyncStorage
import storage from 'redux-persist/lib/storage' // 默认使用localStorage作为存储引擎
// 组合各个模块的reducer
const reducer = combineReducers({
Home: HomeReducer
})
// 配置持久化设置
const persistConfig = {
key: 'root', // 存储的键名
storage,// 持久化存储引擎
// 可选的配置项,如白名单、黑名单等 选其一就好了
// blacklist:['Home'], // 只有 Home 不会被缓存
whitelist: ["Home"], // 只有 Home 会被缓存
}
const persistedReducer = persistReducer(persistConfig, reducer)
export const store = configureStore({
reducer: persistedReducer, // 注册子模块
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false // 关闭默认的序列化检查//关闭严格模式
})
})
export const persistor = persistStore(store)
配置src/models/HomeReducer
import { createSlice } from '@reduxjs/toolkit'
// 定义状态类型
interface Action {
payload: number,
type: string
}
interface State {
count: number
}
export const HomeStore = createSlice({
// 模块名称独一无二
name: 'home',
// 初始数据
initialState: {
count: 1
},
// 修改数据的同步方法
reducers: {
increment: (state: State, action: Action) => {
state.count += action.payload
},
decrement: (state: State, action: Action) => {
state.count -= action.payload
},
}
})
// 导出
export const { increment, decrement } = HomeStore.actions
export default HomeStore.reducer
配置好了以后:
刷新没有变化
mac不太方便做GIF,过程也很简单,大家可以create一个demo自己尝试一下
7. React Hooks
Hooks基本上也是属于热门面试问答题,主要涉及到几个方面:常用的Hooks钩子、Hooks组件与类组件的区别、自定义Hooks、Hooks的逻辑复用
内容太多这里就不详细描述了.有兴趣的同学可以直接跳转查看详细内容React Hooks专题
8. React Fiber架构
React Fiber架构是16.x之后更新的,目的是为了重写原来的React 15以及之前版本的调和过程,这里做一个简短的描述,更多详细内容可以查阅React Fiber专题
8.1. React 15及更老版本 diff算法
React 15中的diff算法采用的是分层递归方式查找更新过程中有差异的一些DOM节点,主要是基于虚拟DOM节点树来递归查询,其主逻辑有以下几个部分组成:(此处简单介绍,详细内容可查阅React diff算法专题)
- 首先判断是否存在旧的虚拟DOM树节点,没有的话直接创建新虚拟DOM树
- 判断旧的虚拟DOM是否与新的虚拟DOM类型一致,不一致重新创建
- 判断旧的虚拟DOM是否为组件,如果为组件又要在内部逻辑判断是否为同组件更新等等
- 如果旧的虚拟DOM与新的虚拟DOM类型一致(高频diff类型),如果存在key值则用key值进行操作,比如移动位置新增一个节点等等,如果不存在key值,那么只能使用diff重新递归更新。当我们没有在批量生成节点的时候标记key值,React官方会给我们抛出一个warning,提示我们必须使用key,否则将影响应用性能
React 15下的diff算法由于采用的是递归的形式,一旦开始不可以暂停/结束,只能等待任务完成,那么我们在更新一个比较庞大的任务的时候,往往会带来页面卡死/卡顿等问题,为了解决这个问题React官方在16版本推出Fiber架构
8.2. React Fiber
Fiber架构推出的原因上面我们上面已经讲过了,主要是为了解决diff算法递归到底不回头的问题,我们先来看看Fiber的特点
Fiber架构的核心: 可中断、可恢复、优先级
在 React 16 之前,React 的渲染和更新阶段依赖的是如下图所示的两层架构:
而在 React 16 中,为了实现“可中断”和“优先级”,两层架构变成了如下图所示的三层架构:
对比React15多出了一个Scheduler(优先级调度器),调度器的作用是,每次触发更新的时候调度更新的优先级。
比如有一个更新任务B抵达调度器,调度器将其塞入Reconciler层,此时又有一个更新任务A抵达,调度器发现A的优先级高于B,那么就会中断B的更新,优先更新A,等A执行完了之后,再将中断的B重新加入Reconciler层,继续它的渲染流程,这就是可恢复
Fiber结构的本质
虽然大部分人都将Fiber的结构称为Fiber树,但是实际上Fiber的数据结构已经从树变成了链表的形式,此处引用某大佬的一张图来直观展示:
(未完待更新)