根据Vue对比来深入学习React 下 props 组件传值 插槽 样式操作 hooks 高阶组件 性能优化

news2024/11/23 20:12:11

在这里插入图片描述

文章目录

      • 函数组件的特点
      • props
      • 组件间的传值
        • 父传子看上例
        • 子传父
        • 兄弟组件传值
        • 祖先组件传值
      • 插槽
        • 基础插槽
        • 具名插槽
        • 作用域插槽
      • 样式操作
        • **CSS Modules**
      • 生命周期
      • useRef
      • 常用hook
        • useState
        • useEffect
        • useContext
        • useReducer
        • useMemo
        • useCallback
      • 高阶组件
        • 什么时候使用
      • react性能问题和优化
        • React的时间切片
        • fiber
        • 避免父组件数据导致子组件更新

函数组件的特点

  1. 函数组件没有生命周期
  2. 函数组件没有this
  3. 函数组件通过hook来完成各种操作
  4. 函数组件本身的函数体相当于render函数
  5. props在函数的第一个参数接受

props

props是react中的核心
在react中,一切写在组件上的属性和子节点都被规划为了props。
所以props是react很多功能的根本。父子传值,插槽全都是基于props,不像Vue有事件监听,emit,专门的插槽这一类东西

//也可以给默认值
function Avatar({ person, size= 100 }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi', 
          imageId: 'YfeOqp2'
        }}
      />
    </div>
  );
}

组件间的传值

父传子看上例
子传父

子组件可以通过回调函数将数据传递回父组件。没有Vue中的emit和expose方法

// 子组件

const ChildComponent = ({ onSendData }) => {

return <button onClick={() => onSendData("Hello from Child!")}>Send Data</button>;

};

// 父组件

const ParentComponent = () => {

const handleData = (data) => {

console.log(data);

};

return <ChildComponent onSendData={handleData} />;

};
兄弟组件传值

与Vue相同 都是通过父组件作为中介来传递数据。

// 第一个兄弟组件

const BrotherOne = ({ onSendData }) => {

return <button onClick={() => onSendData("Data from Brother One")}>Send Data</button>;

};

// 第二个兄弟组件

const BrotherTwo = ({ data }) => {

return <h1>{data}</h1>;

};

// 父组件

const ParentComponent = () => {

const [data, setData] = React.useState("");

const handleDataChange = (newData) => {

setData(newData);

};

return (

<div>

<BrotherOne onSendData={handleDataChange} />

<BrotherTwo data={data} />

</div>

);

};
祖先组件传值
import React, { createContext, useContext, useState } from 'react';

  

JJ// 创建 Context

const MyContext = createContext();

  

// 提供者组件

const MyProvider = ({ children }) => {

const [value, setValue] = useState("Hello from Context!");

  

return (

<MyContext.Provider value={{ value, setValue }}>

J{children}

</MyContext.Provider>

);

};

  

// 使用 Context 的子组件

const ChildComponent = () => {

const { value } = useContext(MyContext);

return <h1>{value}</h1>;

};

  

// 父组件

const ParentComponent = () => {

return (

<MyProvider>

<ChildComponent />

</MyProvider>

);

};

插槽

在 React 中,虽然没有 Vue 的插槽(slot)具名插槽 作用域插槽,但是可以实现

插槽本质上讲就是子组件的html内容需要父组件传入,在jsx的加持下,我可以把html像普通的字符串,数字一样传递,所以插槽只需要直接作为props传入就行

基础插槽

在基础插槽中,我们可以使用 children prop 来接收父组件传递的内容

import React from 'react';

  

// 容器组件,支持基础插槽

const Container = ({ children }) => {

return (

<div className="container">

<h2>Container Header</h2>

<div className="content">{children}</div>

</div>

);

};

  

// 使用容器组件

const App = () => {

return (

<Container>

<p>This is the content inside the container.</p>

</Container>

);

};
export default App;
具名插槽

可以通过将不同的 props 传递给子组件来实现。我们可以使用标准的 JSX 语法来实现具名插槽

import React from 'react';
// 容器组件,支持具名插槽

const Container = ({ header, footer, children }) => {

return (

<div className="container">

<div className="header">{header}</div>

<div className="content">{children}</div>

<div className="footer">{footer}</div>

</div>

);

};

  

