目录

  • React性能优化:父组件如何导致子组件重新渲染及避免策略
    • 什么是重新渲染?
    • 父组件如何"无辜"地让子组件重新渲染?
      • 示例 1: 基础父组件状态变更
      • 示例 2: 传递未变化的原始类型Prop
      • 示例 3: 传递引用类型Prop(对象)
      • 示例 4: 传递引用类型Prop(函数)
    • 如何避免不必要的子组件重新渲染
      • 策略 1: `React.memo`
      • 示例 5: `React.memo` 优化原始类型Prop
      • 示例 6: `React.memo` 对引用类型失效
      • 策略 2: `useMemo` - 缓存引用类型值
      • 示例 7: `useMemo` 配合 `React.memo`
      • 策略 3: `useCallback` - 缓存函数
      • 示例 8: `useCallback` 配合 `React.memo`
      • 策略 4: 状态下放(State Colocation)
      • 示例 9: 状态下放优化
      • 策略 5: 组件组合(使用 `children` Prop)
      • 示例 10: 利用 `children` 隔离渲染
      • 示例 11: 正确使用 `key` Prop
    • 进阶优化策略与常见陷阱
      • 策略 6: `React.memo` 的自定义比较函数
      • 示例 12: 自定义比较函数
      • 策略 7: 小心 `Context` 带来的全局渲染
      • 示例 13: `Context` 的渲染陷阱
      • 策略 8: 优化 `Context` 消费者
    • 优化的代价与时机
    • 结论


React性能优化:父组件如何导致子组件重新渲染及避免策略

在React开发中,组件的重新渲染(re-render)是一个核心概念。虽然React的虚拟DOM和高效的diff算法已经为我们处理了大部分的UI更新,但在复杂的应用中,不必要的渲染仍然是导致性能问题的常见元凶。理解父组件如何以及何时触发子组件的重新渲染,并学会如何优化它,是每一个React开发者进阶的必经之路。

本文将深入探讨React中父子组件的渲染机制,并通过超过10个具体的代码示例,帮助你彻底掌握避免不必要渲染的实用技巧。

什么是重新渲染?

当一个组件的 render 方法(对于类组件)或函数体(对于函数组件)被再次执行时,我们就称之为"重新渲染"。这通常由以下几个原因触发:

  1. 组件自身的 state 发生变化。
  2. 组件接收到的 props 发生变化。
  3. 父组件重新渲染。
  4. 组件订阅的 Context 值发生变化。

其中,"父组件重新渲染"是导致子组件重新渲染的最常见、也最容易被忽视的原因。

父组件如何"无辜"地让子组件重新渲染?

一个核心原则是:如果一个父组件重新渲染,那么默认情况下,它的所有子组件都会无条件地重新渲染,无论子组件的 props 是否发生了变化。


示例 1: 基础父组件状态变更

这是一个最简单的场景。父组件 Parent 有一个计数器,每次点击按钮时,Parentstate 改变,导致其重新渲染。结果,子组件 Child 也跟着重新渲染,即使它没有接收任何 props

import React, { useState } from 'react';const Child = () => {console.log('子组件被渲染了');return <div>我是子组件</div>;
};const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');return (<div><h2>父组件</h2><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><Child /></div>);
};

分析:打开控制台,每次点击按钮,你会看到父子组件的console.log都被打印出来,证明了子组件的重新渲染。


示例 2: 传递未变化的原始类型Prop

即使我们给子组件传递一个 prop,但只要父组件渲染,子组件依然会渲染。

const Child = ({ name }) => {console.log('子组件被渲染了');return <div>你好, {name}</div>;
};const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button>{/* "React" 这个字符串从未改变 */}<Child name="React" /></div>);
};

分析:尽管 name prop 始终是 "React",但 Child 组件仍然在每次 count 改变时重新渲染。


示例 3: 传递引用类型Prop(对象)

这是一个非常常见的性能陷阱。每次父组件渲染时,都会创建一个新的对象,导致子组件接收到的 prop 引用地址不同,从而重新渲染。

const Child = ({ user }) => {console.log('子组件被渲染了 - user.name:', user.name);return <div>用户名: {user.name}</div>;
};const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');// 每次渲染都会创建一个新的 user 对象const user = { name: 'Alice' };return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><Child user={user} /></div>);
};

