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

ops/2024/10/17 17:51:35/

在这里插入图片描述

文章目录

      • 函数组件的特点
      • 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 (<imgclassName="avatar"src={getImageUrl(person)}alt={person.name}width={size}height={size}/>);
}export default function Profile() {return (<div><Avatarsize={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// 创建 Contextconst 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 (<Containerheader={<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 (<MyComponentclassName="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]); // 依赖于 numreturn <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]); // 依赖于 countreturn (<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.ppmy.cn/ops/126262.html

相关文章

iOS 18升级:避免常见陷阱,顺利完成升级

随着iOS 18的发布&#xff0c;许多用户都希望尽快体验到新系统带来的新功能和改进。然而&#xff0c;升级过程可能会因为准备工作不足或对步骤的不熟悉而变得复杂。本文旨在为用户提供一个清晰的升级指南&#xff0c;确保升级过程既平滑又安全。 升级前的准备工作 在开始升级之…

力扣(leetcode)每日一题 3158 求出出现两次数字的 XOR 值 |位运算

3158. 求出出现两次数字的 XOR 值 题干 给你一个数组 nums &#xff0c;数组中的数字 要么 出现一次&#xff0c;要么 出现两次。 请你返回数组中所有出现两次数字的按位 XOR 值&#xff0c;如果没有数字出现过两次&#xff0c;返回 0 。 示例 1&#xff1a; **输入&#…

网络安全(黑客技术)2024年100天学习计划

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 前言 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、…

查询数据库绘制历史曲线

<?php include ("jpgraph/jpgraph.php"); include ("jpgraph/jpgraph_line.php"); // // 假定数据库用户名&#xff1a;root&#xff0c;密码&#xff1a;123456&#xff0c;数据库&#xff1a;RUNOOB $conmysqli_connect("localhost",&qu…

温度控制:精酿啤酒发酵中的关键因素

在精酿啤酒的世界里&#xff0c;每一个细节都至关重要。而在其中&#xff0c;温度控制无疑扮演着至关重要的角色。它如同一位细心的指挥家&#xff0c;在发酵的舞台上&#xff0c;精心调控着每一个音符&#xff0c;确保最终能奏响一曲很好的乐章。今天&#xff0c;就让我们一起…

双目标定的原理

标定目的&#xff1a;建立相机成像几何模型并矫正透镜畸变。 建立相机成像几何模型&#xff1a;计算机视觉的首要任务就是要通过拍摄到的图像信息获取到物体在真实三维世界里相对应的信息&#xff0c;于是&#xff0c;建立物体从三维世界映射到相机成像平面这一过程中的几何模…

Scala链式风格

链式风格:在方法中返回当前对象&#xff01; 对象,方法1().方法2().方法3().方法3() args.map().foreach().toString class Stu11{//this.type就是当前这个方法的返回值类型def say(): this.type {println("say.....")this//当前对象}def run(): this.type {prin…

前端布局与响应式设计综合指南(三)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Css篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:前端布局与响应式设计综合指南(三) 目录 42、px/em/rem有什么区别&#xff1f;为什么通常给font-s…