React 的历史与应用
React 是一个 JavaScript 库
React : A JavaScript library for building user interfaces. ——Jordan Walke
应用场景:
- 前端应用开发,如 Facebook、Instagram、Netflex 网页版
- 移动原生应用开发,如 Instagram、 Discord、Oculus
- 结合 Electron,进行桌面应用开发
- 3D开发:react-three-fiber
React 的历史
- 2010年:Facebook 在其 php 生态中,引入了 xhp 框架,首次引入了组合式组件的思想,启发了后来的 React 的设计
- 2011年:Jordan Walke 创造了 FaxJS ,也就是后来的 React 原型
- 2012年:在 Facebook 收购 Instagram 后,该 FaxJS 项目在内部得到使用,Jordan Walke 基于 FaxJS 的经验创造了 React
- 2013年:React 正式开源,在2013 JSConf 上 Jordan Walke 介绍了这款全新的框架
- 2014年:生态大爆发,各种围绕 React 的新工具/新框架开始涌现
React 的设计思路
原生 JavaScript UI 编程痛点
- 状态更新,UI 不会自动更新,需要手动调用 DOM 进行更新
- 欠缺基本的代码层面的封装和隔离,代码层面没有组件化
- UI 之间的数据依赖关系需要手动维护,如果依赖链路过长会引起回调地狱 Callback Hell
响应式与转换式
- 转换式系统:给定输入求解输出,例如编译器、数值计算
- 响应式系统:监听事件,消息驱动,例如监控系统、UI 界面
响应式系统
事件 -> 执行既定回调 -> 状态变更
响应式的前端 UI 编程:
事件 -> 执行既定回调 -> 状态变更-> UI 更新
React 响应式编程特点
- 状态更新,UI 自动更新
- 前端代码组件化,可复用,可封装
- 状态之间的互相依赖关系,只需声明即可
组件化
- 组件是组件的组合/原子组件
- 组件内部拥有状态,外部不可见
- 父组件可将状态传入组件内部
组件状态归属问题
- 共享状态归属于两个组件节点的最近祖先节点,即状态提升
- React 是单向数据流,永远是父组件给子组件传递状态,子组件只能调用函数更改状态
- 使用状态管理库如 redux 解决状态不合理提升的问题
- 如何修改 DOM : 将 JSX 文件转义为 JS 文件、利用虚拟 DOM 树和 diff 算法来更新 DOM
组件设计
- 组件声明了状态和 UI 的映射
- 组件有 props 和 state 两种状态
- 组件可由其他组件拼装而成
组件特点:
- 组件内部拥有私有状态 state
- 组件接受外部的 props 状态提供复用性
- 根据当前的 state 和 props 返回一个 UI
React Hooks 的写法
- 最常用的两个 Hooks 是 useState 和 useEffect
useState:传入一个初始值,返回一个状态,和set该状态的函数,用户可以通过调用该函数,来实现状态的修改
import React, { useState } from 'react'; const Example = () => { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
useEffect:两个参数,一个是要执行的函数,一个是依赖项数组(可以不传依赖性)。有副作用的函数要传入到 useEffect 来执行。副作用表示除了单纯的计算之外还要做其他的事情,比如网络请求、更新 DOM 、localStorage 数据等。
import React, { useState, useEffect } from 'react'; const Example = () => { const [count, setCount] = useState(0); // 使用一个副作用,传入的[count]数组使得此副作用只有当count变量改变时才会被调用 useEffect(() => { // 副作用:Update the document title using the browser API document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Hooks 的使用规则:
- 只在最顶层使用 Hooks,不要在循环,条件或嵌套函数中调用 Hooks,确保总是在 React 函数的最顶层以及任何 return 之前调用他们。
- 只在 React 函数中调用 Hooks,不要在普通的 JavaScript 函数中调用 Hooks,可以在 React 的函数组件中调用 Hooks,可以在自定义 Hooks 中调用其他 Hooks
React 的实现
React 是不能直接在浏览器中运行的,有这些问题
- React 的 JSX 语法不符合 JavaScript 标准语法
- 返回的 JSX 发生改变时,如何更新 DOM
- state/props 更新时要重新触发 render 函数
如何实现
- 将 JSX 文件转译(transpile)成 JS 文件
利用虚拟 DOM 树
- 真实 DOM (Document Object Model)是浏览器创建页面的文档对象模型
- 虚拟 DOM 树是在 JS 内存中维护的一个对象,具有和 DOM 类似的树状结构,并和 DOM 可以建立一一对应的关系
- 利用虚拟 DOM 就不用操作真实 DOM 提供的 API 来修改 DOM 了:它赋予了 React 声明式的 API,告诉 React 希望让 UI 是什么状态,React 就确保 DOM 匹配该状态,前端工程师便可以从属性操作、事件处理和手动 DOM 更新这些操作中解放出来
利用 Diff 算法
- 状态改变时,先更新虚拟 DOM 树,而不是直接更新 DOM 树,通过 Diff 算法,求出最少要更新的节点,然后再去更新真正的 DOM 树
完美的最小Diff 算法,需要O(n^3)的复杂度;牺牲理论最小 Diff,换取时间,得到了O(n)复杂度的算法
不同类型元素 同类型元素 同类型的组件元素 替换 更新 递归
React 状态管理库
- 核心思想:将状态抽离到 UI 外部进行统一管理
- 状态管理库的弊端:UI 依赖于外部的一个状态管理库会降低代码的复用性,因此状态管理库总是出现在业务代码中
- 常见状态管理库:redux、xstate、mobx、recoil
- 状态机:当前状态收到外部事件,迁移到下一个状态
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。