React 深入學習:React 更新隊列

path:packages/react-reconciler/src/ReactUpdateQueue.js

更新

export type Update<State> = {expirationTime: ExpirationTime, // 到期時間tag: 0 | 1 | 2 | 3, // 更新類型payload: any, // 負載callback: (() => mixed) | null, // 回調函數next: Update<State> | null, // 下一個更新nextEffect: Update<State> | null, // 下一個效果
};
復制代碼

React 的狀態更新分為四種情況,他們分別對應 Update 的 tag 屬性的四個值:

  • UpdateState
  • ReplaceState
  • ForceUpdate
  • CaptureUpdate
export const UpdateState = 0; // 更新狀態
export const ReplaceState = 1; // 替換狀態
export const ForceUpdate = 2; // 強制更新
export const CaptureUpdate = 3; // 捕獲更新
復制代碼

創建更新

/*** 創建更新* @param expirationTime* @returns {{next: null, payload: null, expirationTime: ExpirationTime, callback: null, tag: number, nextEffect: null}}*/
export function createUpdate(expirationTime: ExpirationTime): Update<*> {return {expirationTime: expirationTime,tag: UpdateState,payload: null,callback: null,next: null,nextEffect: null,};
}
復制代碼

調用此方法創建的更新默認為是局部更新,需要合并前后狀態。

更新隊列

export type UpdateQueue<State> = {baseState: State,firstUpdate: Update<State> | null,lastUpdate: Update<State> | null,firstCapturedUpdate: Update<State> | null,lastCapturedUpdate: Update<State> | null,firstEffect: Update<State> | null,lastEffect: Update<State> | null,firstCapturedEffect: Update<State> | null,lastCapturedEffect: Update<State> | null,
};
復制代碼

創建更新隊列

/*** 創建更新隊列* @param baseState* @returns {UpdateQueue<State>}*/
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {const queue: UpdateQueue<State> = {baseState,firstUpdate: null,lastUpdate: null,firstCapturedUpdate: null,lastCapturedUpdate: null,firstEffect: null,lastEffect: null,firstCapturedEffect: null,lastCapturedEffect: null,};return queue;
}
復制代碼

數據結構

從上面的代碼中可以看到,更新隊列是一個單向鏈表:

appendUpdateToQueue

追加更新到鏈表尾部

/*** 添加更新到隊列中* @param queue* @param update*/
function appendUpdateToQueue<State>(queue: UpdateQueue<State>,update: Update<State>,
) {// 將更新追加到列表的末尾。if (queue.lastUpdate === null) {// 隊列是空的queue.firstUpdate = queue.lastUpdate = update;} else {queue.lastUpdate.next = update;queue.lastUpdate = update;}
}
復制代碼

state 更新

每次更新的時候需要根據不同的更新類型來獲取下一次的 state:

  • UpdateState 需要合并前一次的狀態和本次的狀態
  • ReplaceState 直接使用下一次的狀態
  • ForceUpdate 使用前一次的狀態
  • CaptureUpdate
