亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

javascript框架設計讀書筆記之模塊加載系統

2019-11-20 13:50:24
字體:
來源:轉載
供稿:網友

模塊加載,其實就是把js分成很多個模塊,便于開發和維護。因此加載很多js模塊的時候,需要動態的加載,以便提高用戶體驗。

在介紹模塊加載庫之前,先介紹一個方法。

動態加載js方法:

復制代碼 代碼如下:

function loadJs(url , callback){
  var node = document.createElement("script");
      node[window.addEventListener ? "onload":"onreadystatechange"] = function(){
            if(window.addEventListener || /loaded|complete/i.test(node.readyState)){
      callback();
      node.onreadystatechange = null;
    }                                                                            
  }
  node.onerror = function(){};
     node.src = url;
  var head = document.getElementsByTagName("head")[0];
  head.insertBefore(node,head.firstChild);     //插入到head的第一個節點前,防止ie6下head標簽沒閉合前,使用appendChild報錯。 
}

由于司徒正美使用了它寫的mass框架來介紹模塊加載,而業界用的最多的是require.js和sea.js。因此,我覺得他個性有點強。

我來講下sea.js的模塊加載過程吧:

頁面chaojidan.jsp,在head標簽中,引入sea.js,這時就會得到seajs對象。

同時引入index.js。

index.js的代碼如下:

復制代碼 代碼如下:

seajs.use(['./a','jquery'],function(a,$){
    var num = a.a;
    $('#J_A').text(num);
})

a.js :

復制代碼 代碼如下:

define(function(require,exports,module){
    var b = require('./b');
    var a = function(){
        return 1 + parseInt(b.b());
    }
    exports.a = a;
})

b.js :

復制代碼 代碼如下:

define(function(require,exports,module){
   var c = require('./c');

    var b = function(){
        return 2 + parseInt(c.c());
    }
    exports.b = b;
})

c.js :

復制代碼 代碼如下:

define(function(require,exports,module){
    var c = function(){
        return 3;
    }
    exports.c = c;
})

由上可知,a模塊依賴b,b依賴c.

當程序進入到index.js,seajs將調用use方法。

復制代碼 代碼如下:

seajs.use = function(ids, callback) {
  globalModule._use(ids, callback)
}

說明: globalModule 為seajs初始化時(引入sea.js時),Module的實例 var globalModule = new Module(util.pageUri, STATUS.COMPILED)
此時 ids -> ['./a','jquery'], callback -> function(a,$){var num = a.a;$('#J_A').text(num);}

接下來將調用 globalModule._use(ids, callback)

復制代碼 代碼如下:

Module.prototype._use = function(ids, callback) {  
  var uris = resolve(ids, this.uri);      //解析['./a','jquery']
    this._load(uris, function() {    //把解析出來的a,jquery模塊的地址[url1,url2],調用_load方法。
                //util.map : 讓數據成員全部執行一次一個指定的函數,并返回一個新的數組,該數組為原數組成員執行回調后的結果
      var args = util.map(uris, function(uri) {

         return uri ? cachedModules[uri]._compile() : null;//如果存在url,就調用_compile方法。
   })
    if (callback) { callback.apply(null, args) } 
  })
   }

因為調用_load方法后,會出現兩個回調函數,因此我們將function(a,$){var num = a.a;$('#J_A').text(num);}標志為callback1,
把this._load(uris, function() { })回調方法標志為callback2. 
resolve方法就是解析模塊地址的,這里我就不細講了。
最終var uris = resolve(ids, this.uri)中 的uris被解析成了['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery/1.7.2/juqery-debug.js'],模塊路徑解析已經完畢。

而接下來將執行this._load

復制代碼 代碼如下:

// _load()方法主要會先判斷哪些資源文件還沒有ready,如果全部資源文件都處于ready狀態就執行callback2
  // 在這其中還會做循環依賴的判斷,以及對沒有加載的js執行加載
  Module.prototype._load = function(uris, callback2) {  
  //util.filter : 讓數據成員全部執行一次一個指定的函數,并返回一個新的數組,該數組為原數組成員執行回調后返回為true的成員
    //unLoadedUris是那些沒有被編譯的模塊uri數組
    var unLoadedUris = util.filter(uris, function(uri) {
      //返回執行函數布爾值為true的成員,在uri存在并且在內部變量cacheModules中不存在或者它在存儲信息中status的值小于STATUS.READY時返回true
      // STATUS.READY值為4,小于四則可能的情況是獲取中,下載中。
      return uri && (!cachedModules[uri] ||
          cachedModules[uri].status < STATUS.READY)
    });    
  //如果uris中的模塊全部都ready好了,執行回調并退出函數體(這時就會調用模塊的_compile方法了)。
  var length = unLoadedUris.length
  if (length === 0) { callback2() return }
  //還未加載的模塊個數
    var remain = length
    //創建閉包,嘗試去加載那些沒有加載的模塊
    for (var i = 0; i < length; i++) {
      (function(uri) {
        //判斷如果在內部變量cachedModules里面并不存在該uri的存儲信息則實例化一個Module對象
        var module = cachedModules[uri] ||
            (cachedModules[uri] = new Module(uri, STATUS.FETCHING))
        //如果模塊的狀態值大于等于2,也就意味著模塊已經被下載好并已經存在于本地了,這個時候執行onFetched()
        //否則則調用fetch(uri, onFetched) ,嘗試下載資源文件,資源文件下載后會觸發onload,onload中會執行回調onFetched的方法。
        module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
        function onFetched() {
          module = cachedModules[uri]
          //當模塊的狀態值為大于等于STATUS.SAVED的時候,也就意味著該模塊所有的依賴信息已經被拿到
          if (module.status >= STATUS.SAVED) {
            //getPureDependencies:得到不存在循環依賴的依賴數組
            var deps = getPureDependencies(module)
            //如果依賴數組不為空
            if (deps.length) {
              //再次執行_load()方法,直到全部依賴加載完成后執行回調
              Module.prototype._load(deps, function() {
                cb(module)
              })
            }
            //如果依賴數組為空的情況下,直接執行cb(module)
            else {
              cb(module)            
            }
          }
          // 如果獲取失敗后,比如404或者不符合模塊化規范
          //在這種情形下,module.status會維持在 FETCHING 或者 FETCHED
          else {
            cb()
          }
        }
      })(unLoadedUris[i])
    }
    // cb 方法 - 加載完所有模塊執行回調
    function cb(module) {
      // 如果module的存儲信息存在,那么修改它的module存儲信息中的status的值,修改為 STATUS.READY
      module && (module.status = STATUS.READY)
      // 只有當所有模塊加載完畢后執行回調。
      --remain === 0 && callback2()
    }
  }
}

這里unLoadedUris的數組長度為2 ,['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery/1.7.2/juqery-debug.js'],所以 接下來會產生兩個以 js路徑為名稱的閉包。

http://localhost/test/SEAJS/a.js為例
接下來 : 首先會創建一個Module:

復制代碼 代碼如下:

cachedModules('http://localhost/test/SEAJS/a.js') = new Module('http://localhost/test/SEAJS/a.js',1)
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)

因為此時a模塊并沒有加載 所以接下來將會執行 fetch(uri, onFetched) 即fetch('http://localhost/test/SEAJS/a.js',onFetched)。

復制代碼 代碼如下:

function fetch(uri, onFetched) {
    // 根據map中的規則替換uri為新的請求地址
    var requestUri = util.parseMap(uri)
    // 首先在已獲取列表中查找是否含有requestUri記錄
    if (fetchedList[requestUri]) {
      // 這個時候將原始uri的module存儲信息刷新到通過map重定義的requestUri上
      cachedModules[uri] = cachedModules[requestUri]
      // 執行onFetched 并返回,意味著模塊已經獲取成功了
      onFetched()
      return
    }
    //在獲取列表中查詢 requestUri 的存儲信息
    if (fetchingList[requestUri]) {
      //在callbacklist中加入該uri對應下的callback,并返回
      callbackList[requestUri].push(onFetched)    //如果正在獲取中,就把此模塊的onFetched回調方法push進數組中,并返回。
      return
    }
    // 如果嘗試獲取的模塊都未出現在fetchedList和fetchingList中,則分別在請求列表和回調列表中添加其信息
    fetchingList[requestUri] = true
    callbackList[requestUri] = [onFetched]
    // Fetches it
    Module._fetch(
        requestUri,
        function() {
          fetchedList[requestUri] = true
          // Updates module status
          // 如果 module.status 等于 STATUS.FECTCHING ,則修改module狀態為FETCHED
          var module = cachedModules[uri]
          if (module.status === STATUS.FETCHING) {
            module.status = STATUS.FETCHED
          }
          if (fetchingList[requestUri]) {
            delete fetchingList[requestUri]
          }
          // Calls callbackList 統一執行回調
          if (callbackList[requestUri]) {
            util.forEach(callbackList[requestUri], function(fn) {
              fn()    //fn就是模塊a對應的onFeched方法。
            })
            delete callbackList[requestUri]
          }
        },
        config.charset
    )
  }

