精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

手寫一個基于 Proxy 的緩存庫

開發 前端
Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。

兩年前,我寫了一篇關于業務緩存的博客 前端 api 請求緩存方案, 這篇博客反響還不錯,其中介紹了如何緩存數據,Promise 以及如何超時刪除(也包括如何構建修飾器)。如果對此不夠了解,可以閱讀博客進行學習。

[[383008]]

但之前的代碼和方案終歸還是簡單了些,而且對業務有很大的侵入性。這樣不好,于是筆者開始重新學習與思考代理器 Proxy。

Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。關于 Proxy 的介紹與使用,建議大家還是看阮一峰大神的 ECMAScript 6 入門 代理篇。

項目演進

任何項目都不是一觸而就的,下面是關于 Proxy 緩存庫的編寫思路。希望能對大家有一些幫助。

proxy handler 添加緩存

當然,其實代理器中的 handler 參數也是一個對象,那么既然是對象,當然可以添加數據項,如此,我們便可以基于 Map 緩存編寫 memoize 函數用來提升算法遞歸性能。

 

  1. type TargetFun<V> = (...args: any[]) => V 
  2.  
  3. function memoize<V>(fn: TargetFun<V>) { 
  4.   return new Proxy(fn, { 
  5.     // 此處目前只能略過 或者 添加一個中間層集成 Proxy 和 對象。 
  6.     // 在對象中添加 cache 
  7.     // @ts-ignore 
  8.     cache: new Map<string, V>(), 
  9.     apply(target, thisArg, argsList) { 
  10.       // 獲取當前的 cache 
  11.       const currentCache = (this as any).cache 
  12.        
  13.       // 根據數據參數直接生成 Map 的 key 
  14.       let cacheKey = argsList.toString(); 
  15.        
  16.       // 當前沒有被緩存,執行調用,添加緩存 
  17.       if (!currentCache.has(cacheKey)) { 
  18.         currentCache.set(cacheKey, target.apply(thisArg, argsList)); 
  19.       } 
  20.        
  21.       // 返回被緩存的數據 
  22.       return currentCache.get(cacheKey); 
  23.     } 
  24.   }); 
  25.    

我們可以嘗試 memoize fibonacci 函數,經過了代理器的函數有非常大的性能提升(肉眼可見):

 

  1. const fibonacci = (n: number): number => (n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2)); 
  2. const memoizedFibonacci = memoize<number>(fibonacci); 
  3.  
  4. for (let i = 0; i < 100; i++) fibonacci(30); // ~5000ms 
  5. for (let i = 0; i < 100; i++) memoizedFibonacci(30); // ~50ms 

自定義函數參數

