国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

如何開發屬于自己的框架

譯文
開發 前端
我們原本希望尋找一套框架以幫助The Daily Mail網站構建起新的內容管理系統。項目的主要目標在于保證編輯流程與文章中的所有元素實現深度交互(包括圖片、嵌入對象以及呼出對話框等等),并使其能夠支持拖拽操作、模擬化與自我管理等功能。

對于大多數開發人員來說,從零開始構建一套框架聽起來似乎有些陌生甚至聞所未聞。這么干的家伙肯定是瘋了,對吧?既然市面上已經堆滿了各式各樣的JavaScript框架,為什么我們還要費力搞出自己的一套東西?

我們原本希望尋找一套框架以幫助The Daily Mail網站構建起新的內容管理系統。項目的主要目標在于保證編輯流程與文章中的所有元素實現深度交互(包括圖片、嵌入對象以及呼出對話框等等),并使其能夠支持拖拽操作、模擬化與自我管理等功能。

當下我們所能獲取到的所有框架都或多或少地以開發人員所定義的靜態用戶界面為設計基礎。而我們則既需要可編輯文本,又需要動態渲染UI元素。

Backbone的定位實在太低級了一些,它最多只能提供基本對象結構與消息收發機制。我們需要在此基礎上建立大量抽象關系才能滿足自身的實際需要,考慮到這一點、我們決定自己動手重新構建基礎。

我們還曾決定利用AngularJS框架來構建采用相對靜態UI的中小型瀏覽器應用程序。不過遺憾的是,AngularJS是一套徹頭徹尾的黑盒環境——它不會將任何便捷的API擴展或者能夠服務于我們創建對象的操作機制直接擺在臺面上——指令、控制器、服務皆是如此。再有,盡管AngularJS能夠在view與scope表達之間建立響應式連接,但卻不允許我們在不同模式下對這些響應式連接進行定義,因此任何一款中型應用程序都會變得像jQuery應用那樣充斥著事件監聽器(listener)與回調機制。二者之間的惟一區別就是,AngularJS框架會利用watcher取代listener、我們所操作的也將是scope而非DOM。

經過總結,我們理想中的框架需要滿足以下條件:

• 能夠以聲明方式實現應用程序開發,并能為view帶來響應式綁定模式。

• 能夠在應用程序內的不同模型之間建立響應式數據綁定,從而通過聲明而非指令方式對數據擴展加以管理。

• 在綁定結構中插入校驗與翻譯機制,這樣我們就能將view與數據模型加以對接、而不必再通過AngularJS進行查看。

• 對與DOM元素相對接的組件進行精確控制。

• View管理的靈活性允許開發者自動操作DOM變更,并在渲染比DOM操作效率更高時利用實例中的任意模板引擎對某些部分進行重新渲染。

• 擁有動態創建UI的能力。

• 有能力觸及數據響應背后的深層機制并精確控制view更新與數據流。

• 能夠對該框架提供的組件進行功能性擴展,并創建新的組件。

在現有解決方案中,我們實在找不到能夠滿足需求的選項。在這種情況下,我們決定以并行方式開發Milo,并將其作為應用程序的構建基礎。

為什么選擇Milo這個名稱?

之所以選擇Milo這個名稱,是由于Milo Minderbinder這位由Joseph Heller撰寫的《第二十二條軍規》一書中的戰爭商人。盡管是從軍營中的炊事工作起步,但他很快建立起了屬于自己的暴利貿易企業,并將成果與每一個人“共享”。

作為一套框架,Milo具備模塊接駁機制(Binder),能夠將DOM元素與組件相對接(通過特殊的ml-bind屬性)。而且這套模塊接駁機制還允許我們在不同數據源之間建立實時響應式連接(Model與Data類組件正是這樣的數據源)。

巧合的是,Milo正好也是MaIL Online網站的縮寫,而且要不是為了給Mail Online打造獨特的運作環境、我們永遠也不會創建出這樣一套框架。

管理view

Binder