// 使用容器组件

const App = () => {

return (

<Container

header={<h1>This is the Header</h1>}

footer={<h2>This is the Footer</h2>}

>

<p>This is the main content area.</p>

</Container>
);
};
export default App;
作用域插槽

允许我们将数据传递到子组件,使得父组件可以控制子组件的渲染。我们可以通过传递一个函数作为 prop 来实现

import React from 'react';
// 容器组件,支持作用域插槽

const ListContainer = ({ items, renderItem }) => {

return (

<ul>

{items.map(item => (

<li key={item.id}>{renderItem(item)}</li>

))}

</ul>

);

};
// 使用容器组件

const App = () => {

const items = [

{ id: 1, text: 'Item 1' },

{ id: 2, text: 'Item 2' },

{ id: 3, text: 'Item 3' }

];


const renderItem = (item) => <span>{item.text}</span>;

return (

<ListContainer items={items} renderItem={renderItem} />

);

};

export default App;

样式操作

class类名设值:必须写为className 类名和样式写在css文件里 必须接受一个字符串

style内联:不能像原生一样写成字符串,必须写成对象

import React from 'react';

import './styles.css'; // 这里引入你的 CSS 文件

  

const MyComponent = ({ className, style }) => {

return (

<div className={className} style={style}>

Hello, World!

</div>

);

};

  

// 使用组件

const App = () => {

const customStyle = {

color: 'blue',

fontSize: '20px',

backgroundColor: 'lightgray',

};

  

return (

<MyComponent

className="my-custom-class" // 在 CSS 文件中定义的类名

style={customStyle} // 使用对象形式的内联样式

/>

);

};


export default App;
CSS Modules

CSS Modules 提供了局部作用域的 CSS,避免类名冲突。

//styles.module.css
.myCustomClass { color: blue; font-size: 20px; padding: 10px; background-color: lightgray; }

import styles from './styles.module.css'; 
const MyComponent = () => { return <div className={
styles.myCustomClass}>
Hello, World!
</div>;
						  };

生命周期

在 React 函数组件中,没有传统的生命周期方法 ,React 通过 Hooks 提供了类似的功能,允许你在不同的阶段执行副作用。

import React, { useEffect } from 'react';
  

const MyComponent = () => {

useEffect(() => {

// 组件挂载时执行

return () => {

// 组件卸载时执行

};

}, []); // 空数组表示只在挂载和卸载时执行

  

return <div>Hello, World!</div>;

};

useRef

用于获取真实dom和vue中ref一个道理 只不过变成了.current

import React, { useRef } from 'react';

const MyComponent = () => {

const inputRef = useRef(null);

const focusInput = () => {

if (inputRef.current) {

inputRef.current.focus();

}

};

  

return (

<div>

<input ref={inputRef} type="text" />

<button onClick={focusInput}>Focus Input</button>

</div>

);

};

常用hook

useState

语法: const [state, setState] = useState(initialState)
initialState 是初始状态,可以是任何类型(数字、字符串、对象等)。setState 是更新状态的函数。
这个不做举例了

useEffect

语法:useEffect(() => { … },[dependencies])
dependencies 是一个数组,包含 effect 依赖的值。当这些值发生变化时,effect 会重新执行。如果传入空数组 [],effect 只会在组件挂载和卸载时执行一次。
不做举例了

useContext

语法:const value = useContext(MyContext)
useContext 不接受第二个参数。它从最近的 <MyContext.Provider> 中获取当前上下文值

useReducer

用法: const [state, dispatch] = useReducer(reducer, initialState);
第二个参数: initialState 是 reducer 的初始状态,通常是一个对象或基本数据类型。

import React, { useReducer } from 'react';
 
const initialState = { count: 0 };

function reducer(state, action) {

switch (action.type) {

case 'increment':

return { count: state.count + 1 };

case 'decrement':

return { count: state.count - 1 };

default:

throw new Error();

}

}

const Counter = () => {

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

return (

<div>

Count: {state.count}

<button onClick={() => dispatch({ type: 'increment' })}>+</button>

<button onClick={() => dispatch({ type: 'decrement' })}>-</button>

</div>

);

};
useMemo

