React复习全攻略:重温旧知,收获新知

未命名图片 (1).jpeg

简介

大背景

  • 起源于 Facebook 的内部项目,因为对市面上所有JS MVC框架不满意,就自己开发了一套,用来开发Instagram项目。(开源时间:2013年5月)

三句话解释

  • 是用于构建 Web 和原生交互界面的库。
  • 是由各种组件,组成页面。
  • 是用于构建UI,专注于视图层的JS框架

主要特点

  • 组件化编码
    • 具有更强的复用性,维护性、扩展性
  • 高效(Diff算法):
    • 使用虚拟(Virtual)DOM,提升渲染性能
    • 使用Diff算法,提升更新效率
  • 单向数据流
    • 助于简化数据的管理和维护, 提高代码的可维护性和可预测性
  • JSX
    • JavaScript 的语法扩展(JavaScript XML)
    • 逻辑与UI耦合,采取组件单元,实现关注点分离

下面,我们再来与Vue进行对比,更为深入地了解React的特点

React Vue 异同处

相同点

  • 都有 虚拟Dom + Diff算法 的渲染机制,用于提升渲染速度
  • 都使用 组件化开发,通过props传参等方式进行父子数据通信
  • 都具备 状态管理(Vue的Vuex/Pinia,React的Redux/Mobx)
  • 都支持 跨平台开发(Vue的Uniapp,React的React Native)

不同点

框架层面

  • Vue
    • 本质属于MVVM模式(由MVC发展出来的)
    • 由于MVVM模式,双向数据流
  • React
    • 严格意义上,只能算是MVC中的View层
    • 由于MVC模式,单向数据流

数据层面

  • Vue
    • vue推崇响应式,实现了数据的 双向绑定
    • 由于数据可变,当数据发生变化,可通过 getter/setter 以及一些 函数的劫持监听数据的变化
    • 并且当数据变化时,可直接更新到对应的虚拟Dom(VM的理念)
  • React
    • React推崇 不可变(Immutable),为 单向数据流
    • 由于数据不可变,所以React无需监听数据的变化,React只对setState之后会有重新渲染的流程
    • 当数据发生变化(setState之后),React默认是通过比较 引用的方式(Diff) 进行的,所以若是不优化,则会导致大量非必要渲染,从而影响性能(代码要求更高)

渲染层面

  • Vue
    • Vue可以很快的计算出虚拟DOM的差异,这由于它监听了每一个组件的依赖关系,不需要重新渲染整个组件树
  • React
    • React数据变化时,会将全部子组件重新渲染。但可 shouldComponentUpdate 这个生命周期函数进行控制。

Diff 算法

  • Vue
    • 同层比较新老:新的不存在旧的存在就删除,新的存在旧的不存在就创建
    • 基于Snabbdom库,使用双向链表,边对比,边更新DOM
  • React
    • 递归同层比较,标识差异点保存到Diff队列,从而得到patch树,再统一操作批量更新DOM

其他层面

  • 模板语法不同:Vue是指令+模板语法,react是函数式编程
  • 性能优化可控:Vue性能优化(自动的)相对React可控性低,因此大型应用,数据量庞大,无需过量非必要的Watcher,出于性能方面,推荐使用可控的React。
  • 社区生态差异:Vue国内受众人群多,React国际受众人群多(更为成熟↑)

核心

接下来,我们继续来了解React的核心内容:

  • JSX语法:嵌入绑定、条件渲染、列表渲染
  • 组件化:组件分类、组件生命周期、组件通信
  • Hooks:useState、useEffect、useContext、useReducer、useRef、useMemo、useCallBack

JSX语法

我们在简介里,已经了解到,JSX全称为JavaScript XML,是JavaScript的一种语法扩展。本质是 React.createElement 的语法糖,即创建虚拟DOM的方法

解决痛点为了简化创建虚拟DOM,无需每次嵌套使用 React.createElement。

简易写法:

const element = <h1>Hellow World!!!<h1/>