分析:即使 user 对象的内容看似没变,但它的引用地址在每次 Parent 渲染时都变了。这对React来说就是一个全新的 prop


示例 4: 传递引用类型Prop(函数)

与对象类似,在父组件中定义的函数,如果未经优化,每次渲染也都是一个全新的函数。

const Child = ({ onButtonClick }) => {console.log('子组件被渲染了');return <button onClick={onButtonClick}>点击我</button>;
};const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');// 每次渲染都会创建一个新的 handleClick 函数const handleClick = () => {console.log('按钮被点击了!');};return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><Child onButtonClick={handleClick} /></div>);
};

分析handleClick 函数在每次 Parent 渲染时都会被重新创建,导致 Child 组件的 onButtonClick prop 每次都不同。


如何避免不必要的子组件重新渲染

现在我们知道了问题所在,接下来看看如何解决它们。核心武器是 React.memo,以及它的好搭档 useCallbackuseMemo

策略 1: React.memo

React.memo 是一个高阶组件(HOC),它会对组件的 props 进行浅比较。如果 props 没有变化,React.memo 会阻止组件的重新渲染,直接复用上次的渲染结果。

示例 5: React.memo 优化原始类型Prop

让我们用 React.memo 改造示例2。

import React, { useState, memo } from 'react';// 使用 React.memo 包裹子组件
const Child = memo(({ name }) => {console.log('子组件被渲染了');return <div>你好, {name}</div>;
});const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><Child name="React" /></div>);
};

分析:现在,当你点击按钮增加 count 时,只有父组件的日志被打印。子组件不再重新渲染,因为 React.memo 发现 name prop("React")没有发生变化。


示例 6: React.memo 对引用类型失效

然而,React.memo 对示例3和示例4是无效的,因为每次传递的都是新的对象或函数引用。

// 即使使用了 memo,子组件依然会重新渲染
const MemoizedChild = memo(({ user }) => {console.log('子组件被渲染了 - user.name:', user.name);return <div>用户名: {user.name}</div>;
});const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');const user = { name: 'Alice' }; // 依然是新对象return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><MemoizedChild user={user} /></div>);
};

分析:点击按钮,子组件依然会重新渲染。React.memo 进行的是浅比较 (prevProps.user === nextProps.user),由于 user 对象的引用地址每次都不同,比较结果为 false,导致优化失败。


策略 2: useMemo - 缓存引用类型值

useMemo 用于缓存计算结果或对象/数组。它会接收一个"创建"函数和一个依赖项数组。只有当依赖项发生变化时,它才会重新计算/创建值。

示例 7: useMemo 配合 React.memo

让我们用 useMemo 来优化示例6,确保 user 对象的引用保持稳定。

import React, { useState, useMemo, memo } from 'react';const MemoizedChild = memo(({ user }) => {console.log('子组件被渲染了 - user.name:', user.name);return <div>用户名: {user.name}</div>;
});const Parent = () => {const [count, setCount] = useState(0);const [userName, setUserName] = useState('Alice');console.log('父组件被渲染了');// 使用 useMemo 缓存 user 对象// 只有当 userName 改变时,才会创建新的 user 对象const user = useMemo(() => ({ name: userName }), [userName]);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count (子组件不渲染)</button><button onClick={() => setUserName('Bob')}>改变 Name (子组件渲染)</button><MemoizedChild user={user} /></div>);
};

分析

  • 点击 “增加 Count” 按钮,count 改变,Parent 渲染。但因为 userName 没变,useMemo 返回了缓存的 user 对象,其引用地址不变。MemoizedChildprop 没变,因此不渲染。
  • 点击 “改变 Name” 按钮,userName 改变,useMemo 的依赖项变化,它创建了一个新的 user 对象。MemoizedChild 接收到新的 prop,因此重新渲染。

策略 3: useCallback - 缓存函数

useCallbackuseMemo 非常相似,但它专门用于缓存函数。useCallback(fn, deps) 等价于 useMemo(() => fn, deps)

示例 8: useCallback 配合 React.memo

useCallback 来优化示例4。

import React, { useState, useCallback, memo } from 'react';const MemoizedChild = memo(({ onButtonClick }) => {console.log('子组件被渲染了');return <button onClick={onButtonClick}>点击我</button>;
});const Parent = () => {const [count, setCount] = useState(0);console.log('父组件被渲染了');// 使用 useCallback 缓存 handleClick 函数// 依赖项数组为空,意味着此函数永不被重新创建const handleClick = useCallback(() => {console.log('按钮被点击了!');}, []);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>增加 Count</button><MemoizedChild onButtonClick={handleClick} /></div>);
};