接下來 將會執行 Module._fetch(),這里的回調函數我們稱作為callback3.

此方法就是調用loadJs方法動態下載a.js文件。(因為有a和jquery,所以會新建兩個script),這里有一個疑問,新建a的script,并添加到head中,就會下載js文件,但是在seajs中,并沒有下載,而是等jquery的script建立好,并添加到head中,才會下載(谷歌調試器設斷點,一直顯示pending等待中)。這是為毛?
(推薦看這里:下載成功后,就會解析執行,執行的是define方法。這里會先執行a模塊的代碼。
define(id,deps,function(){})方法解析

復制代碼 代碼如下:

//define 定義 ,id : 模塊id , deps : 模塊依賴 , factory
  Module._define = function(id, deps, factory) {
   //解析依賴關系 // 如果deps不是數組類型,同時factory是函數
   if (!util.isArray(deps) && util.isFunction(factory)) { // 函數體內正則匹配require字符串,并形成數組返回賦值給deps
     deps = util.parseDependencies(factory.toString())
   }
  //設置元信息
   var meta = { id: id, dependencies: deps, factory: factory } 
   if (document.attachEvent) {
     // 得到當前script的節點
     var script = util.getCurrentScript()
       // 如果script節點存在
     if (script) {
         // 得到原始uri地址
         derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script)) }
         if (!derivedUri) {
             util.log('Failed to derive URI from interactive script for:', factory.toString(), 'warn')
         }
     }
 .........
 }

define首先會對factory執行一個判斷 ,判斷它是否為一個函數(原因是因為define內也可以包括文件,對象)

如果是函數 , 那么 就會通過factory.toString(),得到函數,并通過正則匹配得 a.js的依賴,并把依賴保存在 deps 中

對于 a.js 而言, 它的依賴 是 b.js 所以 deps為 ['./b']

并對 a.js 的信息進行保存 var meta = { id: id, dependencies: deps, factory: factory }

針對a.js meta = { id : undefined , dependencies : ['./b'] , factory : function(xxx){xxx}}

在 ie 6-9 瀏覽器中可以拿到當前運行js的路徑 但是在標準瀏覽器中 ,這不可行 ,所以暫時先把元信息賦值給anonymousModuleMeta = meta。

然后觸發onload,這時就會調用回調方法callback3,此回調方法就會修改當前回調模塊(a.js)的狀態值,將其設置為 module.status = STATUS.FETCHED。

再接下來 ,將統一 執行回調隊列 callbackList 中的 a.js所對應的回調,也就是onFetched。

onFetched方法會檢查a模塊是否有依賴模塊,因為a依賴于b,所以對模塊a所依賴的b.js 執行_load()。

會去下載b模塊,這時會先執行jquery的define方法。因為jquery沒依賴模塊,所以onload回調后。onFetched調用cb方法。

當b按照a一樣的過程實現后,就會下載c模塊。最終c,b,a模塊都下載執行define,并onload結束后,也會調用cb方法,(先c,再b,后c)

所有模塊都為ready之后,就會調用callback2方法。
最終回調到callback2,執行a和jquery模塊的_compile方法:

首先編譯a.js模塊,模塊a的function執行,因為a里面有require(b.js),因此會去執行b模塊的function.
模塊 a 的function開始執行
模塊 b 的function開始執行
模塊 c 的function開始執行
模塊 c 的function執行完畢
模塊 b 的function執行完畢
模塊 a 的function執行完畢

