React学习笔记

React 基础

setState

  • state 考虑层级
  • state 是不可变数据,不可直接修改
  • 使用setState修改state数据

可能是异步,可能会合并?
react <=17 中:

  • 组件事件中的setState(第一个参数是对象形式)是异步的(需要在第二个参数回调函数中获取),在setTimeout或自定义事件中是同步的
  • setState传入对象会合并(类似Object.assign),传入函数不会合并

react 18中:

  • 都是异步更新和state合并,自动批处理 Automatic Batching

propTypes 与 defaultProps 的应用

import PropTypes from 'prop-types'
// PropTypes.arrayOf(PropTypes.string, PropTypes.number) // 数据为数组时,数组的元素可以是什么类型
// PropTypes.oneOfType(PropTypes.string, PropTypes.number) // 数据可以是string,也可以是number
// 设置类型
TodoItem.propTypes = {
  test: PropTypes.string.isRequired, // test
  content: PropTypes.string,
  index: PropTypes.number,
  deleteItem: PropTypes.func
}

// 设置默认值
TodoItem.defaultProps = {
  test: 'test'
}

React 的生命周期函数

在这里插入图片描述
常用生命周期
在这里插入图片描述

shouldComponentUpdate:可在此生命周期函数中判断子组件是否需要更新,从而提升性能

shouldComponentUpdate(nextProps, nextState) {
console.log('child', 'shouldComponentUpdate')
  if (nextProps.content === this.props.content) {
    return false
  } else {
    return true
  }
}

componentDidMount:可在此生命周期函数中发送ajax请求数据

React 中 ref 的使用

1、使用 createRef创建 ref
2、使用函数创建

<input
  id="input"
  value={this.state.inputValue}
  onChange={this.handleInputChange}
  ref={(input) => this.input = input}
></input>

React高级内容

受控组件和非受控组件

  • 受控组件:受state影响的组件
  • 非受控组件:不收state影响的组件,可使用ref、defaultValue、defaultChecked相关属性
    场景:富文本组件、上传组件等需要操作dom的组件

无状态组件(函数组件)

  • 没有声明周期
  • 没有state
  • 没有实例

比普通组件性能更好
因为无状态组件只是一个函数,而普通组件是一个class类,需要执行生命周期和render函数
所以,如果组件中只有render函数时,可以使用无状态组件

portals(传送门)

ReactDOM.createPortal

使用场景:

  • overflow: hidden
  • 父组件 z-index 值太小
  • fixed 需要放在 body 第一层

context

将公共信息传递给每个组件

import { Component, createContext } from 'react
const ThemeContext = createContext('light')

// 父级
// ...
return <ThemeContext.Provider value={this.state.theme}>
	// ...
</ThemeContext.Provider>
// ...

// 子级(class 组件)
class ThemeButton extends Component {
	// static contextType = ThemeContext
	render() {
		const theme = this.context
		return <div>{theme}</div>
	}
}
ThemeButton.contextType = ThemeContext

// 函数组件
const ThemeLink = () => {
	return <ThemeContext.consumer>
		{ value => <p>{value}</p> }
	</ThemeContext.consumer>
}

异步组件

  • import()
  • lazy
  • Suspense

souldComponentUpdate

  • SCU 默认返回 true
  • 必须配合“不可变值”
  • 可先不用SCU,有性能问题时再考虑使用
  • PureComponent 让class组件默认实现shouldComponentUpdate浅层比较
  • memo 函数组件中的 PureComponent
  • 浅比较已使用大部分情况,尽量不要使用深度比较

高阶组件 HOC

高阶组件不是一种功能,而是一种模式,实现组件之间的公共逻辑抽离

const HOCFactory = (Component) => {
	class HOC extends React.Component {
		// 公共逻辑...
		render () {
			return <Component {...this.props} />
		}
	}
}

export default HOCFactory(WrapperComponent)

与 render props 相比,模式简单,但会增加组件层级

render 和 props

实现之间的逻辑抽离

class Mouse extends Component {
	// 公共逻辑...
	render () {
		return {this.props.render(this.state)}
	}
}

class App extends Component {
	render () {
		return <Mouse render={(props) => <p>{props.x}{props.y}</p>}>
	}
}

与 HOC 相比,代码简洁,学习成本较高

React Hooks

class 组件的问题

  • 大型组件很难拆分和重构,很难测试(即 class 不易拆分)
  • 相同业务逻辑,分散到各个方法中,逻辑混乱
  • 复用逻辑变的复杂,如 Mixins、HOC、 render props

React 组件更易用函数表达

  • React 提倡函数式编程,view = fn(props)
  • 函数更灵活,更易拆分,更易测试
  • 但函数组件太简单,需要增强能力 —— Hooks

Hooks 命名规范

  • 规定所有的 Hooks 都用 use 开头,如 useXxx
  • 自定义 Hook 也要以 use 开头
  • 非 Hooks 的地方,尽量不要使用 useXxx 写法