用法:const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
dependencies 是一个数组,当数组中的任意一个值改变时,computeExpensiveValue 才会重新计算并返回新值
可以提高性能 记忆函数 也就是保存结果,组件多次渲染直接取缓存结果就行

import React, { useMemo } from 'react';

  

const ExpensiveCalculation = ({ num }) => {

const result = useMemo(() => {

// 进行复杂计算

return num * 2; // 示例计算

}, [num]); // 依赖于 num

  

return <div>Result: {result}</div>;

};
useCallback

用法: const memoizedCallback = useCallback(() => { }, [dependencies]);
第二个参数: dependencies 是一个数组,当数组中的任意一个值改变时,返回新的回调函数。
更上面一样 就是保存函数

import React, { useState, useCallback } from 'react';

  

const Button = React.memo(({ onClick }) => {

console.log('Button Rendered');

return <button onClick={onClick}>Click Me</button>;

});

  

const App = () => {

const [count, setCount] = useState(0);

  

const handleClick = useCallback(() => {

setCount(count + 1);

}, [count]); // 依赖于 count

  

return (

<div>

<Button onClick={handleClick} />

<p>Count: {count}</p>

</div>

);

};
 

高阶组件

高阶组件(Higher-Order Component,简称 HOC)是 React 中一种用于复用组件逻辑的模式。它本质上是一个函数,接受一个组件作为参数,并返回一个新的组件。高阶组件可以用于许多场景,例如:状态管理、数据获取、权限控制等。

import React from 'react';

  

// 原始组件

const MyComponent = ({ extraInfo }) => (

<div>

<p>这是我的组件。</p>

<p>{extraInfo}</p>

</div>

);

  

// 高阶组件

const withExtraInfo = (WrappedComponent) => {

return (props) => {

const extraInfo = "附加信息:这是来自高阶组件的内容!";

return <WrappedComponent {...props} extraInfo={extraInfo} />;

};

};

  

// 使用高阶组件

const EnhancedComponent = withExtraInfo(MyComponent);

  

const App = () => (

<div>

<EnhancedComponent />

</div>

);


export default App;
什么时候使用

组件是既包含了ui界面的复用,也包含了逻辑的复用,高阶组件只是复用操作逻辑,运算,类似于vue中mixin的用途,当我们发现某个操作逻辑,或者某个运算经常出现的时候,我们可以提取为高阶组件

react性能问题和优化

Vue的因为是在get和set里触发更新
Vue在get部分有一个重要的操作-依赖收集
这样我们在更改了数据后,只会更新用到了这个数据的地方做到最小的更新范围

React的更新是调用方法时触发的,并没有依赖收集的过程所以他会更新整个组件树也就是会把子组件一起更新即使更新的数据和子组件没有任何关系

所以React最大的一个性能问题就是-React的某个组件的更新会连带着,他的子组件一起更新。所以我们需要解决这个问题让子组件只做合理的更新

React的时间切片

Vue有依赖收集,做到了最小的更新范围,而React没有做这个事情。所以React要更新,就会有很大的diff算法比对和计算工作

这大的更新量,虚拟dom比对和计算会花很大时间,这样可能会阻塞住浏览器的工作,导致页面长时间白屏

React为了解决这个问题选择另一种策略-时间切片,也就是先计算一部分更新,然后让渡给渲染进程 然后再进行下一步更新我从使用者的角度 就不会出现长时间白屏了。

fiber

为了支持这种切片,我们需要把更新化成一个个单元,然后我们也必须有回复上一次计算进度的能力

所以react设计一种数据结构-fiber
每一个组件会被转化为一个fiber结构的对象,组成一个个单元。Fiber让我们有了回复上次中断的计算进度的能力

类似于vue中的虚拟节点vnode, Both Fiber 和 vnode 都是对真实 DOM 的抽象,旨在提高性能和效率。它们允许框架在内存中处理组件树,而不是直接操作 DOM。

避免父组件数据导致子组件更新

React.memo子组件 让它缓存

我们特别注意父组件传入的方法,对象,数组这样的引用类型

用useCallback包裹传递给子组件的方法
state对象,数组数据,要用useMemo包裹起来

import React, { useState, useCallback, useMemo } from 'react';