最后執行jquery的function。

編譯結束后,就執行callback1,就可以使用a和jquery對象了。

PS:seajs版本已經更新,現在沒有_compile方法了。(大家自行去看,我也要去看下)

接著講下seajs的模塊編譯_compile過程。

首先是a.js的編譯

復制代碼 代碼如下:

Module.prototype._compile = function() {
126     var module = this         
127     // 如果該模塊已經編譯過,則直接返回module.exports
128     if (module.status === STATUS.COMPILED) {
129       return module.exports
130     }
133     //  1. the module file is 404.
134     //  2. the module file is not written with valid module format.
135     //  3. other error cases.
136     // 這里是處理一些異常情況,此時直接返回null
137     if (module.status < STATUS.SAVED && !hasModifiers(module)) {
138       return null
139     }
140     // 更改模塊狀態為COMPILING,表示模塊正在編譯
141     module.status = STATUS.COMPILING
142
143     // 模塊內部使用,是一個方法,用來獲取其他模塊提供(稱之為子模塊)的接口,同步操作
144     function require(id) {
145         // 根據id解析模塊的路徑
146         var uri = resolve(id, module.uri)
147         // 從模塊緩存中獲取模塊(注意,其實這里子模塊作為主模塊的依賴項是已經被下載下來的)
148         var child = cachedModules[uri]
149
150         // Just return null when uri is invalid.
151         // 如果child為空,只能表示參數填寫出錯導致uri不正確,那么直接返回null
152         if (!child) {
153           return null
154         }
155
156         // Avoids circular calls.
157         // 如果子模塊的狀態為STATUS.COMPILING,直接返回child.exports,避免因為循環依賴反復編譯模塊
158         if (child.status === STATUS.COMPILING) {
159           return child.exports
160         }
161         // 指向初始化時調用當前模塊的模塊。根據該屬性,可以得到模塊初始化時的Call Stack.
162         child.parent = module
163         // 返回編譯過的child的module.exports
164         return child._compile()
165     }
166     // 模塊內部使用,用來異步加載模塊,并在加載完成后執行指定回調。
167     require.async = function(ids, callback) {
168       module._use(ids, callback)
169     }
170     // 使用模塊系統內部的路徑解析機制來解析并返回模塊路徑。該函數不會加載模塊,只返回解析后的絕對路徑。
171     require.resolve = function(id) {
172       return resolve(id, module.uri)
173     }
174     // 通過該屬性,可以查看到模塊系統加載過的所有模塊。
175     // 在某些情況下,如果需要重新加載某個模塊,可以得到該模塊的 uri, 然后通過 delete require.cache[uri] 來將其信息刪除掉。這樣下次          使用時,就會重新獲取。
176     require.cache = cachedModules
177
178     // require是一個方法,用來獲取其他模塊提供的接口。
179     module.require = require
180     // exports是一個對象,用來向外提供模塊接口。
181     module.exports = {}
182     var factory = module.factory
183
184     // factory 為函數時,表示模塊的構造方法。執行該方法,可以得到模塊向外提供的接口。
185     if (util.isFunction(factory)) {
186       compileStack.push(module)
187       runInModuleContext(factory, module)
188       compileStack.pop()
189     }
190     // factory 為對象、字符串等非函數類型時,表示模塊的接口就是該對象、字符串等值。
191     // 如:define({ "foo": "bar" });
192     // 如:define('I am a template. My name is {{name}}.');
193     else if (factory !== undefined) {
194       module.exports = factory
195     }
196
197     // 更改模塊狀態為COMPILED,表示模塊已編譯
198     module.status = STATUS.COMPILED
199     // 執行模塊接口修改,通過seajs.modify()
200     execModifiers(module)
201     return module.exports
202   }

復制代碼 代碼如下:

if (util.isFunction(factory)) {
186       compileStack.push(module)
187       runInModuleContext(factory, module)
188       compileStack.pop()
189     }

這里就是把module.export進行初始化。runInModuleContext方法:

復制代碼 代碼如下:

// 根據模塊上下文執行模塊代碼
489   function runInModuleContext(fn, module) {
490     // 傳入與模塊相關的兩個參數以及模塊自身
491     // exports用來暴露接口
492     // require用來獲取依賴模塊(同步)(編譯)
493     var ret = fn(module.require, module.exports, module)
494     // 支持返回值暴露接口形式,如:
495     // return {
496     //   fn1 : xx
497     //   ,fn2 : xx
498     //   ...
499     // }
500     if (ret !== undefined) {
501       module.exports = ret
502     }
503   }

執行a.js中的function方法,這時會調用var b = require("b.js"),
require方法會返回b的compile方法的返回值,b模塊中又有var c = require('c.js')。
這時會調用c的compile方法,然后調用c的function,c中,如果要暴露對象,或者是return 對象c,則模塊c的exports = c。或者直接是module.export = c;總之最后會返回module c.export = c;所以var c = module c.export = c,模塊b中,就可以使用變量c調用模塊c中的c對象的方法和屬性。
以此類推,最終a模塊也能調用b模塊中b對象的屬性和方法。
不管什么模塊,只要使用了module.export = xx模塊,其他模塊就可以使用require("xx模塊"),調用xx模塊中的各種方法了。
最終模塊的狀態會變成module.status = STATUS.COMPILED。

復制代碼 代碼如下:

Module.prototype._use = function(ids, callback) {  
  var uris = resolve(ids, this.uri);      //解析['./a','jquery']
    this._load(uris, function() {    //把解析出來的a,jquery模塊的地址[url1,url2],調用_load方法。
                //util.map : 讓數據成員全部執行一次一個指定的函數,并返回一個新的數組,該數組為原數組成員執行回調后的結果
      var args = util.map(uris, function(uri) {

         return uri ? cachedModules[uri]._compile() : null;//如果存在url,就調用_compile方法。
   })
    if (callback) { callback.apply(null, args) } 
  })
   }

這時args = [module a.export, module jquery.export];

復制代碼 代碼如下:

seajs.use(['./a','jquery'],function(a,$){
    var num = a.a;
    $('#J_A').text(num);
})

這時function中的a和$就是module a.export和module jquery.export。

因為本人現在在研究jquery源碼和jquery框架設計,因此共享一些經驗:
jquery源碼,我在網上看了很多解析,看著看著就看不下去了。意義不大,推薦妙味課堂的jquery源碼解析。

司徒正美的javascript框架設計,個人覺得難度大,但是精讀后,你就是高級前端工程師了。

玉伯的sea.js,我建議去學習,去用,畢竟是中國人自己做的。我們公司新的項目或者重構,都會使用seajs來做。