state Hook 让函数组件实现 state 和 setState

  • useState(0) 传入初始值,返回数组 [state, setState]
  • 通过 state 获取值
  • 通过 setState(1) 修改值

Effect Hook 让函数组件模拟生命周期

  • 模拟 componentDidMount、componentDidUpdate - useEffect 无依赖
// 语法:
useEffect(() => {})
  • 模拟 componentDidMount - useEffect 依赖 []
// 语法:
useEffect(() => {}, [])
  • 模拟 componentDidUpdate - useEffect 无依赖,或者依赖 [a, b]
// 语法1:
useEffect(() => {})
// 语法2:
useEffect(() => {}, [a, b])
  • 模拟 componentWillUnMount - useEffect 中返回一个函数
// 语法:
// 模拟 DidMount
useEffect(() => {
	// 模拟 WillUnMount
	return () => {
		// ...组件销毁时的一些逻辑
	}
}, [])

模拟 componentWillUnMount 但不完全相等

  • useEffect 依赖 [],组件销毁是执行fn,等于 WillUnMount
  • useEffect 无依赖,或者依赖 [a, b],组件更新时也会执行 fn
  • 即,下一次执行 useEffect 之前,就会执行 fn,无论更新或卸载

useEffect 让纯函数有了副作用

  • 默认情况下,执行纯函数,输入参数,返回结果,无副作用
  • 所谓副作用,就是对函数之外造成影响,如设置全局定时任务
  • 而组件需要副作用(技术对业务妥协),所以需要 useEffect “钩”入纯函数中

其他 Hook

ref Hook

const btnRef = useRef(null) // 初始值
useEffect(() => {
	console.log(btnRef.current) // dom节点
})

return <button ref={btnRef}>按钮</button>

context Hook

import { Component, createContext, useContext } from 'react
const ThemeContext = createContext('light')

// 父级
// ...
return <ThemeContext.Provider value={this.state.theme}>
	// ...
</ThemeContext.Provider>
// ...

// 函数组件
const ThemeLink = () => {
	const theme = useContext(ThemeContext)
	return <p>{theme}</p>
}

reducer Hook

const [state, dispatch] = useReducer(reducer, initialState)

useReducer 和 redux 的区别

  • useReducer 是 useState的代替方案,用于 state 复杂变化
  • useReducer 是单个组件状态管理,组件通讯还需要props
  • redux 是全局的状态管理,多组件共享数据

memo Hook

  • useMemo 缓存数据
// 子组件用 memo 包裹
const Child = memo(() => {})

// 父组件
const App = () => {
	const [name, setName] = useState('zhangsan')
	// useMemo 缓存数据
	const userInfo = useMemo(
		() => ({name, age: 20}),
		[name]
	)
	return <Child userInfo={userInfo} />
}

callback Hook

  • useCallback 缓存函数
// 子组件用 memo 包裹
const Child = memo(() => {})

// 父组件
const App = () => {
	const [name, setName] = useState('zhangsan')
	// useMemo 缓存数据
	const userInfo = useMemo(
		() => ({name, age: 20}),
		[name]
	)
	// useCallback 缓存函数
	const handleChange = useCallback(() => {
		// ...
	}, [])
	return <Child userInfo={userInfo} handleChange={handleChange} />
}

自定义 Hook

  • 本质是一个函数,以 use 开头(重要)
  • 内部正常使用 useState useEffect 或其他 Hooks
  • 自定义返回结果,格式不限
  • 使用第三方 hooks
    在这里插入图片描述

组件逻辑复用

class组件逻辑复用有哪些问题

Mixins 的问题

  • 变量作用域来源不清
  • 属性重名
  • Mixins 引入过多会导致顺序冲突

高阶组件 HOC

  • 组件层级嵌套过多,不易渲染,不易调试
  • HOC 会劫持 props,必须严格规范,容易出现疏漏

Render props

  • 学习成本高,不易理解
  • 只能传递纯函数,而默认情况下纯函数功能有限

Hooks 做组件逻辑复用的好处

  • 完全符合 Hooks 原有规则,没有其他要求,易理解记忆
  • 变量作用域明确
  • 不会产生组件嵌套

规范和注意事项

Hooks 使用规范

  • 命名规范 useXxx !!!
  • Hooks 使用规范!!!
    只能用于 React 函数组件和自定义 Hook 中,其他地方不可以
    只能用于顶层代码,不能在循环、判断中使用 Hooks
    eslint 插件 eslint-plugin-react-hooks 可以帮到我们
  • Hooks 的调用顺序
    无论是 render 还是 re-render ,Hooks 调用顺序必须一致
    如果 Hooks 出现循环、判断里,则无法保证顺序一致
    Hooks严重依赖于调用顺序!重要!