// 子组件

const ChildComponent = React.memo(({ handleClick, data }) => {

console.log('ChildComponent rendered');

return (

<div>

<button onClick={handleClick}>Click Me</button>

<ul>

{data.map((item, index) => (

<li key={index}>{item}</li>

))}

</ul>

</div>

);

});


// 父组件

const ParentComponent = () => {

const [count, setCount] = useState(0);

const [items, setItems] = useState(['Item 1', 'Item 2']);

  

// 使用 useCallback 包裹传递给子组件的方法

const handleClick = useCallback(() => {

setCount(count + 1);

}, [count]);

  

// 使用 useMemo 包裹传递给子组件的数组数据

const memoizedItems = useMemo(() => {

return items;

}, [items]);

  

return (

<div>

<h1>Count: {count}</h1>

<ChildComponent handleClick={handleClick} data={memoizedItems} />

</div>

);

};

export default ParentComponent;

如果对你有所帮助的话就点个关注吧,会持续更新技术文章

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode讲解篇之2266. 统计打字方案数

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们使用逆向思维发现如果连续按存在三个字母的按键&#xff0c;最后一个按键表示的字母可以是某个字母连续出现一次、两次、三次这三种情况的方案数之和 我们发现连续按存在三个字母的按键&#xff0c;当连续按…

数据治理中的核心 元数据

数据治理中的核心元素——元数据 一、关于元数据 1、什么是元数据 元数据&#xff08;metadata&#xff09;是关于数据的组织、数据域及其关系的信息&#xff0c;简单来说&#xff0c;元数据就是被用来描述数据的数据。 概念阐述总归生涩&#xff0c;下面用几个简单的例子来…

【千图网-登录_注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【双指针算法】移动零

1.题目解析 2.算法分析 可以归结为数组划分/数组分块&#xff08;采用双指针算法&#xff09;-->利用数组下标充当指针 &#xff08;1&#xff09;首先定义两个指针 dest&#xff1a;已处理的区间内&#xff0c;非零元素的最后一个位置cur&#xff1a;从左往右扫描数组&…

Stm32+Esp8266连接阿里云程序移植教程(MQTT协议)

Stm32Esp8266连接阿里云程序移植教程&#xff08;MQTT协议&#xff09; 一、前期准备二、移植过程三、程序的使用3.1 连接上阿里云3.2 传输用户数据到阿里云3.3 解析从阿里云下发给用户的数据3.4 关于调试接口 一、前期准备 自己要的工程文件移植所需的文件&#xff08;如下图&…

CentOS 7.9安装MySQL

下载Linux版MySQL安装包 下载地址https://downloads.mysql.com/archives/community/ 下载解压后 安装&#xff0c;按照从上至下顺序&#xff0c;一条一条执行即可安装完毕。 进入到rpm所在目录rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm rpm -ivh mysql-comm…

计算机网络——CDN

空间编码例子&#xff1a;不是发送N个相同颜色值&#xff0c;而是仅发送2个值&#xff0c;颜色和重复个数 时间编码例子&#xff1a;不是发送i1帧的全部编码&#xff0c;而是仅发送帧i差别的地方 视频播放时&#xff0c;先下载manifest file文件——>解析&#xff08;不…

vscode中关闭cmake自动配置

前言 最近误触了一个操作&#xff0c;导致&#xff0c;一旦使用vscode打开项目&#xff0c;就会去配置cmake。或者你一旦更改cmakelists.txt&#xff0c;就会去配置cmake。 这个操作&#xff0c;结果对不对还另说&#xff0c;关键是增加计算机开销&#xff0c;使得vscode打开后…

【华为】配置BGP协议

边界网关协议BGP是一种实现自治系统AS之间的路由可达&#xff0c;并选择最佳路由的距离矢量路由协议。BGP在不同自治系统之间进行路由转发&#xff0c;分为EBGP&#xff08;外部边界网关协议&#xff09;和IBGP&#xff08;内部边界网关协议&#xff09;两种情况。 [A]in g0/0/…

使用docker搭建lnmp运行WordPress