/*** 從跟新獲取狀態* @param workInProgress* @param queue* @param update* @param prevState* @param nextProps* @param instance* @returns {State|*}*/
function getStateFromUpdate<State>(workInProgress: Fiber,queue: UpdateQueue<State>,update: Update<State>,prevState: State,nextProps: any,instance: any,
): any {switch (update.tag) {case ReplaceState: {const payload = update.payload;if (typeof payload === 'function') {// 更新器函數const nextState = payload.call(instance, prevState, nextProps);return nextState;}// 狀態對象return payload;}case CaptureUpdate: {workInProgress.effectTag =(workInProgress.effectTag & ~ShouldCapture) | DidCapture;}// Intentional fallthroughcase UpdateState: {const payload = update.payload;let partialState;if (typeof payload === 'function') {// Updater functionpartialState = payload.call(instance, prevState, nextProps);} else {// 部分狀態對象partialState = payload;}if (partialState === null || partialState === undefined) {// Null 和 undefined 被視為 no-ops。return prevState;}// 合并部分狀態和前一個狀態。return Object.assign({}, prevState, partialState);}case ForceUpdate: {hasForceUpdate = true;return prevState;}}return prevState;
}
復制代碼

從上面的代碼可以看到,更新 state 時可以接收一個更新器函數,這個更新器函數被綁定到當前的實例上運行,也就是在 React 文檔 中寫到的,setState 可以接收一個函數作為參數:

setState((prevState, nextProps) => {// do something
})
復制代碼
  • prevState 參數是上一次調用 setState 之后的狀態,而不是已經更新到 dom 中的狀態,因為狀態更新是異步的,為了避免不必要的重新渲染來提升性能。
  • nextProps 參數是下一次的 props 對象

處理更新

/*** * @param workInProgress* @param queue* @param props* @param instance* @param renderExpirationTime*/
export function processUpdateQueue<State>(workInProgress: Fiber,queue: UpdateQueue<State>,props: any,instance: any,renderExpirationTime: ExpirationTime,
): void {hasForceUpdate = false;// 確保處理的更新隊列的 work 是一個復制品queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);if (__DEV__) {currentlyProcessingQueue = queue;}// These values may change as we process the queue.// 當我們處理隊列時,這些值可能會改變。let newBaseState = queue.baseState;let newFirstUpdate = null;let newExpirationTime = NoWork;// Iterate through the list of updates to compute the result.// 迭代更新列表以計算結果。let update = queue.firstUpdate;let resultState = newBaseState;while (update !== null) {const updateExpirationTime = update.expirationTime;if (updateExpirationTime < renderExpirationTime) {// This update does not have sufficient priority. Skip it.// 此更新沒有足夠的優先級。跳過它。if (newFirstUpdate === null) {// This is the first skipped update. It will be the first update in// the new list.// 這是第一個跳過的更新。這將是新列表中的第一個更新。newFirstUpdate = update;// Since this is the first update that was skipped, the current result// is the new base state.// 由于這是跳過的第一個更新,所以當前結果是 new base state。newBaseState = resultState;}// Since this update will remain in the list, update the remaining// expiration time.// 由于此更新將保留在列表中,所以更新剩余的過期時間。if (newExpirationTime < updateExpirationTime) {newExpirationTime = updateExpirationTime;}} else {// This update does have sufficient priority. Process it and compute// a new result.// 這次更新確實有足夠的優先級。處理它并計算一個新的結果。resultState = getStateFromUpdate(workInProgress,queue,update,resultState,props,instance,);const callback = update.callback;if (callback !== null) {workInProgress.effectTag |= Callback;// Set this to null, in case it was mutated during an aborted render.// 將其設置為null,以防在中止渲染期間發生突變。update.nextEffect = null;if (queue.lastEffect === null) {queue.firstEffect = queue.lastEffect = update;} else {queue.lastEffect.nextEffect = update;queue.lastEffect = update;}}}// Continue to the next update.// 繼續下一個更新。update = update.next;}// Separately, iterate though the list of captured updates.// 另外,遍歷捕獲的更新列表。let newFirstCapturedUpdate = null;update = queue.firstCapturedUpdate;while (update !== null) {const updateExpirationTime = update.expirationTime;if (updateExpirationTime < renderExpirationTime) {// This update does not have sufficient priority. Skip it.// 這個更新沒有足夠的優先級。跳過它。if (newFirstCapturedUpdate === null) {// This is the first skipped captured update. It will be the first// update in the new list.// 這是第一次跳過捕獲的更新。這將是新列表中的第一個更新。newFirstCapturedUpdate = update;// If this is the first update that was skipped, the current result is// the new base state.// 如果這是跳過的第一個更新,則當前結果是新的基本狀態。if (newFirstUpdate === null) {newBaseState = resultState;}}// Since this update will remain in the list, update the remaining// expiration time.// 由于此更新將保留在列表中,所以更新剩余的過期時間。if (newExpirationTime < updateExpirationTime) {newExpirationTime = updateExpirationTime;}} else {// This update does have sufficient priority. Process it and compute// a new result.// 這次更新確實有足夠的優先級。處理它并計算一個新的結果。resultState = getStateFromUpdate(workInProgress,queue,update,resultState,props,instance,);const callback = update.callback;if (callback !== null) {workInProgress.effectTag |= Callback;// Set this to null, in case it was mutated during an aborted render.// 將其設置為 null,以防在中止 render 期間發生突變。update.nextEffect = null;if (queue.lastCapturedEffect === null) {queue.firstCapturedEffect = queue.lastCapturedEffect = update;} else {queue.lastCapturedEffect.nextEffect = update;queue.lastCapturedEffect = update;}}}update = update.next;}if (newFirstUpdate === null) {queue.lastUpdate = null;}if (newFirstCapturedUpdate === null) {queue.lastCapturedUpdate = null;} else {workInProgress.effectTag |= Callback;}if (newFirstUpdate === null && newFirstCapturedUpdate === null) {// We processed every update, without skipping. That means the new base// state is the same as the result state.// 我們處理了每個更新,沒有跳過。這意味著新的基狀態與結果狀態相同。newBaseState = resultState;}queue.baseState = newBaseState;queue.firstUpdate = newFirstUpdate;queue.firstCapturedUpdate = newFirstCapturedUpdate;// Set the remaining expiration time to be whatever is remaining in the queue.// This should be fine because the only two other things that contribute to// expiration time are props and context. We're already in the middle of the// begin phase by the time we start processing the queue, so we've already// dealt with the props. Context in components that specify// shouldComponentUpdate is tricky; but we'll have to account for// that regardless.// 將剩余的過期時間設置為隊列中剩余的時間。// 這應該沒問題,因為影響過期時間的另外兩個因素是 props 和 context。// 在開始處理隊列時,我們已經處于 begin 階段的中間,// 所以我們已經處理了這些 props。// 指定 shouldComponentUpdate 的組件中的 Context 比較復雜;// 但無論如何我們都要考慮到這一點。workInProgress.expirationTime = newExpirationTime;workInProgress.memoizedState = resultState;if (__DEV__) {currentlyProcessingQueue = null;}
}
復制代碼

提交更新

提交更新

/*** 提交更新隊列* @param finishedWork* @param finishedQueue* @param instance* @param renderExpirationTime*/
export function commitUpdateQueue<State>(finishedWork: Fiber,finishedQueue: UpdateQueue<State>,instance: any,renderExpirationTime: ExpirationTime,
): void {// 如果已完成的渲染包含捕獲的更新,// 并且仍然有較低優先級的更新遺留下來,// 那么我們需要將捕獲的更新保存在隊列中,// 以便在以較低優先級再次處理隊列時重新基于它們,而不是丟棄它們。if (finishedQueue.firstCapturedUpdate !== null) {// 將捕獲的更新列表連接到普通列表的末尾。if (finishedQueue.lastUpdate !== null) {finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;}// 清除捕獲的更新列表。finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;}// 提交效果commitUpdateEffects(finishedQueue.firstEffect, instance);finishedQueue.firstEffect = finishedQueue.lastEffect = null;commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
}
復制代碼

提交更新效果

/*** 提交更新效果* @param effect* @param instance*/
function commitUpdateEffects<State>(effect: Update<State> | null,instance: any,
): void {while (effect !== null) {const callback = effect.callback;if (callback !== null) {effect.callback = null;callCallback(callback, instance);}effect = effect.nextEffect;}
}
復制代碼

其他方法

ensureWorkInProgressQueueIsAClone

/*** 確保工作中的處理隊列是復制品*  1. 判斷當前隊列和更新隊列是不是相等*  2. 若相等則克隆,若不等則返回當前隊列* @param workInProgress* @param queue* @returns {UpdateQueue<State>}*/
function ensureWorkInProgressQueueIsAClone<State>(workInProgress: Fiber,queue: UpdateQueue<State>,
): UpdateQueue<State> {const current = workInProgress.alternate;if (current !== null) {// 如果正在工作的隊列等于當前隊列,我們需要首先克隆它。if (queue === current.updateQueue) {queue = workInProgress.updateQueue = cloneUpdateQueue(queue);}}return queue;
}
復制代碼

cloneUpdateQueue

/*** 克隆更新隊列* @param currentQueue* @returns {UpdateQueue<State>}*/
function cloneUpdateQueue<State>(currentQueue: UpdateQueue<State>,
): UpdateQueue<State> {const queue: UpdateQueue<State> = {baseState: currentQueue.baseState,firstUpdate: currentQueue.firstUpdate,lastUpdate: currentQueue.lastUpdate,// TODO: With resuming, if we bail out and resuse the child tree, we should// keep these effects.firstCapturedUpdate: null,lastCapturedUpdate: null,firstEffect: null,lastEffect: null,firstCapturedEffect: null,lastCapturedEffect: null,};return queue;
}
復制代碼

enqueueUpdate

/*** 排隊更新* @param fiber* @param update*/
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {// 更新隊列是惰性創建的。const alternate = fiber.alternate;let queue1;let queue2;if (alternate === null) {// 只有一個 fiberqueue1 = fiber.updateQueue;queue2 = null;if (queue1 === null) {queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);}} else {// 有兩個 owner。queue1 = fiber.updateQueue;queue2 = alternate.updateQueue;if (queue1 === null) {if (queue2 === null) {// Neither fiber has an update queue. Create new ones.// 這兩種 fiber 都沒有更新隊列。創造一個新隊列。queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState,);} else {// Only one fiber has an update queue. Clone to create a new one.// 只有一個 fiber 有更新隊列。克隆以創建一個新的。queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);}} else {if (queue2 === null) {// Only one fiber has an update queue. Clone to create a new one.// 只有一個 fiber 有更新隊列。克隆以創建一個新的。queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);} else {// Both owners have an update queue.// 兩個所有者都有一個更新隊列。}}}if (queue2 === null || queue1 === queue2) {// There's only a single queue.// 只有一個隊列。appendUpdateToQueue(queue1, update);} else {// There are two queues. We need to append the update to both queues,// while accounting for the persistent structure of the list — we don't// want the same update to be added multiple times.// 有兩個隊列。我們需要將更新附加到兩個隊列,// 同時考慮到列表的持久結構——我們不希望將相同的更新添加多次。if (queue1.lastUpdate === null || queue2.lastUpdate === null) {// One of the queues is not empty. We must add the update to both queues.// 其中一個隊列不是空的。我們必須將更新添加到兩個隊列。appendUpdateToQueue(queue1, update);appendUpdateToQueue(queue2, update);} else {// Both queues are non-empty. The last update is the same in both lists,// because of structural sharing. So, only append to one of the lists.// 兩個隊列都不是空的。由于結構共享,這兩個列表中的最新更新是相同的。// 因此,只向其中一個列表追加。appendUpdateToQueue(queue1, update);// But we still need to update the `lastUpdate` pointer of queue2.// 但是我們仍然需要更新 queue2 的 `lastUpdate` 指針。queue2.lastUpdate = update;}}if (__DEV__) {if (fiber.tag === ClassComponent &&(currentlyProcessingQueue === queue1 ||(queue2 !== null && currentlyProcessingQueue === queue2)) &&!didWarnUpdateInsideUpdate) {warningWithoutStack(false,'An update (setState, replaceState, or forceUpdate) was scheduled ' +'from inside an update function. Update functions should be pure, ' +'with zero side-effects. Consider using componentDidUpdate or a ' +'callback.',);didWarnUpdateInsideUpdate = true;}}
}
復制代碼

enqueueCapturedUpdate

/*** 排隊捕獲的更新* @param workInProgress* @param update*/
export function enqueueCapturedUpdate<State>(workInProgress: Fiber,update: Update<State>,
) {// 捕獲的更新進入一個單獨的列表,并且只在正在進行的隊列中。let workInProgressQueue = workInProgress.updateQueue;if (workInProgressQueue === null) {workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(workInProgress.memoizedState,);} else {// TODO:我把它放在這里,而不是 createWorkInProgress,這樣我們就不會不必要地克隆隊列。也許有更好的方法來構造它。。workInProgressQueue = ensureWorkInProgressQueueIsAClone(workInProgress,workInProgressQueue,);}// Append the update to the end of the list.// 將更新追加到列表的末尾。if (workInProgressQueue.lastCapturedUpdate === null) {// This is the first render phase update// 這是第一個渲染階段的更新workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;} else {workInProgressQueue.lastCapturedUpdate.next = update;workInProgressQueue.lastCapturedUpdate = update;}
}
復制代碼

callCallback

/*** 調用回調* 1. 回調不存在則拋出錯誤* 2. 回調存在則使用上下文執行回調** @param callback* @param context*/
function callCallback(callback, context) {invariant(typeof callback === 'function','Invalid argument passed as callback. Expected a function. Instead ' +'received: %s',callback,);callback.call(context);
}
復制代碼

遺留問題

  1. commitUpdateEffects 提交更新效果的時候是根據 Effect 效果的鏈表進行迭代的?這些 Update 的 nextEffect 是什么時候構成了鏈表結構?因為我沒上面看到的更新隊列只是一個 Update 使用 next 組成的一個鏈表結構
  2. commitUpdateQueue 提交的時候調用的也是 commitUpdateEffects,傳入的 finishedQueue.firstEffect 和 finishedQueue.firstCapturedEffect,createUpdate 是在何處被調用創建了更新的?這些 Effect 又是些什么東西呢?
  3. 提交更新的時候為什么不是使用 Update.next 而是 Update.nextEffect 呢
  4. enqueueUpdate、enqueueCapturedUpdate、processUpdateQueue、createUpdate 在什么時候被調用

轉載于:https://juejin.im/post/5d04251de51d4556be5b3a49

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279381.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279381.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279381.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

長時間曝光計算_如何拍攝好長時間曝光的照片

長時間曝光計算In long exposure photography, you take a picture with a slow shutter speed—generally somewhere between five and sixty seconds—so that any movement in the scene gets blurred. It’s a way to show the passage of time in a single image. Let’s …

思科設備snmp配置。

1、設置IOS設備在IOS的Enable狀態下&#xff0c;敲入 config terminal進入全局配置狀態 Cdp run啟用CDP snmp-server community gsunion ro \\配置本路由器的只讀字串為gsunion snmp-server community gsunion rw \\配置本路由器的讀寫字串為gsunion snmp-server enable trap…

Python——邏輯運算(or,and)

print(0 and 2 > 1) #結果0 print(0 and 2 < 1) #結果0 print(1 and 2 > 1) #結果True print(1 and 2 < 1) #結果False print(2 > 1 and 0) #結果0 print(2 < 1 and 0) #結果False print(2 > 1 and 1) #結果1 print(2 < 1 and 0) #結果False# and 前或…

深度學習入門3

CNN 第一周&#xff1a; title: edge detection example 卷積核在邊緣檢測中的應用&#xff0c;可解釋&#xff0c;卷積核的設計可以找到像素列突變的位置 把人為選擇的卷積核參數&#xff0c;改為學習參數&#xff0c;可以學到更多的特征 title: padding n * n圖片&#xff0c…

圖像大小調整_如何在Windows中調整圖像和照片的大小

圖像大小調整Most image viewing programs have a built-in feature to help you change the size of images. Here are our favorite image resizing tools for Windows. We’ve picked out a built-in option, a couple of third party apps, and even a browser-based tool.…

Spring Data JPA例子[基于Spring Boot、Mysql]

閱讀目錄 關于Spring Data關于Spring Data子項目關于Spring Data Jpa例子&#xff0c;Spring Boot Spring Data Jpa運行、測試程序程序源碼參考資料關于Spring Data Spring社區的一個頂級工程&#xff0c;主要用于簡化數據&#xff08;關系型&非關系型&#xff09;訪問&am…

The way of Webpack learning (IV.) -- Packaging CSS(打包css)

一&#xff1a;目錄結構 二&#xff1a;webpack.config.js的配置 const path require(path);module.exports {mode:development,entry:{app:./src/app.js},output:{path:path.resolve(__dirname,dist),publicPath:./dist/,//設置引入路徑在相對路徑filename:[name].bundle.js…

文本文檔TXT每行開頭結尾加內容批處理代碼

文本文檔TXT每行開頭結尾加內容批處理代碼 讀A.TXT ,每行開頭加&#xff1a;HTMLBodytxt HTMLBodytxt chr(10) aaaaaaaa結尾加&#xff1a;bbbbbbbb處理后的文檔寫入到B.TXT For /f "delims" %%i in (a.txt) do echo HTMLBodytxt HTMLBodytxt chr(10) aaaaaaaa%%…

windows運行對話框_如何在Windows運行對話框中添加文本快捷方式?

windows運行對話框Windows comes prepackaged with a ton of handy run-dialog shortcuts to help you launch apps and tools right from the run box; is it possible to add in your own custom shortcuts? Windows預包裝了許多方便的運行對話框快捷方式&#xff0c;可幫助…

前后臺分離--概念相關

js 包管理器:  1、npm  2、bower 包管理器的作用&#xff1a;&#xff08;之前滿世界找代碼&#xff0c;現在統一地址了。類似于360軟件管家&#xff0c;maven倉庫。&#xff09;  1、復用別人已經寫好的代碼。  2、管理包之間的依賴關系。 JS &#xff1a;語言&#…

Zabbix 3.0 安裝

Zabbix 3.0 For CentOS6安裝 1 概述2 安裝MySQL3 安裝WEB4 安裝Zabbix-Server5配置WEB1概述 對于3.0&#xff0c;官方只提供CentOS7的RPM包&#xff0c;Ubuntu的DEB包&#xff0c;對于CentOS6&#xff0c;默認不提供RPM包&#xff0c;為了照顧到使用CentOS6的兄弟們&#xff0c…

[Hadoop in China 2011] 中興:NoSQL應用現狀及電信業務實踐

http://tech.it168.com/a2011/1203/1283/000001283154.shtml 在今天下午進行的NoSQL系統及應用分論壇中&#xff0c;中興云計算平臺研發總工、中興通訊技術專家委員會專家高洪發表主題演講“NoSQL技術的電信業務實踐”&#xff0c;介紹了NoSQL的發展現狀及其在電信業務中的應用…

qmediaplayer獲取流類型_Java 流API

流相關的接口和類在java.util.stream包中。AutoCloseable接口來自java.lang包。所有流接口從繼承自AutoCloseable接口的BaseStream接口繼承。AutoCloseable|--BaseStream|--IntStream|--LongStream|--DoubleStream|--Stream如果流使用集合作為其數據源&#xff0c;并且集合不需…

田剛:龐加萊猜想與幾何

&#xff08;作者 田剛&#xff09; 時間&#xff1a;2015年11月1日 地點&#xff1a;北京大學北京國際數學研究中心 主題&#xff1a;未來論壇“理解未來”講座北大專場&#xff1a;龐加萊猜想與幾何 田剛&#xff1a; 非常高興能夠有這個機會來參加未來論壇講演。我今天要講的…

進化:從孤膽極客到高效團隊_極客學校:學習Windows 7 –遠程管理

進化:從孤膽極客到高效團隊In this installation of Geek School, we look at how we can administer our machines remotely using Remote Assistance, Remote Desktop, Windows Remote Management also known as WinRM, and PowerShell. 在此Geek School安裝中&#xff0c;我…

新秀翻譯(兩)——使用Java通用配置模板方法模式

假設你發現你已經非常重碼&#xff0c;你可能會考慮使用模板的方法來消除easy重復錯誤代碼。下面是一個示例:以下兩類,他完成了幾乎相同的功能&#xff1a; 實例化并初始化一個Reader來讀取CSV文件。讀取每一行并解析&#xff1b;把每一行的字符填充到Product或Customer對象&am…

框架基礎:深入理解Java注解類型(@Annotation)

注解的概念 注解的官方定義 首先看看官方對注解的描述&#xff1a; An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the opera…

打印墨水調鋼筆墨水_如何節省墨水并改善網站打印質量

打印墨水調鋼筆墨水Printing out web pages you want a hard copy of can be a little hit and miss. Unlike other documents, it is not easy to tell exactly how many pieces of paper will be needed, and whether or not there will be any awkward clipping. Add to thi…

highcharts 怎么去掉鼠標懸停效果_練瑜伽減肥沒效果什么原因?

沒有心的參與&#xff0c;瑜伽就不是瑜伽曾經有很多人問&#xff1a;自己想用瑜伽來減肥&#xff0c;但練習瑜伽這么久&#xff0c;為什么還是減不下來&#xff1f;一點效果都沒有。瑜伽是什么&#xff1f;瑜伽只是一種單純的運動嗎&#xff1f;只讓身體參與進去就可以了嗎&…

百度地圖1

百度地圖BMap的類 BMap的屬性是一些構造函數,主大類有&#xff1a;核心類、基礎類、控件類、覆蓋物類、右鍵菜單類、地圖類型類、地圖吐槽類、服務類、全局類 核心類Map Map&#xff1a;最主要的一個類&#xff0c;集成了其他模塊的方法&#xff0c;是一個集成了整個地圖功能的…