Hooks 注意事项

  • useState 初始化值,只有第一次有效
    如果 useState 传入的是动态值,第二次赋值时无效
  • useEffect 内部不能修改state
    解决办法:
    1、第二参数不传入依赖或者传入修改的那个state;
    2、可以借助 useRef
    3、可以借助临时变量,在useEffect 外面定义一个变量做转换
  • useEffect可能出现死循环
    第二个值传入的是引用类型的依赖时,会出现死循环,因为 useEffect 内部使用 Object.is 来判断的,引用类型相等时也为false;
    解决办法:将引用类型的数据打散后作为依赖

React 原理

函数式编程

  • 一种编程范式,概念比较多
  • 纯函数(返回一个新值)
  • 不可变值(setState和redux中都要使用不可变值)

vdom 和 diff

  • h函数
  • vnode 数据结构
  • patch函数
  • 只比较同一层级,不跨级比较
  • tag不相同,则直接删掉重建,不做深度比较
  • tag和key都相同,则认为相同节点,不做深度比较

JSX的本质

React.createElement(tag/component, props/null, [child, child]/child, child),类似vue的h函数,执行后返回vnode,执行patch函数生成dom

React的合成事件机制

  • 所有事件挂在到 root上
  • event 原生的,是 SyntheticEvent 合成事件对象(event.nativeEvent 是原生事件对象)
  • 和 vue 事件不同,和 DOM 事件也不同

为何要合成事件机制?

  • 更好的兼容性和跨平台
  • 载到 root ,减少内存消耗,避免频繁解绑
  • 方便事件的统一管理(如事务机制)

更新:

  • React 16 事件绑定到 document 上
  • React 17 事件绑定到root组件
  • 有利于多个 React 版本并存,列如微前端

setState 和 batchUpdate

  • setState 主流程
  • batchUpdate 机制
  • transaction 事务机制

setState 是异步还是同步?

  • setState 无所谓异步还是同步
  • 看是否能命中 batchUpdate 机制
  • 判断 isBatchingUpdates

transaction 事务机制

  • 定义开始逻辑和结束逻辑
  • 执行开始逻辑 -> 执行函数 -> 执行结束逻辑

组件渲染和更新过程

组件渲染过程

  • props state
  • render() 生成 vnode
  • patch(elm, vnode)(不一定叫patch这个名字)

组件更新过程

  • setState(newState) --> 生成 dirtyComponents(可能有子组件)
  • 遍历所有的 dirtyComponents ,根据新的props和state,执行 render 生成 newVnode
  • patch(vnode, newVnode)

React fiber 如何性能优化

path 更新的两个阶段

  • reconciliation 阶段-执行 diff 算法,纯 js 计算
  • commit 阶段-将 diff 结果渲染 DOM

可能会有性能问题

  • JS是单线程,切和DOM渲染共用一个线程
  • 当组件足够复杂,组件更新时计算和渲染都压力大
  • 同时再有DOM操作需求(动画,鼠标拖拽等),将卡顿

解决方案 fiber

  • 将 reconciliation阶段进行任务拆分
  • DOM 需要渲染时暂停,空闲时恢复
  • window.requestIdleCallback 可以知道浏览器需要做DOM渲染,该Api不是所有浏览器都支持,不支持执行就是会慢一点

redux 入门

核心API

createStore:创建store
store.dispatch:派发action
store.getState:获取store所有的数据
store.subscribe:订阅store的改变,store发生改变,subscribe中接受的回调函数就会被触发

注:
reducer 可以接受state,但是绝不能修改state
reducer 必须是纯函数,给定固定的输入,就一定会有固定的输出,而且不会有任何副作用

单向数据流

在这里插入图片描述

redux 进阶

redux-thunk

中小型项目中使用,异步请求或者非常复杂的逻辑,可以放到redux-thunk中统一管理
可以在action中写函数

redux-saga

大型项目中可使用该库进行action管理

react-redux

在react组件中方便操作store

React 周边

immutable、redux-immutable

管理store中的数据,防止不小心直接在reducer中改变state的值(彻底拥抱“不可变值”)
如果使用pureComponent需要使用immutable管理数据
Api:

  • Map
  • set
  • get
  • getIn

styled-components

在react中编写样式,区分在组件中生效还是全局生效

react-router、react-router-dom

路由

react-transition-group

动画库

React 性能优化

  • 渲染列表时加 key
  • 自定义事件、DOM事件及时销毁
  • 合理使用异步组件
  • 减少函数 bind this 的次数
  • 合理使用 SCU PureComponent 和 memo
  • 合理使用 immutable.js
  • webpack 层面的优化
  • 前端通用的性能优化,如图片懒加载
  • 使用 SSR

React 和 Vue 的区别

相同点

  • 都支持组件化
  • 都是数据驱动视图
  • 都使用 vdom 操作 DOM

不同点

  • React 使用 JSX 拥抱JS,Vue 使用模版拥抱 html
  • React 是函数式编程,Vue 是声明式编程
  • React 更多需要自力更生,Vue 把想要的都给你
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值