React 原理

函数式编程

  • 纯函数
    reducer 必须是一个纯函数,即没有副作用的函数,不修改输入值,相同的输入一定会有相同的输出
  • 不可变值
    state 必须是不可变值,否则在 shouldComponentUpdate 中无法拿到更新前的值,无法做性能优化操作。

vdom 和 diff 算法

JSX 本质

  • React.createElement 函数
    • React.createElement(tag, props, child1, child2, child3)
    • React.createElement(tag, props, [child1, child2, child3])
  • 执行生成 vnode
const elem = <div>
		<p>aaa</p>
		<p style={
   {
    color: 'red' }}>bbb</p>
	</div>;
const elem = React.createElement(
	"div", 
	null, 
	React.createElement("p", null, "aaa"), 
	React.createElement("p", {
    style: {
    color: "red" } }, "bbb")
);
const lisElem = <div>
  {
   
    this.state.list.map((item, index) => {
   
	    return (<span key={
   item.id}>
	        {
   item.name}
	    </span>);
	 })
  }
</div>;
const listElem = React.createElement(
	"div", 
	null, 
	(void 0).state.list.map((item, index) => {
   
		return React.createElement("span", {
    key: item.id }, item.name);
	})
);

合成事件

  • react 的事件不是原生事件 MouseEvent,而是合成事件 SyntheticEvent
  • react16 是挂载到 document 上的;react17 开始是挂载到 root 上的
  • 事件处理函数交给合成事件,事件冒泡到 document / root 上进行处理

出处:https://coding.imooc.com/lesson/419.html#mid=41943
在这里插入图片描述

合成事件的好处:

  • 更好的兼容性和跨平台:比如 react-native
  • 全部挂载到 document / root上,减少内存消耗,避免频繁解绑
  • 方便事件的统一管理(事务机制)

出处:https://coding.imooc.com/lesson/419.html#mid=41943
在这里插入图片描述

React17 开始挂载到 root 组件上:
- document 只有一个,root 有多个,有利于多个 react 版本共存,例如:微前端

setState 和 batchUpdate

setState 主流程

出处:https://coding.imooc.com/lesson/419.html#mid=41943
在这里插入图片描述

  • 异步:左边分支
  • 非异步:右边分支

isBatchingUpdates

class ListDemo extends React.Component
	constructor(props) {
   
		// ...
	}
	render() {
   
		// ...
	}
	
	increase = () => {
   
		// 开始: 处于 batchUpdate
		// isBatchingiUpdates = true 
		this.setState({
   
			count: this.state.count + 1
		});
		// 结束
		// isBatchingUpdates = false
	}
}
class ListDemo extends React.Component
	constructor(props) {
   
		// ...
	}
	render() {
   
		// ...
	}
	
	increase = () => {
   
		// 开始: 处于 batchUpdate
		// isBatchingUpdates = true 
		setTimeout(() => {
   
			// 此时 isBatchingUpdates 是 false
			this.setState({
   
				count: this.state.count + 1
			});
		});
		// 结束
		// isBatchingUpdates = false
	}
}
componentDidMount() {
   
	// 开始: 处于 batchUpdate
	// isBatchingUpdates = true 
	document.body.addEventListener('click', () => {
   
		// 此时 isBatchingUpdates 是 false
		this.setState({
   
			count: this.state.count + 1
		});
		console.log('count in body event', this.stae.count);
	});
	// 结束
	// isBatchingUpdates = false
}

哪些能命中 batchUpdate 机制:

  • 生命周期(和它调用的函数)
  • React 中注册的事件(和它调用的函数)
  • React 可以“管理”的入口

transaction 事务机制

class ListDemo extends React.Component
	constructor(props) {
   
		// ...
	}
	render() {
   
		// ...
	}

	increase = () => {
   
		// 	开始:处于 batchUpdate
		// isBatchingUpdates = true

		// 其他任何操作

		// 结束
		// isBatchingUpdates = false
	}
}

出处:https://coding.imooc.com/lesson/419.html#mid=41943
在这里插入图片描述

