久久亚洲成a人片熟女精品色一区二区三区|国产精品视频第一精品视频|av天堂热无码手机版|亚洲?v无码久久无遮挡|国产精品偷伦视频免费观看国产|麻豆国产自产精品丰满熟妇|av无码av不卡一区二区|久久亚洲精品中文字

ARTICLE DETAIL

資訊詳情

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

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

Undo Manager 實(shí)現(xiàn)原理:操作建模、雙棧調(diào)度與狀態(tài)快照 簡(jiǎn)介本資源是一套面向C桌面應(yīng)用開(kāi)發(fā)者的撤銷(xiāo)/重做Undo/Redo功能完整實(shí)現(xiàn)方案適用于文本編輯器、富文本處理工具等需狀態(tài)回溯能力的中高級(jí)項(xiàng)目。壓縮包共61個(gè)文件含28個(gè).cpp源文件如UndoManager.cpp、RETypingAction.cpp、REDropAction.cpp等承載核心操作封裝與狀態(tài)管理邏輯、31個(gè).h頭文件定義Action基類(lèi)、視圖接口、文本范圍與樣式結(jié)構(gòu)等另含1個(gè)說(shuō)明文檔和1個(gè)資源腳本整體僅70KB輕量但結(jié)構(gòu)嚴(yán)謹(jǐn)。目前已有155人學(xué)習(xí)下載適合希望深入理解命令模式、棧式歷史管理、細(xì)粒度操作建模打字、替換、拖放、插入對(duì)象等的開(kāi)發(fā)者。代碼采用模塊化設(shè)計(jì)支持RichEdit與BetterEdit雙視圖體系涵蓋多步菜單交互MultipleUndoRedoMenu.cpp與變更日志記錄Change Log.txt可直接集成或作為教學(xué)范例剖析撤銷(xiāo)系統(tǒng)架構(gòu)與事件協(xié)同機(jī)制。1. Undo Manager 是什么一個(gè)被低估的“操作后悔藥”不是 UI 組件而是狀態(tài)變更的底層契約你寫(xiě)了個(gè)表單編輯器用戶(hù)改了 5 行數(shù)據(jù)、刪了 2 個(gè)字段、又拖拽重排了順序——然后猛按 CtrlZ結(jié)果只撤回了最后一次拖拽前面的修改全丟了或者更糟撤回后界面沒(méi)刷新但內(nèi)部狀態(tài)已回滾下一次提交直接把臟數(shù)據(jù)發(fā)了出去。這不是前端框架的鍋是 Undo Manager 沒(méi)立住契約。Undo Manager 不是某個(gè) React 庫(kù)里的 Hook也不是 Vue 的插件它是一套對(duì)狀態(tài)變更行為建模、封裝、可追溯、可原子回滾的通用機(jī)制。它的核心價(jià)值不在“能撤銷(xiāo)”而在“知道誰(shuí)在什么時(shí)候以什么方式改了什么并且能精準(zhǔn)還原到那個(gè)瞬間”。它適用于任何需要操作歷史、多級(jí)回退、協(xié)同編輯、調(diào)試回放或狀態(tài)快照的場(chǎng)景——從代碼編輯器、圖形設(shè)計(jì)工具到工業(yè) HMI 系統(tǒng)、低代碼平臺(tái)的邏輯編排畫(huà)布。如果你正在手寫(xiě)history.push()或靠useState 數(shù)組堆棧硬扛撤銷(xiāo)邏輯那說(shuō)明你還沒(méi)真正接入 Undo Manager 的抽象層它不關(guān)心 UI 怎么渲染只負(fù)責(zé)把“變更”變成可序列化、可比較、可執(zhí)行的 Operation 對(duì)象。下面我們就從零搭起這個(gè)“后悔藥”的骨架不依賴(lài)任何框架封裝直擊底層實(shí)現(xiàn)邏輯。2. 從零實(shí)現(xiàn)一個(gè)最小可用 Undo Manager用 Operation 建模變更用 Stack 管理歷史Undo Manager 的本質(zhì)是把“用戶(hù)的一次操作”翻譯成一段可逆的、帶元信息的狀態(tài)變更指令。它不能只存舊值和新值太粗暴也不能只存函數(shù)引用不可序列化、難調(diào)試。我們采用經(jīng)典的Operation 模式每個(gè)操作是一個(gè)對(duì)象包含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)類(lèi)我們先定義 Operation 的 TypeScript 接口再給出一個(gè)通用基類(lèi)BaseOperation它提供默認(rèn)的redo()實(shí)現(xiàn)即再次調(diào)用execute()并強(qiáng)制子類(lèi)實(shí)現(xiàn)execute()和undo()// types.ts export interface Operation { execute(): void; undo(): void; redo(): void; timestamp: number; label: string; source?: string; // 標(biāo)識(shí)操作來(lái)源如 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()是常見(jiàn)做法但某些場(chǎng)景如異步操作需重載。此處保持簡(jiǎn)潔后續(xù)會(huì)說(shuō)明如何擴(kuò)展。2.2 構(gòu)建 UndoManager 核心類(lèi)雙棧調(diào)度與邊界控制UndoManager類(lèi)不持有任何業(yè)務(wù)狀態(tài)只管理 Operation 實(shí)例的生命周期。它維護(hù)兩個(gè)數(shù)組棧undoStack待撤銷(xiāo)的操作和redoStack已撤銷(xiāo)、待重做的操作。關(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(); } // 撤銷(xiāo)彈出 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ù)輸入等場(chǎng)景 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 子類(lèi)實(shí)現(xiàn) if (typeof (last as any).merge function) { (last as any).merge(operation); } } } // 清理?xiàng)13珠L(zhǎng)度不超過(guò) 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)前可撤銷(xiāo)/重做數(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安全檢查——這是為了保持基類(lèi)輕量把合并邏輯下沉到具體業(yè)務(wù) Operation 中。trimStacks()在每次execute()后調(diào)用確保內(nèi)存可控canUndo/canRedo是典型的 UI 友好屬性方便綁定按鈕禁用狀態(tài)。2.3 實(shí)戰(zhàn)為表單字段創(chuàng)建 TextChangeOperation現(xiàn)在我們寫(xiě)一個(gè)真實(shí)可用的 Operation 子類(lèi)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)同字段、且新值以舊值開(kāi)頭時(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 } }); }邏輯說(shuō)明TextChangeOperation的execute()和undo()都調(diào)用同一個(gè)updateField()但傳入不同值。這保證了正向/反向行為完全對(duì)稱(chēng)。merge()方法判斷other.newValue是否以當(dāng)前newValue開(kāi)頭——這是連續(xù)輸入的典型特征a → ab → abc滿(mǎn)足則更新newValue和timestamp實(shí)現(xiàn)視覺(jué)上的“一次輸入”效果。參數(shù)說(shuō)明field是字段唯一標(biāo)識(shí)如user_nameoldValue/newValue是字符串快照l(shuí)abel和source用于 UI 展示和審計(jì)。3. 與 React 深度集成用自定義 Hook 封裝狀態(tài)聯(lián)動(dòng)避免 useEffect 嵌套陷阱Undo Manager 本身是框架無(wú)關(guān)的但落地時(shí)必須與 UI 框架的狀態(tài)更新機(jī)制對(duì)齊。在 React 中常見(jiàn)錯(cuò)誤是在useEffect里監(jiān)聽(tīng)輸入事件并execute()但execute()觸發(fā)的updateField()又導(dǎo)致組件重渲染進(jìn)而再次觸發(fā)useEffect形成無(wú)限循環(huán)。正確解法是分離“操作捕獲”與“狀態(tài)更新”用 ref 緩存當(dāng)前值用useCallback固化操作創(chuàng)建邏輯讓 UndoManager 成為純命令總線(xiàn)。3.1 創(chuàng)建 useUndoableState Hook接管 useState 并注入 Undo 能力我們不替換useState而是在其之上疊加一層“可撤銷(xiāo)狀態(tài)”。該 Hook 返回[value, setValue, undo, redo, canUndo, canRedo]其中setValue是增強(qiáng)版自動(dò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, // 單值場(chǎng)景用 root 標(biāo)識(shí) 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] ); // 撤銷(xiāo)/重做方法直接代理給 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ù)說(shuō)明initialValue是初始狀態(tài)options.manager允許復(fù)用外部 UndoManager 實(shí)例如跨多個(gè)表單項(xiàng)共享歷史source參數(shù)用于區(qū)分操作來(lái)源鍵盤(pán)輸入、API 回填、程序初始化等便于后續(xù)審計(jì)或條件過(guò)濾。關(guān)鍵點(diǎn)在于currentValueRef它始終保存最新值undoableSetValue比較的是 ref 值而非 state 值避免因setState異步性導(dǎo)致的誤判。Object.is()用于嚴(yán)格相等判斷支持 NaN、Object 等。3.2 在組件中使用一個(gè)可撤銷(xiāo)的搜索框現(xiàn)在我們寫(xiě)一個(gè)真實(shí)組件展示如何用useUndoableState構(gòu)建帶撤銷(xiāo)能力的輸入框// 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防止用戶(hù)誤重做搜索 // 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} 撤銷(xiāo) (CtrlZ) /button button onClick{redo} disabled{!canRedo} 重做 (CtrlY) /button span classNamehistory-count 歷史: {canUndo ? ● : ○} {canRedo ? ● : ○} /span /div /div ); }邏輯說(shuō)明onChange事件中調(diào)用setQuery(value, keyboard)自動(dòng)觸發(fā)TextChangeOperation創(chuàng)建與執(zhí)行onKeyDown捕獲 Enter 提交此時(shí)query是最新值可安全用于 API 調(diào)用。按鈕disabled屬性直連canUndo/canRedoUI 狀態(tài)與 Manager 內(nèi)部棧完全同步。這里沒(méi)有useEffect沒(méi)有競(jìng)態(tài)所有狀態(tài)流轉(zhuǎn)都由setQuery這一個(gè)入口驅(qū)動(dòng)。3.3 處理復(fù)雜狀態(tài)用 ObjectChangeOperation 管理嵌套對(duì)象單值夠用但表單常是對(duì)象結(jié)構(gòu)如{ name: , email: }。我們擴(kuò)展一個(gè)ObjectChangeOperation它記錄整個(gè)對(duì)象的深拷貝快照并支持按路徑更新// 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 修改對(duì)象字段, public source user ) { super(); } execute(): void { // 更新全局狀態(tài)樹(shù)假設(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ù)簡(jiǎn)化版 function updateStateTree(path: string, value: unknown): void { // 實(shí)際項(xiàng)目中對(duì)接 Zustand / Redux / Jotai 等 } function restoreStateTree(snapshot: Recordstring, unknown): void { // 用 snapshot 替換整個(gè)狀態(tài)樹(shù) }使用提示ObjectChangeOperation通過(guò)snapshotBefore/After記錄完整狀態(tài)比單字段更新更魯棒尤其當(dāng)字段間有聯(lián)動(dòng)計(jì)算時(shí)。path支持點(diǎn)號(hào)分隔user.address.citysetByPath和getByPath是標(biāo)準(zhǔn)工具函數(shù)。合并邏輯同樣基于路徑和時(shí)間窗口確保同一字段的連續(xù)修改被聚合。4. 避坑指南Undo Manager 實(shí)戰(zhàn)中 4 個(gè)高頻翻車(chē)點(diǎn)與血淚修復(fù)方案Undo Manager 看似簡(jiǎn)單但落地時(shí)極易因狀態(tài)耦合、時(shí)機(jī)錯(cuò)位、邊界遺漏而集體翻車(chē)。以下是某跨平臺(tái)低代碼平臺(tái)在灰度階段踩過(guò)的 4 個(gè)真實(shí)坑每條都附帶可立即復(fù)用的修復(fù)代碼。4.1 現(xiàn)象撤銷(xiāo)后 UI 未更新但內(nèi)部狀態(tài)已回滾 → “界面黑匣子”原因undo()執(zhí)行了op.undo()但該方法只修改了業(yè)務(wù)狀態(tài)如 Redux store未觸發(fā) React 組件重新渲染。常見(jiàn)于op.undo()直接修改了ref.current或全局變量卻忘了通知 UI。解決所有undo()和execute()方法內(nèi)必須顯式觸發(fā)狀態(tài)更新。不要依賴(lài)“狀態(tài)變了 UI 自動(dòng)更新”的幻覺(jué)。修復(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基類(lèi)中預(yù)留setter屬性構(gòu)造時(shí)傳入強(qiáng)制子類(lèi)使用統(tǒng)一更新通道。避免任何 Operation 直接操作 DOM 或 ref。4.2 現(xiàn)象連續(xù)快速輸入如粘貼長(zhǎng)文本導(dǎo)致撤銷(xiāo)粒度太細(xì)一次 CtrlZ 只刪一個(gè)字符原因TextChangeOperation的合并閾值300ms在粘貼場(chǎng)景下失效——粘貼是單次事件但內(nèi)部觸發(fā)了多次input事件每次input都生成獨(dú)立 Operation且時(shí)間戳相同無(wú)法合并。解決增加“批量操作標(biāo)記”。在粘貼開(kāi)始前用manager.beginBatch(paste)標(biāo)記批次結(jié)束時(shí)manager.endBatch()期間所有 Operation 自動(dòng)合并為一個(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 保存后撤銷(xiāo)狀態(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); }對(duì)應(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)象頁(yè)面刷新后 Undo 歷史丟失用戶(hù)抱怨“剛做的修改全沒(méi)了”原因UndoManager 實(shí)例是內(nèi)存對(duì)象刷新即銷(xiāo)毀。需持久化歷史棧到localStorage或 IndexedDB。解決在UndoManager構(gòu)造時(shí)加載歷史并在每次execute()后自動(dòng)保存。為防序列化失敗只存 Operation 的可序列化部分label,payload,timestamp重建時(shí)動(dòng)態(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í)例需注冊(cè) Operation 類(lèi)型映射 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 類(lèi)型返回純對(duì)象deserializeOperation根據(jù)type字段 new 對(duì)應(yīng)類(lèi)。這是典型的“序列化-反序列化”模式務(wù)必測(cè)試邊緣 case如函數(shù)、undefined、Date。5. 進(jìn)階技巧用 Operation 快照做狀態(tài)調(diào)試回放把 Undo Manager 變成黑盒分析儀Undo Manager 的最大隱藏價(jià)值不是讓用戶(hù)按 CtrlZ而是讓你在生產(chǎn)環(huán)境回放任意一次用戶(hù)操作流精準(zhǔn)定位 UI Bug。我們把每個(gè) Operation 的execute()和undo()執(zhí)行前后自動(dòng)采集狀態(tài)快照snapshot構(gòu)建一條可追溯的“狀態(tài)時(shí)間線(xiàn)”。這不需要額外埋點(diǎn)只需在UndoManager.execute()中插入快照邏輯。5.1 在 Operation 執(zhí)行前后自動(dòng)采集狀態(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ù)無(wú)副作用??煺沾鎯?chǔ)為輕量對(duì)象before/after是執(zhí)行前后的狀態(tài)副本可用于 diff 分析。5.2 構(gòu)建簡(jiǎn)易調(diào)試面板可視化狀態(tài)變化鏈我們寫(xiě)一個(gè) React 組件列出所有歷史操作并允許點(diǎn)擊任一操作將應(yīng)用狀態(tài)“跳轉(zhuǎn)”到該時(shí)刻類(lèi)似 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) 或類(lèi)似方法 }效果當(dāng)用戶(hù)報(bào)告“我在第 3 步點(diǎn)擊按鈕后頁(yè)面空白”你打開(kāi)調(diào)試面板找到第 3 步點(diǎn)擊jumpTo(3)UI 瞬間回到那個(gè)時(shí)刻你就能看到空白頁(yè)面并 inspect 元素、查 console。這比看日志快 10 倍。5.3 生產(chǎn)環(huán)境安全開(kāi)關(guān)快照功能默認(rèn)關(guān)閉按需啟用快照采集有性能開(kāi)銷(xiāo)深拷貝、內(nèi)存占用絕不能在生產(chǎn)環(huán)境默認(rèn)開(kāi)啟。我們?cè)赨ndoManager中添加enableSnapshot()方法并用環(huán)境變量控制// undo-manager.ts class UndoManager { private isSnapshotEnabled false; enableSnapshot(stateGetter: () unknown): void { // 僅在開(kāi)發(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()); }最后一句我在線(xiàn)上系統(tǒng)跑了兩年 Undo Manager最大的教訓(xùn)是——永遠(yuǎn)不要相信“操作很簡(jiǎn)單不用管歷史”的想法。哪怕只是一個(gè)開(kāi)關(guān)按鈕的 toggle一旦用戶(hù)需要“剛才我關(guān)掉的那個(gè)現(xiàn)在想打開(kāi)回來(lái)”你就得有一套完整的、可驗(yàn)證的、帶快照的 Undo 機(jī)制。它不是錦上添花而是交互健壯性的底線(xiàn)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
搡老女人老熟女91老熟女综合网| 久热影视| 久久久麻豆精品| ′ !γ}丶。。久久精品欧美一区二区三区| 丁香五月天激情| 色婷婷久久综合超碰| 日韩本不卡视频在线观看| 欧美一区二区三区互相| 9999免费精彩视频| 日本999精品| 亚洲三级。日韩三级| 亚洲 自拍偷拍 欧美| 精品 码产区一区二-1080P高清在线www-B029AV| 欧美激情色婷婷花野真衣一区二区| 欧美综合综合| 日本视频一区二区三区| 亚洲老司机123专区| 精品国产乱码久久久A| 国产精品白领在线观看 | 两女互慰AV高潮喷水在线观看| 婷婷精品视频| 超碰色大香蕉| 91 在线亚洲| 久久啊啊啊| 99精品免费| 日产成人久久| 国产一级高跟丝袜| 色九月综合| 久久人人爽爽人人爽人人片αV| 激情综合网激情综合| 日本少妇va7777| 九九九九AV| 狠狠爱综合| 久久九操在线观看| 欧美成人午夜免费福利785| 色诱avtt| 乱伦图av| 免费国产| 久久精品亚洲成a人天堂| 久久久噜噜噜久久人妻| 色哟哟 日韩精品| 五月香婷婷| 粉嫩av一区二区三区天美传媒 | 国模限制级电影| 美女诱惑久久| 欧色综合| 欧美性爱1080p| 欧美 亚洲精品首页| 欧美|91色综合| 曰韩人妻中文字幕在线| 丝袜色综合| 久久九色| 少妇厨房愉情理伦片bd在线观看| 亚洲综合在线91| 久久超碰免费的| 大香蕉一区二区在线观看.| 极品久久久久久久久久久久久久| 国产精品秘 福利姬在线观看| 一区二区三区色综合| 岛国片国产成人亚洲播放| 色www精品视频在线观看| 亚洲电影中字一区二区| 99999久久精| 香蕉在线一区二区三区| 欧美视频在线视频免费va| 熟女人妻精品一区二区视频 | 91视频伊人| 国产免费一区| 亚洲天堂,男人| 亚洲伊人久久综合97| 97超碰亚洲| 宅男影院久久久,99| 人人透人人操| 国产精品网址| 久久春色| q2午夜理论片夜色av| 亚洲色图久久精品蜜| 1级黄色夫妻对换性交免费看| 土豪酒店各种姿势玩弄极品幼稚| 日本韩国一本产品小视频日本韩国一本产品久久久产品小视频日本韩国一本产品久 | 婷婷五月花| 色网在线视频观看免费| 成人一级性爱| 亚洲97成人在线观看| 清纯唯美第一页| 天美传媒Av在线| 精品久久久九九九孕妇| 青青草操逼逼视频| 日本肉体xxxx裸交| 免费看日本操逼视频| 蜜乳AV一区| 日日干天天干夜夜爽| 国产后入式在线观看| 亚洲一区二区 麻豆传媒| 天堂九九九九九九九九九| 国产高清成人传媒影视| 国产精品成人蜜臀AV在线| 国产成人91一区二区三区| 精品国产99999| 色情婷婷久久五月天| 亚洲瓯美色图| 久久亚州高清| 欧美色图在线视频少妇| 夜夜爽爽夜夜精品视频| 精品人妻一区春色| 天天大干大香蕉| 性性久久| 亚洲综合一| 亚洲色图伊人网| 久久久久9999精品九九九| 黄网站黄视频网站进入口| 人妻久热在线| 蜜臀久久在线视频| 欧美伦乱爱| 91美乳| 美女诱惑1区2区| 久久久98网站免费视频| 天美传媒一二三区永久网站| 熟妇人妻精品一区二区视频色欲| 欧美日韩丝袜| 色婷婷蜜臀av| 超碰久久精品| 色欧美在线| K8久久久久| 亚洲男人天堂手机版| 日韩三级av片| 欧美日韩啪啪电影| 操逼视频亚洲| 日韩啪啪啪啪啪| 婷婷五月天丁香| 亚洲午夜免费狠狠干| 婷婷日韩一区二区三区中文字幕在线| 久久亚洲不卡| 黄色高清久久无码依人| 蜜桃狠狠色伊人亚洲综合| 91青青在线| 大JI巴好深好爽又大又粗视频| 青娱乐淫乱1314| 被男人吃奶很爽的毛片| 日韩AV无码中文一区二区| 亚洲精品亚洲人成人网| 国产日本熟女顶级一区二区三区视频| 玖玖爱综合| 综合久久99亚洲人妻中文在线| 蜜桃狠狠色伊人亚洲综合| 美女高潮国产高清| 人妻偷拍一区二区三区| 色婷婷综合久久久久中文国产精品一区中文字幕,国产福利电影一区二区三区 | 天久久久噜噜噜久久国产精品爽爽| 亚州一区二区| 亚洲中文日韩精品| 激情自拍 校园春色| 熟妇艹鸡八| 欧美草草| 亚洲一区中文精品| 日本网色| 超碰97最新人妻| 熟女熟妇伦久久影院毛片一区二区| 亚洲女优有码无码高清| 大屁股人妻女教师撅着屁股| 精品一区二区成人| 国产女人高潮嗷嗷嗷叫小说 | 精品少妇99| 青青操网| 激情五月天婷婷| 欧美情色贴图| 成人毛片免费| 99热免费| 99草精| 26uuu偷拍亚洲欧洲综合| 国产人妻天天干精品| 69视频福利导航| 91老司机视频| 九九99精品| 99re69| 大香蕉中文在线| 人干人人人操人人摸| 一道本久久棕合爱| 夜夜高潮夜夜爽国产伦精品| AV和黑人在线播放| 日本不卡卡一区| 91白嫩| 青娱乐国产精品| 伊人色综合超碰| 一区二区视频在看| 偷拍欧美激情| 精品91| 极品色www影院| 久久久久久久久久久久久久久乱码 | 久久9精品视频| 91 丝袜在线播放| 日韩无码成人电影| 97欧美久久久久久久| 黄色人人| 日韩不卡码| 88xx成人精品视频| 欧美日韩理论一区| 日本国产亚洲一区在线观看| 男人天堂日日夜夜| 曰韩中文人妻视频| 激情抓乳插进去啪啪啪日韩| 69精品人人人人| 欧美一区二区三区不卡高清视频| 插入综合网| 四虎影视欧美| 亚洲色鬼| 中文字幕在线免费观看2| 婷婷色色五月天| 色原狠狠天天天| JULIA一区二区三区在线播放| 亚洲啪啪啪啪视香蕉| 园内精品自拍视频在线播放| 神马久久69| 福利社区午夜一区二区| 国产精品一区二区麻豆| 国产在线精品偷| www.人人摸在线视频| 日本韩欧美在线播放a| 九九九九热只有精品| 欧美激情综合色综合啪啪五月| 欧美三四五区| 亚洲性爱高潮影院| 老司机福利青青草| 粉嫩AV一区夜夜嗨| 国产精品制服丝袜清纯唯美| 亚洲色图欧美色18直播在线| 97午夜剧场日韩| 在线日韩精品一区二区三区| 激情综合av| 日本成人A片免费看| 91在线/欧洲| 五月天婷精品激情| 久久系列| 九九九热精品| 五月综合激情网| 午夜福利无毒不卡| 女人18精品一区二区三区| 91美女中出| AAAA欧美日韩| 偷拍视频青青草在线视频| 91精品国产综合久久久蜜臀| 国产情侣自拍在线播放| 日韩欧美加勒比| 久久久中文版| 9l视频自拍9l九色成人| 欧美性生活男人的天堂| 综合网天天| 亚洲综合夜色| 男人的天堂在线2| 亚洲欧美在线观看免费| 精品黄色电影| 青青草无码视频| 中文字幕在线观看丝袜| 综合五月婷婷亚洲一区| 屁股久久久久久| 麻豆精品三区视频| 少妇无码太爽| 国产av高清版| 最新日韩黄片| 亚洲精品一区二区精品| 日韩电影在线观看网址| 亚洲激情久久久伊人综合| 麻豆久久久久久久久丝袜| 亚洲熟女av日韩熟女| 久久产精品一区二区三区电影| 色色青青久久| 久久久久无码一妻区| 欧美成人9797| 性爱视频久久| 亚洲日韩精品在线播放| 久久久久亚洲av综合波多野制衣| 午夜性刺激视频免费观看| 亚洲资源吧| 亚洲自拍一区夜夜操| 狠日操| 亚洲av青草久久一区二区| 日本性一区| 欧美亚洲20p| www.天天干| 人人摸人人添人人操| 人人搞人人插人人操| 久热精品在线| 欧美国产操逼| 亚州高清色综合| 一本正道久久熟女| 丰满翘臀美女影院视频| 欧美色三级片91| 久综合网| · —级AA伦aa坐爱午夜极速ⅴA一区天天噪天天噪天天噪 | 欧美日韩理论一区| 亚洲成人一二三区| 久草色悠悠在线视频| 人妻久久久久久久久久久久久久久| 女人喷水视频在线观看| 国产三级日产三级韩国三级| 成人性爱美曰韩| 国产理论视频在线播放| 天堂无码精品国产久| 精品丝袜无码一区二区三APP| 丁香九月婷婷| 久久久久久亚洲Av无码精| 久久久新亚洲AV| 青草精品视频日本久久久久网站在线| 久久这里只精品| 九t超碰| 我要色综合网站| 久久这里只精品| 蜜臀99精品国产高清在线观看| 中国和日本人色哪个不下载能放| 久久精品无码一区二区三区| 国产v亚洲v日韩v欧美v片另类| 免费精品无码一级毛片牛牛影视 | 99色网| 欧美精品97| 啊啊啊啊操死我| 大香蕉一级黄色片久久| 国产传媒操逼视频| 九九亚洲| www老逼91| 亚洲蜜臀懂色| 超碰久草| V A在线| 日韩激情毛片一级久久久| 亚洲一区二区三区不卡国产欧美| 亚州操逼网| 日韩99神马视频播放片在线播放| 久久成年精品| 久久精品无码不卡| 亚洲97超碰| 亚洲日韩精品久久久久一区壹牛| 成人性爱全视频观看| 色婷网| 99国产精品人妻人伦| 蜜桃在线观看一区二区三区| 亚州五月| 日韩免费看在线黄色片| 国产精品国产拍高清AV| 大香蕉伊在线久草麻豆天堂故事| 96一区二区三区| 老熟女乱伦片| 日本女人操逼| 欧美在线l亚洲| 美女t无毒不卡不卡| 蜜臀无码视频在线观看| 久久三| 超碰 97国产熟女| 欧美精品久久| 69精品| 亚洲 日韩 丝袜 熟女 变态| 狠狠中文字幕| 国偷自 一区| 无码精品久久久久久亚洲| 亚洲中文日韩欧美大香蕉视频| 九九激情网| 日本99一区二区| 黑人在线91| 密乳无码| 国产性感骚丝袜在线| 女同性恋中文字幕| 欧美一区二区三区入口| 中文字幕一区 二 区 三 四 五 区日 日 骚| 91丝袜激情在线| 欧美激情亚洲| 亚洲精品无码久久AV| 青青草日韩无码| 98精品国产乱码久久久久久| 五月丁香黄色网| 亚洲天堂99| 1区2区3区中文字幕日韩| 欧美另类丝袜熟女| 手机在线中文字幕国产| 女一区二区| 91性片| 手机在线观看不卡无码av| 日本一区二区三区精品| 欧美日韩在线小说 | 91人妻在线视频| 国产亚洲国产超碰| 午夜毛片高清免费不卡| 美女熟妇色| 九九热男人天堂| 男人的天堂va在线| 狠狠干综合| 国内外激情在线| 日韩欧美字幕亚洲一区二区| 天天舔天天日天天射| 91久久久亚洲| 日本免费不卡二区| 老熟女网站| 98色网| 天天做天天爱| 欧亚乱色熟一区二区三四区| 亚洲一区中文精品| 色 婷97| 国产拍偷精品网站| 丝袜性亚洲| 成人26uuu| 欧美色图天堂网m| www熟女乱伦com| 嗯嗯嗯啊啊啊操的我好爽 | 992大香蕉| 91chinese在线| 欧中美三级一区二区三区| 中文字幕在线高清男人的天堂| 色色青青久久| 亚洲精品99| 亚洲日韩电影| 欧美 综合| 亚洲精品国产无码高清| 2026国产精品视频| 中文字幕在线高清男人的天堂 | 风骚少妇视频中文字幕| 性爱乱伦一区| 久久久久久久| 天天亚洲综合| 秋霞免费AV| 天天影视综合色| 97色色色| 情趣丝袜无码操逼视频| 60秒免费视频| 婷婷久久综合| a一区二区三区乱码在线| 夜夜免费视频| 囯产精品一区二区三区线|亚洲人成无码网WWW动漫|国产精品免费一级... | 欧美性第1页| 啊啊啊好爽快点啊啊啊嗯嗯| 成人97人人超碰人人| 九九九精品一区二区无码| 色一射色一射| 欧美综合网站999| 黄片免费日韩| 日韩av电影网站| 久久伊人五月天| av三级电影在线播放| 欧美午夜色妇色鬼| 欧美一级黄片免费播放| 欧美少妇色图| 偷拍2020| 久久一二三级一一一| 看黄片视频免费| 97久久国产| 国产精品天干天干综合网麻豆| 日韩成人电影AV| 免费无码婬片AAAA片直播色戒| 中文久久96| 五月天婷婷影院| 亚洲一区二区三区不卡国产欧美| 久草这里只有精品| 中文字幕、久久精品国产2020、久久综合久久自在自线精品自、亚洲 | 久久久国产三级黄色片| 91东京热男人的天堂| 97操B| 色超碰综合| 蜜桃久久综合视频| 日本在线不卡v二区| 一级做受视频免费是看美女| 啊啊啊啊好多水| 亚洲凸凹超碰成人| 亚洲第一页欧美| 精品色色| 97五月天| 丁香五月久久| 国产精品岛国片在线观看| 亚洲乱码精品一区二区| 日逼97| 久久久久婷婷精品av电影| 大香蕉十区| 欧美国产日韩清纯唯美| 国产成人在线观看综合| 中文字幕精品日韩中文字幕| 国产亚洲福利第一页丝袜| 美欧色综合| 综合网少妇| 欧美青青草视频| 神马久久69| 91五月天| 亚洲成人妻日韩在线| 伊人久久大香蕉线AV五月天| 午夜激情床戏激情| 少妇免费视频| 日韩AV色图| 亚洲图片欧美91N| 婷婷色在线| 欧美亚洲中文字幕| 中文字幕亚洲热播人妻| 欧美色性爱| 免费观看国产小粉嫩喷水精品午| 久久草草亚洲蜜桃臀| 91伊人| 国语精品对白| 97在线视频网站| 日韩大香蕉| 99re9| 丁香五六月啪啪| 日韩无码视频黄色| 99re免费视频精品全部| 操婢日韩| 日韩 欧美 另类 人妻| 欧美亚洲| 色婷婷香蕉| 开心激情婷婷| 欧洲综合视频| 97色妞| 性交一区二区在线播放| 欲色啪| 欧美同性恋 的搜索结果 - 91n| 国产丁香精品露脸视频| 久久岛国| 一级人妻性爱视频| 亚洲天堂7777| 99热精品在线在线| 99啪啪视频| 日韩少妇无吗| 久久久精选| 国产拍偷精品网站| 国产亚洲日韩在线三区黑人| 在线观看日韩av不卡| 狠狠色丁香| 无码一区二区三区四区五区六区七区八区九区十区视频 | 成功精品影院| 久久曰曰| 精品久久97观看在线视频| 亚洲九九九| 天天摸夜夜添无码小视频| 一区二区三区视频在线观看免费| 欧美操逼一二三区| 人妻 制服 日韩 中文 在线| 后入精品| 人人看人人爰人人操| 伊人久久艹| 蜜奶av| 日韩人妻精品| 久久久性| 偷窥自拍亚洲天堂网爆| 热久久99999| 校园春色亚洲色图| 欧美不卡二区| JULIA人妻风俗店中出电影| 在线小视频| 中文字幕熟女人妻丝袜| AV污污污污| 亚洲天堂性爱| 婷婷五月天色网| 久久无码一区二区二三区性色| 国产自偷自拍一区| 丝袜无码a片| 一二三区精品视频| 日本操逼视频导航| 欧美熟女激情| 99re视频这里只有精品| 色嗨嗨在线| 校园春色综合色| 中国和日本人色哪个不下载能放| 中文字幕91页| 久久深夜无码| 国产又黄又爽| 色色色综合网| 91中文字幕在线观看| www亚洲免费| 久久久久日本视| 欧美亚州综合网图片| 亚洲成人免费中文字幕| 亚洲欧美另类少妇精品| 欧美高清第一页| 色色激情五月天| 超碰到97情色| 操高情无码| 女人的天堂大香蕉网| 97超碰人人模人人拍人人| 五月丁香婷婷综合| 亚洲一区二区中文字幕| 亚洲精品成人| 9久9久| 日韩性爱再线视频| 啊啊啊好疼| 欧美顶级黄色大片免费| 最新av在线| 色色色色日本| 青青草日韩免费观看高清在线| 欧美日韩大黄片| 五月天久久婷婷亚洲| 乱伦日本中文自拍| 欧美美女在线高潮999| AVE乱伦| 国产自产91区13区| 操操操五月天婷婷丁香影院| 色狠狠综合噜一二三区| 在线中文字幕| 大乔未久88一区| 国产精品一级毛片不卡视| 免费成人在线熟妇网| 亚洲第一精品在线视频| 亚洲日韩久久精品一区| 久久久精品视频免费观看| 午夜精品久久久久久久| 久草视频分类在线| 色欧洲| 国产青一二三| 亚洲天天做日日做天天谢日日| 久久熟妇五十路一区| 亚洲国产精品99久久久| 蜜桃精品一区二区三区久在线| 欧美日韩在线国产在线| 综合网亚洲在线| 午夜男人天堂| 涩涩这里只有精品视频| 欧美,日韩,中文,另类| 丁香五月天社区| 欧美91精品国产自产| 国产a级精品| www.99中文字幕| 暴力av在线| 久久欧美按摩999| 色操逼网| 99rre在线精品99re8| 一二三啪啪专区| 密乳无码| 老妇女91| 日韩三级性| 大香蕉伊人色偷偷在线| gogogo免费高清看中国国语| 久久久久久九九九九九九| 久久黄黄| 中日韩熟女| 青青草好吊色| 少妇色综合| Julia在线播放亚洲久久| 亚洲av综合伊人久久| 亚洲极品| 亚洲综合图文| 丁香五月婷婷啪啪| 人妻一二三区| 无码人妻一区二区三区免费九色| 欧美在线视频播放| 夜夜爽77777| 青青操综合网| 亚洲人成网站7777| 大香蕉一人| 国产十八禁视频| 在线观看十八禁| 九九综合久久| 91深夜夜| 国产精品999aaa| 国产又粗又长的视频| 99热导航| 情色日播放AV| 婷婷色影院| 日本免费中文字幕在线| 欧美成人免费在线观看| 久久亚洲一区二区色婷婷| 先锋音影AV| 青青草吊丝| 人妻少妇精品一区二区三区| 搡老人老9丨女老熟人| 人人干人人操人人爱| 欧美的精品的视频| 青草视频人妻在线观看| 夜夜国产一区| 色婷婷综合久久中文字幕雪峰| 91色久| 国产精品免费视频人成| 秋霞一级A片黄色视频| 亚洲阿v天堂在线| 91亚洲欧美激情| 美女天天干| 亚州大图综合色图| 日日妻色网| 成人黄页| 色大香蕉97N| 97超久碰| 在线黄色污污网站| 99热免费精品| 夜夜爽妓女| 不卡超碰护士AV在线免费播放| 色色色热| 欧美男人一区| 人妻激情另类| 神马视频久久久久久| 中文字暮97| 九九热精品免费视频| 亚洲人成网www| 国产欧美伊人| 日本一区二区三区欧美日韩中文字幕| oumeisetu综合| 国产一区二区三区中文字幕| www老逼91| 中文字幕丝袜美腿| 超碰97色色| 动漫av中文| 在线中文AV| 91亚洲狠狠色| 夜夜欧美| 欧美日韩国产成人高清| 中文字幕AV乱伦| 成人精品一区二区91毛片不卡| 欧美一区二区三区入口| 亚洲制服欧美另类内射| 色九九综合| 青青草原人妻| 日本超碰97日韩精品人妻| 亚洲日韩久久精品一区| 尤物AV免费网站| 91丝袜| 久久亚洲中文字幕视频| 免费一级a毛片久久久久久鸭绿欲| 精品中文字幕第一页| 久久一级无码精品毛片6| 动漫爆乳3D奶水一区在线观看| 色综合色欲色综合色综合色综合| 色色五月天婷婷| 啊啊啊啊啊在线观看网址 | 天综合中文| 欧美日韩亚洲高清不卡一区二区三区| 97频视在线| 女人香蕉久久毛毛片精品| 亚洲欧美日韩中文播放| xxxx网站亚洲精品| 99久久9| 久久超碰亚洲人| 加勒比综合a∨| 天美传媒婬乱在| Av手机版天堂网| 黄片在线免费在线观看| 熟妇人妻一区二区三区| 麻豆人妻偷人精品无码视频| 国产精品96| 日本不卡高清视频| 国产天美欧美| 99国产精品自在自在| 97天天| 日韩欧美国产一区二区三区四区| 五月丁香啪| 亚洲综合性网址| 欧美日韩中文字幕人妻| 日韩精品人妻一| 台欧久久精品视频| 加勒比性爱成人在线| 成·人免费午夜在线观看| 亚洲一欧洲中文字幕在线| 97超碰69| 欧美女同在线| 亚洲欧洲日韩天堂av| 青青草操逼逼视频| 十八禁电影伊人网| 精品无码一区二区三区色欲| 超碰在线成人| 97超碰中文| 少妇高潮流水av免费| www久久久| 青操影院| 九月激情婷婷| 福利风月五月天影院| 青青草一区二区高清无码视频| 6080YYY午夜理论片在线观看| 超碰人人妻| 色欧美色交综合| 美女午夜福利免费视频| 青青草在线视频人人想人人上| 91中文字幕在线观看| 国产欧美成人第一页在线观看| 男人天堂2019亚洲| 东北老熟女| 免费强奸av| 亚洲揄拍网| 久久国产熟女影院| 久都青青视频| 国产91 丝袜在线播放00-百度| 久久这里只精品99re66图| 亚洲精品蜜桃久久久久久久| 国产午夜福利专区综合| 色999人与兽| 亚洲另类春色| 亚洲综合春色| 啊啊啊啊啊好多水| 天天摸,夜夜摸| 欧美激情在线观看视频| 国产精品青草综合久久| 国产精品粉嫩福利在线| 无码人妻丰满热妇又大又粗| 久久久久密臀视频| 性爱视频无打码在线观看| 99精品热| 国产av强奸美女| 亚洲一二三| 在线国产福利网址导航| 超碰综合色| 亚洲无码免费看| 亚洲污污网站| 日韩一级成人毛片免费观看 | 婷婷色一区| 久久久96| av婷婷色网| 岛国黄片网站| 欧美A√综合网 | 国产对白刺激视频| 天天操人人操骚逼网站| 少妇xx精品| 国产精品高朝久久久久久久| 日本午夜久久电影| 伊人网高清| 看黑人AV不卡| 国产Av超碰| 热热色色综合| 五月丁香影视| 少妇被c 黄 免费观看| 麻豆久久久久久久久丝袜 | 欧美激情性爱视频网站| 精品妇操一区二区三区| 九月婷婷久久| 91久久久亚洲| 亚洲精品不卡一二三区| 日韩免费性爱视频在线观看| 91综合熟女| 日本不卡二三区| 熟女人妻一区二区三区| 美女91在线| 大香网伊人久久综合| 男人的天堂VA在线| 天天躁日日躁AAA片李宗瑞| 色九九久九九| 97这里都是精品| 欧美成人一区二区三区在线播放| 91A欧美电影网站| 欧美狠狠弄| 国产亚洲精品第一最新| 屁股久久久久久| 欧美日韩一干二干| 久综合网| 精品国产无码中文| 男人的天堂com| 久操大香蕉超碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰 | 中文字幕人乱码中文字的预防方法| 凸凹视频在线观看| 亚洲男人天堂av| 夜夜免费视频| 九九热精品在线| 日美免费黄片| 成人国产视频在线观看| 中文无线日韩一区| 厕所偷拍在线| 99熟女| 巨爆乳一区二区爆乳区| 亚洲性天堂| 97视频在线视频| 嗯啊啊啊轻点视频| 成人性爱电影一区二区| 女人与公拘交酡2020视频| 亚洲清纯综合| 97超碰在线资源网站| 欧美毛片在线网| 国产女人成人精品视频| 国产无码高清操逼视频| 91五十路| 综合亚洲网| 91男人天堂网| 91网站18在线| 东北女人av| 色婷婷电影网| 性爱1区| 亚洲欧综合另类无码一区| 精品人成视频在线观看| 欧成人精品一区二区三区| 大香蕉之青青草原| 色av中文字幕| 日韩av熟女一区二区三区成人| 五月婷婷丁香| 免费强奸av| 国产熟女完整版中字 | 2017,超碰| 二级毛片| 天天综合网~91综合网| 香港日本韩国人妇99www.wccm20| 麻豆亚洲Av成人无码一区精品| 亚洲aV无码成人在线观看| 国产www色在线观看| 久久黄色视频一区二区三区| av毛片aaaaa免费看| 97资源久久| 天天日夜干| 少妇三p| 精品人妻一区二区三区四区| 97天天在线| 激情婷婷五月天| 九色PORNY9l原创自拍| 精品国产久热在线观看| 在免费jIzzjIzz在线视频| 亚洲暴力强奸AV| 人妻 丝袜美腿 中文字幕| 国产人妖的免费的视频| 久久五月天婷婷丁香中文字幕| 人妻偷拍一区二区三区| 亚洲成人在线高清| 老司机深夜18禁污污网站| 热无码中文亚洲H一道本一区二区| 国产 日韩 欧美一区| 新亚洲无码| 18禁在线视频| 性爱乱伦视频免费| 无码天天操| 伊人黄色视频免费观看| 手机看av网站在线看| 国产在线视视频有精品| 婷婷色导航| 嗯嗯啊啊视频在线看| 久草成人福利导航| 久久久久亚洲AV无码专区少妇| 婷婷探花久久精品一区| 成人熟女区| 精品精品精品| 天天躁日日躁XXXXYY| 少妇一级无码精品| 精品人妻一区二区三区四区| 欧美在线l亚洲| 玖色AV| 久热91| 日本韩欧美在线播放a| www.av在线视频| 欧美日产国产在线成人第一区| 99自拍视频在线| 高精欧美色| 超碰97人妻| 久久久一区二区三区三州| 久久成人国产| 伊人久久艹| 欧色综合| 破处bbq| 屁股久久久久久| 欧美一级美片在线观看免费| 成人三一级一片aaa| 色婷婷久久| 亚洲综合性感在线| 国产乱子伦一区二区三区免看| 亚洲天堂一二| 国产亚洲99久久精品熟| 91高清无码下载| 精品区9| 97超碰资源网| 久一区久久蜜桃| 中国一级操逼视频| AV九九| 国产成人主播| 麻豆成人AV| 思思热免费在线视频| 九九热精品| 96国产污污污丝袜| 精品久久久久综合无码| 欧美丰满少妇xx高潮| 啊啊啊啊嗯嗯嗯用力好爽 | 91狠狠色丁香婷婷综合久久| 欧美激情一区二区| 国产又粗又长又大的视频| 久久久精品中文字幕爱豆| 中文字幕一区日韩精| 天堂精品小草| 91亚洲人| 国产亚州高清国产拍精| 日本午夜福利视频| 91N综合网| 黄色一区二区秘书性感| 99久久无色码| 亚洲丨在线| 全免费a敌肛交毛片免费| 日韩精品三区四区| 91少妇高潮| 久干9操| 成人色女网| 国产97在线 | 亚洲| 国产精品自拍欧美在线| 日韩欧美丝袜诱惑| 中文字幕在线观看网址| 3p国产欧美99热| 日韩欧美中文字| 欧美日韩*字幕一区| 色色99| 亚洲有薄码区久久在线一区| 亚洲啪啪视频一区二区| 亚洲黄日韩无码专区| 久久久96| 欧美淫穴| 欧美乱伦专区| 人妻欧美| 色吧综合网| 精品高清牛人盗摄一区二区三区中文字幕A片免费在线观看 | 亚洲天堂加勒比| 极品尤物在线观看| 久久机热| 午夜精品探花| 99福利社| 91干熟女| 久久久久久久久久久久97 | 成人无码在线超碰网| 国产精品久久久久绯色| 熟妇乱伦一区二区| 九九精品美女高溯喷水| 69一区二区| 成人五月香网在线| 精品久久久久久久| 欧州色图区| site:sinbotex.com| 日本人体九九九九九九| 97操| 国产无遮挡| 欧美人人操人人插| 日本99一区二区| 中国国产精品一区视频| 亚洲熟女乱色一区二区三区久久久 | 国产夫妻性生活视频| 手机看片1024你懂的国产| 一本一道久久综合久久| 色婷视频| 少妇综合网| 东京成人一区| 曰本人妻人人澡人人夹| 欧亚日本情色| 黄污污污污| 丝袜人妻av一区二区| 97色涩| 中文字幕精品区先锋资源| 超碰导航97| 99热97| 精品国产一区二区三区四区在线看| 亚洲自拍欧美色综合| 秋霞一级视频在线观看免费| 久久久久久久91| 日少妇亚洲版| 男人a天堂手机在线版| 99re这里只有精品2| 日本韩国一本产品小视频日本韩国一本产品久久久产品小视频日本韩国一本产品久 | 亚洲一本大道中文字幕无码在线| 国产一区二区在线看| 久久99黄色卞西瓜| 蜜乳Av成人片网站| 啊啊啊轻点在线观看| 麻豆久久久一区二区| 91丨九色丨国产丨人妻在线 | 亚洲综合大片| 丝袜美腿诱惑亚洲欧美视频在线观看 | 1204av韩国| 黄片在线免费在线观看| 亚洲精品九九九| 中国小夫妻勾搭露脸淫荡对白| 亚洲国产成人精品无码专区| 丰满人妻-区二区三区| 一区二区 电影 亚洲| 中文字幕性感少妇av| 日本操逼无码| 97久久国产亚洲精品超碰热| 红杏大香蕉| 超碰在线观看av不卡| 亚洲综合网91| 国产精品欧美激在线| 99操视频| 免费av在线播放二区| 99在线观看视频在线高清| 欧美亚综合色图| 国产sv美女内射| 亚洲熟妇无码一区二区三区| 亚洲女毛多水多21P| 亚欧免费观看视频| 久久久久久中文| 少妇同性| 日本色婷婷| 日韩亚洲中文字幕在线| 国产熟女精品一区二区| 蜜乳av一区二区三区| 91网亚洲| www.夜夜操| 精品亚洲黄色片 国产精品导航一区二区| 夜夜躁狠狠躁日日躁av| 美女丝袜激情小说| 人妻出轨一区二区三区| 久/久精品99看9| 波多野42部无码喷潮在线观看 | 少妇三p| 欧美丝袜美女电影一二三四区| 岛国小电影| 91美女丝袜诱惑视频| 人人干人人搞人人摸| 午夜a成v人电影| 激情五月天中文字幕色| 91白虎| 欧美日韩国产色五月综合在线| 97久久久精品| 少妇毛片久久| 中文字幕一区av| 精品人人插人人操| 国产女人操逼视频| 欧美激情视频在线一区| 白丝av| 色欧洲| 好涩综合| 成人三级片一区二区三区视频| 果冻传媒A片一二三区| 91爱看| 91高跟美女在线播放| 性色高清在线| 不卡人妻少妇精品毛片一区23区视频| 91 刺激在线| 五月婷婷丁香| 可以看的av| 色婷婷影院| 99热婷婷| 色999亚洲人成色| 精品人人插人人操| 国产女人成人精品视频| 强奸乱伦动态污图免费 | 日本丝袜人妻内射| 欧美黄色大片在线观看| 中文字幕日韩电影人妻| 夜夜操美女| AV一起草在线| 免费的黄片有限公司| 久久男人精品| 18禁精品网站在线看| 国产成人一级av88| 欧美顶级黄色大片免费| 美中日韩无码| 人妻中文字幕日韩电影| 亚洲制服欧美另类内射| 亚洲欧洲综合视频在线| 天天热精品| 久久9亚洲| 蜜桃午夜视频一区二区| 91N欧美| 精品亚洲国产成人AV制服丝袜| 粉嫩av在线| 久久在肏| 96超碰网| 熟人人妻少妇精品久久| 日韩熟女无码| 天天操福利视频综合网站| 国产偷人妻精品一区二区在线| 精品国产污一区二区三区| 日日干夜夜干| 五月丁香影视| 色色丁香| 青青11操操操操操操操操| 啊啊啊不要好疼视频| 67914亚洲精品| 天天操女人| 青青草精品| 国产精品电影推荐| 射丝袜大香蕉| 熟女熟妇一区二区三四区| 精品丰满熟妇人妻一区 | 人人综合| 免费视频在线观看啊啊啊啊啊| 99九九久久| 九九九九一级| 亚洲男人电影天堂| 北约熟女超碰| 4399成人黄A片| 国产成自自拍在线观看| 97人人夜| 亚洲一区二区三区不卡国产欧美| 色吧五月| 久久日本熟妇熟色高清| 日本男人天堂| 久久亚洲AV成人精品无码| 在线啊啊啊啊|