分析:现在,handleClick 函数的引用在 Parent 的多次渲染之间保持不变。因此,MemoizedChild 不会因为父组件的 count 状态变化而重新渲染。


策略 4: 状态下放(State Colocation)

有时最好的优化不是 memo,而是改变组件结构。将不影响某个子树的状态和逻辑移动到更深层级的组件中,可以有效减少不必要的渲染。

示例 9: 状态下放优化

假设一个表单中,只有一个输入框需要频繁更新状态,其他部分都是静态的。

优化前:

const HeavyComponent = () => {console.log('重量级组件渲染了');// 假设这里有非常复杂的计算或DOM结构return <div>一个很重的组件</div>;
}const FormContainer = () => {const [text, setText] = useState('');console.log('FormContainer 渲染了');return (<div><input value={text} onChange={(e) => setText(e.target.value)} /><p>当前输入: {text}</p><HeavyComponent /></div>);
};

分析:每次输入一个字符,FormContainer 都会重新渲染,导致 HeavyComponent 也跟着重新渲染,这是极大的浪费。

优化后 (状态下放):
我们将 text 状态移动到一个新的 InputManager 组件中。

const HeavyComponent = () => {console.log('重量级组件渲染了');return <div>一个很重的组件</div>;
}// 新组件,管理自己的状态
const InputManager = () => {const [text, setText] = useState('');console.log('InputManager 渲染了');return (<><input value={text} onChange={(e) => setText(e.target.value)} /><p>当前输入: {text}</p></>);
}const FormContainer = () => {console.log('FormContainer 渲染了');return (<div><InputManager /><HeavyComponent /></div>);
};

分析:现在,当你在输入框中打字时,只有 InputManager 组件在重新渲染。FormContainerHeavyComponent 在首次渲染后就不再变化。


策略 5: 组件组合(使用 children Prop)

React中的 children prop 和其他 prop 一样。如果父组件重新渲染,但 children 的引用没有改变,那么 React.memo 也可以阻止 children 的重新渲染。一个更巧妙的方法是利用 children 的特性来"隔离"不希望重新渲染的部分。

示例 10: 利用 children 隔离渲染

import React, { useState } from 'react';const Frame = ({ children }) => {const [count, setCount] = useState(0);console.log('Frame 组件渲染了');return (<div style={{ border: '2px solid blue', padding: '10px' }}><h2>Frame (父)</h2><button onClick={() => setCount(c => c + 1)}>Frame Count: {count}</button>{/* children 在这里被渲染 */}{children}</div>);
};const StaticContent = () => {console.log('StaticContent 组件渲染了');return <p>这是一段静态内容,不应随Frame的count变化而渲染。</p>;
};const App = () => {return (<Frame>{/* StaticContent 在 App 组件的作用域内创建,而不是在 Frame 组件内 */}<StaticContent /></Frame>);
};

分析StaticContent 组件是在 App 组件的渲染过程中被创建并作为 children prop 传递给 Frame 的。当 Frame 组件内部的 count 状态改变时,Frame 会重新渲染。但是,它从 App 组件接收到的 children prop 的引用并没有改变。因此,React会跳过对 StaticContent 的重新渲染。控制台会显示 “Frame 组件渲染了”,但 “StaticContent 组件渲染了” 只会在初始时打印一次。


示例 11: 正确使用 key Prop

key 的主要作用是帮助React识别列表中的哪些项被更改、添加或删除。不稳定的 key(如 Math.random() 或数组索引)会导致不必要的组件重新创建和DOM重建,这比重新渲染的成本更高。

错误示例(使用index作为key)