一&#xff0c;部署目的 使用 Docker 技术在单机上部署 LNMP 服务&#xff08;Linux Nginx MySQL PHP&#xff09;。部署并运行 WordPress 网站平台。掌握 Docker 容器间的互联及数据卷共享。 二&#xff0c;部署环境 操作系统&#xff1a;CentOS 7Docker 版本&#xff1…

Spring 的依赖注入的最常见方式

在 Spring 中&#xff0c;依赖注入的方式有多种选择。下面我们来逐一分析它们的特点、适用场景和注意事项&#xff1a; 1. 构造函数注入 构造函数注入要求在对象创建时提供所有依赖。这种方式确保依赖在对象创建后不可变&#xff0c;特别适合必须强制存在的依赖。所有依赖在对…

Windows的Conda环境下使用PlotNeuralNet来绘制神经网络

项目场景&#xff1a; Windows环境下&#xff0c;使用了anaconda的Python环境管理器&#xff0c;使用PlotNeuralNet来绘制神经网络图 问题描述 在运行以下shell命令的时候 cd pyexamples/ bash ../tikzmake.sh test_simple出现了访问被拒绝的错误&#xff0c;如下所示&#x…

【数据结构】:破译排序算法--数字世界的秩序密码(一)

文章目录 一.排序算法概述1.定义和目的2.排序算法的分类2.1比较排序2.2非比较排序 二.插入排序算法1.InsertSort直接插入排序1.1.插入排序原理1.2.插入排序过程1.3.代码实现1.4.复杂度和稳定性 2.ShellSort希尔排序2.1.希尔排序原理2.2.希尔排序过程2.3.代码实现2.4.复杂度和稳…

LeetCode 132. 分割回文串 II(经典必会)

LeetCode 132. 分割回文串 II 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;1 解释&#xff1a;只需一次分割就可将 s 分割成 [“…

EditPlus安装使用

1.进入EditPlus官网(https://www.editplus.com/)点击第二行蓝字 2.点击More options第一行蓝字 3.点击exe文件进入安装&#xff0c;点击Accept 4.选择下载路径 4.点击editplus.exe 5.在许可证协议中点击yes 6.输入username和regcode即可使用(也可以试用30天)

Leetcode 岛屿数量

首先检查网格是否为空&#xff0c;如果为空&#xff0c;直接返回 0。遍历网格中的每一个元素&#xff0c;当遇到陆地&#xff08;1&#xff09;时&#xff0c;计数器加 1&#xff0c;并且通过 DFS 将与该陆地相连的所有部分标记为已访问&#xff08;即设为 0&#xff09;。DFS …

第 22 章 - 你不能错过的Elasticsearch核心知识点-BM25相关性评分算法(进阶)

文章目录 前言分片对 Elasticsearch 相关性评分的影响BM25 算法和它的变量效果应用将 b b b 值设置为 0将 k 1 k1 k1 设置为0 总结 前言 上一章介绍了 Elasticsearch 的读写优化技巧。本章将深入探讨与 Elasticsearch 相关的 BM25 相关性评分公式。 我们将全面解析 BM25 如…

【Java】C++转Java基础知识

1. Java基础知识 1.1 JDK和JVM 在Java中&#xff0c;JDK称为Java开发工具包(Java Development Kit)&#xff0c;包含了Java开发需要使用的工具包&#xff0c;前面的版本中JRE和JDK是分开的两个文件夹&#xff0c;从Java9开始&#xff0c;JDK中还包含了JRE(Java Runtime Envir…

STM32外设详解——ADC

来源&#xff1a;铁头山羊 基本概念 ①ADC是模数转换器的统称&#xff0c;stm32f103c8t6内部集成了2个12位主次逼近型ADC&#xff0c;外设名称为ADC1、ADC2。 ② 采样深度为12位意味着ADC可以将0~3.3V的模拟电压等比转换为0~4095的数字值&#xff08;分割为2的12次方份&…

猫头虎分享:Python库 Selenium 的简介、安装、用法详解入门教程

&#x1f42f; 猫头虎分享&#xff1a;Python库 Selenium 的简介、安装、用法详解入门教程 &#x1f680; 今天&#xff0c;猫头虎带大家深入了解 Selenium&#xff0c;这是一个非常流行的自动化测试工具&#xff0c;用于浏览器自动化。无论你是进行网页数据抓取&#xff0c;还…