我們仍舊可以利用之前博客介紹的的函數生成唯一值,只不過我們不再需要函數名了:

 

  1. const generateKeyError = new Error("Can't generate key from function argument"
  2.  
  3. // 基于函數參數生成唯一值 
  4. export default function generateKey(argument: any[]): string { 
  5.   try{ 
  6.     return `${Array.from(argument).join(',')}` 
  7.   }catch(_) { 
  8.     throw generateKeyError 
  9.   } 

雖然庫本身可以基于函數參數提供唯一值,但是針對形形色色的不同業務來說,這肯定是不夠用的,需要提供用戶可以自定義參數序列化。

 

  1. // 如果配置中有 normalizer 函數,直接使用,否則使用默認函數 
  2. const normalizer = options?.normalizer ?? generateKey 
  3.  
  4. return new Proxy<any>(fn, { 
  5.   // @ts-ignore 
  6.   cache, 
  7.   apply(target, thisArg, argsList: any[]) { 
  8.     const cache: Map<string, any> = (this as any).cache 
  9.      
  10.     // 根據格式化函數生成唯一數值 
  11.     const cacheKey: string = normalizer(argsList); 
  12.      
  13.     if (!cache.has(cacheKey)) 
  14.       cache.set(cacheKey, target.apply(thisArg, argsList)); 
  15.     return cache.get(cacheKey); 
  16.   } 
  17. }); 

添加 Promise 緩存

在之前的博客中,提到緩存數據的弊端。同一時刻多次調用,會因為請求未返回而進行多次請求。所以我們也需要添加關于 Promise 的緩存。

 

  1. if (!currentCache.has(cacheKey)){ 
  2.   let result = target.apply(thisArg, argsList) 
  3.    
  4.   // 如果是 promise 則緩存 promise,簡單判斷!  
  5.   // 如果當前函數有 then 則是 Promise 
  6.   if (result?.then) { 
  7.     result = Promise.resolve(result).catch(error => { 
  8.       // 發生錯誤,刪除當前 promise,否則會引發二次錯誤 
  9.       // 由于異步,所以當前 delete 調用一定在 set 之后, 
  10.       currentCache.delete(cacheKey) 
  11.      
  12.       // 把錯誤衍生出去 
  13.       return Promise.reject(error) 
  14.     }) 
  15.   } 
  16.   currentCache.set(cacheKey, result); 
  17. return currentCache.get(cacheKey); 

此時,我們不但可以緩存數據,還可以緩存 Promise 數據請求。

添加過期刪除功能

我們可以在數據中添加當前緩存時的時間戳,在生成數據時候添加。

 

  1. // 緩存項 
  2. export default class ExpiredCacheItem<V> { 
  3.   data: V; 
  4.   cacheTime: number; 
  5.  
  6.   constructor(data: V) { 
  7.     this.data = data 
  8.     // 添加系統時間戳 
  9.     this.cacheTime = (new Date()).getTime() 
  10.   } 
  11.  
  12. // 編輯 Map 緩存中間層,判斷是否過期 
  13. isOverTime(name: string) { 
  14.   const data = this.cacheMap.get(name
  15.  
  16.   // 沒有數據(因為當前保存的數據是 ExpiredCacheItem),所以我們統一看成功超時 
  17.   if (!data) return true 
  18.  
  19.   // 獲取系統當前時間戳 
  20.   const currentTime = (new Date()).getTime() 
  21.  
  22.   // 獲取當前時間與存儲時間的過去的秒數 
  23.   const overTime = currentTime - data.cacheTime 
  24.  
  25.   // 如果過去的秒數大于當前的超時時間,也返回 null 讓其去服務端取數據 
  26.   if (Math.abs(overTime) > this.timeout) { 

到達這一步,我們可以做到之前博客所描述的所有功能。不過,如果到這里就結束的話,太不過癮了。我們繼續學習其他庫的功能來優化我的功能庫。

添加手動管理

通常來說,這些緩存庫都會有手動管理的功能,所以這里我也提供了手動管理緩存以便業務管理。這里我們使用 Proxy get 方法來攔截屬性讀取。

 

  1.  return new Proxy(fn, { 
  2.   // @ts-ignore 
  3.   cache, 
  4.   get: (target: TargetFun<V>, property: string) => { 
  5.      
  6.     // 如果配置了手動管理 
  7.     if (options?.manual) { 
  8.       const manualTarget = getManualActionObjFormCache<V>(cache) 
  9.        
  10.       // 如果當前調用的函數在當前對象中,直接調用,沒有的話訪問原對象 
  11.       // 即使當前函數有該屬性或者方法也不考慮,誰讓你配置了手動管理呢。 
  12.       if (property in manualTarget) { 
  13.         return manualTarget[property] 
  14.       } 
  15.     } 
  16.     
  17.     // 當前沒有配置手動管理,直接訪問原對象 
  18.     return target[property] 
  19.   }, 
  20.  
  21.  
  22. export default function getManualActionObjFormCache<V>( 
  23.   cache: MemoizeCache<V> 
  24. ): CacheMap<string | object, V> { 
  25.   const manualTarget = Object.create(null

當前情況并不復雜,我們可以直接調用,復雜的情況下還是建議使用 Reflect 。

添加 WeakMap

我們在使用 cache 時候,我們同時也可以提供 WeakMap ( WeakMap 沒有 clear 和 size 方法),這里我提取了 BaseCache 基類。

 

  1. export default class BaseCache<V> { 
  2.   readonly weak: boolean; 
  3.   cacheMap: MemoizeCache<V> 
  4.  
  5.   constructor(weak: boolean = false) { 
  6.     // 是否使用 weakMap 
  7.     this.weak = weak 
  8.     this.cacheMap = this.getMapOrWeakMapByOption() 
  9.   } 
  10.  
  11.   // 根據配置獲取 Map 或者 WeakMap 
  12.   getMapOrWeakMapByOption<T>(): Map<string, T> | WeakMap<object, T>  { 
  13.     return this.weak ? new WeakMap<object, T>() : new Map<string, T>() 
  14.   } 

之后,我添加各種類型的緩存類都以此為基類。

添加清理函數

在緩存進行刪除時候需要對值進行清理,需要用戶提供 dispose 函數。該類繼承 BaseCache 同時提供 dispose 調用。

 

  1. export const defaultDispose: DisposeFun<any> = () => void 0 
  2.  
  3. export default class BaseCacheWithDispose<V, WrapperV> extends BaseCache<WrapperV> { 
  4.   readonly weak: boolean 
  5.   readonly dispose: DisposeFun<V> 
  6.  
  7.   constructor(weak: boolean = false, dispose: DisposeFun<V> = defaultDispose) { 
  8.     super(weak) 
  9.     this.weak = weak 
  10.     this.dispose = dispose 
  11.   } 
  12.  
  13.   // 清理單個值(調用 delete 前調用) 
  14.   disposeValue(value: V | undefined): void { 
  15.     if (value) { 
  16.       this.dispose(value) 
  17.     } 
  18.   } 
  19.  
  20.   // 清理所有值(調用 clear 方法前調用,如果當前 Map 具有迭代器) 
  21.   disposeAllValue<V>(cacheMap: MemoizeCache<V>): void { 
  22.     for (let mapValue of (cacheMap as any)) { 
  23.       this.disposeValue(mapValue?.[1]) 
  24.     } 
  25.   } 

當前的緩存如果是 WeakMap,是沒有 clear 方法和迭代器的。個人想要添加中間層來完成這一切(還在考慮,目前沒有做)。如果 WeakMap 調用 clear 方法時,我是直接提供新的 WeakMap 。

 

  1. clear() { 
  2.   if (this.weak) { 
  3.     this.cacheMap = this.getMapOrWeakMapByOption() 
  4.   } else { 
  5.     this.disposeAllValue(this.cacheMap) 
  6.     this.cacheMap.clear!() 
  7.   } 

添加計數引用

在學習其他庫 memoizee 的過程中,我看到了如下用法:

  1. memoized = memoize(fn, { refCounter: true }); 
  2.  
  3. memoized("foo", 3); // refs: 1 
  4. memoized("foo", 3); // Cache hit, refs: 2 
  5. memoized("foo", 3); // Cache hit, refs: 3 
  6. memoized.deleteRef("foo", 3); // refs: 2 
  7. memoized.deleteRef("foo", 3); // refs: 1 
  8. memoized.deleteRef("foo", 3); // refs: 0,清除 foo 的緩存 
  9. memoized("foo", 3); // Re-executed, refs: 1 

于是我有樣學樣,也添加了 RefCache。

 

  1. export default class RefCache<V> extends BaseCacheWithDispose<V, V> implements CacheMap<string | object, V> { 
  2.     // 添加 ref 計數 
  3.   cacheRef: MemoizeCache<number> 
  4.  
  5.   constructor(weak: boolean = false, dispose: DisposeFun<V> = () => void 0) { 
  6.     super(weak, dispose) 
  7.     // 根據配置生成 WeakMap 或者 Map 
  8.     this.cacheRef = this.getMapOrWeakMapByOption<number>() 
  9.   } 
  10.    
  11.  
  12.   // get has clear 等相同。不列出 
  13.    
  14.   delete(key: string | object): boolean { 
  15.     this.disposeValue(this.get(key)) 
  16.     this.cacheRef.delete(key
  17.     this.cacheMap.delete(key
  18.     return true
  19.   } 
  20.  
  21.  
  22.   set(key: string | object, value: V): this { 
  23.     this.cacheMap.set(key, value) 
  24.     // set 的同時添加 ref 
  25.     this.addRef(key

同時修改 proxy 主函數:

 

  1. if (!currentCache.has(cacheKey)) { 
  2.   let result = target.apply(thisArg, argsList) 
  3.  
  4.   if (result?.then) { 
  5.     result = Promise.resolve(result).catch(error => { 
  6.       currentCache.delete(cacheKey) 
  7.       return Promise.reject(error) 
  8.     }) 
  9.   } 
  10.   currentCache.set(cacheKey, result); 
  11.  
  12.   // 當前配置了 refCounter 
  13. else if (options?.refCounter) { 
  14.   // 如果被再次調用且當前已經緩存過了,直接增加        
  15.   currentCache.addRef?.(cacheKey) 

添加 LRU

LRU 的英文全稱是 Least Recently Used,也即最不經常使用。相比于其他的數據結構進行緩存,LRU 無疑更加有效。

這里考慮在添加 maxAge 的同時也添加 max 值 (這里我利用兩個 Map 來做 LRU,雖然會增加一定的內存消耗,但是性能更好)。

如果當前的此時保存的數據項等于 max ,我們直接把當前 cacheMap 設為 oldCacheMap,并重新 new cacheMap。

 

  1. set(key: string | object, value: V) { 
  2.   const itemCache = new ExpiredCacheItem<V>(value) 
  3.   // 如果之前有值,直接修改 
  4.   this.cacheMap.has(key) ? this.cacheMap.set(key, itemCache) : this._set(key, itemCache); 
  5.   return this 
  6.  
  7. private _set(key: string | object, value: ExpiredCacheItem<V>) { 
  8.   this.cacheMap.set(key, value); 
  9.   this.size++; 
  10.  
  11.   if (this.size >= this.max) { 
  12.     this.size = 0; 
  13.     this.oldCacheMap = this.cacheMap; 
  14.     this.cacheMap = this.getMapOrWeakMapByOption() 
  15.   } 

重點在與獲取數據時候,如果當前的 cacheMap 中有值且沒有過期,直接返回,如果沒有,就去 oldCacheMap 查找,如果有,刪除老數據并放入新數據(使用 _set 方法),如果都沒有,返回 undefined.

 

  1. get(key: string | object): V | undefined { 
  2.   // 如果 cacheMap 有,返回 value 
  3.   if (this.cacheMap.has(key)) { 
  4.     const item = this.cacheMap.get(key); 
  5.     return this.getItemValue(key, item!); 
  6.   } 
  7.  
  8.   // 如果 oldCacheMap 里面有 
  9.   if (this.oldCacheMap.has(key)) { 
  10.     const item = this.oldCacheMap.get(key); 
  11.     // 沒有過期 
  12.     if (!this.deleteIfExpired(key, item!)) { 
  13.       // 移動到新的數據中并刪除老數據 
  14.       this.moveToRecent(key, item!); 
  15.       return item!.data as V; 
  16.     } 
  17.   } 
  18.   return undefined 
  19.  
  20.  
  21. private moveToRecent(key: string | object, item: ExpiredCacheItem<V>) { 
  22.   // 老數據刪除 
  23.   this.oldCacheMap.delete(key); 

整理 memoize 函數

事情到了這一步,我們就可以從之前的代碼細節中解放出來了,看看基于這些功能所做出的接口與主函數。

 

  1. // 面向接口,無論后面還會不會增加其他類型的緩存類 
  2. export interface BaseCacheMap<K, V> { 
  3.   delete(key: K): boolean; 
  4.  
  5.   get(key: K): V | undefined; 
  6.  
  7.   has(key: K): boolean; 
  8.  
  9.   set(key: K, value: V): this; 
  10.  
  11.   clear?(): void; 
  12.  
  13.   addRef?(key: K): void; 
  14.  
  15.   deleteRef?(key: K): boolean; 
  16.  
  17. // 緩存配置 
  18. export interface MemoizeOptions<V> { 
  19.   /** 序列化參數 */ 
  20.   normalizer?: (args: any[]) => string; 
  21.   /** 是否使用 WeakMap */ 
  22.   weak?: boolean; 
  23.   /** 最大毫秒數,過時刪除 */ 
  24.   maxAge?: number; 
  25.   /** 最大項數,超過刪除  */ 

最終的 memoize 函數其實和最開始的函數差不多,只做了 3 件事

  • 檢查參數并拋出錯誤
  • 根據參數獲取合適的緩存
  • 返回代理

 

  1. export default function memoize<V>(fn: TargetFun<V>, options?: MemoizeOptions<V>): ResultFun<V> { 
  2.   // 檢查參數并拋出錯誤 
  3.   checkOptionsThenThrowError<V>(options) 
  4.  
  5.   // 修正序列化函數 
  6.   const normalizer = options?.normalizer ?? generateKey 
  7.  
  8.   let cache: MemoizeCache<V> = getCacheByOptions<V>(options) 
  9.  
  10.   // 返回代理 
  11.   return new Proxy(fn, { 
  12.     // @ts-ignore 
  13.     cache, 
  14.     get: (target: TargetFun<V>, property: string) => { 
  15.       // 添加手動管理 
  16.       if (options?.manual) { 
  17.         const manualTarget = getManualActionObjFormCache<V>(cache) 
  18.         if (property in manualTarget) { 
  19.           return manualTarget[property] 
  20.         } 
  21.       } 
  22.       return target[property] 
  23.     }, 
  24.     apply(target, thisArg, argsList: any[]): V { 

完整代碼在 memoizee-proxy 中。大家自行操作與把玩。

下一步

測試

測試覆蓋率不代表一切,但是在實現庫的過程中,JEST 測試庫給我提供了大量的幫助,它幫助我重新思考每一個類以及每一個函數應該具有的功能與參數校驗。之前的代碼我總是在項目的主入口進行校驗,對于每個類或者函數的參數沒有深入思考。事實上,這個健壯性是不夠的。因為你不能決定用戶怎么使用你的庫。

Proxy 深入

事實上,代理的應用場景是不可限量的。這一點,ruby 已經驗證過了(可以去學習《ruby 元編程》)。

開發者使用它可以創建出各種編碼模式,比如(但遠遠不限于)跟蹤屬性訪問、隱藏屬性、阻止修改或刪除屬性、函數參數驗證、構造函數參數驗證、數據綁定,以及可觀察對象。

當然,Proxy 雖然來自于 ES6 ,但該 API 仍需要較高的瀏覽器版本,雖然有 proxy-pollfill ,但畢竟提供功能有限。不過已經 2021,相信深入學習 Proxy 也是時機了。

深入緩存

緩存是有害的!這一點毋庸置疑。但是它實在太快了!所以我們要更加理解業務,哪些數據需要緩存,理解那些數據可以使用緩存。

當前書寫的緩存僅僅只是針對與一個方法,之后寫的項目是否可以更細粒度的結合返回數據?還是更往上思考,寫出一套緩存層?

小步開發

在開發該項目的過程中,我采用小步快跑的方式,不斷返工。最開始的代碼,也僅僅只到了添加過期刪除功能那一步。

但是當我每次完成一個新的功能后,重新開始整理庫的邏輯與流程,爭取每一次的代碼都足夠優雅。同時因為我不具備第一次編寫就能通盤考慮的能力。不過希望在今后的工作中,不斷進步。這樣也能減少代碼的返工。

其他

函數創建

事實上,我在為當前庫添加手動管理時候,考慮過直接復制函數,因為函數本身是一個對象。同時為當前函數添加 set 等方法。但是沒有辦法把作用域鏈拷貝過去。

雖然沒能成功,但是也學到了一些知識,這里也提供兩個創建函數的代碼。

我們在創建函數時候基本上會利用 new Function 創建函數,但是瀏覽器沒有提供可以直接創建異步函數的構造器,我們需要手動獲取。

 

  1. AsyncFunction = (async x => x).constructor  
  2. foo = new AsyncFunction('x, y, p''return x + y + await p' 
  3. foo(1,2, Promise.resolve(3)).then(console.log) // 6 

對于全局函數,我們也可以直接 fn.toString() 來創建函數,這時候異步函數也可以直接構造的。

 

  1. function cloneFunction<T>(fn: (...args: any[]) => T): (...args: any[]) => T { 
  2.   return new Function('return '+ fn.toString())(); 

鼓勵一下

如果你覺得這篇文章不錯,希望可以給與我一些鼓勵,在我的 github 博客下幫忙 star 一下。

責任編輯:未麗燕 來源: Segmentfault.com
相關推薦

2022-03-09 09:43:01

工具類線程項目

2022-10-31 08:27:53

Database數據數據庫

2015-06-02 10:24:43

iOS網絡請求降低耦合

2015-06-02 09:51:40

iOS網絡請求封裝接口

2020-11-02 08:19:18

RPC框架Java

2021-12-07 06:55:17

節流函數Throttle

2021-03-18 08:04:54

AQS工具CAS

2018-02-08 18:00:49

Spark文件測試

2022-01-26 15:20:00

配置微服務架構

2021-12-09 10:57:19

防抖函數 Debounce

2017-03-02 13:31:02

監控系統

2020-12-13 11:57:57

Nodejs微信開發

2022-01-17 11:50:38

Linux CPULinux 系統

2015-06-02 09:41:00

iOS網絡請求NSURLSessio

2020-09-27 14:13:50

Spring BootJava框架

2022-01-10 11:04:41

單鏈表面試編程

2012-02-01 14:12:55

iOS本地緩存機制

2024-08-02 09:49:35

Spring流程Tomcat

2022-02-06 20:55:39

jsEsbuild項目

2022-02-22 11:12:38

點贊
收藏

51CTO技術棧公眾號

91精品人妻一区二区三区果冻| 国产精品亚发布| 国产69精品99久久久久久宅男| 日韩成人三级视频| 日韩欧美综合视频| jizz性欧美| 蜜桃视频m3u8在线观看| 麻豆视频久久| 99精品视频一区二区| 亚洲国产日韩欧美综合久久| 欧美人与性禽动交精品| 精品无码一区二区三区| 午夜视频在线| 亚洲欧美日韩国产| 欧美伊人久久久久久久久影院| 国产极品jizzhd欧美| 黑人玩弄人妻一区二区三区| 成人免费在线电影| 亚洲综合丁香| 日韩中文字幕亚洲| 欧美人成在线观看| 国产又粗又爽视频| 欧美黄页免费| 91亚洲精品一区二区乱码| 国产精品久久久999| 九九九九九伊人| 国产高清在线观看| 一区二区蜜桃| 黄色一区二区在线观看| 7777精品伊久久久大香线蕉语言 | 免费在线看v| 精品在线观看入口| 精品福利免费观看| 色一情一乱一伦一区二区三区丨 | 欧美一区二区少妇| 国内精品福利| 日韩一区国产二区欧美三区| 日韩久久精品一区二区三区| 国产精品1000| 日本成人在线网站| 国产精品久久久久久久久免费桃花| 欧美激情精品久久久久久大尺度| 亚洲精品mv在线观看| 国产在线视频你懂得| 日本系列欧美系列| 国产午夜精品久久久| 国产精品av免费观看| 亚洲美女综合网| 99欧美视频| 欧美一区二区三区日韩| 99精品视频网站| 91亚洲国产成人精品一区| 影音先锋久久资源网| 亚洲成人av片在线观看| 在线观看av的网址| 国产精品一区二区av白丝下载| 成人高清电影网站| 一区二区三区四区高清精品免费观看| 国产91久久婷婷一区二区| 波多野结衣加勒比| 神马久久午夜| 亚洲aⅴ怡春院| 欧美日韩国产一二| 亚洲一级av毛片| 自拍视频亚洲| 久热精品视频在线观看一区| 手机视频在线观看| 黄色在线论坛| 成人免费视频国产在线观看| 欧美高清激情视频| 欧美日韩偷拍视频| 亚洲电影男人天堂| 欧美日韩国产电影| 国产精品视频网站在线观看| 黄网站免费在线观看| 日韩久久一区二区| 精品国产一区二区三区四区vr | 欧美sss在线视频| 日韩欧美亚洲综合| 国产大尺度在线观看| 亚洲1卡2卡3卡4卡乱码精品| 国产精品精品国产色婷婷| 亚洲欧洲日夜超级视频| 一级久久久久久久| 黄页视频在线91| 操人视频在线观看欧美| 欧美黑吊大战白妞| 亚洲一区不卡| 在线看日韩av| 国产高清999| 免费一级欧美在线大片 | 日韩一级片免费| 奇米色777欧美一区二区| 国产精品白丝jk喷水视频一区| 国产91av视频| 精品在线观看入口| xvideos亚洲| 久草视频精品在线| 石原莉奈在线亚洲三区| 欧美成人中文字幕在线| 欧美大片免费播放器| 亚洲精品大全| 精品久久99ma| 免费成人在线视频网站| 日本暖暖在线视频| 夜夜亚洲天天久久| 三区精品视频观看| 中国av在线播放| 综合在线观看色| 亚洲欧美久久234| 色yeye免费人成网站在线观看| 国产精品伦一区| 欧美日韩精品免费观看| 自拍视频在线网| 国产日韩v精品一区二区| 91视频免费进入| 天堂在线中文| 国产一区二区三区四区五区入口| 18性欧美xxxⅹ性满足| 国产传媒免费在线观看| 久久av免费| 久热爱精品视频线路一| 国产日韩久久久| 日韩午夜精品| 久久福利视频网| 免费黄色av片| 亚洲一区二区三区高清| 国产成人在线一区| www夜片内射视频日韩精品成人| 精品亚洲porn| 精品一区二区不卡| 日本一本在线免费福利| 欧美日韩国产综合一区二区| 亚洲午夜久久久久久久久| 一区二区亚洲视频| 91麻豆精品国产91久久久久久| 中文字幕在线观看第三页| 666av成人影院在线观看| 黑人巨大精品欧美一区二区一视频 | 中文字幕丰满孑伦无码专区| 操欧美女人视频| 欧美精品乱码久久久久久| 少妇一级淫免费放| 日韩一区免费| 亚洲国产精品久久久久秋霞不卡| 成年人网站在线观看视频| 日本一本不卡| 欧美一级久久久| 丝袜熟女一区二区三区| 经典一区二区| 精品国产百合女同互慰| 五十路六十路七十路熟婆| 午夜性色一区二区三区免费视频| 久久av红桃一区二区小说| 国产精华7777777| 久久久高清一区二区三区| 欧美二区三区在线| 午夜影视一区二区三区| 欧美日韩精品一区二区三区四区| asian性开放少妇pics| 亚洲欧美日韩精品一区二区| 精品国产乱码久久久久久蜜柚| www.在线播放| 精品视频1区2区3区| 国产成人精品一区二区三区在线观看 | 久草视频国产在线| 伊人久久亚洲| 性色av香蕉一区二区| 国产一级片毛片| 99久久99久久免费精品蜜臀| 喜爱夜蒲2在线| 北条麻妃在线一区二区免费播放 | 国产精品裸体一区二区三区| 天堂av资源在线| 中文在线免费一区三区高中清不卡| 欧美二区三区| 97久久网站| 亚洲激情在线观看| 在线观看免费av片| 国产欧美一区二区精品久导航| 艳母动漫在线观看| 中老年在线免费视频| 亚洲精品一区中文字幕乱码| 日日夜夜狠狠操| 成人免费视频国产在线观看| 美女福利视频在线| 日韩在线亚洲| 欧美成人精品一区| 理论片中文字幕| 欧美午夜美女看片| 调教驯服丰满美艳麻麻在线视频| 欧美一区网站| 99热最新在线| 免费日本一区二区三区视频| 亚洲欧美一区二区不卡| www.51色.com| 国产欧美日韩亚洲一区二区三区| 日韩偷拍一区二区| 四虎在线精品| 97免费视频在线| 在线观看h片| 亚洲第一区第一页| 自拍偷拍校园春色| 99re热这里只有精品视频| 青青草精品视频在线观看| 欧美特黄一区| 国产成人精品av在线| 久cao在线| 亚洲欧美制服中文字幕| 欧美国产日韩在线观看成人| zzijzzij亚洲日本少妇熟睡| 免费一级淫片aaa片毛片a级| 伦理一区二区| 国语自产在线不卡| 国自产拍在线网站网址视频| 欧美放荡的少妇| 久草视频一区二区| 99久久婷婷国产综合精品电影 | 日本欧美一区二区三区乱码| 超级碰在线观看| 欧美系列电影免费观看| 精品日韩电影| 亚洲国产aⅴ精品一区二区| 国产成人高清激情视频在线观看| 国产丝袜在线播放| 精品1区2区3区| 国产区一区二区三| 亚洲视频在线观看三级| 欧洲美熟女乱又伦| 免费观看在线色综合| 麻豆精品蜜桃一区二区三区| 中文字幕资源网在线观看免费| 久久成人在线视频| 日本中文在线观看| 国产一区二区三区18| 成人免费区一区二区三区| 91社区在线播放| 久久久免费视频网站| 欧美中文字幕一区二区| 久久五月天婷婷| 久久久精品国产**网站| 成人欧美一区二区三区在线观看| av电影免费在线看| 亚洲国产高清福利视频| 国产丰满果冻videossex| 亚洲美女淫视频| 人妻 丝袜美腿 中文字幕| 丝袜亚洲另类欧美综合| 凹凸国产熟女精品视频| 波多野结衣在线一区二区| 国产男女猛烈无遮挡91| 欧洲成人一区| 欧美精品免费播放| 国产成人l区| 不卡伊人av在线播放| 搞黄网站在线观看| 欧美成人精品激情在线观看| 国产激情视频在线| 色综合久综合久久综合久鬼88 | 777亚洲妇女| 91九色蝌蚪91por成人| 精品露脸国产偷人在视频| 激情五月色婷婷| 狠狠操狠狠色综合网| 毛片毛片女人毛片毛片| 色婷婷久久久久swag精品| 91精品久久久久久久久久久久| 久久久久99精品一区| 国产又粗又猛又爽又黄av| 国产精品网站在线播放| 亚洲欧美另类日本| 91麻豆文化传媒在线观看| 国产精品无码网站| 美腿丝袜在线亚洲一区| 青青在线免费观看视频| 国产精品www.| 免费一级特黄毛片| 999久久久国产精品| 国产亚洲二区| 激情久久免费视频| av资源一区二区| 香蕉视频一区| av一区和二区| 天堂99x99es久久精品免费| 国内外成人免费视频| 国产一区三区在线播放| 蜜桃麻豆www久久国产精品| 久久伦理在线| www.国产在线视频| 久久一综合视频| 色91精品久久久久久久久| 国产69精品久久久久777| 九热视频在线观看| 国产麻豆精品一区二区| 一级黄色香蕉视频| 久久99精品久久久久久国产越南 | 国产玖玖精品视频| 日本免费精品| 欧美一区2区三区4区公司二百| 一区二区三区自拍视频| 欧美日韩在线一区二区三区| 欧美wwwww| 激情伊人五月天| 亚洲欧美日韩高清在线| 在线精品日韩| 国产精品密蕾丝视频下载| 亚洲人成影视在线观看| 精品九九在线| 国产乱淫av片杨贵妃| 日本不卡的三区四区五区| 乱码一区二区三区| 国产欧美精品日韩区二区麻豆天美| 中文字幕黄色网址| 亚洲成人动漫av| 中文字幕亚洲欧美日韩| 欧美性xxxxxx| 亚洲精品视频专区| 精品国产电影一区二区| 午夜视频成人| 国语自产精品视频在线看抢先版图片| 国产精品久久久久77777丨| 超碰97在线资源| 久久要要av| 国产 欧美 日韩 一区| 美女网站在线免费欧美精品| 波多野结衣影院| 中文字幕亚洲在| 波多野结衣高清视频| 亚洲精品国产电影| 久久亚洲资源| 最好看的2019年中文视频| 在线中文资源天堂| 欧美在线视频网| 日本黄色免费在线| 国产精品专区第二| 精品视频97| 国产免费人做人爱午夜视频| 亚洲综合不卡| 999精品免费视频| 亚洲综合一区在线| 99热这里只有精品1| 久久精品夜夜夜夜夜久久| 性国产高清在线观看| 国产精品老女人视频| 九色成人搞黄网站| 欧美日韩亚洲在线| 亚洲自啪免费| 久久久久久国产精品无码| 精品女同一区二区三区在线播放| 亚洲AV无码国产精品午夜字幕| 久久精品在线视频| 欧美成人黄色| 性欧美精品一区二区三区在线播放| 午夜在线观看免费一区| 精品人妻一区二区三区视频| 亚洲成人7777| 日韩欧美电影在线观看| 羞羞色国产精品| 色婷婷久久久| 久草资源站在线观看| 久久综合久久鬼色中文字| 国产一二三四视频| 亚洲精品亚洲人成人网| 国产精品免费无遮挡| 久久亚洲精品国产亚洲老地址| 色综合久久久| 欧美h视频在线观看| 国产一区二区不卡在线| 国产高清在线免费观看| 日韩视频中午一区| 18+激情视频在线| 999国内精品视频在线| 99热这里只有精品8| 国产全是老熟女太爽了| 色偷偷成人一区二区三区91| 国产日韩精品在线看| 成人国产精品日本在线| 国产无遮挡裸体免费久久| 久久综合狠狠综合久久综青草| 美女尤物久久精品| 第一区免费在线观看| 激情六月婷婷综合| 久久精品国产亚洲av香蕉 | 国产aⅴ一区二区三区| 欧美精品激情在线| 91久久久久久白丝白浆欲热蜜臀| 日韩欧美三级电影| 国产伦理精品不卡| 国产性一乱一性一伧一色| 日韩欧美亚洲综合| 日韩在线资源| 国产无套精品一区二区| 欧美专区在线| 性欧美疯狂猛交69hd| 亚洲国产成人精品电影| 国产一区二区精品调教| 国产小视频免费| a级高清视频欧美日韩| 在线免费观看av片| 欧美人与性动交a欧美精品|