const Item = ({ text }) => {// 假装有一个内部状态,比如一个输入框const [value, setValue] = useState(text);console.log(`Item "${text}" 渲染了`);return <li><input value={value} onChange={e => setValue(e.target.value)} /></li>;
};const List = () => {const [items, setItems] = useState(['A', 'B', 'C']);const addItemToTop = () => {setItems(['X', ...items]);};return (<div><button onClick={addItemToTop}>在顶部添加 "X"</button><ul>{items.map((item, index) => (// 使用 index 作为 key 是不稳定的<Item key={index} text={item} />))}</ul></div>);
}

分析:当你在顶部添加 “X” 时,新的 items 数组是 ['X', 'A', 'B', 'C']。React 会看到:

  • key={0} 的组件,prop.text 从 ‘A’ 变成了 ‘X’。
  • key={1} 的组件,prop.text 从 ‘B’ 变成了 ‘A’。
  • key={2} 的组件,prop.text 从 ‘C’ 变成了 ‘B’。
  • 新增一个 key={3} 的组件,prop.text 为 ‘C’。
    这导致所有已存在的 Item 组件都接收了新的 props 并重新渲染,而不是仅仅插入一个新组件。如果 Item 内部有自己的 state(如示例中的 input),你会看到 stateprop 不匹配的混乱情况。

正确示例(使用稳定的ID作为key)

// Item 组件同上const List = () => {const [items, setItems] = useState([{ id: 1, text: 'A' },{ id: 2, text: 'B' },{ id: 3, text: 'C' },]);const addItemToTop = () => {const newItem = { id: Date.now(), text: 'X' };setItems([newItem, ...items]);};return (<div><button onClick={addItemToTop}>在顶部添加 "X"</button><ul>{items.map((item) => (// 使用稳定的 id 作为 key<Item key={item.id} text={item.text} />))}</ul></div>);
}

分析:使用稳定 id 作为 key 后,在顶部添加新项时,React能够精确地知道只需要创建一个 key 为新 id 的组件,而其他组件 (key 为1, 2, 3) 保持不变,因此不会重新渲染。


进阶优化策略与常见陷阱

掌握了基础的优化手段后,让我们来看一些更高级或在特定场景下非常关键的策略。

策略 6: React.memo 的自定义比较函数

默认情况下,React.memoprops 对象进行的是浅比较。但如果 prop 是一个包含复杂数据结构的嵌套对象,浅比较就无能为力了。此时,你可以给 React.memo 传递第二个参数:一个自定义的比较函数。

此函数接收 prevPropsnextProps 两个参数。如果它返回 true,则组件不会重新渲染;如果返回 false,则会重新渲染。

示例 12: 自定义比较函数

假设子组件只关心 user 对象中的 id,而不关心 lastLogin 时间。

import React, { useState, memo } from 'react';const UserProfile = memo(({ user }) => {console.log(`子组件 UserProfile 渲染了, user: ${user.name}`);return <div>用户: {user.name}</div>;
}, (prevProps, nextProps) => {// 当 user.id 相同时,我们认为 props 没有变化,返回 true,阻止渲染return prevProps.user.id === nextProps.user.id;
});const App = () => {const [count, setCount] = useState(0);// 即使每次都创建新对象,但只要 id 不变,子组件就不渲染const user = { id: 1, name: 'Alice', lastLogin: Date.now() };return (<div><p>父组件刷新时间戳: {count}</p><button onClick={() => setCount(c => c + 1)}>刷新父组件</button><UserProfile user={user} /></div>);
};

分析:尽管父组件每次渲染都创建了全新的 user 对象(lastLogin 时间戳在变),但我们的自定义比较函数告诉 React.memo 只关心 user.id。由于 id 始终为 1,函数返回 trueUserProfile 组件成功避免了不必要的重新渲染。


策略 7: 小心 Context 带来的全局渲染

Context 是一个强大的状态共享工具,但也很容易成为性能瓶颈。当 Context 的值发生变化时,所有消费该 Context 的组件都会重新渲染,无论它们是否真正使用了值的变化部分。

示例 13: Context 的渲染陷阱

假设我们有一个包含主题和用户信息的 Context。一个组件只用了主题,另一个组件只用了用户信息。

import React, { useState, useContext, createContext } from 'react';// 创建一个包含多个值的 Context
const AppContext = createContext();const ThemeDisplay = () => {const { theme } = useContext(AppContext);console.log('ThemeDisplay 渲染了');return <div style={{ color: theme === 'dark' ? 'white' : 'black' }}>当前主题: {theme}</div>;
};const UserDisplay = () => {const { user } = useContext(AppContext);console.log('UserDisplay 渲染了');return <div>当前用户: {user.name}</div>;
};const App = () => {const [theme, setTheme] = useState('light');const [user, setUser] = useState({ name: 'Guest' });const contextValue = { theme, user, setTheme, setUser };return (<AppContext.Provider value={contextValue}><h2>Context 示例</h2><button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>切换主题</button><button onClick={() => setUser({ name: 'Admin' })}>登录</button><ThemeDisplay /><UserDisplay /></AppContext.Provider>);
};

分析:点击 “切换主题” 时,不仅 ThemeDisplay 会重新渲染,UserDisplay 也会重新渲染,尽管 user 对象并未改变。反之亦然。这是因为它们都订阅了同一个 AppContext,只要 value 对象的任何一部分改变,所有消费者都会更新。

策略 8: 优化 Context 消费者

解决 Context 陷阱的常用方法有两种:

  1. 拆分 Context:将不常一起变化的 value 拆分到不同的 Provider 中。
  2. 使用 React.memouseMemo:将 ContextvalueuseMemo 包裹,并对消费者组件使用 React.memo

示例 14: 拆分 Context 进行优化

const ThemeContext = createContext();
const UserContext = createContext();// ThemeDisplay 只消费 ThemeContext
const ThemeDisplay = () => {const { theme } = useContext(ThemeContext);console.log('ThemeDisplay 渲染了');return <div>当前主题: {theme}</div>;
};// UserDisplay 只消费 UserContext
const UserDisplay = () => {const { user } = useContext(UserContext);console.log('UserDisplay 渲染了');return <div>当前用户: {user.name}</div>;
};const App = () => {const [theme, setTheme] = useState('light');const [user, setUser] = useState({ name: 'Guest' });// 分别用 useMemo 缓存 value,避免不必要的对象创建const themeValue = useMemo(() => ({ theme, setTheme }), [theme]);const userValue = useMemo(() => ({ user, setUser }), [user]);return (<ThemeContext.Provider value={themeValue}><UserContext.Provider value={userValue}><h2>优化后的 Context 示例</h2><button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>切换主题</button><button onClick={() => setUser({ name: 'Admin' })}>登录</button><ThemeDisplay /><UserDisplay /></UserContext.Provider></ThemeContext.Provider>);
};

分析:现在,当切换主题时,只有 ThemeDisplay 会重新渲染。当登录时,只有 UserDisplay 会重新渲染。通过拆分 Context,我们实现了更精确的依赖追踪和渲染控制。


优化的代价与时机

虽然我们有很多优化的工具,但这并不意味着应该滥用它们。

  • useMemouseCallback 的成本:这些 Hooks 并非没有成本。它们需要存储依赖项并进行比较,这会消耗额外的内存和 CPU。对于简单的计算或者不会作为 prop 传递给 memo 子组件的值,使用它们可能得不偿失。
  • 过早优化是万恶之源:不要一开始就将所有东西都用 memouseMemouseCallback 包裹起来。这会使代码变得更复杂、更难阅读。
  • 何时进行优化?:当你的应用遇到实际的性能问题时——例如,UI响应卡顿、交互延迟等。

经验法则

  1. 首先保证代码能正常工作且可读性好。
  2. 使用 React DevTools Profiler 等工具来识别性能瓶颈。React DevTools 有一个 “Highlight updates when components render” 的选项,可以非常直观地看到哪些组件在何时被重新渲染。
  3. 针对性地对那些渲染缓慢或渲染过于频繁的组件,应用上述优化策略。

结论

理解和优化React组件的渲染是提升应用性能的关键。以下是本文的核心要点总结:

  • 默认行为:父组件渲染会导致所有子组件无条件重新渲染。
  • 性能杀手:在父组件的渲染函数中直接创建对象、数组或函数,并将它们作为props传递,是导致子组件不必要渲染的主要原因。
  • 主要武器
    • React.memo():用于包裹子组件,当 props 浅比较无变化时,阻止其重新渲染。可以通过第二个参数提供自定义比较逻辑。
    • useMemo():用于缓存对象或数组,确保它们的引用在依赖项不变时保持稳定。
    • useCallback():用于缓存函数,确保其引用在依赖项不变时保持稳定。
  • 架构级优化
    • 状态下放:将状态移动到真正需要它的最小组件单元中。
    • 组件组合:巧妙利用 children prop 来隔离不需要随父组件状态变化的静态部分。
    • 拆分Context:将大的 Context 拆分为多个更小的 Context,以减少不必要的消费者渲染。
  • 基础但重要:始终为列表中的元素提供稳定且唯一的 key
  • 优化原则:不要过早优化。使用性能分析工具定位瓶颈,然后进行针对性的优化。

掌握了这些策略,你就能在开发中写出性能更优、响应更快的React应用。记住,不要过度优化,首先要让代码工作,然后在遇到性能瓶颈时,利用这些工具进行精确打击。

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

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

相关文章

图的拓扑排序管理 Go 服务启动时的组件初始化顺序

在构建复杂的 Go 应用程序时&#xff0c;服务的启动过程往往涉及多个组件的初始化&#xff0c;例如日志、配置、数据库连接、缓存、服务管理器、适配器等等。这些组件之间通常存在着复杂的依赖关系&#xff1a;日志可能需要配置信息&#xff0c;数据库连接可能依赖日志和追踪&a…

【物理重建】SPLART:基于3D高斯泼溅的铰链估计与部件级重建

标题&#xff1a;《SPLART: Articulation Estimation and Part-Level Reconstruction with 3D Gaussian Splatting》 项目&#xff1a;https://github.com/ripl/splart 文章目录 摘要一、引言二、相关工作2.1 数据驱动的铰链学习2.2 物体重建的表征方法2.3 铰链物体重建 三、方…

vscode中vue自定义组件的标签失去特殊颜色高亮

遇到的问题 最近接触了一个历史遗留项目时&#xff0c;我遭遇了堪称"史诗级屎山"的代码结构——各种命名混乱的自定义组件和原生HTML标签混杂在一起&#xff0c;视觉上完全无法区分。这让我突然想起&#xff0c;之前在使用vue或者其他框架开发的时候&#xff0c;只要…

【Dify精讲】第19章:开源贡献指南

今天&#xff0c;让我们深入 Dify 的开源贡献体系&#xff0c;看看这个项目是如何在短短时间内聚集起一个活跃的开发者社区的。作为想要参与 Dify 开发的你&#xff0c;这一章将是你的实战指南。 一、代码贡献流程&#xff1a;从想法到合并的完整路径 1.1 贡献前的准备工作 …

Web攻防-CSRF跨站请求伪造Referer同源Token校验复用删除置空联动上传或XSS

知识点&#xff1a; 1、Web攻防-CSRF-原理&检测&利用&防御 2、Web攻防-CSRF-防御-Referer策略隐患 3、Web攻防-CSRF-防御-Token校验策略隐患 一、演示案例-WEB攻防-CSRF利用-原理&构造 CSRF 测试功能点 删除帐户 更改电子邮件 如果不需要旧密码&#xff0c;请…

Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights

“拖拽式大模型定制”&#xff08;Drag-and-Drop LLMs: Zero-Shot Prompt-to-Weights&#xff09;。 核心问题&#xff1a; 现在的大模型&#xff08;比如GPT-4&#xff09;很厉害&#xff0c;但想让它们专门干好某个特定任务&#xff08;比如解数学题、写代码&#xff09;&am…

抖音视频怎么去掉抖音号水印保存

随着抖音成为短视频平台的领军者&#xff0c;越来越多的人喜欢在上面拍摄、观看和分享各种创意内容。对于用户来说&#xff0c;下载抖音视频并去除水印保存&#xff0c;以便后续使用或分享成为了一种常见需求。抖音号水印的存在虽然能帮助平台追溯视频源头&#xff0c;但也让许…

【RAG技术(1)】大模型为什么需要RAG

文章目录 为什么需要RAG&#xff1f;RAG的工作原理关键的Embedding技术 RAG vs 模型微调&#xff1a;选择的核心逻辑RAG的关键挑战与解决思路1. 检索质量决定一切2. 上下文长度限制 实际应用场景分析企业知识问答技术文档助手法律咨询系统 构建RAG系统的关键步骤总结 为什么需要…

JS红宝书笔记 - 8.1 理解对象

对象就是一组没有特定顺序的值&#xff0c;对象的每个属性或者方法都可由一个名称来标识&#xff0c;这个名称映射到一个值。可以把对象想象成一张散列表&#xff0c;其中的内容就是一组名值对&#xff0c;值可以是数据或者函数 创建自定义对象的通常方式是创建Object的一个新…

Meson介绍及编译Glib库

一.概述 1.Meson 的简介 Meson&#xff08;The Meson Build System&#xff09;是个项目构建系统&#xff0c;类似的构建系统有 Makefile、CMake、automake …。 Meson 是一个由 Python 实现的开源项目&#xff0c;其思想是&#xff0c;开发人员花费在构建调试上的每一秒都是…

Qt元对象系统实践指南:从入门到应用

目录 摘要 元对象系统核心概念 项目示例&#xff1a;动态UI配置工具 元对象系统在项目中的应用 1. 信号与槽机制 2. 动态属性系统 3. 运行时反射能力 4. 属性绑定与响应 实际项目应用场景 动态UI配置 对象序列化 插件系统 性能优化建议 结论 参考资料 摘要 本文…

Kafka 与其他 MQ 的对比分析:RabbitMQ/RocketMQ 选型指南(一)

消息队列简介 在当今的分布式系统架构中&#xff0c;消息队列&#xff08;Message Queue&#xff0c;MQ&#xff09;扮演着举足轻重的角色。随着业务规模的不断扩大和系统复杂度的日益提升&#xff0c;各个组件之间的通信和协同变得愈发关键 。消息队列作为一种异步的通信机制…

[创业之路-441]:行业 - 互联网+移动互联网和大数据时代的100个预言:技术个性、商业变革、社会重构、文化娱乐、环境、教育、健康医疗、未来生活方式

目录 一、技术革新 二、商业变革 三、社会重构 四、文化与娱乐 六、环境与可持续发展 七、教育与知识传播 八、健康与医疗 九、伦理与法律 十、未来生活方式 十一、终极预言 结语 在移动互联网和大数据时代&#xff0c;技术革新正以前所未有的速度重塑社会、经济与文…

基于STM32单片机WIFI无线APP控灯亮度灭设计

基于STM32单片机控灯设计 &#xff08;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 本设计由STM32F103C8T6单片机核心电路两位白色高亮LED灯电路WIFI模块ESP8266电路电源电路组成。 1、stm32实时监测wifi数据&#xff0c;解析数…

学会C++中的vector的基本操作

vector 是 C 标准库中的一个动态数组类&#xff0c;它可以在运行时自动调整大小&#xff0c;非常适合用于处理大小不确定的集合。下面是 vector 的常见用法示例&#xff0c;帮助你更好地理解如何使用它。 注意&#xff1a;所有用数组完成的任务都可以用vector完成。 1. 引入头…

AI时代工具:AIGC导航——AI工具集合

大家好!AIGC导航是一个汇集多种AIGC工具的平台&#xff0c;提供了丰富的工具和资源。 工具功能​: 该平台整合了多样的AIGC工具&#xff0c;涵盖了绘画创作、写作辅助以及视频制作等多个领域。绘画工具能够生成高质量的图像作品&#xff1b;写作工具支持从构思到润色的全流程写…

java-SpringBoot框架开发计算器网页端编程练习项目【web版】

今天分享一个使用springboot 写一个 前后端不分离的项目&#xff0c;网页计算器&#xff0c;来熟悉springboot框架的使用。 java版本&#xff1a;8。 springboot&#xff1a;2.6.13 使用的技术是&#xff1a; Java Spring Boot Thymeleaf HTML/CSS/JS 构建的 Web 端简约按钮…

linux操作系统的软件架构分析

一、linux操作系统的层次结构 1.内核的主要功能 1&#xff09;进程管理 2&#xff09;内存管理 3&#xff09;文件系统 4&#xff09;进程间通信、I/O系统、网络通信协议等 2.系统程序 1&#xff09;系统接口函数库&#xff0c;比如libc 2)shell程序 3&#xff09;编译器、编辑…

浅谈Java对象在内存中的存储形式

我们知道计算机以二进制的方式存储数据&#xff0c;以 64 位虚拟机为例&#xff0c;Java 对象在内存中的存储形式为&#xff1a; 开头是 8 个字节的 markword&#xff0c;用于标记对象的状态。&#xff08;也就是一个 long 型数据的大小。不妨记作对象头里有一个长长的 markwo…

Android 开发问题:Wrong argument type for formatting argument ‘#2‘ in info_message

<string name"info_message">name: %1$s, age: %2$d</string>String str getString(R.string.info_message, "zs");在 Android 开发中&#xff0c;上述代码&#xff0c;出现如下警告信息 Wrong argument type for formatting argument #2 in…