语法规范

  • JSX只能有一个根标签
  • 标签使用变量(JS表达式),用大括号{}包起来
  • 类名class,变className使用
  • 内联样式,使用键值对写法,例如style={{color:‘#fff’}}
  • 标签首字母
    • 小写字母开头:html标签
    • 大写字母开头:JSX组件

简易例子

const MyComponent = () => {const divStyle = {color: 'blue',backgroundColor: 'yellow',padding: '10px',border: '1px solid black'};return (<><div className="divClass">外链样式</div><div style={divStyle}>内嵌样式</div></>);
};export default MyComponent;

嵌入绑定

JSX可嵌入变量和表达式(运算符、函数调用),绑定属性和方法

import React, { useState } from 'react';  const MyComponent = () => {// 嵌入变量  const name = 'React Developer';  const age = 25;  // 使用useState来管理一个状态变量  const [visible, setVisible] = useState(true);  // 嵌入表达式 - 函数调用(例如,计算年龄的平方)  const ageSquared = () => age * age; const handleClick = () => {setVisible(!visible);  }return (  <>  {/* 嵌入变量 */}  <p>Name: {name}</p>  <p>Age: {age}</p>  {/* 嵌入表达式 - 运算符 */}  <p>Age Squared: {age * age}</p>{/* 嵌入表达式 - 三元 */}<p>Is Age Blow 20: {age < 20 ? 'Yes' : 'No'}</p>{/* 嵌入表达式 - 函数调用 */}<p>Age Squared: {ageSquared()}</p>{/* 绑定属性(事件处理器) */}  <button onClick={handleClick}>  {visible ? 'Hide' : 'Show'}  </button></>  );  
}  export default MyComponent;

条件渲染

常见的条件渲染,有以下三种方式:

  • 方法1:条件函数判断:适合逻辑多,拆解成函数,进行if,switch 或策略模式
  • 方法2:三元运算符:适合逻辑简单的,最好就单层。
  • 方法3:与运算符&&:条件成立渲染后面的标签或组件。
import React, { useState } from 'react'; 
import Admin from './Admin'; // 假设Admin组件在'./Admin'文件中const MyComponent = () => {const [userRole, setUserRole] = useState(1);const changeUserRole = (role) => {setUserRole(role === 1 ? 2 : 1)}// 函数条件判断const roleComponents = () => {if(userRole === 1) return <Admin />else return null;}return (<><button onClick={changeUserRole}>切换身份</button>  {/* 条件函数 */} {roleComponents()}{/* 三元运算 */} {role === 1 ? <Admin /> : null}{/* 与运算 */} {role === 1 && <Admin />}</>)
}export default MyComponent;

列表渲染

我们一般使用 map函数 来遍历数组,如下面的代码所示:

function MyComponent() {  const lists = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];  return (  <>  <h1>Fruit List</h1>  <ul>  {lists.map((item, index) => (  <li key={index}>{item}</li>  ))}  </ul>  </>  );  
}  export default MyComponent;

注:key 和 React 中的 Diff 算法密切相关。所以我们需要给每个map列表循环加上key值。
不然会报错:warning: Each child in a list should have a unique “key” prop.


组件化

组件化是一种高效的处理复杂应用系统,更好的明确功能模块作用的方式。即为一种分而治之的思想。

而React整个框架都围绕着组件化这一核心概念展开,可以说组件化是React的核心思想

  • 组件是构建用户界面的基本单元
  • React组件可以分为两类:类组件函数组件
  • 组件化的好处:提高代码的可维护性、以及可复用性(提效)

类组件

React一开始,以类组件作为主体,在React16.8版本(引入React Hooks)后,解决了类组件的部分痛点(Hooks部分再说明),函数组件后来者居上。

定义特点

  • 组件首字母大写
  • 继承自React.Component
  • 必须实现render函数

实现方式

  • 使用ES6 Class语法
  • 构造器是可选的,用于初始化数据
import React, { Component } from 'react';export default class App extends Component {constructor() {super();this.state = {};}render() {return (<div><h1>我是类组件</h1></div>);}
}

函数组件

函数组件,字面意思,就是JavaSript函数作为React组件。

定义特点

  • 组件首字母大写
  • 无生命周期函数,可用useEffect替代
  • 无状态管理,可用useState替代
// 定义一个函数组件  
function Greeting({ name }) {  // 使用JSX来渲染组件的UI  return (  <div>  Hello, {name}!  </div>  );  
}  // 在另一个组件或应用中使用Greeting组件  
function App() {  return (  <div className="App">  <Greeting name="Alice" />  <Greeting name="Bob" />  </div>  );  
}  export default App;

生命周期

生命周期,指的是一个组件从其创建到销毁的整个过程。(React里面,只有类组件有,函数组件没有生命周期)

在这个过程中,React为组件划分了多个不同的阶段,每个阶段都配备了专门的方法和用途。正确理解和运用React的生命周期,对于构建既稳定又易于维护的React应用来说,显得至关重要。

掌握并恰当应用这些生命周期方法,将使得我们的React应用更加健壮和高效。

下面是最新16.3版本后的生命周期函数:

e4fe31797a6b417baaf7ff25044ea570.webp


挂载时

挂载时,是指组件实例被创建,首次插入到页面的过程。主要涉及到四个方法:constructorrendercomponentDidMount,还有16.3新增的 getDerivedStateFromProps

以下是使用这些方法的示例:

class MyComponent extends React.Component {  constructor(props) {  super(props);  // 1、初始化state  this.state = {  data: props.initialData, // 假设从props中接收initialData  };  }  // 2、静态方法static getDerivedStateFromProps(props, state) {  // 当props变化时,根据新的props和当前的state更新state  if (props.initialData !== state.data) {  return {  data: props.initialData,  };  }  // 如果没有变化,返回null表示不更新state  return null;  }  componentDidMount() {  // 3、组件挂载到DOM后执行,适合发起网络请求、订阅事件等操作  console.log('Component has been mounted!');  }  render() {  // 4、渲染组件  return (  <div>  <h1>My Component</h1>  <p>Data: {this.state.data}</p>  </div>  );  }  
}  export default MyComponent;

以上例子,总结:

  • constructor(props):方法用于初始化组件的state,并绑定事件处理函数到组件实例上。
  • getDerivedStateFromProps:是一个静态方法,它在每次组件的props更新并且即将重新渲染前被调用。
  • render:用于渲染组件的UI。
  • componentDidMount:在组件挂载到DOM后立即执行。

更新时

当React组件的props或state发生变化时,组件会经历更新阶段。在更新阶段,React会根据这些变化决定是否重新渲染组件,并调用相应的生命周期方法。

主要涉及这几个方法:getDerivedStateFromPropsshouldComponentUpdategetSnapshotBeforeUpdatecomponentDidUpdate

以下是一个例子,说明了React组件在更新时涉及的主要生命周期方法:

class MyComponent extends Component {  constructor(props) {  super(props);  this.state = {  count: 0,  prevCount: null,  };  }  static getDerivedStateFromProps(props, state) {  // 根据props更新state,比如初始化state或者响应props的变化  if (props.initialCount !== state.count) {  return {  count: props.initialCount,  prevCount: state.count,  };  }  return null;  }  shouldComponentUpdate(nextProps, nextState) {  // 根据props或state的变化来决定是否重新渲染组件  // 这里我们简单地比较count是否变化  return nextState.count !== this.state.count;  }  getSnapshotBeforeUpdate(prevProps, prevState) {  // 在DOM更新之前获取一些信息  // 假设我们要获取滚动位置  const scrollPosition = this.listRef.scrollTop;  return {  scrollPosition,  };  }  componentDidUpdate(prevProps, prevState, snapshot) {  // DOM更新后,使用snapshot中的信息  if (snapshot && snapshot.scrollPosition !== undefined) {  this.listRef.scrollTop = snapshot.scrollPosition;  }  // 假设我们还需要比较prevCount和currentCount  if (prevState.prevCount !== null && prevState.prevCount !== this.state.count) {  console.log(`Count changed from ${prevState.prevCount} to ${this.state.count}`);  }  }  handleIncrement = () => {  this.setState((prevState) => ({  count: prevState.count + 1,  prevCount: prevState.count,  }));  };  render() {  return (  <div>  <p>Count: {this.state.count}</p>  <button onClick={this.handleIncrement}>Increment</button>  <div  ref={(element) => (this.listRef = element)}  style={{ height: '200px', overflowY: 'scroll' }}  >  {/* Some long content here that causes scrolling */}  </div>  </div>  );  }  
}  export default MyComponent;

以上例子,总结:

  • getDerivedStateFromProps 用于根据传入的props(initialCount)来初始化或更新组件的state。如果initialCount与当前count不同,它会返回一个新的state对象,其中包含更新后的 count 和旧的 count 值(prevCount)。
  • shouldComponentUpdate 是一个方法,用于根据props或state的变化来决定是否重新渲染组件。这里我们简单地比较了 nextState.countthis.state.count,如果它们不同,则组件会重新渲染。
  • getSnapshotBeforeUpdate 在DOM更新之前被调用,它返回一个对象,该对象在 componentDidUpdate 的第三个参数中可用。这里我们获取了滚动列表的滚动位置。
  • componentDidUpdate 在组件更新后被调用,并且接收上一个props、上一个state和getSnapshotBeforeUpdate 返回的snapshot作为参数。我们使用snapshot中的滚动位置来恢复滚动位置,同时比较 prevCountthis.state.count 来记录变化。

卸载时

卸载时阶段指的是组件从DOM中被完全移除时。

例如在类组件中,componentWillUnmount 是一个在组件卸载及销毁之前直接调用的生命周期方法。你可以在这个方法中执行任何必要的清理操作,例如取消网络请求、清除定时器、解除事件监听器等。

class MyComponent extends React.Component {  componentDidMount() {  // 设置定时器  this.timer = setInterval(() => {  console.log('This runs every second');  }, 1000);  }  componentWillUnmount() {  // 清除定时器  clearInterval(this.timer);  }  render() {  return <div>MyComponent</div>;  }  
}export default MyComponent;

在上面的例子中,componentDidMount 中设置了一个定时器,而 componentWillUnmount 中则清除了这个定时器,以确保在组件卸载时不会继续执行定时器的回调。


Hooks 放下一篇文章里,这里字已经水的差不多了😀

总结

温故而知新,可以为师矣~

重温React,不仅是对知识的回顾,更是对技术的敬畏和热爱。感谢React带给我们的每一次成长与收获,期待在未来的开发中,我们能与React一起创造更多的奇迹。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/601305.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【代码随想录】day37:递增数字,监控二叉树

递增数字 def monotoneIncreasingDigits(self, n):""":type n: int:rtype: int"""# 找到不递增的位置i对应的数字k&#xff0c;前一个数字-1,后面都变为9&#xff0c;# 后序遍历nlist(str(n))for i in range(len(n)-1,0,-1):# 如果不递增if n[i]…

开放式耳机什么牌子的好?五大硬核宝藏品牌收藏备用

入耳式耳机虽然隔音效果好&#xff0c;但长时间佩戴可能会让耳朵感到不适。而且&#xff0c;它过于封闭的听音环境&#xff0c;有时会让人感觉与周围环境脱节。相比之下&#xff0c;开放式耳机则更为通透、自然。它不仅能让你清晰地听到音乐中的每一个细节&#xff0c;还能让你…

arm64 - 系统调用

起因 群里做网络的小伙伴问了一个问题&#xff0c;他在wifi驱动的某个函数里加了dump stack&#xff0c;然后插入驱动&#xff0c;发现调用栈是这样的&#xff0c;为什么呢&#xff1f; 代码追溯 insmod这个app&#xff0c;是在busybox中的&#xff0c;所以找到busybox的代…

spring-cloud微服务负载均衡器ribbon

注意&#xff1a;2020年前SpringCloud是采用Ribbon作为负载均衡实现&#xff0c;但是在2020后采用了LoadBalancer替代&#xff0c;所以要查看springboot&#xff0c;springcloud&#xff0c;sprincloudalibaba的版本链接对应&#xff0c;Ribbon负载均衡都是在springboot版本2.4…

静态中间继电器 HJZ-J908 AC380V 导轨安装 JOSEF约瑟

系列型号&#xff1a; HJZ-J902静态中间继电器&#xff1b;HJZ-J905静态中间继电器&#xff1b; HJZ-J907静态中间继电器&#xff1b;HJZ-J907L静态中间继电器&#xff1b; HJZ-J908静态中间继电器&#xff1b;HJZ-J909静态中间继电器&#xff1b; HJZ-J910静态中间继电器&…

FireProx:一款功能强大的AWS API网关管理与IP地址轮换代理工具

关于FireProx FireProx是一款功能强大的AWS API网关安全管理工具&#xff0c;该工具可以帮助广大研究人员创建实现唯一IP地址轮换的实时HTTP转发代理。 在发送网络请求或进行网络交互时&#xff0c;实现源IP地址轮换是一个非常复杂的过程&#xff0c;虽然社区中也有相关的工具…

自定义复选款与单选框,input

注&#xff1a;字体文字取自bootstrap字体库https://icons.bootcss.com/icons <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.checkbox-com,.radio-com {position: relative;display: inlin…

数据结构---绪论

一、绪论&#xff1a; 1.什么是数据&#xff1f; 数据是信息的载体&#xff0c;是描述客观事物属性的数&#xff0c;字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 数据元素--描述一个个体 数据元素&#xff0c;数据项&am…

从用友U9到钉钉通过接口配置打通数据

从用友U9到钉钉通过接口配置打通数据 接通系统&#xff1a;用友U9 用友U9cloud深耕制造领域十三载&#xff0c;U9cloud在机械、电子、汽配、家具、整车、军工等细分行业有着深厚的积累&#xff0c;尤其是机械、电子和汽配行业&#xff0c;不但打造了多个成熟的产品模式和应用场…

双系统安装05--在已有中科方德系统基础上安装Windows10

原文链接&#xff1a;双系统安装05–在已有中科方德系统基础上安装Windows10 Hello&#xff0c;大家好啊&#xff01;继我们之前关于双系统安装的讨论&#xff0c;今天我为大家带来双系统安装系列的第五篇文章——在已有的中科方德桌面操作系统上安装Windows 10。中科方德作为一…

事务隔离:为什么你改了我还看不见?

事务隔离&#xff1a;为什么你改了我还看不见&#xff1f; 提到事务&#xff0c;你肯定不陌生&#xff0c;和数据库打交道的时候&#xff0c;我们总是会用到事务。最经典的例子就是转账&#xff0c;你要给朋友小王转 100 块钱&#xff0c;而此时你的银行卡只有 100 块钱。 转账…

JMeter 使用

初衷 网上有很多JMeter的教程都很优秀&#xff0c;但是我想按照我对JMeter的理解出一篇教程&#xff0c;以便于我以后作为开发人员可以自己对自己写的代码进行性能测试。 1、首先JMeter它的主要作用是性能测试 &#xff08;1&#xff09;负载测试&#xff1a;同时发生的用户…