transaction.initialize = function() {
   
	console.log('initialize');
};

transaction.close = function() {
   
	console.log('close');
};

function method() {
   
	console.log('abc');
}

transaction.perform(method);

// 输出 'initialize'
// 输出 'abc'
// 输出 'close'

react 组件渲染过程

  1. JSX 如何渲染为页面:
    • 初始化时候继承 props 和 生成 state
    • 通过 render() 函数 生成 vnode
    • patch(elem, vnode):通过 patch 函数将 vonde 更新到 dom
  2. setState 之后如何更新页面:
    • setSate(newState) -> dirtyComponents(可能有子组件):通过 setState 产生新的 state,存到 dirtyComponent 进行异步更新
    • 通过 render() 函数生成新的 vnode
    • patch(elem, vnode):再通过 patch 函数用 newVnode 去更新旧的 vnode

react-fiber

react 的 patch 被拆分为两个阶段:

  • reconciliation阶段:执行 diff 算法,纯 js 计算
  • commit 阶段:将 diff 结果渲染成 dom

背景

  • js 是单线程的,且和 dom 渲染共用一个线程
  • 当组件足够复杂,组件更新时计算和渲染压力都很大
  • 同时再有 dom 操作需求,比如动画、鼠标拖拽等,那么将会卡顿

解决方案:fiber

fiber

  • react 内部的运行机制,开发者体会不到
  • reconciliation 阶段进行任务拆分(commit 无法拆分)
  • dom 需要渲染时暂停,空闲时恢复
  • 通过 window.requestidleCallback 进行控制(并非所有浏览器支持)

FQA

  1. JSX 的本质是什么?
    • jsx 的本质是 React.createElement 函数,执行生返回 vnode
  2. react 组件更新渲染的过程。
    • 初始化时候继承 props 和 生成 state
    • 通过 render() 函数生成 vnode
    • 再通过 patch 函数将 vonde 渲染成真实 dom
    • 通过 setState 修改产生新的 state
    • 触发 re-render 生成新的 vnode
    • 再通过 patch 函数用 newVnode 去更新旧的 vnode
  3. react 为什么要将 patch 过程拆分成 reconciliationcommit 两个阶段?
    • 因为js 是单线程的,且和 dom 渲染共用一个线程
    • 当组件很复杂的时候,组件更新时计算和渲染压力都很大
    • 同时再有 dom 操作需求,比如动画、鼠标拖拽等,那么将会卡顿

相关推荐

  1. React原理

    2024-01-18 02:36:01       31 阅读
  2. react diff 原理

    2024-01-18 02:36:01       21 阅读
  3. React Fiber的原理

    2024-01-18 02:36:01       21 阅读
  4. React】useState 的原理

    2024-01-18 02:36:01       7 阅读
  5. ReactNative进阶(五):React Native与原生通信

    2024-01-18 02:36:01       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-18 02:36:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-18 02:36:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-18 02:36:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-18 02:36:01       18 阅读

热门阅读

  1. WebFlux中使用WebSocket的拓展功能分析

    2024-01-18 02:36:01       36 阅读
  2. 使用Spring Boot和JPA实现多数据源的方法

    2024-01-18 02:36:01       33 阅读
  3. 互联网程序员面试方向有哪些

    2024-01-18 02:36:01       37 阅读
  4. CSAPP - bomblab phase_2 分析

    2024-01-18 02:36:01       20 阅读
  5. vue脚手架,路由,过滤器,自定义指令

    2024-01-18 02:36:01       31 阅读
  6. DevOps与测试、左移的方法

    2024-01-18 02:36:01       25 阅读
  7. Pandas实战100例 | 案例 46: 列重新排序

    2024-01-18 02:36:01       27 阅读
  8. 学习记录1.13

    2024-01-18 02:36:01       33 阅读
  9. jackson null值 序列化

    2024-01-18 02:36:01       36 阅读
  10. 组件v-model

    2024-01-18 02:36:01       29 阅读
  11. logback日志记录器

    2024-01-18 02:36:01       35 阅读