Milo當中的view由組件負責管理,而這些組件基本上可以算是JavaScript類的各項實例、分別管理與之對應的DOM元素。很多框架都會利用組件還作為UI元素管理概念,但其中最引人注目的無疑要數Ext JS。我們在很多場景中使用過Ext JS(我們要替換掉的遺留應用就是用它構建起來的),但同時也發現了兩種令人避之而惟恐不速的缺陷。

This is where binder comes in.首先,Ext JS讓我們無法輕松對標記加以管理。構建一套UI的惟一方式就是將所有組件配置放在一起進行分層嵌套。這種作法會給標記渲染帶來不必要的復雜性,開發人員也將失去應有的控制能力。我們需要一種能夠以內聯方式手動制作HTML標記并創建組件的方法。

Binder會掃描我們的標記并從中查找ml-bind屬性,這樣它才能讓組件完成實例化并將其與對應元素相綁定。該屬性中包含著與對應組件相關的信息;其中可能包括組件 class、facet以及必須具備的組件名稱。

 

  1. <div ml-bind=”ComponentClass[facet1, facet2]:componentName”> 
  2.   Our milo component  
  3. </div> 

 

我們會在接下來的部分進一步討論facet,但目前讓我們先看看該如何獲取這項屬性值并利用一條正則表達式將其提取出來。

 

  1. var bindAttrRegex = /^([^\:\[\]]*)(?:\[([^\:\[\]]*)\])?\:?([^:]*)$/;  
  2.    
  3. var result = value.match(bindAttrRegex);  
  4. // result is an array with  
  5. // result[0] = ‘ComponentClass[facet1, facet2]:componentName’;  
  6. // result[1] = ‘ComponentClass’;  
  7. // result[2] = ‘facet1, facet2’;  
  8. // result[3] = ‘componentName’; 

 

有了這些信息,我們接下來要做的就是對整個ml-bind屬性進行遍歷、提取此類值并通過創建實例對各個元素加以管理。

  1. var bindAttrRegex = /^([^\:\[\]]*)(?:\[([^\:\[\]]*)\])?\:?([^:]*)$/;  
  2.    
  3. function binder(callback) {  
  4.     var scope = {};  
  5.        
  6.     // we get all of the elements with the ml-bind attribute  
  7.     var els = document.querySelectorAll('[ml-bind]');  
  8.     Array.prototype.forEach.call(els, function(el) {  
  9.         var attrText = el.getAttribute('ml-bind');  
  10.         var result = attrText.match(bindAttrRegex);  
  11.            
  12.         var className = result[1] || 'Component';  
  13.         var facets = result[2].split(',');  
  14.         var compName = results[3];  
  15.            
  16.         // assuming we have a registry object of all our classes  
  17.         var comp = new classRegistry[className](el);  
  18.         comp.addFacets(facets);  
  19.         comp.name = compName;  
  20.         scope[compName] = comp;  
  21.            
  22.         // we keep a reference to the component on the element  
  23.         el.___milo_component = comp;  
  24.     });  
  25.        
  26.     callback(scope);  
  27. }  
  28.    
  29. binder(function(scope){  
  30.     console.log(scope);   
  31. }); 

所以只需要利用正則表達式與DOM遍歷,大家就可以利用自定義語法創建出符合特定業務邏輯與背景需求的迷你框架。只需少許代碼,我們已經設置出一套能夠容納模塊化、自我管理組件的架構,并能夠隨意加以運用。我們還可以創建出便捷的聲明式語法、從而對HTML中的組件進行實例化及配置;不過與AngularJS不同,我們能夠根據實際需求對這些組件進行管理。

#p#

由職責驅動的設計方案

Ext JS令人不滿的第二大原因在于,它所采用的類結構在分層方面極具剛性且存在跳躍性,這使其很難與我們的組件類實現有機結合。我們希望編寫出一套全面的特性列表,一篇文章中可能包含的任何組件都能從中找到對應特性。舉例來說,某個組件具備可編輯特性,可以作為事件加以監聽,甚至能夠成為拖拽目標或者本身就屬于拖拽對象。這還只是所需特性當中的一小部分。我們在初步名單中包含了大約十五種不同的功能類型,囊括了多數特定組件有可能用到的特性。

