欧美成人午夜精品久久久,国产?V天堂一区二区三区,欧美精品va在线观看,亚洲一区二区三区免费在线观看,av无码精品一区二区久久,欧美性爱视频不卡一区三区,欧美乱人伦视频在线观看,国产一级牲交高潮

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營的一線實(shí)戰(zhàn)洞察。

Undo Manager 實(shí)現(xiàn)原理:操作建模、雙棧調(diào)度與狀態(tài)快照

Undo Manager 實(shí)現(xiàn)原理:操作建模、雙棧調(diào)度與狀態(tài)快照 簡介本資源是一套面向C桌面應(yīng)用開發(fā)者的撤銷/重做Undo/Redo功能完整實(shí)現(xiàn)方案適用于文本編輯器、富文本處理工具等需狀態(tài)回溯能力的中高級項(xiàng)目。壓縮包共61個(gè)文件含28個(gè).cpp源文件如UndoManager.cpp、RETypingAction.cpp、REDropAction.cpp等承載核心操作封裝與狀態(tài)管理邏輯、31個(gè).h頭文件定義Action基類、視圖接口、文本范圍與樣式結(jié)構(gòu)等另含1個(gè)說明文檔和1個(gè)資源腳本整體僅70KB輕量但結(jié)構(gòu)嚴(yán)謹(jǐn)。目前已有155人學(xué)習(xí)下載適合希望深入理解命令模式、棧式歷史管理、細(xì)粒度操作建模打字、替換、拖放、插入對象等的開發(fā)者。代碼采用模塊化設(shè)計(jì)支持RichEdit與BetterEdit雙視圖體系涵蓋多步菜單交互MultipleUndoRedoMenu.cpp與變更日志記錄Change Log.txt可直接集成或作為教學(xué)范例剖析撤銷系統(tǒng)架構(gòu)與事件協(xié)同機(jī)制。1. Undo Manager 是什么一個(gè)被低估的“操作后悔藥”不是 UI 組件而是狀態(tài)變更的底層契約你寫了個(gè)表單編輯器用戶改了 5 行數(shù)據(jù)、刪了 2 個(gè)字段、又拖拽重排了順序——然后猛按 CtrlZ結(jié)果只撤回了最后一次拖拽前面的修改全丟了或者更糟撤回后界面沒刷新但內(nèi)部狀態(tài)已回滾下一次提交直接把臟數(shù)據(jù)發(fā)了出去。這不是前端框架的鍋是 Undo Manager 沒立住契約。Undo Manager 不是某個(gè) React 庫里的 Hook也不是 Vue 的插件它是一套對狀態(tài)變更行為建模、封裝、可追溯、可原子回滾的通用機(jī)制。它的核心價(jià)值不在“能撤銷”而在“知道誰在什么時(shí)候以什么方式改了什么并且能精準(zhǔn)還原到那個(gè)瞬間”。它適用于任何需要操作歷史、多級回退、協(xié)同編輯、調(diào)試回放或狀態(tài)快照的場景——從代碼編輯器、圖形設(shè)計(jì)工具到工業(yè) HMI 系統(tǒng)、低代碼平臺的邏輯編排畫布。如果你正在手寫history.push()或靠useState 數(shù)組堆棧硬扛撤銷邏輯那說明你還沒真正接入 Undo Manager 的抽象層它不關(guān)心 UI 怎么渲染只負(fù)責(zé)把“變更”變成可序列化、可比較、可執(zhí)行的 Operation 對象。下面我們就從零搭起這個(gè)“后悔藥”的骨架不依賴任何框架封裝直擊底層實(shí)現(xiàn)邏輯。2. 從零實(shí)現(xiàn)一個(gè)最小可用 Undo Manager用 Operation 建模變更用 Stack 管理歷史Undo Manager 的本質(zhì)是把“用戶的一次操作”翻譯成一段可逆的、帶元信息的狀態(tài)變更指令。它不能只存舊值和新值太粗暴也不能只存函數(shù)引用不可序列化、難調(diào)試。我們采用經(jīng)典的Operation 模式每個(gè)操作是一個(gè)對象包含execute()正向執(zhí)行、undo()反向回滾、redo()重做三個(gè)方法以及timestamp、label、source等元數(shù)據(jù)。所有 Operation 實(shí)例由一個(gè)雙棧undoStack redoStack統(tǒng)一調(diào)度。2.1 定義 Operation 接口與基礎(chǔ)實(shí)現(xiàn)類我們先定義 Operation 的 TypeScript 接口再給出一個(gè)通用基類BaseOperation它提供默認(rèn)的redo()實(shí)現(xiàn)即再次調(diào)用execute()并強(qiáng)制子類實(shí)現(xiàn)execute()和undo()// types.ts export interface Operation { execute(): void; undo(): void; redo(): void; timestamp: number; label: string; source?: string; // 標(biāo)識操作來源如 keyboard | mouse | api } // base-operation.ts export abstract class BaseOperation implements Operation { timestamp Date.now(); abstract execute(): void; abstract undo(): void; redo(): void { this.execute(); // 默認(rèn)重做 再執(zhí)行一次 } }提示redo()默認(rèn)復(fù)用execute()是常見做法但某些場景如異步操作需重載。此處保持簡潔后續(xù)會說明如何擴(kuò)展。2.2 構(gòu)建 UndoManager 核心類雙棧調(diào)度與邊界控制UndoManager類不持有任何業(yè)務(wù)狀態(tài)只管理 Operation 實(shí)例的生命周期。它維護(hù)兩個(gè)數(shù)組棧undoStack待撤銷的操作和redoStack已撤銷、待重做的操作。關(guān)鍵約束有三最大歷史深度限制避免內(nèi)存爆炸合并連續(xù)操作如連打 10 個(gè)字符應(yīng)合并為一個(gè)“輸入文本”操作而非 10 個(gè)禁止空操作入棧execute()后若狀態(tài)未變不記錄。// undo-manager.ts export class UndoManager { private undoStack: Operation[] []; private redoStack: Operation[] []; private maxHistory 200; constructor(options: { maxHistory?: number } {}) { this.maxHistory options.maxHistory ?? 200; } // 執(zhí)行操作先運(yùn)行再入 undoStack清空 redoStack execute(op: Operation): void { op.execute(); this.undoStack.push(op); this.redoStack []; // 一旦新操作發(fā)生重做歷史失效 this.trimStacks(); } // 撤銷彈出 undoStack 頂部執(zhí)行 undo()推入 redoStack undo(): void { if (this.undoStack.length 0) return; const op this.undoStack.pop()!; op.undo(); this.redoStack.push(op); } // 重做彈出 redoStack 頂部執(zhí)行 redo()推入 undoStack redo(): void { if (this.redoStack.length 0) return; const op this.redoStack.pop()!; op.redo(); this.undoStack.push(op); } // 合并最近操作用于連續(xù)輸入等場景 mergeLast(operation: Operation): void { if (this.undoStack.length 0) return; const last this.undoStack[this.undoStack.length - 1]; // 此處需業(yè)務(wù)判斷是否可合并例如同 label、同 source、時(shí)間間隔 300ms if ( last.label operation.label last.source operation.source Date.now() - last.timestamp 300 ) { // 調(diào)用業(yè)務(wù)自定義的 merge 方法需 Operation 子類實(shí)現(xiàn) if (typeof (last as any).merge function) { (last as any).merge(operation); } } } // 清理?xiàng)13珠L度不超過 maxHistory private trimStacks(): void { if (this.undoStack.length this.maxHistory) { this.undoStack this.undoStack.slice(-this.maxHistory); } if (this.redoStack.length this.maxHistory) { this.redoStack this.redoStack.slice(-this.maxHistory); } } // 獲取當(dāng)前可撤銷/重做數(shù)量供 UI 顯示按鈕狀態(tài) get canUndo(): boolean { return this.undoStack.length 0; } get canRedo(): boolean { return this.redoStack.length 0; } get historySize(): number { return this.undoStack.length; } }這段代碼實(shí)現(xiàn)了 Undo Manager 的骨架。注意mergeLast()并未強(qiáng)制要求所有 Operation 都實(shí)現(xiàn)merge()而是用typeof安全檢查——這是為了保持基類輕量把合并邏輯下沉到具體業(yè)務(wù) Operation 中。trimStacks()在每次execute()后調(diào)用確保內(nèi)存可控canUndo/canRedo是典型的 UI 友好屬性方便綁定按鈕禁用狀態(tài)。2.3 實(shí)戰(zhàn)為表單字段創(chuàng)建 TextChangeOperation現(xiàn)在我們寫一個(gè)真實(shí)可用的 Operation 子類TextChangeOperation用于處理輸入框內(nèi)容變更。它需記錄字段名、舊值、新值并支持合并連續(xù)輸入時(shí)將多次setValue(a) → setValue(ab) → setValue(abc)合并為一次setValue(abc)。// operations/text-change-operation.ts import { BaseOperation } from ../base-operation; export interface TextChangePayload { field: string; oldValue: string; newValue: string; } export class TextChangeOperation extends BaseOperation { constructor( public payload: TextChangePayload, public label 修改文本, public source keyboard ) { super(); } execute(): void { // 注意execute 不直接改 DOM而是調(diào)用業(yè)務(wù) setter // 這里假設(shè)有一個(gè)全局 store 或 context 提供 updateField 方法 updateField(this.payload.field, this.payload.newValue); } undo(): void { updateField(this.payload.field, this.payload.oldValue); } // 合并邏輯僅當(dāng)同字段、且新值以舊值開頭時(shí)擴(kuò)展 newValue merge(other: TextChangeOperation): void { if (this.payload.field ! other.payload.field) return; if (other.payload.newValue.startsWith(this.payload.newValue)) { this.payload.newValue other.payload.newValue; this.timestamp other.timestamp; // 更新時(shí)間戳為最新 } } } // 模擬業(yè)務(wù) setter實(shí)際項(xiàng)目中應(yīng)注入 function updateField(field: string, value: string): void { // 例如document.getElementById(field)?.value value; // 或 dispatch({ type: UPDATE_FIELD, payload: { field, value } }); }邏輯說明TextChangeOperation的execute()和undo()都調(diào)用同一個(gè)updateField()但傳入不同值。這保證了正向/反向行為完全對稱。merge()方法判斷other.newValue是否以當(dāng)前newValue開頭——這是連續(xù)輸入的典型特征a → ab → abc滿足則更新newValue和timestamp實(shí)現(xiàn)視覺上的“一次輸入”效果。參數(shù)說明field是字段唯一標(biāo)識如user_nameoldValue/newValue是字符串快照label和source用于 UI 展示和審計(jì)。3. 與 React 深度集成用自定義 Hook 封裝狀態(tài)聯(lián)動避免 useEffect 嵌套陷阱Undo Manager 本身是框架無關(guān)的但落地時(shí)必須與 UI 框架的狀態(tài)更新機(jī)制對齊。在 React 中常見錯(cuò)誤是在useEffect里監(jiān)聽輸入事件并execute()但execute()觸發(fā)的updateField()又導(dǎo)致組件重渲染進(jìn)而再次觸發(fā)useEffect形成無限循環(huán)。正確解法是分離“操作捕獲”與“狀態(tài)更新”用 ref 緩存當(dāng)前值用useCallback固化操作創(chuàng)建邏輯讓 UndoManager 成為純命令總線。3.1 創(chuàng)建 useUndoableState Hook接管 useState 并注入 Undo 能力我們不替換useState而是在其之上疊加一層“可撤銷狀態(tài)”。該 Hook 返回[value, setValue, undo, redo, canUndo, canRedo]其中setValue是增強(qiáng)版自動記錄 Operation。// hooks/use-undoable-state.ts import { useState, useRef, useCallback } from react; import { UndoManager } from ../undo-manager; import { TextChangeOperation } from ../operations/text-change-operation; export function useUndoableStateT( initialValue: T, options: { manager?: UndoManager; maxHistory?: number } {} ) { const [value, setValue] useStateT(initialValue); const currentValueRef useRefT(initialValue); const manager options.manager || new UndoManager({ maxHistory: options.maxHistory }); // 增強(qiáng)版 setValue生成 Operation 并執(zhí)行 const undoableSetValue useCallback( (newValue: T | ((prev: T) T), source: string user) { const resolvedValue typeof newValue function ? (newValue as (prev: T) T)(currentValueRef.current) : newValue; // 僅當(dāng)值真正改變時(shí)才記錄操作防抖 避免空操作 if (Object.is(resolvedValue, currentValueRef.current)) return; const payload { field: root, // 單值場景用 root 標(biāo)識 oldValue: currentValueRef.current as unknown as string, newValue: resolvedValue as unknown as string, }; const op new TextChangeOperation(payload, 設(shè)置值, source); manager.execute(op); // 同步更新本地 ref 和 state currentValueRef.current resolvedValue; setValue(resolvedValue); }, [manager] ); // 撤銷/重做方法直接代理給 manager const undo useCallback(() manager.undo(), [manager]); const redo useCallback(() manager.redo(), [manager]); return [ value, undoableSetValue, undo, redo, manager.canUndo, manager.canRedo, ] as const; }參數(shù)說明initialValue是初始狀態(tài)options.manager允許復(fù)用外部 UndoManager 實(shí)例如跨多個(gè)表單項(xiàng)共享歷史source參數(shù)用于區(qū)分操作來源鍵盤輸入、API 回填、程序初始化等便于后續(xù)審計(jì)或條件過濾。關(guān)鍵點(diǎn)在于currentValueRef它始終保存最新值undoableSetValue比較的是 ref 值而非 state 值避免因setState異步性導(dǎo)致的誤判。Object.is()用于嚴(yán)格相等判斷支持 NaN、Object 等。3.2 在組件中使用一個(gè)可撤銷的搜索框現(xiàn)在我們寫一個(gè)真實(shí)組件展示如何用useUndoableState構(gòu)建帶撤銷能力的輸入框// components/SearchInput.tsx import React, { KeyboardEvent } from react; import { useUndoableState } from ../hooks/use-undoable-state; export function SearchInput() { const [query, setQuery, undo, redo, canUndo, canRedo] useUndoableStatestring(); const handleKeyDown (e: KeyboardEventHTMLInputElement) { if (e.key Enter) { // 模擬搜索提交 console.log(Search:, query); // 提交后可選擇清空 redoStack防止用戶誤重做搜索 // manager.redoStack []; } }; return ( div classNamesearch-container input typetext value{query} onChange{(e) setQuery(e.target.value, keyboard)} onKeyDown{handleKeyDown} placeholder輸入搜索關(guān)鍵詞... / div classNamecontrols button onClick{undo} disabled{!canUndo} 撤銷 (CtrlZ) /button button onClick{redo} disabled{!canRedo} 重做 (CtrlY) /button span classNamehistory-count 歷史: {canUndo ? ● : ○} {canRedo ? ● : ○} /span /div /div ); }邏輯說明onChange事件中調(diào)用setQuery(value, keyboard)自動觸發(fā)TextChangeOperation創(chuàng)建與執(zhí)行onKeyDown捕獲 Enter 提交此時(shí)query是最新值可安全用于 API 調(diào)用。按鈕disabled屬性直連canUndo/canRedoUI 狀態(tài)與 Manager 內(nèi)部棧完全同步。這里沒有useEffect沒有競態(tài)所有狀態(tài)流轉(zhuǎn)都由setQuery這一個(gè)入口驅(qū)動。3.3 處理復(fù)雜狀態(tài)用 ObjectChangeOperation 管理嵌套對象單值夠用但表單常是對象結(jié)構(gòu)如{ name: , email: }。我們擴(kuò)展一個(gè)ObjectChangeOperation它記錄整個(gè)對象的深拷貝快照并支持按路徑更新// operations/object-change-operation.ts import { BaseOperation } from ../base-operation; import { deepClone, setByPath, getByPath } from ../utils; export interface ObjectChangePayload { path: string; // 如 user.profile.name oldValue: unknown; newValue: unknown; snapshotBefore: Recordstring, unknown; snapshotAfter: Recordstring, unknown; } export class ObjectChangeOperation extends BaseOperation { constructor( public payload: ObjectChangePayload, public label 修改對象字段, public source user ) { super(); } execute(): void { // 更新全局狀態(tài)樹假設(shè)存在 updateStateTree 函數(shù) updateStateTree(this.payload.path, this.payload.newValue); } undo(): void { // 恢復(fù)整個(gè)快照更安全避免路徑更新遺漏 restoreStateTree(this.payload.snapshotBefore); } merge(other: ObjectChangeOperation): void { // 同路徑、時(shí)間相近則更新 newValue 和快照 if ( this.payload.path other.payload.path Date.now() - this.timestamp 300 ) { this.payload.newValue other.payload.newValue; this.payload.snapshotAfter other.payload.snapshotAfter; this.timestamp other.timestamp; } } } // 輔助函數(shù)簡化版 function updateStateTree(path: string, value: unknown): void { // 實(shí)際項(xiàng)目中對接 Zustand / Redux / Jotai 等 } function restoreStateTree(snapshot: Recordstring, unknown): void { // 用 snapshot 替換整個(gè)狀態(tài)樹 }使用提示ObjectChangeOperation通過snapshotBefore/After記錄完整狀態(tài)比單字段更新更魯棒尤其當(dāng)字段間有聯(lián)動計(jì)算時(shí)。path支持點(diǎn)號分隔user.address.citysetByPath和getByPath是標(biāo)準(zhǔn)工具函數(shù)。合并邏輯同樣基于路徑和時(shí)間窗口確保同一字段的連續(xù)修改被聚合。4. 避坑指南Undo Manager 實(shí)戰(zhàn)中 4 個(gè)高頻翻車點(diǎn)與血淚修復(fù)方案Undo Manager 看似簡單但落地時(shí)極易因狀態(tài)耦合、時(shí)機(jī)錯(cuò)位、邊界遺漏而集體翻車。以下是某跨平臺低代碼平臺在灰度階段踩過的 4 個(gè)真實(shí)坑每條都附帶可立即復(fù)用的修復(fù)代碼。4.1 現(xiàn)象撤銷后 UI 未更新但內(nèi)部狀態(tài)已回滾 → “界面黑匣子”原因undo()執(zhí)行了op.undo()但該方法只修改了業(yè)務(wù)狀態(tài)如 Redux store未觸發(fā) React 組件重新渲染。常見于op.undo()直接修改了ref.current或全局變量卻忘了通知 UI。解決所有undo()和execute()方法內(nèi)必須顯式觸發(fā)狀態(tài)更新。不要依賴“狀態(tài)變了 UI 自動更新”的幻覺。修復(fù)方案是在undo()中調(diào)用setValue()或setState就像execute()一樣// ? 錯(cuò)誤只改 ref不觸發(fā)渲染 undo(): void { this.payload.fieldRef.current this.payload.oldValue; // UI 不知道 } // ? 正確調(diào)用狀態(tài) setter保證 UI 同步 undo(): void { // 假設(shè)我們有注入的 setter this.setter(this.payload.field, this.payload.oldValue); }血淚經(jīng)驗(yàn)在BaseOperation基類中預(yù)留setter屬性構(gòu)造時(shí)傳入強(qiáng)制子類使用統(tǒng)一更新通道。避免任何 Operation 直接操作 DOM 或 ref。4.2 現(xiàn)象連續(xù)快速輸入如粘貼長文本導(dǎo)致撤銷粒度太細(xì)一次 CtrlZ 只刪一個(gè)字符原因TextChangeOperation的合并閾值300ms在粘貼場景下失效——粘貼是單次事件但內(nèi)部觸發(fā)了多次input事件每次input都生成獨(dú)立 Operation且時(shí)間戳相同無法合并。解決增加“批量操作標(biāo)記”。在粘貼開始前用manager.beginBatch(paste)標(biāo)記批次結(jié)束時(shí)manager.endBatch()期間所有 Operation 自動合并為一個(gè)// undo-manager.ts 新增方法 beginBatch(label: string): void { this.batchLabel label; this.batchStartTimestamp Date.now(); } endBatch(): void { if (!this.batchLabel) return; // 將 batch 期間所有操作合并為一個(gè) CompositeOperation const batchOps this.undoStack.splice(-this.batchOpCount); if (batchOps.length 0) { const composite new CompositeOperation(batchOps, this.batchLabel); this.undoStack.push(composite); } this.batchLabel undefined; this.batchOpCount 0; }然后在輸入框onPaste事件中調(diào)用input onPaste{() { manager.beginBatch(paste); setTimeout(() manager.endBatch(), 0); // 微任務(wù)等待 input 事件完成 }} /提示setTimeout(..., 0)確保endBatch()在所有input事件處理完畢后執(zhí)行捕獲全部操作。4.3 現(xiàn)象異步操作如 API 保存后撤銷狀態(tài)回滾但服務(wù)器數(shù)據(jù)未回退 → “數(shù)據(jù)不一致”原因execute()中調(diào)用了saveToServer()但undo()只回滾本地狀態(tài)未調(diào)用revertOnServer()。Undo Manager 默認(rèn)不處理副作用需業(yè)務(wù)顯式聲明。解決擴(kuò)展 Operation 接口增加asyncUndo()方法并在UndoManager.undo()中優(yōu)先調(diào)用它export interface AsyncOperation extends Operation { asyncUndo?(): Promisevoid; // 可選僅異步操作需實(shí)現(xiàn) } // 在 UndoManager.undo() 中 async undo(): Promisevoid { if (this.undoStack.length 0) return; const op this.undoStack.pop()!; if (asyncUndo in op typeof op.asyncUndo function) { await op.asyncUndo(); // 等待服務(wù)端回滾 } op.undo(); // 再回滾本地 this.redoStack.push(op); }對應(yīng)地創(chuàng)建ApiSaveOperationclass ApiSaveOperation extends BaseOperation { constructor(public apiCall: () Promisevoid) { super(); } asyncUndo(): Promisevoid { // 調(diào)用反向 API如 DELETE /drafts/{id} await fetch(/api/drafts/${this.id}, { method: DELETE }); } execute(): void { this.apiCall(); // 保存 } undo(): void { // 本地回滾如恢復(fù)草稿緩存 } }4.4 現(xiàn)象頁面刷新后 Undo 歷史丟失用戶抱怨“剛做的修改全沒了”原因UndoManager 實(shí)例是內(nèi)存對象刷新即銷毀。需持久化歷史棧到localStorage或 IndexedDB。解決在UndoManager構(gòu)造時(shí)加載歷史并在每次execute()后自動保存。為防序列化失敗只存 Operation 的可序列化部分label,payload,timestamp重建時(shí)動態(tài)實(shí)例化constructor(options: { maxHistory?: number; storageKey?: string; // 如 my-app-undo-history } {}) { super(options); this.storageKey options.storageKey; if (this.storageKey) { this.loadFromStorage(); } } private loadFromStorage(): void { try { const saved localStorage.getItem(this.storageKey); if (saved) { const data JSON.parse(saved); // 重建 Operation 實(shí)例需注冊 Operation 類型映射 this.undoStack data.undo.map((item: any) this.deserializeOperation(item) ); this.redoStack data.redo.map((item: any) this.deserializeOperation(item) ); } } catch (e) { console.warn(Failed to load undo history, e); } } private saveToStorage(): void { if (!this.storageKey) return; try { const data { undo: this.undoStack.map(op this.serializeOperation(op)), redo: this.redoStack.map(op this.serializeOperation(op)), }; localStorage.setItem(this.storageKey, JSON.stringify(data)); } catch (e) { console.warn(Failed to save undo history, e); } } // 在 execute() 末尾調(diào)用 this.saveToStorage();注意serializeOperation需根據(jù) Operation 類型返回純對象deserializeOperation根據(jù)type字段 new 對應(yīng)類。這是典型的“序列化-反序列化”模式務(wù)必測試邊緣 case如函數(shù)、undefined、Date。5. 進(jìn)階技巧用 Operation 快照做狀態(tài)調(diào)試回放把 Undo Manager 變成黑盒分析儀Undo Manager 的最大隱藏價(jià)值不是讓用戶按 CtrlZ而是讓你在生產(chǎn)環(huán)境回放任意一次用戶操作流精準(zhǔn)定位 UI Bug。我們把每個(gè) Operation 的execute()和undo()執(zhí)行前后自動采集狀態(tài)快照snapshot構(gòu)建一條可追溯的“狀態(tài)時(shí)間線”。這不需要額外埋點(diǎn)只需在UndoManager.execute()中插入快照邏輯。5.1 在 Operation 執(zhí)行前后自動采集狀態(tài)快照我們擴(kuò)展UndoManager增加captureSnapshot()方法它接受一個(gè)stateGetter函數(shù)返回當(dāng)前業(yè)務(wù)狀態(tài)并在execute()前后各調(diào)用一次// undo-manager.ts export class UndoManager { // ... 其他屬性 private snapshots: Array{ timestamp: number; before: unknown; after: unknown; operationLabel: string; } []; constructor(options: { maxHistory?: number; stateGetter?: () unknown; // 業(yè)務(wù)狀態(tài)獲取函數(shù) } {}) { this.stateGetter options.stateGetter; } execute(op: Operation): void { // 執(zhí)行前快照 const before this.stateGetter?.() ?? null; op.execute(); // 執(zhí)行后快照 const after this.stateGetter?.() ?? null; this.snapshots.push({ timestamp: Date.now(), before, after, operationLabel: op.label, }); // 入棧... this.undoStack.push(op); this.redoStack []; this.trimStacks(); } // 獲取指定索引的操作及快照用于調(diào)試面板 getSnapshotAt(index: number): { op: Operation; snapshot: NonNullabletypeof this.snapshots[number] } | undefined { if (index 0 || index this.snapshots.length) return; return { op: this.undoStack[index], snapshot: this.snapshots[index], }; } }關(guān)鍵點(diǎn)stateGetter是業(yè)務(wù)方傳入的函數(shù)例如() store.getState()或() ({ form: formData, errors: validationErrors })。它必須是純函數(shù)無副作用??煺沾鎯檩p量對象before/after是執(zhí)行前后的狀態(tài)副本可用于 diff 分析。5.2 構(gòu)建簡易調(diào)試面板可視化狀態(tài)變化鏈我們寫一個(gè) React 組件列出所有歷史操作并允許點(diǎn)擊任一操作將應(yīng)用狀態(tài)“跳轉(zhuǎn)”到該時(shí)刻類似 Chrome DevTools 的 replay// components/DebugReplayPanel.tsx import React, { useState, useEffect } from react; import { UndoManager } from ../undo-manager; interface SnapshotItem { timestamp: number; before: unknown; after: unknown; operationLabel: string; } export function DebugReplayPanel({ manager }: { manager: UndoManager }) { const [snapshots, setSnapshots] useStateSnapshotItem[]([]); const [activeIndex, setActiveIndex] useStatenumber | null(null); // 訂閱快照更新假設(shè) manager 提供 onSnapshot 事件 useEffect(() { const unsubscribe manager.onSnapshot((snapshot) { setSnapshots(prev [...prev, snapshot]); }); return unsubscribe; }, [manager]); const jumpTo (index: number) { if (index 0 || index snapshots.length) return; const target snapshots[index]; // 這里調(diào)用業(yè)務(wù) restoreState(target.before) 方法 restoreState(target.before); setActiveIndex(index); }; return ( div classNamedebug-panel h3狀態(tài)回放面板/h3 div classNametimeline {snapshots.map((snap, i) ( div key{i} className{step ${activeIndex i ? active : }} onClick{() jumpTo(i)} span classNamelabel{snap.operationLabel}/span span classNametime{new Date(snap.timestamp).toLocaleTimeString()}/span div classNamediff {/* 可接入 react-diff-view 顯示 before/after 差異 */} {JSON.stringify(snap.before).length 50 ? ${JSON.stringify(snap.before).slice(0, 50)}... : JSON.stringify(snap.before)} → {JSON.stringify(snap.after).length 50 ? ${JSON.stringify(snap.after).slice(0, 50)}... : JSON.stringify(snap.after)} /div /div ))} /div /div ); } function restoreState(state: unknown): void { // 實(shí)際項(xiàng)目中調(diào)用 store.replaceState(state) 或類似方法 }效果當(dāng)用戶報(bào)告“我在第 3 步點(diǎn)擊按鈕后頁面空白”你打開調(diào)試面板找到第 3 步點(diǎn)擊jumpTo(3)UI 瞬間回到那個(gè)時(shí)刻你就能看到空白頁面并 inspect 元素、查 console。這比看日志快 10 倍。5.3 生產(chǎn)環(huán)境安全開關(guān)快照功能默認(rèn)關(guān)閉按需啟用快照采集有性能開銷深拷貝、內(nèi)存占用絕不能在生產(chǎn)環(huán)境默認(rèn)開啟。我們在UndoManager中添加enableSnapshot()方法并用環(huán)境變量控制// undo-manager.ts class UndoManager { private isSnapshotEnabled false; enableSnapshot(stateGetter: () unknown): void { // 僅在開發(fā)環(huán)境或特定 feature flag 下啟用 if (process.env.NODE_ENV development || window.DEBUG_UNDO) { this.stateGetter stateGetter; this.isSnapshotEnabled true; } } execute(op: Operation): void { if (this.isSnapshotEnabled this.stateGetter) { // 執(zhí)行快照邏輯 } // ... 其余邏輯 } }然后在應(yīng)用入口// main.tsx const manager new UndoManager(); if (process.env.NODE_ENV development) { manager.enableSnapshot(() store.getState()); }最后一句我在線上系統(tǒng)跑了兩年 Undo Manager最大的教訓(xùn)是——永遠(yuǎn)不要相信“操作很簡單不用管歷史”的想法。哪怕只是一個(gè)開關(guān)按鈕的 toggle一旦用戶需要“剛才我關(guān)掉的那個(gè)現(xiàn)在想打開回來”你就得有一套完整的、可驗(yàn)證的、帶快照的 Undo 機(jī)制。它不是錦上添花而是交互健壯性的底線。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
丁香伊人激情| 丁香五月第四色88| 狠狠干综合| 超碰国产AV| 精品五月天| 婷婷丁香五| 欧美日本高清视频99| 五月婷婷六月丁香| 9九色首页| 久久久er热| 毛片蕉地一二| 婷婷伊人久久无码色五月| 五月丁香色六月激情干大屄| 丁香五月天殴美激情| 丁香久月| 五月丁香婷婷综合网色欲| 九九人人精品| 玖玖热视频| 九九视频免费| 五月激情婷婷综合| 六月丁香停| 久久免费精彩视频| 五月丁香婷婷色色色| 99在线看片| 超碰97人人操| 超碰人人操| 五月丁香婷婷综合网| 精品成人a v无码内射| 99热色无码| 天天综合色99| 婷婷五月美女直播| 五月婷婷深深的爱| 99久久婷婷国产综合精品草原| 色啪久 | 久久99精品久| 亚洲中文字幕在线电影| 免费超碰在线观看| 久久婷婷六月综合综合| 亚洲热视频在线| 99在线观看精品视频| 婷婷六月综合在线| 综合激情网五月激情| 无码九九九九| 亚洲婷婷五月| 国产综合视频婷婷| 色综合婷婷| 婷婷9月天| 色狠狠色噜噜AV天堂五区| 亚洲成人免费在线| 丁香五月天堂网| 99热精品在线| 99九九在线精品热动漫| 在线成人网址| 97久久精品| 丁香六月婷婷综合| 91丨九色丨国产打屁股| 狠狠色丁香久久久婷| 一起草Av| 激情综合5月| 色综合网上班开心婷婷久久| 五月综合激情久久| 亚洲mm色| 免费观看欧美成人AA片爱我多深 | 9九热视频| enecarbon-materials.comWu染请涟系Bao护@wip1688 | 久久大香蕉同僚| 亚洲综合色色色| 26uuuuuuuu国产| 五月天婷婷导航| 丁香激情合作五月| 色婷婷小说| 91久久婷婷人人澡草| 亚洲五月天色| 婷婷丁香基地在线| 天天舔天天操| 91综合在线| 欧美精品999| 久久资源网五月婷| 成人综合网站| 九九热内射| 国产精品国产| 97福利视频| 天天日天天爽夜夜爽| 精品久久99| 六月激情婷婷| 日本不卡高字幕在线2019| 青青久在线视频免费观看| 激情丁香五月婷婷| 免费啪啪啪网站 | 这里只有精品视频看看| 久久人妻人人| 狼人久草| 五月天操逼激情| 色婷小说| 激情AV| 激情丁香淫荡婷婷| 婷婷五月天小说| 久久婷婷网| 99爱在线视频| 丁香婷婷六月| 91碰碰碰| 婷婷综合爱| 狠狠搞狠狠操| 精品夜夜澡人妻无码AV| 9精品视频在线| jiujiu热在线视频| 日本五月天婷婷丁香| 热99在线精品| 99久久综合| 六月色婷婷| 能看的av网站| 丁香五月天婷婷91| 深爱五月激情网| 婷婷丁香五月欧美人| 五月天婷婷乱论小说| 99热在线精品观看| 97伊人综合婷婷| 91色五月| 天天干天干| 久操大香蕉| 日产精品久久久久久久蜜臀| 久久99久久99精品免观看粉| 丁香五月六月欧美| 开心激情站| 伊综合蕉| 激情图片婷婷| 婷婷五月丁香五月| 色欧美日| 天天综合网亚洲综合网| 国产无套精品一区二区| 婷婷久久精品| 激情五月婷婷网| 91久久久久久| 激情综合五月| 激情久久久久久久久久久| 色小说婷婷五月天天天| 日韩成人网址| 六月婷婷最新网址| 激情小说之五月| 激情五月天色网站| 日本色色网站| 激情五月开心五月丁香五月| 久久AV无码精品人妻系列试探| 久久综合中文字幕| 婷婷五月激情中文字幕| 久热久69| 99ri视频| 久久五月情| 99久热在线精品| 日本99色| 婷婷射图| 婷婷金品综合视频| 97日在线视频| 亚洲人成色A777777在线观看| 国产真人做爰视频免费| 婷婷激情五月综合基地| 91人妻人人操| 9久热在线视频精品| 天天干,天天操,天天射| 俺去也五月天婷婷| 婷婷五月天日日日干干干| 亚洲精品久久久久久久久久飞鱼| 六月婷婷狠狠色在线观看| 欧美色色色色色色色| 97成人在线视频精品| 丁香六月视频免费观看| 亚洲欧美国产A片免费观看| 狠狠草天天草| 日韩一本操| 亚洲激情 久久| 精品久久99码| 日韩三级片一区二区| 丁香激情五月天| 开心五月婷婷综合在线精品素人| 激情性爱五月| A一级操| 99在线精品视频| 99这里只有精品视频免费| 婷婷大乡焦噜噜| 亚洲天堂啪啪| 国产精品18久久久| 国产精品香蕉| 婷婷五月天综合网| 婷婷丁香社区| 色色色色五月| 婷婷五月小说色综合| 久久女婷| 天天综合永久| 五月天停停日日| 天天爱天天操| A√天堂网在线| 免费无码毛片一区二区A片 | 五月久久网| 欧美精产国品一二三区| 国产色色网址网站| 玖玖婷婷五月天毛片| 狼人婷婷久久| 激情五月六月丁香| 婷婷五月丁香五月| 婷婷九色| 操逼视频网址| 亚洲无AV在线中文字幕| 成片免费播放| 婷婷色片| 久久丁香五月婷| 久久色婷婷| 任你躁XXXXX麻豆精品| 人人干人人看| 天天干狠狠艹| 五月天婷婷丁香花| 亚洲丁香五月综合| 国产在线黄色| 久久丁香婷婷五月| 国产九月婷婷| 大香蕉AV电影在线| 国产成人综合网| 五月天婷婷在线AN| 色五月开心开心五月激情五月| 91人人操人人| 91九色国产熟女| 日本人人超碰| www久久久久久久久久久| 五月婷婷激情网| 亚洲激情色色| 狠狠干青青草| 亚洲五月丁| 色婷五月| 99ri在线观看视频| 一级性感黄色内射视频| 大香蕉伊人爱在线| 操逼三区| 色婷婷偷拍| 亚洲影院婷婷色| 日韩一本操| 思思热视频在线| 五月天天久久香| 5月丁香六月婷婷| 丁香激惜男女| 另类图片激情五月天| 99久久综合网| 婷婷丁香六月综合激情站| 婷婷玖玖五月天| 26uuu成人网| 亚洲精品久久久久久久久久吃药| www久久99| 亚洲综合色色| 免费看欧美成人A片无码| 伊人干综合| 激情综合激情综合| 色综合久久中文| 岛国av网站| 久久久五月四色| 五月丁香婷婷激情| 一级二级香港秋霞欧美欧美秋霞| www激情婷婷com| 青草青青草| 怡红院视频| 影音先锋777xfplay色资源网站| 丁香五月中文字幕久色| 五月综合婷婷五月| 亚洲综合1024| 国产av天堂| 国自产拍偷拍精品啪啪一区二区| 色狠狠色综合久久久绯色aⅴ影视| 人妻丰满精品一区二区A片| 中文字幕 中文字幕明步| 亚洲天堂婷婷丁香| 免费成人中文字幕| 91婷婷丁香| 狠狠搞五月天| 国产另类综合| 国产色网站| 欧美婷婷丁香五月社区| JAPANRCEP老熟妇乱子伦视频 | www..999热久| 思思热思在线精品视频| 色综合天天天天做夜夜| 99啪| 熟女少妇内射日韩亚洲| 人人操91色| 五月天激情啪啪| 久热久| 狠狠综合网| 激情小说色五月| 日本三级99人妇网站| 激情宗合网激情五月天| 六月丁香五月激情婷婷| 五月婷婷人妻| 日本婷色| 婷婷久久五月丁香| www.色婷婷| 五月天激情影院| 99久在线观看| 99色在线| 亚洲日韩操B| 免费观看全黄做爰的视频| 中文字幕欧美日韩VA免费视频| 中文字幕在线日亚洲9| 五月丁香六月婷婷久久| 中文字幕按摩做爰| 不卡在线视频| 人人操人人看97干| 色婷婷伊人| 久久九九爽| 超碰大香蕉网| 99热日韩这里只有精品| 色情五月天A片| 丁香婷婷十月| 久久综合影院| 亚洲成人免费电影| 色色综合网www| 2015在线中文字幕| 激情综合网址| 秋霞少妇AV网站| 五月天丁香色色| 91怕怕网| 日韩九区| 99热精品一区| 无码动漫av| 国产操碰| 秋霞少妇AV网站| 成人国产网| 97在线/日本| 六月丁香色色| 啪啪五月天啪啪| 婷婷五月色亚洲| 五月天成人在线视频网站| 日本久久婷婷| 亚洲av| 亚洲顶级VA在线观看-高清完整版在线影院观看-S022AV | 97伦色婷婷| 精品99在线看| 丁香五月天五码婷婷| 色色免费网战视频| 婷婷综合网| 色综合久久之分久久| 开心久久网婷婷| 色综合激情| 五月丁香婷婷激情久久| 五月丁香色婷婷色| 五月天色色无码| 色五月中文字幕| 九九碰九九爱97| 色情免费视频播放| 亚洲六月色| 国产99久| 九九热这里有精品23| 情久久综合五月天| 免费观看的AV| 国产精品久久久久久白浆色欲| A片试看120分钟做受图片| 丁香五月天婷婷91| 天天草天天日| 99热这里只有精品9| 9九九久久精品无码专区| 久久五月天色婷婷| 亚洲激情免费视频| 开心激情站| 综合五月丁香97| 五月天激情网图片 - 百度| 26uuu精品国产| 激情综合五月| 人人澡玖玖一| 天天操天天操天天操天天操天天操天天操天天操天天操天天操 | 亚洲精品色| 久久在线视频免费观看| 97色片| 丁香五月区| 丁香婷婷大香蕉| 色狠狠综合入口| 激情综合网 激情五月天| 婷婷五月天久久久| 久激情网| 天天操比比| 婷婷97C| 99在线观看亚洲| 精品,99| 深爱网深爱综合网| 久久网日本| 热99热久| 色性五月天| 五月天婷婷基地| 激情六月婷婷| 一月婷婷色色| 光棍影院日韩精品| 五月婷婷天| 8090在线影视少妇| 丁香六月 婷婷六月| 日本九九网| 亚洲99一级无嗎特制在线| 五月亭亭激情综合| 99热这里有精品| 六月丁香激情综合网| 国产精品色婷婷久久久精品| 综合激情在线观看| 五月天网站免费欧美| 8050一级网| 五月丁香好婷婷姑娘综合网| 天天天天操| 1024人妻| 99精品在线| 免费黄色视频网址| 亚洲欧美婷婷五月色综合| 亚洲九区| 午夜丁香婷婷| 激情播丁香| 涩涩五月天| 亚洲精品第一色色色色色色| 狠狠久久婷五月综合色| 欧美性猛交XXXX乱大交极品| 欧美婷| 国产精品18久久久| 五月天伊人日日噜影片AV| 国产美女无遮挡裸体毛片A片| 五月天婷婷色在线视频免费观看| 色色色网站| 亚洲va日| 亚洲成人AV高清字幕| 亚洲综合视频一下| 精品9久| 婷婷.com| 久9热视频| 99色在线观看视频| 二色av| 大香蕉九九| 久久只有精| 丁香激情网| 亚洲五月综合色播| 五月天丁香看婷婷| 欧美综合123区| 激情婷婷五月| 六月丁香五月婷婷| 另类激情网| 丁香五月婷婷www..com| 九色91国产| 伊人91| 国产精品人人做人人爽人人添| 色五月丁香婷婷综合| www.超碰97| 天天综合网网欲色| 伊人激情综合网| 97五月天| 涩五月色婷婷| 在线中文av| 午夜大香蕉| 五月丁香花激情啪啪网| 97干在线视频| 激情五月婷婷中文字幕| 亚洲小电影在线观看黄999| 99久久久久| 五月色婷婷综合丁香精品无遮挡| 亚洲免费电影2| 欧美人人操| www.99热最新视频8| www.com久久久久久久久久久久久久久久久| 色在线视频网2025| 亚洲综合激情五月天婷婷| 国产午夜精品一区二区三区四区| 操骚货在线| 婷婷五月播| 91919191919久久成人视频| 色色亚洲视频| 开心亚洲久久开心| 九九精品热播| 国产婷婷婷| 91丁香五月| 深爱网深爱综合网| 久久人人九| 国产午夜伦鲁鲁| 99热碰碰| 激情都市五月天| 日韩 中文 欧美| 欧美性猛交XXXX乱大交极品| 操逼六区| 这里只有精彩视| 91欧美日韩综合| 欧美精品18| 五月天婷亚洲天综合网综合| 草榴视频网| 性av| 丁香五月久久| 色五月天在线观看| 国产一级婬片毛片| 久草婷婷| 五月丁香激情综合六月涩涩爱| 第九色区av天堂| 99啪99| 丁香婷婷六月在线资源观看| 色欲Av五月天| 97人人操在线| www.精品久9| 婷婷99狠狠躁天天久久久九九九| 超碰av在线| 色婷婷五月中文字幕在线dvd| 免费的日逼视频| 超碰在线观看9| 67194中文在线| 成人电影一区| 五月婷综合| 欧洲色| 五月婷婷我| 亚洲小说五月婷婷| 欧美日韩成人在线| 五月婷婷丁香大陆免费| 99热大| 丁香五月-激情综合| 五月天婷婷无码| 婷婷五月色惰| 色婷婷欧美| 狠狠色噜噜色狠狠狠综合色 | 26uuu国产色| 影音先锋一区| 操日视频| 99热网址| 97热久久| 另类亚洲视频| 婷婷 丁香 久久| 久草视频大香蕉99| 婷婷六月偷拍| 色婷婷丁香AV综合| 亚洲无码猫咪| 色色色天堂网| 热996精品在线观看| www.久久久久久久久久.com| 婷婷狠狠青青| 日韩一级| 成人视频免费观看高清完整版在线观看| 麻豆WWWCOM内射软件| 99热18| 五月天成人免费视频| 狠狠爱综合网| 日日日日日| 久久婷婷色| 色五月激情综合网| 538在线精品| 久久久久久人妻| 国产 码在线成人网站| 五月天另类激情在线| 99色热| 婷婷综合网| 亚洲无码色| 色色色色色色97| 婷婷丁香五月,狠狠综合| 只有久久精品免费| 五月婷婷丁香在线| 99热丁香| 婷婷五月天小说网| 成人片黄网站色大片免费毛片 | 色综天天综合| 2023天天日夜夜爽| 狠狠色噜噜狠狠| 日婷婷久久开心| 婷婷五月天AV在线| 丁香婷婷深情五月亚洲| A片试看50分钟做受视频| 亚洲无码 图片区| 99re在线免费视频| 中文字幕av在线| 天天天操天天天爰| 五月天婷婷人妻| 另类 在线| 色色色色综合| 九九XX视频| 五月天丁香欧美激情| 怡春院久操| 99ri精品| 思思w99| 综合久久五月天| 最新色色五月天| 日本色色色| 五月婷婷很很色| 97人妻碰碰碰久久久久-最近国语高清| 丁香六月狠狠干| 日本激情ⅩXX免费视频| 99热这里只有精品搜| 天天做天天爱天天爽夜夜揉| 亚洲九九九九| 综合激情在线| 婷婷香草网| 无码A片一区二区免费| 色五月激情综合网| 五月丁香欧美在线| 日韩AV大全| 人人摸人人操人人爱| 日本免费91| 色爱综合视频| 在线视频激情网站| 成人做爰黄A片免费看直播室男男| 国产综合丁香五月天| 五月丁香啪啪啪| 激情色情五月天| 激情六月丁香| 久久99久久99久久99| 欧美三级欧美一级| 开心五月深爱五月丁香五月激情五月| 91啪啪| www久久久久久久| 婷婷97| 五月婷婷之综合激情| 99无码| 91干在线视频| 综合久久丁香婷婷,五月婷婷六月丁香,开心激情综合网,六月丁香在线观看,婷婷丁 | 风流少妇A片一区二区蜜桃| 五月婷婷激情性爱| 欧美日韩婷婷五月天| 超碰免费人人| 天天日本夜夜谢| 欧亚成人A片一区二区| 色欲婷婷五月天丁香| 色播五月丁香婷婷| 美日韩成人| 日本三日本三级少妇三级66| 久久狠狠色| 久久人人看| 人妻在线网站| 五月草影视| 乱精品一区字幕二区| 婷婷久久五月天丁香| 婷婷五月18永久免费视频| 亚洲黄色影视| 99综合免费视频| 丁香五月综合| 久热99久热| 天天久久综合| 成人国产欧美大片一区| 狠狠操.com| 久久网日本| 婷婷丁香视频| 五月天天爱| 七月婷婷色香综合网| 日本色噜| www.操.com| 99激情在线| 激情网婷婷五月天| 97精品综合久久内射| 色五月丁香五月婷婷五月成人网 | 亚洲婷婷丁香五月在线| 久久久久久综合88| 九九re精品视频在线观看| 久久婷婷五月天蜜桃| 丁香 婷婷 亚洲 熟女| 日韩AV中文在线观看| 性 色 婷婷| 欧美久热| 婷婷色五月亚洲| 久久99网站| 亚洲视99| 97人妻碰碰中文无码久热丝袜| 99在线观看| 大香蕉520| 亚洲精品成人| 深爱激情五月网| 色综合久久久久| www.久久99| 丁香六月婷婷| 91啪啪视频| av婷婷丁香 六月| 亚洲九九夜夜| 五五月五月| 婷婷中文字幕网站| 五月天,激情四射,婷婷频道| www.99久久久久99| 97人人搞| 久久66er久久| 中文字幕精品无码一区二区| 婷婷五月偷拍| 国产精品成人网站| 欧美超级视频97| 丁香五月婷婷啪| 婷婷激情综合| 一本道综合网| 91久久九九| 丁香五月开心亚洲| 五月婷丁香花| 国产熟妇乱子伦hd| 26uuu精品国产| 无码橾| 五月婷婷就去色| 在线观看免费观看在线9久| 亚洲 五月 婷婷 成人| 91jiuseshunv| 九九99免费视频| 色婷婷中文字母五月丁香| 无人区码一码二码三码医生系列| 色丁香影院| 任你操精品免费| 泰州成人视频| 久久99久久99精品免观看软件 | 懂色AⅤ| 吊色AV男人的天堂| 狠狠综合网| 这里只有精品免费视频在线观看| 激情五月色婷婷| 色色五月婷| 俺也高清无码高清视频| 99热这里只有精品1025| 欧美69久成人做爰视频| 99久久五月婷婷| 亚洲婷婷在线播放十月| 在线观看日韩12345区| 噜噜色五月| 欧洲亚洲免费视频区| 天天色凹凸| 五月香婷婷| 亚洲行行色色| 亚洲综合网 665566| 国产偷人爽久久久久久老妇APP | 久久九九@| 日本欧美成人片AAAA| 青草五月天| 天天日夜夜B久久| 99啪视频在线观看| 天天爽人人综合免费7799| 婷婷开心久久| 五月天激情站| 3p久久| 91中文狠狠综合| 久久99久久99精品免视看婷| 99国产性感视频| 亚洲激情综合| 超碰色天堂| 日日插日日干| 天天狠狠婷婷在线| 婷婷中文字幕在线| 国产成人AV| 日日噜狠狠| 久播影院免费观看电视剧大全最新网| 噜综合| 99人人干人人| 1024欧美看片| 激情婷婷五月天| 婷婷五月综合色拍| 久热网站| 26uuuavcom| 五月丁香香蕉| 99热精品10| 99久操视频| 91超级碰碰碰| 日韩操人| 色哟哟www| 激情小说视频图片网| 热的国产,热的综合,热的有码| 大香蕉婷婷丁香| 成人小说色图婷婷五月| 亚洲亚洲人成综合网络| 久久精品人妻| 久久久久9| 激情五月天开心| 婷婷五月天激情综合深爱激情 | 天天色域综合网| 五月丁香va| 婷婷在线播放av| 五月天丁香婷| 99视频在线观看网址| 色五月噜噜| 人妻第九页| 拍真实国产伦偷精品| 这里只有精品1| 91九色国产| 九九在线视频| 亚洲操B| 人人草开心五月天| 五月成人天| 久久五月综合| 五月天激情电影| 久久思思精品| 狠狠狠夜夜夜| 天天操天天操天天操天天操天天操天天操| 色五月丁香在线| 丁香五月婷婷激情123| H亚洲| 久久久婷婷五月天| www.丁香黄色五月天人与| 538在线精品| 99ER热精品视频| 国产精品18久久久| yazhoujiqingav| 成人在线观看精品| 天天操天天干天天射| 久久久全国免费视频| 久久久婷丁香五月| 99热亚州综合| 激情婷婷五月少妇| 激情99| 成人在线精品| 婷婷激情丁香五月天综合| 亚洲日本激情| 久久五月天黄色五月天色网址| 综合激情综合啪啪| 狠狠干 狠狠操| 天天综合影院| 五月天婷婷成人网| 五月婷婷黄色| 五月丁香婷婷综合| 99免费超碰在线| 婷婷丁香精品视频在线观看| 丁香色影院| 99热精品观看| 噜综合| 99色播| 欧美成人网99网| www.色色五月天.com| 五月天开心激情综合网| 青柠影视免费高清电视剧| 丁香婷婷老熟女综合网| 亚洲精品444久久久久久| 亚洲婷婷久久综合| 青青草原中文字幕| 亚洲人妻一区二区| 99综合| 婷婷综合| 亚洲狠狠色丁香婷婷综合久久| 新男人天堂人妻| 激情婷婷丁香五月天| 天天色天天色天天色天天色天天色天天色| 怡红院院久久| 六月婷婷激情| 亚洲久久婷婷丁香五月天| 激情综合网五月激情| 影音先锋色婷婷| 亚洲精品V天堂中文字幕| 亚州第一黄网| www.99热视频| 另类国产综合| 婷婷激情人妻| 黑人糟蹋人妻HD中文字幕| 97涩婷婷婷婷基地| 丁香五月首页| 2w在线视频| 久久婷婷五月综合激情国产 | 五月婷婷六月天| 婷婷五月天激情网| 九九色video| 91丨九色丨东北熟女| 五月综合激情综合久| 狠狠色五月| 亚洲亚洲人成综合网络| 97电影99热| 激情的五月| 99热成人| 变态 另类 在线 | 婷婷色五月久久| 99热99精品| 国产精品电| 97人人干人人操| 天天操狠狠操| 97色色婷婷| 亚洲AV无码成人精品区电影网| 夜夜操夜夜爽| 五月婷婷色播| 99re8热精品免费视频| 六月丁香久久| 色婷婷久久综合中文久久一本 | 深爱丁香网| 激情六月丁| 亚洲精品国产A久久久久久| 天堂资源欧日浪女在线播放| 狠狠撸激情综合丁香五月天俺来啦| 91精品久久久久久77777| 五月色亭丁香| 综合在线网| 五月婷婷激情| 人人操人人添人人摸97| 五月综合婷婷网| 97干在线视频| 韩国19 主播内部福利vip免费播放| 天天摸夜夜爽天天做| 狠狠操天天操天天操| 天天色丁香| 9999热精品| 天天插天天干天天舔| 五月天色婷婷综合| 色婷婷五月在线| 99热这里只| 日韩欧美成人一区二区三区| 99热大全在线观看| 99在线69| 九九综舍久久| 婷婷五月丁香综合激情| 中文字幕婷婷9月天| 99在线爽| 国产成人亚洲综合A∨婷婷| 人人操人人干AV| 很很干夜夜干| 五月婷婷欧洲| 五月天激情久色| 少妇性BBB搡BBB爽爽爽电影| 五月婷婷色播视频| 五月婷色| 国产日比| 99热最新网址| 99人人干| 五月天大香蕉| 中文字幕永久免费| 丁香五月另类色婷婷麻豆| 日韩一级网站| 激情五月激情综合网| www,久久久人人| 婷婷色色欧美| 一区操| 99re这里只有精品视频了| 丁香五月婷婷社区| 丁香婷婷久久老熟女综合网| 午夜无码熟熟妇丰满人妻| 久久激情综合| 国外亚洲成AV人片在线观看| 婷婷六月色丁香视频在线观看| 激情六月婷婷| 久久ER视频com| 公的粗大挺进了我的密道| 操操操av| AA片在线观看视频在线播放 | 我爱大香蕉| 久久久激情| 四色综合网| 久久婷婷热| 亚洲色五月天是什么| 激情内射人妻1区2区3区| 激情亚洲色图片丁香综合| 日日影院 | 色婷婷丁香AV综合| 五月丁香亚州综合网| 婷婷综合精品视频97| 97在线/日本| 丁香婷最新动态| 任你爽视频| 五月天婷婷色五月天| 无码人妻少妇色欲AV一区二区| 另类图片五月天婷婷| 少妇水多A片太爽了| 久久久久人无码人妻| 日本色超碰| 91se在线观看| 99欧美| 国产肥白大熟妇BBBB视频| 影音先锋 婷婷| 激情五月婷婷色色| 激情婷婷久久| 婷婷欧美色| 色五月综合激情网| 色情五月综合婷婷| 99热都是精品| 久久久国产精品黄毛片| 夜夜操激情| 久9热视频| 99自拍视频在线观看| 色婷五月| 九九99热| 婷婷亚洲色| 色人妻五月| Www99热| 777精品久无码人妻蜜桃| 五月天婷婷高清无码| 五月丁香婷婷综合视频| 欧美在线干| 激情五月天无人视频在线| 色婷婷a v| 丁香婷婷综合激情五月色| 国产午夜精品久久久观看| 五月婷婷色综图片| 8090在线影视少妇| 欧美Va日本Va| 激情宗合哪里能看| 国产特黄色精品一区二区三区精品无广告 | 国产av天堂| 丁香五月婷久久| 99热九九这里只有精品| 9视频在线成人网站| 色五月婷婷91| 五月的婷婷六月丁香| 婷婷中文字幕在线| 五月综合激情网| 激情婷婷六月天| 亚洲欧洲色色| 黑人无码一区| 亚洲熟妇AV乱码在线观看| 高清无码网址| 天天操夜夜玩!| 五月婷婷啪啪啪| 亚洲狠狠丁香婷婷香蕉| 97操在线视频| www夜夜操comwww| 天天人人综合| 色五月婷婷激情基地| 另类少妇人与禽zOZZ0性伦| 可以直接看的av| 南京搡BBBB搡BBBB| 激情婷婷久久| 中文AV网站| 青青草深爱激情网| 五月丁香久久呀| AV色色天堂中文| 天天天天干| 91久久五月天| 狠狠操性爱av| 全网最新网黄大秀直播高清,主播国产录屏在线 | 五月天成人在线播放| 森林影视大全,最好看的2019年视频 | 人妻人人操| 香蕉中文在线| 中文字幕中文有码在线| 99热精品中文字幕| 五月天 婷 欧美亚洲| 男女久久婷婷五月天| 狠狠五月激情在线| 久久99久久99精品免观看粉嫩| 中文AV网站| 99国产精品白浆在线观看免费| 日本色99| 99这里只有精| 丁香婷婷浪潮AV久久综合| WWW.五月天9999| 免费看欧美成人A片无码| 秋霞AV美国| 五月天丁香婷婷久久九| 中文字幕在线观看视频www| 五月婷六月| 丁香五月婷老师| 丁香五月亚洲综合| 丁香五月婷婷少妇| 久久综合九九| 在线中文字幕av| 99碰网站| 五月丁香婷婷基地| 欧美日韩大黄| 亚洲超碰中文字幕| 直接看的av| 五月伊人网| 色婷婷综合网| 日日做夜夜爱| 五月丁香综合中文| 99操逼| 情五月亚洲婷婷| 色婷婷综合久久| 婷婷五月天美女21p| 色婷婷亚洲| 国产精自产拍久久久久久蜜 | 亚洲国产成人在线| 超碰人妻在线| 五月丁香狠狠| 亚洲性爱电影| 超碰在线91| va亚洲中文在线| 久久刺激网| 五月婷婷综合网| 99免费视频网| 激情综合网五月丁香| 免费视频无码| 丁香婷婷月| 《诡秘之主》在线观看| 日B日潘金莲BB| 日日干日日| 玖玖伦理电影| 99日在线观看视频| 99色视频| 99亚州综合精品成人网| 日本99热| 婷婷丁香精品视频在线观看| 人妻九九九九| 甈你aaaaa| 久久婷婷综合基地| AV网在线| 91人人网| 欧美综合激情五月天| 影音先锋噜一噜| 激情久久伊人| 久久久com| 婷婷五月天堂一本在线| 九九热在线观看6| 九九在线91| 婷婷五月在线观看| 精品人妻一区二区| 色五月丁香激情视频| 久久99久久99精品免观看粉嫩| 99视频| 午夜丁香婷婷| 婷婷性色| 五月综合在线婷婷图片| 亚洲AV网址| 九九综合九色欧美狠狠| 狠狠干狠狠干| 国产无人区大片| 日韩欧美骚货| www.狠狠操.co m| 大香蕉中文| 亚洲殴洲精品Av在线| 国产人妻777人伦精品HD| 久久人人九九| 五月丁香六月情| 六月婷婷网站| 天天综合久久| 成人av中文字幕| 日韩精品无码AV| 亚洲欧美日韩VIP| 天天日天天干天天操| 69色婷婷| 激情深爱五月| 只有久久精品免费| 九九热这里精品| 日韩欧美一级大黄网站| 国产九九一区二区三区| 激情五月婷婷色综合| 婷婷五月花| 99热只有| 色婷婷成人| 日韩欧美成人片| 91精品久久久久、久五月天| 伊人午夜综合色啪| 狠狠擼综合| 99热国内| 色婷婷丁香六月| 亚洲综合在线伊人婷| 久久网日本| 色综合久久88色综合天天看| 性爱激情五月| www.激情五月天。com| 丁香婷婷91在线观看视频| 丁香五月天社区婷婷| 五月婷婷很很色| 婷婷丁香无码专区| 丰满少妇乱A片无码| 色优久久| 桃色五月天| 色婷婷五月天| av亚洲国产小电影| 国自产拍偷拍精品啪啪一区二区| 色婷婷av在线观看| 欧美激情五月天| 午夜天堂一区人妻| 丰满少妇猛烈A片免费看观看| 天天弄天天操| 亚洲日韩乱码一区二区三区四区| 天天日天天插| 大地资源色婷婷视频在线| 无码人妻丰满熟妇奶水区码| 亚洲A片成人无码久久精品青桔| 久久精品国产一区二区三区四区| 欧美顶级少妇做爰HD| 99热99艹在线观看| 五月婷婷黄色网址| 丁香六月综合| 久久人操-久草婷婷-成人AV| 伊人三级激情| 色九月婷婷综合| 天天日天天添| 桃色激情婷婷伊人网| 新激情五月开心五月婷婷五月丁香五月| 婷婷五月天影视| 久久精品66| 五月丁香六月色| 97碰碰在线看视频免费| 91丨九色丨熟女高潮| 色五月首页| 日韩青青| 狠狠色丁香久久婷婷综合五月| 久久艹99| 久久综合激情五月天| 狠狠狠激情网| 亭亭五月色男人| 亚洲午夜视频| 国产精品99久久久久久久女警| 琪琪布丁香社区激情五月天| www.99热精品99.com| 婷婷综合97| 久久人妻乱子伦| 天堂AV在线看| 婷婷色色丁香五月天| 中文字幕黄色片| 国产操B| 情一色一乱一伦一91A| 久久黄A片| 丁香五月婷婷成人网| 亚洲欧美婷婷五月色综合| 日本天天操| 久久99网| 激情五月丁香五月| 天天射影视综合网| 日韩欧美成人片| 中文字幕按摩做爰| 狠狠色丁香婷婷五月| 深爱五月月天| 99re这里只有精品视频了| 色激情综合| 热91久| 五月丁香好婷婷A片网| 婷婷在线操| 天天爱天天狠天天透| 五月婷婷激情网| 人人草碰| 久1色色| 另类小说色婷婷| 国产精品第一国产精品| 日韩欧美一道四区中文字幕| 婷婷五月天另类视频| 性色天| 久久综合久色欧美综合狠狠| 99人人干人人| 激情 婷婷 丁香五月天| 亚洲午夜av| 丁香五月激情啪啪啪| 五月婷婷伦理| 天天操夜夜操| 超碰在线观看三级片| www.婷婷六月天| 五月婷婷影| 欧美韩国日本| 婷婷丁香97| 久久偷拍综合五月天| 粉嫩AV久久一区二区三区| 黄网在线观看免费| 激情五月婷在线精品| 337p大胆噜噜噜噜噜91Av| 香蕉伊人综合| 极品五月天| 99视频内射三四| 色婷婷免费观看| 5月丁香婷婷激情网| 91紱請| 五月天婷婷午夜丁香| 久久婷婷五月丁香网| 91热久| www五月天激情com| 伊人综合网站| 五月天婷婷综合久久| xx久久| 欧美色偷偷大香| 99九九视屏| 狠狠色色| 97天堂| 五月丁香花视频| 色五月大香蕉婷婷| 丁香婷婷婷五月| 夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂夂亚洲亚洲亚洲亚洲亚洲亚洲亚洲亚洲色 | 免费观看的av| 亚洲国产成人在线| h在线看免费版在线看| 婷婷五月日本| 欧美久久婷婷| 狠狠爱深色婷婷综合| 婷婷丁香久久五月综合| 五月天com| 五月天啪啪| 精品久久久人妻| 日本不卡中文字幕| 日韩色久| 色情婷婷。| 中文字幕成人影视| 99综合熟女| 日日夜夜干| 三级毛片7979| 色婷婷a| 久久99精品久久久久久噜噜| 激情五月天婷婷| 久久新| 九九色99| 婷婷亚洲日本| 狠狠色婷婷7777久| 亚洲熟妇AV综合网五月丁香伊人 | 丁香亭亭激情四射| 激情色视频| 中文字幕欧美精品久久| 色婷婷狠狠18| 99精品高潮| 97久久人人| 久久狠狠欧美| 日本熟妇乱妇熟色A片蜜桃|