React性能優化:父組件如何導致子組件重新渲染及避免策略

目錄

  • 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…