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

Redux異步方案選型

開發 架構
作為react社區最熱門的狀態管理框架,相信很多人都準備甚至正在使用Redux。本文會從一些常見的Redux異步方案出發,介紹它們的優缺點,進而討論一些與異步相伴的常見場景,幫助你在選型時更好地權衡利弊。

作為react社區最熱門的狀態管理框架,相信很多人都準備甚至正在使用Redux。

由于Redux的理念非常精簡,沒有追求大而全,這份架構上的優雅卻在某種程度上傷害了使用體驗:不能開箱即用,甚至是異步這種最常見的場景也要借助社區方案。

如果你已經挑花了眼,或者正在挑但不知道是否適合,或者已經挑了但不知道會不會有坑,這篇文章應該適合你。

本文會從一些常見的Redux異步方案出發,介紹它們的優缺點,進而討論一些與異步相伴的常見場景,幫助你在選型時更好地權衡利弊。

簡單方案

redux-thunk:指路先驅

Github:https://github.com/gaearon/redux-thunk

Redux作者Dan寫的中間件,因官方文檔出鏡而廣為人知。

它向我們展示了Redux處理異步的原理,即:

Redux本身只能處理同步的Action,但可以通過中間件來攔截處理其它類型的action,比如函數(Thunk),再用回調觸發普通Action,從而實現異步處理,在這點上所有Redux的異步方案都是類似的。