將這些特性整理成為某種分層式結構不僅僅令人頭痛,同時也會給我們對特定組件類的功能變更帶來嚴重局限(我們隨后投入大量精力處理這個問題)。有鑒于此,我們決定采取另一套更具靈活性的面向對象設計方案。

我們已經了解過由職責驅動的設計方案。與最常見的類及其所包含數據特性定義模式相反,職責驅動設計更多將關注重點放在對象負責執行的操作身上。由于我們需要處理的是復雜度頗高且無法預測的數據模型,因此這種方式無疑非常合適。這類解決方案還允許我們日后根據實際需要對細節加以調整。

在職責驅動設計當中,我們摒棄了“角色(Role)”這一關鍵性組成部分。一個角色指的是一系列相關責任的集合。在我們的項目當中,我們將角色設定為編輯、拖拽、拖拽區、可選或者事件等等。但大家要如何將這些角色體現在代碼當中?考慮到這一點,我們借鑒了裝飾者模式的作法。

裝飾者模式允許我們將特性添加到單一對象當中,無論靜態或者動態,而且完全不會對同一類中其它對象的特性造成影響。盡管類特性的運行時操作在此次項目中并非必要,但我們對這種處理思路所提供的封裝方式很感興趣。Milo在具體實施過程中采取了混合型方式,即將被稱為facet的對象作為屬性附加到該組件實例當中。作為配置對象,facet會引用該組件、而組件則是facet的“持有者”。這種機制允許我們根據每個組件類對facet加以定制。

大家可以將facet視為高級、可配置混合類型,它們在持有者對象上擁有自己的命名空間甚至是自己的init方法——該方法需要被該facet子類所覆蓋。

  1. function Facet(owner, config) {  
  2.     this.name = this.constructor.name.toLowerCase();  
  3.     this.owner = owner;  
  4.     this.config = config || {};  
  5.     this.init.apply(this, arguments);  
  6. }  
  7. Facet.prototype.init = function Facet$init() {};  

因此我們可以將這個示例Facet類劃入子類并為自己需要的每種特性類型創建特定facet。Milo預先內置有多種不同類型的facet,例如DOM facet、負責提供能夠在其持有者組件元素上執行的DOM功能集合,外加List與Item facet、二者能夠共同創建出重復組件列表。

這些facet隨后會被我們稱為的FacetedObject類匯聚到一起,這是一個抽象類、繼承自全部組件。FacetedObject還擁有一個名為createFacetedClass的類方法,能夠將其自身劃入子類并將所有包含facets屬性的facet附加至該類。通過這種方式,當FacetedObject轉化為實例后就會接入其全部facet類,并可以通過迭代方式實現組件引導。

  1. function FacetedObject(facetsOptions /*, other init args */) {  
  2.    
  3.     facetsOptions = facetsOptions ? _.clone(facetsOptions) : {};  
  4.    
  5.     var thisClass = this.constructor  
  6.         , facets = {};  
  7.    
  8.     if (! thisClass.prototype.facets)  
  9.         throw new Error('No facets defined');  
  10.    
  11.     _.eachKey(this.facets, instantiateFacet, thistrue);  
  12.    
  13.     Object.defineProperties(this, facets);  
  14.    
  15.     if (this.init)  
  16.         this.init.apply(this, arguments);  
  17.    
  18.     function instantiateFacet(facetClass, fct) {  
  19.         var facetOpts = facetsOptions[fct];  
  20.         delete facetsOptions[fct];  
  21.    
  22.         facets[fct] = {  
  23.             enumerable: false,  
  24.             value: new facetClass(this, facetOpts)  
  25.         };  
  26.     }  
  27. }  
  28.    
  29. FacetedObject.createFacetedClass = function (name, facetsClasses) {  
  30.     var FacetedClass = _.createSubclass(this, name, true);  
  31.    
  32.     _.extendProto(FacetedClass, {  
  33.         facets: facetsClasses  
  34.     });  
  35.     return FacetedClass;  
  36. };  