接下來就是模塊化handbars以及mvc的backbone或者mvvm的angular的源碼精讀。這里我希望有人給我提建議,看什么書,看什么網站,看什么視頻能夠快速的學習。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲www永久成人夜色| 日本精品在线视频| 日韩高清人体午夜| 国产精品入口夜色视频大尺度| 中文字幕在线视频日韩| 亚洲色无码播放| 欧美一级在线播放| 午夜精品久久久久久久99黑人| 午夜精品久久17c| 92看片淫黄大片欧美看国产片| 国内精品400部情侣激情| 欧美高清无遮挡| 亚洲国产又黄又爽女人高潮的| 亚洲一区999| 精品久久久久久久久久久久| 国产69精品久久久久99| 久久免费少妇高潮久久精品99| 人九九综合九九宗合| 91视频国产高清| 久久九九有精品国产23| 国产日韩欧美91| 日韩av手机在线看| 成人久久一区二区三区| xxxxx成人.com| 日韩第一页在线| 欧美成人免费全部观看天天性色| 欧美伊久线香蕉线新在线| 欧美日韩精品在线播放| 久久久久久综合网天天| 久久久精品久久久久| 91精品国产91久久久久久| 日韩精品中文在线观看| 98午夜经典影视| 亚洲性生活视频在线观看| 欧美视频在线免费| 欧美日韩不卡合集视频| 亚洲美女精品久久| 国产精品第2页| 亚洲福利视频网站| 黑人精品xxx一区一二区| 97不卡在线视频| 最近2019中文字幕第三页视频| 91色琪琪电影亚洲精品久久| 97不卡在线视频| 久久久国产精品免费| 日韩欧美在线第一页| 91视频免费网站| 九九热这里只有精品6| 亚洲成色999久久网站| 国产一区二区三区在线播放免费观看| 欧美一级淫片播放口| 久久久国产在线视频| 久久精品91久久香蕉加勒比| 成人中文字幕在线观看| 亚洲全黄一级网站| 日韩av在线免播放器| 欧美精品一本久久男人的天堂| 欧美黑人性猛交| 欧美性生交xxxxx久久久| 亚洲欧美日韩区| 成人免费视频网| 中文字幕精品一区二区精品| 欧美激情第一页xxx| 久久国内精品一国内精品| 国产精品h片在线播放| 国产精品福利在线观看网址| 国产精品一区电影| 日韩免费在线视频| 亚洲精选在线观看| 欧美激情欧美激情在线五月| 欧美成人精品一区| 欧美一区二三区| 国产精品久久久久久久久久新婚| 日韩欧美在线看| 成人欧美一区二区三区黑人| 国产精品久久久久久久午夜| 中文字幕av一区| 久久久久久欧美| 亚洲一区二区三区视频播放| 久久久久久久国产| 影音先锋欧美精品| 国产欧美精品久久久| 欧美影院成年免费版| 成人免费视频网址| 欧美日韩国产一中文字不卡| 国产综合福利在线| 国内精品久久久久久久久| 国产日韩精品视频| 日韩精品欧美激情| 欧洲一区二区视频| 日韩欧美aaa| 日韩中文字幕在线看| 国产精品啪视频| 久久成人综合视频| 国产精品国语对白| 一区二区亚洲欧洲国产日韩| 欧美日韩国产激情| 久久99精品久久久久久青青91| 亚洲精品国产精品乱码不99按摩| 亚洲精品之草原avav久久| 欧美精品在线免费观看| 91免费观看网站| 国产欧美va欧美va香蕉在线| 欧美老女人在线视频| 欧美性猛交xxxx黑人猛交| 高清日韩电视剧大全免费播放在线观看| 亚洲国产精品va在线观看黑人| 亚洲国产婷婷香蕉久久久久久| 成人激情av在线| 亚洲毛片在线免费观看| 国产精品永久免费在线| 精品欧美aⅴ在线网站| 欧美激情精品久久久久久黑人| 精品久久久久久中文字幕| 亚洲japanese制服美女| 国模极品一区二区三区| 人妖精品videosex性欧美| 精品呦交小u女在线| 韩国国内大量揄拍精品视频| 色中色综合影院手机版在线观看| 97超碰蝌蚪网人人做人人爽| 91视频九色网站| 久久色在线播放| 国产精品久久久久久久久影视| 欧洲一区二区视频| 国产一区二区三区四区福利| 日韩午夜在线视频| 中文字幕免费国产精品| 欧美黄色小视频| 日韩乱码在线视频| 欧美激情免费观看| 国产成人福利夜色影视| 亚洲资源在线看| 日韩精品在线免费观看视频| 欧美在线视频一区| 亚洲va国产va天堂va久久| 国产精品第一页在线| 福利二区91精品bt7086| 91精品国产91久久久| 伊人成人开心激情综合网| 欧美日韩国产第一页| 国产福利精品在线| 亚洲一区二区久久久久久久| 欧美人与性动交| 国产自产女人91一区在线观看| 欧美激情中文字幕乱码免费| 久久久精品免费视频| 亚洲图片欧洲图片av| 91豆花精品一区| 欧美大片欧美激情性色a∨久久| 自拍偷拍亚洲精品| 中文字幕精品在线视频| 一区二区三区在线播放欧美| 在线视频日本亚洲性| 久久精品夜夜夜夜夜久久| 亚洲人成在线观| 97国产精品人人爽人人做| 欧美性猛交xxxx免费看| 国产精品久久综合av爱欲tv| 欧美最顶级的aⅴ艳星| 日韩欧美国产黄色| 自拍亚洲一区欧美另类| 国产日韩欧美另类| 亚洲精品不卡在线|