而它使用起來***的問題,就是重復的模板代碼太多: 

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.     GET_DATA_SUCCESS = 'GET_DATA_SUCCESS'
  4.     GET_DATA_FAILED = 'GET_DATA_FAILED'
  5.      
  6. //action creator 
  7. const getDataAction = function(id) { 
  8.     return function(dispatch, getState) { 
  9.         dispatch({ 
  10.             type: GET_DATA,  
  11.             payload: id 
  12.         }) 
  13.         api.getData(id) //注:本文所有示例的api.getData都返回promise對象 
  14.             .then(response => { 
  15.                 dispatch({ 
  16.                     type: GET_DATA_SUCCESS, 
  17.                     payload: response 
  18.                 }) 
  19.             }) 
  20.             .catch(error => { 
  21.                 dispatch({ 
  22.                     type: GET_DATA_FAILED, 
  23.                     payload: error 
  24.                 }) 
  25.             })  
  26.     } 
  27.  
  28. //reducer 
  29. const reducer = function(oldState, action) { 
  30.     switch(action.type) { 
  31.     case GET_DATA :  
  32.         return oldState; 
  33.     case GET_DATA_SUCCESS :  
  34.         return successState; 
  35.     case GET_DATA_FAILED :  
  36.         return errorState; 
  37.     } 
  38.  

這已經是最簡單的場景了,請注意:我們甚至還沒寫一行業務邏輯,如果每個異步處理都像這樣,重復且無意義的工作會變成明顯的阻礙。

另一方面,像GET_DATA_SUCCESS、GET_DATA_FAILED這樣的字符串聲明也非常無趣且易錯。

上例中,GET_DATA這個action并不是多數場景需要的,它涉及我們將會提到的樂觀更新,保留這些代碼是為了和下面的方案做對比

redux-promise:瘦身過頭

由于redux-thunk寫起來實在是太麻煩了,社區當然會有其它輪子出現。redux-promise則是其中比較知名的,同樣也享受了官網出鏡的待遇。

它自定義了一個middleware,當檢測到有action的payload屬性是Promise對象時,就會:

  • 若resolve,觸發一個此action的拷貝,但payload為promise的value,并設status屬性為"success"
  • 若reject,觸發一個此action的拷貝,但payload為promise的reason,并設status屬性為"error"

說起來可能有點不好理解,用代碼感受下:

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.  
  4. //action creator 
  5. const getData = function(id) { 
  6.     return { 
  7.         type: GET_DATA, 
  8.         payload: api.getData(id) //payload為promise對象 
  9.     } 
  10.  
  11. //reducer 
  12. function reducer(oldState, action) { 
  13.     switch(action.type) { 
  14.     case GET_DATA:  
  15.         if (action.status === 'success') { 
  16.             return successState 
  17.         } else { 
  18.                return errorState 
  19.         } 
  20.     } 
  21.  

進步巨大! 代碼量明顯減少! 就用它了! ?

請等等,任何能明顯減少代碼量的方案,都應該小心它是否過度省略了什么東西,減肥是好事,減到骨頭就殘了。

redux-promise為了精簡而做出的妥協非常明顯:無法處理樂觀更新

場景解析之:樂觀更新

多數異步場景都是悲觀更新(求更好的翻譯)的,即等到請求成功才渲染數據。而與之相對的樂觀更新,則是不等待請求成功,在發送請求的同時立即渲染數據。

最常見的例子就是微信等聊天工具,發送消息時消息立即進入了對話窗,如果發送失敗的話,在消息旁邊再作補充提示即可。這種交互"樂觀"地相信請求會成功,因此稱作樂觀更新。

由于樂觀更新發生在用戶操作時,要處理它,意味著必須有action表示用戶的初始動作

在上面redux-thunk的例子中,我們看到了GET_DATA, GET_DATA_SUCCESS、GET_DATA_FAILED三個action,分別表示初始動作、異步成功和異步失敗,其中***個action使得redux-thunk具備樂觀更新的能力。

而在redux-promise中,最初觸發的action被中間件攔截然后過濾掉了。原因很簡單,redux認可的action對象是 plain JavaScript objects,即簡單對象,而在redux-promise中,初始action的payload是個Promise。

另一方面,使用status而不是type來區分兩個異步action也非常值得商榷,按照redux對action的定義以及社區的普遍實踐,個人還是傾向于使用不同的type,用同一type下的不同status區分action額外增加了一套隱形的約定,甚至不符合該redux-promise作者自己所提倡的FSA,體現在代碼上則是在switch-case內再增加一層判斷。

redux-promise-middleware:拔亂反正

redux-promise-middleware相比redux-promise,采取了更為溫和和漸進式的思路,保留了和redux-thunk類似的三個action。

示例:

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.     GET_DATA_PENDING = 'GET_DATA_PENDING'
  4.     GET_DATA_FULFILLED = 'GET_DATA_FULFILLED'
  5.     GET_DATA_REJECTED = 'GET_DATA_REJECTED'
  6.      
  7. //action creator 
  8. const getData = function(id) { 
  9.     return { 
  10.         type: GET_DATA, 
  11.         payload: { 
  12.             promise: api.getData(id), 
  13.             data: id 
  14.         } 
  15.     } 
  16.  
  17. //reducer 
  18. const reducer = function(oldState, action) { 
  19.     switch(action.type) { 
  20.     case GET_DATA_PENDING : 
  21.         return oldState; // 可通過action.payload.data獲取id 
  22.     case GET_DATA_FULFILLED :  
  23.         return successState; 
  24.     case GET_DATA_REJECTED :  
  25.         return errorState; 
  26.     } 
  27.  

如果不需要樂觀更新,action creator可以使用和redux-promise完全一樣的,更簡潔的寫法,即:

  1. const getData = function(id) { 
  2.     return { 
  3.         type: GET_DATA, 
  4.         payload: api.getData(id) //等價于 {promise: api.getData(id)} 
  5.     } 
  6.  

此時初始actionGET_DATA_PENDING仍然會觸發,但是payload為空。

相對redux-promise于粗暴地過濾掉整個初始action,redux-promise-middleware選擇創建一個只過濾payload中的promise屬性的XXX_PENDING作為初始action,以此保留樂觀更新的能力。

同時在action的區分上,它選擇了回歸type的"正途",_PENDING、_FULFILLED 、_REJECTED等后綴借用了promise規范 (當然它們是可配置的) 。

它的遺憾則是只在action層實現了簡化,對reducer層則束手無策。另外,相比redux-thunk,它還多出了一個_PENDING的字符串模板代碼(三個action卻需要四個type)。

社區有類似type-to-reducer這樣試圖簡化reducer的庫。但由于reducer和異步action通常是兩套獨立的方案,reducer相關的庫無法去猜測異步action的后綴是什么(甚至有沒有后綴),社區也沒有相關標準,也就很難對異步做出精簡和抽象了。

redux-action-tools:軟文預警

無論是redux-thunk還是redux-promise-middleware,模板代碼都是顯而易見的,每次寫XXX_COMPLETED這樣的代碼都覺得是在浪費生命——你得先在常量中聲明它們,再在action中引用,然后是reducer,假設像redux-thunk一樣每個異步action有三個type,三個文件加起來你就得寫九次!

國外開發者也有相同的報怨:

 有沒有辦法讓代碼既像redux-promise一樣簡潔,又能保持樂觀更新的能力呢?

  1. const GET_DATA = 'GET_DATA'
  2.  
  3. //action creator 
  4. const getData = createAsyncAction(GET_DATA, function(id) { 
  5.     return api.getData(id) 
  6. }) 
  7.  
  8. //reducer 
  9. const reducer = createReducer() 
  10.     .when(getData, (oldState, action) => oldState) 
  11.     .done((oldState, action) => successState) 
  12.     .failed((oldState, action) => errorState) 
  13.     .build()  

redux-action-tools在action層面做的事情與前面幾個庫大同小異:同樣是派發了三個action:GET_DATA/GET_DATA_SUCCESS/GET_DATA_FAILED。這三個action的描述見下表:

type When payload meta.asyncPhase
${actionName} 異步開始前 同步調用參數 'START'
${actionName}_COMPLETED 異步成功 value of promise 'COMPLETED'
${actionName}_FAILED 異步失敗 reason of promise 'FAILED'
 

createAsyncAction參考了redux-promise作者寫的redux-actions ,它接收三個參數,分別是:

  1. actionName 字符串,所有派生action的名字都以它為基礎,初始action則與它同名
  2. promiseCreator 函數,必須返回一個promise對象
  3. metaCreator 函數,可選,作用后面會演示到

目前看來,其實和redux-promise/redux-promise-middleware大同小異。而真正不同的,是它同時簡化了reducer層! 這種簡化來自于對異步行為從語義角度的抽象:

當(when)初始action發生時處理同步更新,若異步成功(done)則處理成功邏輯,若異步失敗(failed)則處理失敗邏輯

抽離出when/done/failed三個關鍵詞作為api,并使用鏈式調用將他們串聯起來:when函數接收兩個參數:actionName和handler,其中handler是可選的,done和failed則只接收一個handler參數,并且只能在when之后調用——他們分別處理`${actionName}_SUCCESS` 和 `${actionName}_FAILED`.

無論是action還是reducer層,XX_SUCCESS/XX_FAILED相關的代碼都被封裝了起來,正如在例子中看到的——你甚至不需要聲明它們!創建一個異步action,然后處理它的成功和失敗情況,事情本該這么簡單。

更進一步的,這三個action默認都根據當前所處的異步階段,設置了不同的meta(見上表中的meta.asyncPhase),它有什么用呢?用場景說話:

場景解析:失敗處理與Loading

它們是異步不可回避的兩個場景,幾乎每個項目會遇到。

以異步請求的失敗處理為例,每個項目通常都有一套比較通用的,適合多數場景的處理邏輯,比如彈窗提示。同時在一些特定場景下,又需要繞過通用邏輯進行單獨處理,比如表單的異步校驗。

而在實現通用處理邏輯時,常見的問題有以下幾種:

1. 底層處理,擴展性不足

  1. function fetchWrapper(args) { 
  2.     return fetch.apply(fetch, args) 
  3.         .catch(commonErrorHandler) 
  4.  

在較底層封裝ajax庫可以輕松實現全局處理,但問題也非常明顯:

一是擴展性不足,比如少數場景想要繞過通用處理邏輯,還有一些場景錯誤是前端生成而非直接來自于請求;

二是不易組合,比如有的場景一個action需要多個異步請求,但異常處理和loading是不需要重復的,因為用戶不需要知道一個動作有多少個請求。

2. 不夠內聚,侵入業務代碼

  1. //action creator 
  2. const getData = createAsyncAction(GET_DATA, function(id) { 
  3.     return api.getData(id) 
  4.         .catch(commonErrorHandler) //調用錯誤處理函數 
  5. })  

在有業務意義的action層調用通用處理邏輯,既能按需調用,又不妨礙異步請求的組合。但由于通用處理往往適用于多數場景,這樣寫會導致業務代碼變得冗余,因為幾乎每個action都得這么寫。

3. 高耦合,高風險

也有人把上面的方案做個依賴反轉,改為在通用邏輯里監聽業務action:

  1. function commonErrorReducer(oldState, action) { 
  2.     switch(action.type) { 
  3.     case GET_DATA_FAILED: 
  4.     case PUT_DATA_FAILED: 
  5.     //... tons of action type 
  6.         return commonErrorHandler(action
  7.     } 
  8.  

這樣做的本質是把冗余從業務代碼中拿出來集中管理。

問題在于每添加一個請求,都需要修改公共代碼,把對應的action type加進來。且不說并行開發時merge沖突,如果加了一個異步action,但忘了往公共處理文件中添加——這是很可能會發生的——而異常是分支流程不容易被測試發現,等到發現,很可能就是事故而不是bug了。

通過以上幾種常見方案的分析,我認為比較完善的錯誤處理(Loading同理)需要具備如下特點:

  • 面向異步動作(action),而非直接面向請求
  • 不侵入業務代碼
  • 默認使用通用處理邏輯,無需額外代碼
  • 可以繞過通用邏輯

而借助redux-action-tools提供的meta.asyncPhase,可以輕易用middleware實現以上全部需求!

  1. import _ from 'lodash' 
  2. import { ASYNC_PHASES } from 'redux-action-tools' 
  3.  
  4. function errorMiddleWare({dispatch}) { 
  5.   return next => action => { 
  6.     const asyncStep = _.get(action'meta.asyncStep'); 
  7.  
  8.     if (asyncStep === ASYNC_PHASES.FAILED) { 
  9.       dispatch({ 
  10.         type: 'COMMON_ERROR'
  11.         payload: { 
  12.           action 
  13.         } 
  14.       }) 
  15.     } 
  16.      
  17.     next(action); 
  18.   } 
  19.  

以上中間件一旦檢測到meta.asyncStep字段為FAILED的action便觸發新的action去調用通用處理邏輯。面向action、不侵入業務、默認工作 (只要是用createAsyncAction聲明的異步) ! 輕松實現了理想需求中的前三點,那如何定制呢?既然攔截是面向meta的,只要在創建action時支持對meta的自定義就行了,而createAsyncAction的第三個參數就是為此準備的:

  1. import _ from 'lodash' 
  2. import { ASYNC_PHASES } from 'redux-action-tools' 
  3.  
  4. const customizedAction = createAsyncAction( 
  5.   type,  
  6.   promiseCreator, //type 和 promiseCreator此處無不同故省略 
  7.   (payload, defaultMeta) => { 
  8.     return { ...defaultMeta, omitError: true }; //向meta中添加配置參數 
  9.   } 
  10.  
  11. function errorMiddleWare({dispatch}) { 
  12.   return next => action => { 
  13.     const asyncStep = _.get(action'meta.asyncStep'); 
  14.     const omitError = _.get(action'meta.omitError'); //獲取配置參數 
  15.  
  16.     if (!omitError && asyncStep === ASYNC_PHASES.FAILED) { 
  17.       dispatch({ 
  18.         type: 'COMMON_ERROR'
  19.         payload: { 
  20.           action 
  21.         } 
  22.       }) 
  23.     } 
  24.      
  25.     next(action); 
  26.   } 
  27.  

類似的,你可以想想如何處理Loading,需要強調的是建議盡量用增量配置的方式進行擴展,而不要輕易刪除和修改meta.asyncPhase。

比如上例可以通過刪除meta.asyncPhase實現同樣功能,但如果同時還有其它地方也依賴meta.asyncPhase(比如loadingMiddleware),就可能導致本意是定制錯誤處理,卻改變了Loading的行為,客觀來講這層風險是基于meta攔截方案的***缺點,然而相比多數場景的便利、健壯,個人認為特殊場景的風險是可以接受的,畢竟這些場景在整個開發測試流程容易獲得更多關注。

進階方案

上面所有的方案,都把異步請求這一動作放在了action creator中,這樣做的好處是簡單直觀,且和Flux社區一脈相承(見下圖)。因此個人將它們歸為相對簡單的一類。

下面將要介紹的,是相對復雜一類,它們都采用了與上圖不同的思路,去追求更優雅的架構、解決更復雜的問題

redux-loop:分形! 組合!

眾所周知,Redux是借鑒自Elm的,然而在Elm中,異步的處理卻并不是在action creator層,而是在reducer(Elm中稱update)層:

圖片來源于: https://github.com/jarvisaoie...

這樣做的目的是為了實現徹底的可組合性(composable)。在redux中,reducer作為函數是可組合的,action正常情況下作為純對象也是可組合的,然而一旦涉及異步,當action嵌套組合的時候,中間件就無法正常識別,這個問題讓redux作者Dan也發出感嘆 There is no easy way to compose Redux applications并且開了一個至今仍然open的issue,對組合、分形與redux的故事,有興趣的朋友可以觀摩以上鏈接,甚至了解一下Elm,篇幅所限,本文難以盡述。

而redux-loop,則是在這方面的一個嘗試,它更徹底的模仿了Elm的模式:引入Effects的概念并將其置入reducer,官方示例如下:

  1. import { Effects, loop } from 'redux-loop'
  2. import { loadingStart, loadingSuccess, loadingFailure } from './actions'
  3.  
  4. export function fetchDetails(id) { 
  5.   return fetch(`/api/details/${id}`) 
  6.     .then((r) => r.json()) 
  7.     .then(loadingSuccess) 
  8.     .catch(loadingFailure); 
  9.  
  10. export default function reducer(state, action) { 
  11.   switch (action.type) { 
  12.     case 'LOADING_START'
  13.       return loop( 
  14.         { ...state, loading: true }, 
  15.         Effects.promise(fetchDetails, action.payload.id) 
  16.       ); // 同時返回狀態與副作用 
  17.  
  18.     case 'LOADING_SUCCESS'
  19.       return { 
  20.         ...state, 
  21.         loading: false
  22.         details: action.payload 
  23.       }; 
  24.  
  25.     case 'LOADING_FAILURE'
  26.       return { 
  27.         ...state, 
  28.         loading: false
  29.         error: action.payload.message 
  30.       }; 
  31.  
  32.     default
  33.       return state; 
  34.   } 
  35.  

注意在reducer中,當處理LOADING_START時,并沒有直接返回state對象,而是用loop函數將state和Effect"打包"返回(實際上這個返回值是數組[State, Effect],和Elm的方式非常接近)。

然而修改reducer的返回類型顯然是比較暴力的做法,除非Redux官方出面,否則很難獲得社區的廣泛認同。更復雜的返回類型會讓很多已有的API,三方庫面臨危險,甚至combineReducer都需要用redux-loop提供的定制版本,這種"破壞性"也是Redux作者Dan沒有采納redux-loop進入Redux核心代碼的原因:"If a solution doesn’t work with vanilla combineReducers(), it won’t get into Redux core"。

對Elm的分形架構有了解,想在Redux上繼續實踐的人來說,redux-loop是很好的參考素材,但對多數人和項目而言,***還是更謹慎地看待。

redux-saga:難、而美

Github: https://github.com/yelouafi/r...

另一個著名的庫,它讓異步行為成為架構中獨立的一層(稱為saga),既不在action creator中,也不和reducer沾邊。

它的出發點是把副作用 (Side effect,異步行為就是典型的副作用) 看成"線程",可以通過普通的action去觸發它,當副作用完成時也會觸發action作為輸出。

  1. import { takeEvery } from 'redux-saga' 
  2. import { call, put } from 'redux-saga/effects' 
  3. import Api from '...' 
  4.  
  5. function* getData(action) { 
  6.    try { 
  7.       const response = yield call(api.getData, action.payload.id); 
  8.       yield put({type: "GET_DATA_SUCCEEDED", payload: response}); 
  9.    } catch (e) { 
  10.       yield put({type: "GET_DATA_FAILED", payload: error}); 
  11.    } 
  12.  
  13. function* mySaga() { 
  14.   yield* takeEvery("GET_DATA", getData); 
  15.  
  16. export default mySaga;  

相比action creator的方案,它可以保證組件觸發的action是純對象,因此至少在項目范圍內(middleware和saga都是項目的頂層依賴,跨項目無法保證),action的組合性明顯更加優秀。

而它最為主打的,則是可測試性和強大的異步流程控制。

由于強制所有saga都必須是generator函數,借助generator的next接口,可以輕易對異步行為的每個中間步驟做mock,實現對異步邏輯"step by step"的測試,這在其它方案中是很少看到的 (當然也可以借鑒generator這一點,但缺少約束)。

而強大得有點眼花繚亂的API,特別是channel的引入,則提供了武裝到牙齒級的異步流程控制能力。

然而,回顧我們在討論簡單方案時提到的各種場景與問題,redux-saga并沒有去嘗試回答和解決它們,這意味著你需要自行尋找解決方案。而generator、相對復雜的API和單獨的一層抽象也讓不少人望而卻步。

包括我在內,很多人非常欣賞redux-saga。它的架構和思路毫無疑問是優秀甚至優雅的,但使用它之前,***想清楚它帶來的優點(可測試性、流程控制、高度解耦)與付出的成本是否匹配,特別是異步方面復雜度并不高的項目,比如多數以CRUD為主的管理系統。

小結

本文包含了一些redux社區著名、非著名 (恩,我的redux-action-tools) 的異步方案,這些其實并不重要。

因為方案是一家之作,結論也是一家之言,不可能放之四海皆準。個人更希望文中探討過的常見問題和場景,比如模板代碼、樂觀更新、錯誤處理等,能夠成為你選型時的尺子,為你的權衡提供更好的參考,而不是等到項目熱火朝天的時候,才發現當初選型的硬傷。

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2015-10-22 10:28:45

MySQL高可用方案

2012-11-14 09:42:16

Pikacode技術選項項目

2021-09-26 05:03:31

數據流Redux

2020-06-12 08:22:27

React ReduxReact開發

2017-12-20 16:23:18

抓娃娃

2021-10-19 07:27:07

邊緣集群管理

2024-04-24 10:24:09

2021-02-28 13:45:12

邊緣計算云計算Kubernetes

2013-03-14 11:18:30

Microsoft A解決方案

2010-10-12 14:58:28

通信行業UPS

2025-11-05 08:23:56

2025-08-04 02:21:00

2021-03-31 10:16:00

架構運維技術

2015-08-18 08:55:03

redux核心

2019-01-17 10:58:52

JS異步編程前端

2023-11-03 14:32:38

2020-03-26 10:05:18

大數據IT互聯網

2015-02-02 15:22:31

私有云OpenStackCloudStack

2022-08-12 11:42:44

終端管理方案UEM解決方案

2023-05-11 07:25:57

ReduxMiddleware函數
點贊
收藏

51CTO技術棧公眾號

夜夜嗨av一区二区三区四季av| 国产制服丝袜一区| 亚洲人成电影在线观看天堂色| 日本成人黄色网| 欧美日韩xx| 不卡一二三区首页| 国产精品三级久久久久久电影| 国产真人真事毛片视频| 999久久久久久久久6666| 欧美三级免费观看| 青少年xxxxx性开放hg| 欧美一级做性受免费大片免费| 狂野欧美性猛交xxxx巴西| 欧美成年人网站| 精品人妻无码一区二区三区| 日本欧美在线| 日韩欧美在线视频日韩欧美在线视频| 亚洲精品一区二区三区蜜桃久| 丰满肉嫩西川结衣av| 日韩高清不卡在线| 国内精品久久久久久久| 国产午夜福利一区| 久久久久97| 91精品国产综合久久久久久| 日韩av在线综合| 国产三线在线| 亚洲精品日产精品乱码不卡| 麻豆久久久9性大片| www.激情五月.com| 蜜桃久久av一区| 欧美中文字幕精品| 日韩经典在线观看| 欧美视频二区| 久久精品国产一区二区三区| 精品成人无码一区二区三区| 天堂成人娱乐在线视频免费播放网站 | 国产伦精品一区二区三区视频孕妇| 中文字幕乱伦视频| 老牛嫩草一区二区三区日本| 97在线观看视频国产| 欧美日韩精品亚洲精品| 久久精品亚洲人成影院 | 国产在线观看h| 天堂网av成人| 日韩精品亚洲元码| www.日本高清| 美女av一区| 亚洲国产日韩欧美综合久久| 香蕉视频污视频| 凹凸成人在线| 亚洲第一网中文字幕| 欧美午夜精品一区二区| 粉嫩一区二区三区在线观看| 欧美男女性生活在线直播观看| 韩国视频一区二区三区| 美女100%一区| 欧美在线小视频| 男女无套免费视频网站动漫| 88xx成人永久免费观看| 日本乱人伦一区| 91淫黄看大片| 久久久久久久性潮| 欧美一级久久久| www.四虎精品| 天天躁日日躁狠狠躁欧美巨大小说| 亚洲国产一区自拍| 欧美做受xxxxxⅹ性视频| 国产精品亚洲人成在99www| 亚洲欧洲免费视频| 国产白丝一区二区三区 | 日韩中文字幕在线观看| 可以免费看av的网址| 91精品国产调教在线观看| 欧美成人亚洲成人| 日产精品久久久久| 老鸭窝91久久精品色噜噜导演| 国产成人av网| 91成人在线免费| 国产经典欧美精品| 久久久久久久久一区二区| 女人偷人在线视频| 亚洲欧洲性图库| 女人帮男人橹视频播放| 亚洲午夜天堂| 欧美日韩精品福利| 中文字幕在线观看91| 日韩三级视频| 日日骚av一区| 国产主播在线观看| 日韩精品国产欧美| 97视频中文字幕| 日韩一二三四| **性色生活片久久毛片| 国产av人人夜夜澡人人爽麻豆| 亚洲人体视频| 91精品福利在线一区二区三区| 亚洲一区二区三区四区av| 国产一区2区| 欧美大胆在线视频| 欧美国产成人精品一区二区三区| 精品系列免费在线观看| 国产一区二区在线网站| 色的视频在线免费看| 亚州成人在线电影| jizz大全欧美jizzcom| 国内毛片久久| 久久精品福利视频| 性色av免费观看| 国产精品自拍网站| 日韩精彩视频| 午夜影院在线观看国产主播| 欧美一区二区三区在线电影| 蜜桃av乱码一区二区三区| 国语对白精品一区二区| 国产精品亚洲激情| 五月婷中文字幕| 亚洲美女区一区| 亚洲免费av一区二区三区| 牛牛影视一区二区三区免费看| 最近2019年手机中文字幕 | 激情影院在线观看| 色老头久久综合| 中文字幕 日本| 综合精品一区| 成人做爰www免费看视频网站| 蜜芽tv福利在线视频| 午夜久久久久久久久 | 26uuu欧美| 欧美一区二区视频在线播放| 欧美黑粗硬大| 中文字幕一区二区三区电影| 天天干天天插天天射| 99国产精品国产精品久久| 成人在线视频一区二区三区| 小说区图片区亚洲| 自拍偷拍亚洲一区| 中文字幕在线观看1| 久久综合网色—综合色88| 福利视频一二区| 99久久免费精品国产72精品九九| 欧美理论电影在线播放| 午夜精品久久久久久久99热黄桃| 中文字幕一区二区三区在线观看| 蜜桃免费在线视频| 日韩成人三级| 成人福利在线观看| 免费a级毛片在线播放| 在线欧美日韩国产| a天堂中文字幕| 日韩av一二三| 一区二区不卡在线观看| 在线日韩三级| 九九热这里只有在线精品视| av免费观看在线| 亚洲综合色成人| 成年人小视频在线观看| 99热这里只有精品8| 久久一区二区三区av| 欧美gay囗交囗交| 正在播放国产一区| 国产免费高清av| 一区二区三区在线视频免费观看 | 成人h动漫精品一区二区下载| 国产午夜亚洲精品羞羞网站| 亚洲欧美日韩一级| 亚洲情侣在线| 国产亚洲欧美一区二区三区| www成人免费观看| 精品视频在线观看日韩| 无码人妻精品一区二| 国产精品美女视频| 爽爽爽在线观看| 亚洲国产婷婷| 欧洲一区二区日韩在线视频观看免费| 91国拍精品国产粉嫩亚洲一区| 日韩中文字幕视频| 性一交一乱一透一a级| 精品日韩中文字幕| 五月婷婷六月香| 国产91丝袜在线播放| 男人的天堂99| 国产精品99一区二区三| 国产一区在线免费观看| 国产精品扒开腿做爽爽爽视频软件| 日韩在线小视频| 亚洲第一天堂网| 91极品美女在线| 欧美毛片在线观看| 国产亚洲精品bt天堂精选| 国产永久免费网站| 亚洲欧美不卡| 美女av免费观看| 国产精品欧美在线观看| 亚洲最大福利视频网站| 亚洲天堂资源| 欧美激情国产精品| 九色在线播放| 精品国内片67194| 国产午夜无码视频在线观看| 亚洲精品国产无天堂网2021| 新91视频在线观看| 国产在线视频一区二区三区| 国产精品视频一区二区三区四区五区| 欧美电影一二区| 欧美aaaaa喷水| 99久久人爽人人添人人澡| 国产日韩专区在线| 伊人久久精品一区二区三区| 久久69精品久久久久久国产越南| 成人网视频在线观看| 亚洲精品一区二区三区99| 一卡二卡在线观看| 色狠狠色噜噜噜综合网| 久久久久久国产精品视频| 国产拍揄自揄精品视频麻豆 | 国产精品久久久一区麻豆最新章节| 北京富婆泄欲对白| 国产一区二区h| 一路向西2在线观看| 久久一区二区三区超碰国产精品| 日韩人妻一区二区三区蜜桃视频| av中字幕久久| 人禽交欧美网站免费| 日本在线中文字幕一区| 成人免费91在线看| 国产aⅴ精品一区二区四区| 国产精品日韩专区| 欧美电影免费观看网站| 97精品伊人久久久大香线蕉| 性国产高清在线观看| www亚洲欧美| 91精品专区| 中文字幕av一区二区三区谷原希美| 日本视频在线观看一区二区三区| 亚洲成人激情在线观看| www黄色在线观看| 日韩亚洲电影在线| 国产三级漂亮女教师| 欧美福利视频一区| 国产美女主播在线观看| 欧美一区二区三区在线看 | 亚洲国产精品成人va在线观看| 国产成人精品白浆久久69| 在线播放一区二区三区| 91亚洲精品国偷拍自产在线观看| 欧美日韩在线三级| 一本色道久久综合无码人妻| 欧美猛男男办公室激情| 国产又黄又爽视频| 91精品国产欧美一区二区18| 99视频免费看| 精品久久久三级丝袜| 欧美熟妇交换久久久久久分类 | 国产欧美在线观看| 日韩毛片网站| 69174成人网| 国偷自产av一区二区三区| 久久99影院| 精品高清久久| 中文字幕精品一区日韩| 在线精品小视频| 欧美亚洲日本一区二区三区| 国产日韩高清一区二区三区在线| 欧美性大战久久久久xxx| 久久尤物视频| 欧美国产日韩另类| 国产v综合v亚洲欧| 国内精品久久99人妻无码| 国产亚洲人成网站| 久草福利资源在线| 亚洲一区二区三区美女| 日韩成人在线免费视频| 91国在线观看| 国产精品女同一区二区| 亚洲精品在线免费播放| 国产综合在线观看| 久久精品国产亚洲| a天堂资源在线| 国产精品99久久久久久久久| 大胆国模一区二区三区| 国产在线精品一区| 欧美伦理影院| 乱熟女高潮一区二区在线| 亚洲综合精品| 亚洲黄色片免费看| 久久综合999| 在线看的片片片免费| 亚洲444eee在线观看| www.av88| 精品国产一区二区亚洲人成毛片| 日本电影一区二区在线观看| 久久久999国产| 周于希免费高清在线观看| 成人日韩在线电影| 你懂的在线观看一区二区| 亚洲日本一区二区三区在线不卡| 欧美福利在线| 久热精品在线播放| 97成人超碰视| 强制高潮抽搐sm调教高h| 精品久久久在线观看| 91在线你懂的| 日韩成人小视频| √天堂8在线网| 国产精品成人v| 久久久久高潮毛片免费全部播放| 亚洲日本无吗高清不卡| 米奇777在线欧美播放| 18深夜在线观看免费视频| 国产人伦精品一区二区| 日本一区二区欧美| 日韩女优av电影在线观看| 亚洲精品传媒| 欧美中在线观看| 一区二区日韩| www.黄色网址.com| 精品一区二区三区在线播放| 法国伦理少妇愉情| 亚洲成a人片综合在线| a视频免费在线观看| 色小说视频一区| 8av国产精品爽爽ⅴa在线观看| 国产一区二区视频在线免费观看| 午夜精品剧场| 伊人色在线视频| 中文字幕一区二区不卡| 久久精品久久久久久久| 精品亚洲一区二区三区在线观看| 性欧美高清come| 99久久国产免费免费| 婷婷丁香综合| 亚洲免费999| 国产精品另类一区| 亚洲影院一区二区三区| 国产亚洲精品91在线| 成人做爰视频www网站小优视频| 黄色国产精品一区二区三区| 亚洲精品乱码| 熟女人妻在线视频| 天天av天天翘天天综合网色鬼国产 | 欧美一区二区在线| 视频一区中文字幕国产| 国产交换配乱淫视频免费| 精品久久久久久久久久久久久 | 中文字幕乱码一区二区| 国产一区二区三区免费视频| gogo亚洲高清大胆美女人体 | 免费不卡的av| 偷偷要91色婷婷| 亚洲av无码乱码在线观看性色| 超薄丝袜一区二区| 视频欧美一区| 极品粉嫩国产18尤物| www.爱久久.com| 国产精品21p| 色噜噜狠狠色综合网图区| 综合欧美精品| 久久综合亚洲精品| 91在线国内视频| 亚洲精品中文字幕乱码三区91| 亚洲人成免费电影| 日韩大陆av| 黄色成人在线免费观看| 99re这里都是精品| 香蕉污视频在线观看| 日韩在线免费av| 亚洲免费一区三区| av黄色在线网站| 国产精品色哟哟| 亚洲xxxx天美| 欧美亚洲激情在线| 色97色成人| 性囗交免费视频观看| 色噜噜夜夜夜综合网| 欧美三级电影一区二区三区| av一区二区在线看| 久久国产99| 免费在线观看黄色小视频| 精品久久久久久无| 午夜av成人| av动漫在线播放| 国产婷婷色一区二区三区 | 成人精品一区二区三区四区| 好看的av在线| 久久中文字幕国产| 在线亚洲a色| 日本一本在线视频| 色综合 综合色| 色网在线观看| 日韩三级电影网站| 国产成人在线网站| 国产精品久久久久久久久夜色| 久久久国产一区| 久久99国产精品视频| 欧美一级大片免费看| 色视频成人在线观看免| 欧美xxxx少妇| 亚洲午夜精品久久| 91视视频在线观看入口直接观看www| 中文字幕第31页|