在Milo中,我們還創建出一個包含匹配createComponentClass類方法的基礎Component類、旨在進一步實現抽象化,不過其基本原則仍然保持不變。由于關鍵特性仍然由可配置facet負責管理,我們可以在一種聲明樣式內創建多種不同組件類,而且完全不必編寫太多自定義代碼。下面來看如何在Milo當中使用一部分可以直接使用的facet。

  1. var Panel = Component.createComponentClass(‘Panel’, {  
  2.     dom: {  
  3.         cls: ‘my-panel’,  
  4.         tagName: ‘div’  
  5.     },  
  6.     events: {  
  7.         messages: {‘click’: onPanelClick}  
  8.     },  
  9.     drag: {messages: {...},  
  10.     drop: {messages: {...},  
  11.     container: undefined  
  12. });  

在這里,我們已經創建出一個名為Panel的組件類且已經與DOM功能方法相對接,它會在init上自動設置其CSS類、能夠監聽DOM事件并在init上設置點擊處理程序、能夠作為拖拽對象也可以作為拖拽目標。作為最后一個facet,container負責確保該組件對自己的scope進行設置并擁有實際起效的子組件機制。

Scope

接下來的話題讓我們討論了很長一段時間,即附加至文檔的所有組件到底應該采用扁平化結構還是樹狀結構——在樹狀結構中,子分支只能接受來自父分支的訪問。

在某些情況下,我們肯定需要scope機制的介入,但其更多涉及到的是實施層而非框架層。舉例來說,我們擁有多套包含有圖片的圖片組。這些組能夠輕松追蹤其子圖片的當前狀態,而無需涉及通用scope。

我們最終決定在文檔中建立一套組件scope樹狀結構。引入scope能讓很多工作變得更易于打理,也讓我們可以使用更多通用組件命名方式,但這種機制顯然需要加以管理。如果大家刪除了某個組件,則必須將其從父scope當中移除出去。如果大家對某個組件進行移動,則必須將其由原scope內移除再添加至另一個scope當中。

事實上,scope是一種特殊的散列或者映射對象,scope當中容納的第一個子分支都擁有該對象的屬性。在Milo當中,scope存在于容器facet之上,而后者本身幾乎沒有任何功能性可言。不過scope對象卻擁有一系列不同類型的方法,能夠對自身進行操作與迭代。但為了避免命名空間沖突,這些方法在命名時都會以下劃線作為起始字符。

  1. var scope = myComponent.container.scope;  
  2.    
  3. scope._each(function(childComp) {  
  4.     // iterate each child component  
  5. });  
  6.    
  7. // access a specific component on the scope  
  8. var testComp = scope.testComp;  
  9.    
  10. // get the total number of child components  
  11. var total = scope._length();  
  12.    
  13. // add a new component ot the scope  
  14. scope._add(newComp);  

#p#

消息收發——同步與異步

我們希望能在不同組件之間實現松散耦合,因此我們決定將消息收發功能附加到所有組件與facet之上。

消息機制實施工作的第一步在于建立起方法集合,旨在對訂閱者數組進行管理。方法與數組以混合方式存在于對象當中,并借此實現消息收發功能。

對于消息機制實施方案的第一步,我們姑且采用一套簡化版本,具體代碼如下所示:

  1. var messengerMixin =  {  
  2.     initMessenger: initMessenger,  
  3.     on: on,  
  4.     off: off,  
  5.     postMessage: postMessage  
  6. };  
  7.    
  8.    
  9. function initMessenger() {  
  10.     this._subscribers = {};  
  11. }  
  12.    
  13. function on(message, subscriber) {  
  14.     var msgSubscribers = this._subscribers[message] =  
  15.         this._subscribers[message] || [];  
  16.    
  17.     if (msgSubscribers.indexOf(subscriber) == -1)  
  18.         msgSubscribers.push(subscriber);  
  19. }  
  20.    
  21. function off(message, subscriber) {  
  22.     var msgSubscribers = this._subscribers[message];  
  23.     if (msgSubscribers) {  
  24.         if (subscriber)  
  25.             _.spliceItem(msgSubscribers, subscriber);  
  26.         else 
  27.             delete this._subscribers[message];  
  28.     }  
  29. }  
  30.    
  31. function postMessage(message, data) {  
  32.     var msgSubscribers = this._subscribers[message];  
  33.     if (msgSubscribers)  
  34.         msgSubscribers.forEach(function(subscriber) {  
  35.             subscriber.call(this, message, data);  
  36.         });  

任何使用這種混合類型的對象都能利用postMessage方法自行實現消息收發(由對象本身或者任何其它代碼實現),而該代碼的訂閱功能可通過其它擁有相同名稱的方法實現開啟與關閉。

現在,我們的消息機制已經具備以下特性:

• 附加外部消息來源(包括DOM消息、窗口消息、數據變更以及其它消息機制等)——例如由Events facet利用其通過Milo消息機制來顯示DOM事件。這項功能依靠單獨的MessageSource類及其子類實現。

• 定義定制化消息收發API,從而將消息與外部消息數據翻譯成內部消息。例如Data facet就能利用其翻譯變更內容,并將DOM事件輸入到數據變更事件(詳見下文model)當中。這項功能領先單獨的MessengerAPI類及其子類實現。

• 模式訂閱(利用正則表達式)例如model(如下所示)利用內部模式訂閱以實現深層model變更訂閱。

• 利用以下語法作為訂閱機制的組成部分,進而實現背景信息(即訂閱者中的對應值)定義:

  1. component.on('stateready',  
  2.     { subscriber: func, context: context }); 

• 利用once方法創建只發送一次的訂閱機制。

• 在postMessage中將回調作為第三項參數進行傳遞(我們將postMessage當中的參數數量視為變量,但希望能用更為一致的消息收發API來代替這種變量參數機制)。

• 其它。

我們在開發消息機制時犯下了一個嚴重的設計錯誤,即所有消息都會被同步發出。由于JavaScript采用單線程機制,大量有待執行的復雜消息操作會構成長隊列,而且很容易造成UI卡死。通過調整讓Milo擁有異步式消息發送機制并非難事(所有訂閱者利用setTimeout(subscriber, 0)接受其自身執行block的調用),變更框架及應用程序的其余部分則相對更難——雖然大多數消息都能夠以異步方式發送,但也有一些必須采用同步發送機制(主要是那些內部包含數據或者需要調用preventDefault的DOM事件)。在默認情況下,消息現在會以異步方式發送,但我們可以利用以下方式在消息發出時使其遵循同步機制:

  1. component.postMessageSync('mymessage', data); 

或者在創建訂閱時:

  1. component.onSync('mymessage'function(msg, data) {  
  2.     //...  
  3. }); 

我們作出的另一項設計決策是將消費機制方法顯示在使用它們的對象之上。最初,這些方法只是簡單被混雜在對象當中,但我們并不希望將所有方法都顯示出來、而且也不可能使用彼此獨立的消息機制。因此我們通過調整讓消息機制作為以Mixin抽象類為基礎的單獨類。

Mixin類允許我們將某個類的各項方法顯示在宿主對象之上,在這種情況下,當這些方法被調用時、其背景仍然是Mixin而非宿主對象。

事實證明這是一套非常便捷的處理機制——我們可以對需要顯示的方法進行全面控制,并根據需要變更其名稱。它還允許我們在同一對象上配備兩種消息機制,并將其用于model。

總體而言,Milo消息機制確實是一套穩定可靠的解決方案,而且足以在瀏覽器內以及Node.js中發揮作用。在構成我們這套產品內容管理系統的數萬行代碼中,它一直擁有相當出色的使用頻率。

下期預告

在下一篇文章中,我們將探討Milo項目最實用但可能也是最復雜的部分。Milo方案不僅允許用戶安全而且深入地進行屬性訪問,同時也能夠從任意層級對事件訂閱作出調整。

我們將一同探索實施記錄,了解我們如何利用connector對象實現數據源的單向或者雙向綁定。

原文鏈接:http://code.tutsplus.com/articles/rolling-your-own-framework--cms-21810

責任編輯:林師授 來源: 51CTO
相關推薦

2014-08-12 09:54:05

Android定制化啟動器

2017-05-08 14:27:49

PHP框架函數框架

2017-08-18 08:00:04

SaaS云計算公有云

2024-09-14 14:09:40

2021-12-23 06:07:21

前端技術編程

2019-01-31 13:43:48

Spring BootStarter開發

2011-09-16 16:22:45

Android應用DXHome

2021-05-26 10:21:31

Python音樂軟件包

2021-01-18 05:30:22

串口通信Qt

2024-10-16 09:49:18

2022-08-11 07:32:51

Starter自動裝配

2010-10-14 14:31:31

Ubuntu發行版

2022-11-30 07:49:49

交互事件屏幕手勢識別

2018-01-29 20:12:11

python翻譯命令行

2010-07-12 10:48:36

2020-11-03 10:35:39

Python

2021-02-05 05:29:51

服務器GitGogs

2023-04-12 07:35:33

2021-04-21 08:01:31

Googleprotobuf嵌入式系統

2022-04-18 19:02:53

chrome擴展瀏覽器
點贊
收藏

51CTO技術棧公眾號

日韩精品免费| 日本一区二区高清| 91免费在线视频网站| 香蕉久久网站| 亚洲国产另类久久久精品极度| 国产伦精品一区二区三区在线观看| 久久久国产精品一区| 美女视频亚洲色图| 久久男人的天堂| 日韩欧美高清在线播放| 国产精品99导航| 鲁大师成人一区二区三区| 久久视频这里有精品| 亚洲人成网站色在线观看| 国内精品久久久久久野外| 日韩精品资源二区在线| 台湾佬综合网| 91视频 - 88av| 亚洲一区二区三区精品在线| av大片在线| 国产精品极品美女在线观看免费| 亚洲综合激情| 蜜桃视频免费网站| 欧美日韩视频在线第一区| 91精品久久| 亚洲女在线观看| 中文字幕高清在线播放| 成人午夜小视频| 91视频观看免费| av中文字幕在线| 国产精品电影网站| 成人免费视频一区| 国产高清一区二区三区视频| 国产精品免费久久久| 伊人久久久大香线蕉综合直播| 成人在线观看你懂的| 亚洲精品一区二区三区精华液| 88国产精品视频一区二区三区| 热99在线观看| 亚洲18私人小影院| 91日韩一区二区三区| 亚洲欧美在线人成swag| 亚洲精品国产精品久久| 亚洲大胆人体av| 久久久久久久波多野高潮日日| 香蕉视频国产在线观看| 国产一区二区久久久| 亚洲一区二区三区三| 福利在线导航136| 在线成人性视频| 国产一区二区动漫| 奇米888四色在线精品| 在线国产91| 久久免费看av| 亚洲国产精品高清久久久| 国产一区二区三区蝌蚪| 91福利在线免费| 亚洲精品二区| 日韩有码片在线观看| 精品亚洲成a人在线观看 | 国产一区视频在线看| 国产一区二区视频在线看| 天堂av在线网站| 91精品国产高清久久久久久91裸体| 欧洲在线/亚洲| 国产一区二区高清| 国产精品xx| baoyu777.永久免费视频| 国产精品久久久久久久久借妻| 欧美丝袜丝nylons| 国产成人精品亚洲午夜麻豆| 西瓜成人精品人成网站| 欧美色18zzzzxxxxx| 久久久久久久久久久综合| 国产69久久精品成人看| 制服丝袜亚洲网站| 久久精品亚洲麻豆av一区二区 | 欧美美女在线直播| av免费在线一区二区三区| 猫咪在线永久网站| 中文字幕av一区二区三区四区| 国产精品国码视频| 丝袜亚洲精品中文字幕一区| 91丨九色porny丨蝌蚪| 黑色丝袜福利片av久久| 成人h动漫在线| 亚洲成人福利在线| 精品久久久久久综合日本| 91在线观看免费高清完整版在线观看| 久久精品一二三| 国产伦乱精品| 91成人在线精品视频| 青草在线视频| 国产在线精品91| 精品综合在线| a级网站在线观看| 欧美日本中文字幕| 国产精品91久久| 久久影院在线观看| 国产精品久久久久久一区二区三区 | 国产亚洲精品v| 国产在线播放精品| 免费一级在线观看| 亚洲四虎av| 美女日批免费视频| 日韩精品一区二区三区四区五区| 国产在线999| 国产xxx69麻豆国语对白| 色多多国产成人永久免费网站| 欧美在线观看视频一区二区三区| 亚洲欧美日韩国产另类专区| 美女久久久久| 欧美激情极品| 中日韩免视频上线全都免费| 黄色av电影在线观看| 日本欧美视频在线观看| 国产精品亚洲综合一区在线观看| 涩涩漫画在线观看| 日韩精品av一区二区三区| 就去色蜜桃综合| 亚洲精品日韩在线观看| 超薄肉色丝袜足j调教99| 日本在线高清视频一区| 国产成一区二区| 国产精品免费一区二区三区在线观看| www亚洲精品| 欧美高清不卡在线| 97国产精品人人爽人人做| 国内久久久精品| 国产精品久久网| 韩剧1988免费观看全集| 国产精品久久久久av免费| 国产精品久久久久久五月尺| 成人精品在线观看| 精品福利影视| 亚洲一区二区三区午夜| 99久久伊人精品影院| 一区二区三区四区免费视频| 国产精品中文久久久久久久| 91一区二区三区| 亚洲熟妇av日韩熟妇在线| 你懂的在线播放| 久久久久久久久久久一区| 北条麻妃在线一区| 日本黄色a视频| aaaaaa亚洲| 一不卡在线视频| 国产鲁鲁视频在线观看免费| 一级日本在线| 国产a亚洲精品| 一本色道久久综合亚洲精品不卡| 精品一区二区久久| 免费视频最近日韩| 国产伦精品一区二区三区在线观看| 国产麻豆午夜三级精品| 精品一区二区三区的国产在线播放| 色综合久久久久网| 九九国产精品视频| 伊甸园亚洲一区| gogo高清在线播放免费| 九色成人在线| 色就是色欧美| 97香蕉久久超级碰碰高清版| 欧美久久久久久久久| 久久久www成人免费毛片麻豆| 亚洲欧美亚洲| www.久久热| 97caopor国产在线视频| 九色在线91| 成人一区二区av| 91九色露脸| 色综合久久天天综线观看| 91精品国产黑色紧身裤美女| 国产精品丝袜91| 日韩高清不卡一区| 伊人成综合网伊人222| 中文字幕影音在线| 国产女主播在线写真| 免费看污黄网站| 亚洲蜜桃在线| 91九色国产在线| 欧美老少配视频| 亚洲国产成人精品久久久国产成人一区| 亚洲精品中文字幕在线观看| 风间由美一区二区三区在线观看| 亚洲人人精品| 99精品视频在线| 国产96在线亚洲| 成人国产精品| 97蜜桃久久| 午夜视频在线看| 最新中文字幕在线观看| 男女视频一区二区三区| 久久久久久久久网| 日韩一区不卡| av资源一区二区| 国产成人精品国内自产拍免费看| 欧美成人午夜激情| 亚洲一区999| 亚洲成人久久一区|