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

前端模塊化的前世

開發 前端
隨著前端項目的越來越龐大,組件化的前端框架,前端路由等技術的發展,模塊化已經成為現代前端工程師的一項必備技能。無論是什么語言一旦發展到一定地步,其工程化能力和可維護性勢必得到相應的發展。

[[342490]]

前言

隨著前端項目的越來越龐大,組件化的前端框架,前端路由等技術的發展,模塊化已經成為現代前端工程師的一項必備技能。無論是什么語言一旦發展到一定地步,其工程化能力和可維護性勢必得到相應的發展。

模塊化這件事,無論在哪個編程領域都是相當常見的事情,模塊化存在的意義就是為了增加可復用性,以盡可能少的代碼是實現個性化的需求。同為前端三劍客之一的 CSS 早在 2.1 的版本就提出了 @import 來實現模塊化,但是 JavaScript 直到 ES6 才出現官方的模塊化方案: ES Module (import、export)。盡管早期 JavaScript 語言規范上不支持模塊化,但這并沒有阻止 JavaScript 的發展,官方沒有模塊化標準開發者們就開始自己創建規范,自己實現規范。

CommonJS 的出現

十年前的前端沒有像現在這么火熱,模塊化也只是使用閉包簡單的實現一個命名空間。2009 年對 JavaScript 無疑是重要的一年,新的 JavaScript 引擎 (v8) ,并且有成熟的庫 (jQuery、YUI、Dojo),ES5 也在提案中,然而 JavaScript 依然只能出現在瀏覽器當中。早在2007年,AppJet 就提供了一項服務,創建和托管服務端的 JavaScript 應用。后來 Aptana 也提供了一個能夠在服務端運行 Javascript 的環境,叫做 Jaxer。網上還能搜到關于 AppJet、Jaxer 的博客,甚至 Jaxer 項目還在github上。

Jaxer

 

但是這些東西都沒有發展起來,Javascript 并不能替代傳統的服務端腳本語言 (PHP、Python、Ruby) 。盡管它有很多的缺點,但是不妨礙有很多人使用它。后來就有人開始思考 JavaScript 要在服務端運行還需要些什么?于是在 2009 年 1 月,Mozilla 的工程師 Kevin Dangoor 發起了 CommonJS 的提案,呼吁 JavaScript 愛好者聯合起來,編寫 JavaScript 運行在服務端的相關規范,一周之后,就有了 224 個參與者。

“"[This] is not a technical problem,It's a matter of people getting together and making a decision to step forward and start building up something bigger and cooler together."

CommonJS 標準囊括了 JavaScript 需要在服務端運行所必備的基礎能力,比如:模塊化、IO 操作、二進制字符串、進程管理、Web網關接口 (JSGI) 。但是影響最深遠的還是 CommonJS 的模塊化方案,CommonJS 的模塊化方案是JavaScript社區第一次在模塊系統上取得的成果,不僅支持依賴管理,而且還支持作用域隔離和模塊標識。再后來 node.js 出世,他直接采用了 CommonJS 的模塊化規范,同時還帶來了npm (Node Package Manager,現在已經是全球最大模塊倉庫了) 。

CommonJS 在服務端表現良好,很多人就想將 CommonJS 移植到客戶端 (也就是我們說的瀏覽器) 進行實現。由于CommonJS 的模塊加載是同步的,而服務端直接從磁盤或內存中讀取,耗時基本可忽略,但是在瀏覽器端如果還是同步加載,對用戶體驗極其不友好,模塊加載過程中勢必會向服務器請求其他模塊代碼,網絡請求過程中會造成長時間白屏。所以從 CommonJS 中逐漸分裂出來了一些派別,在這些派別的發展過程中,出現了一些業界較為熟悉方案 AMD、CMD、打包工具(Component/Browserify/Webpack)。

AMD規范:RequireJS

RequireJS logo

 

RequireJS 是 AMD 規范的代表之作,它之所以能代表 AMD 規范,是因為 RequireJS 的作者 (James Burke) 就是 AMD 規范的提出者。同時作者還開發了 amdefine,一個讓你在 node 中也可以使用 AMD 規范的庫。

AMD 規范由 CommonJS 的 Modules/Transport/C 提案發展而來,毫無疑問,Modules/Transport/C 提案的發起者就是 James Burke。

James Burke 指出了 CommonJS 規范在瀏覽器上的一些不足:

  1. 缺少模塊封裝的能力:CommonJS 規范中的每個模塊都是一個文件。這意味著每個文件只有一個模塊。這在服務器上是可行的,但是在瀏覽器中就不是很友好,瀏覽器中需要做到盡可能少的發起請求。
  2. 使用同步的方式加載依賴:雖然同步的方法進行加載可以讓代碼更容易理解,但是在瀏覽器中使用同步加載會導致長時間白屏,影響用戶體驗。
  3. CommonJS 規范使用一個名為 export 的對象來暴露模塊,將需要導出變量附加到export 上,但是不能直接給該對象進行賦值。如果需要導出一個構造函數,則需要使用module.export,這會讓人感到很疑惑。

AMD 規范定義了一個 define 全局方法用來定義和加載模塊,當然 RequireJS 后期也擴展了require 全局方法用來加載模塊 。通過該方法解決了在瀏覽器使用 CommonJS 規范的不足。

  1. define(id?, dependencies?, factory); 

使用匿名函數來封裝模塊,并通過函數返回值來定義模塊,這更加符合 JavaScript 的語法,這樣做既避免了對 exports 變量的依賴,又避免了一個文件只能暴露一個模塊的問題。

提前列出依賴項并進行異步加載,這在瀏覽器中,這能讓模塊開箱即用。

  1. define("foo", ["logger"], function (logger) { 
  2.     logger.debug("starting foo's definition"
  3.     return { 
  4.         name"foo" 
  5.     } 
  6. }) 

為模塊指定一個模塊 ID (名稱) 用來唯一標識定義中模塊。此外,AMD的模塊名規范是 CommonJS 模塊名規范的超集。

  1. define("foo"function () { 
  2.     return { 
  3.         name'foo' 
  4.     } 
  5. }) 

RequireJS 原理

在討論原理之前,我們可以先看下 RequireJS 的基本使用方式。

  • 模塊信息配置:
  1. require.config({ 
  2.   paths: { 
  3.     jquery: 'https://code.jquery.com/jquery-3.4.1.js' 
  4.   } 
  5. }) 
  • 依賴模塊加載與調用:
  1. require(['jquery'], function ($){ 
  2.   $('#app').html('loaded'
  3. }) 
  • 模塊定義:
  1. if ( typeof define === "function" && define.amd ) { 
  2.   define( "jquery", [], function() { 
  3.     return jQuery; 
  4.   } ); 

我們首先使用 config 方法進行了 jquery 模塊的路徑配置,然后調用 require 方法加載 jquery 模塊,之后在回調中調用已加載完成的 $ 對象。在這個過程中,jquery 會使用define 方法暴露出我們所需要的 $ 對象。

在了解了基本的使用過程后,我們就繼續深入 RequireJS 的原理。

模塊信息配置

模塊信息的配置,其實很簡單,只用幾行代碼就能實現。定義一個全局對象,然后使用 Object.assign 進行對象擴展。

  1. // 配置信息 
  2. const cfg = { paths: {} } 
  3.  
  4. // 全局 require 方法 
  5. req = require = () => {} 
  6.  
  7. // 擴展配置 
  8. req.config = config => { 
  9.   Object.assign(cfg, config) 

依賴模塊加載與調用

require 方法的邏輯很簡單,進行簡單的參數校驗后,調用 getModule 方法對 Module 進行了實例化,getModule 會對已經實例化的模塊進行緩存。因為 require 方法進行模塊實例的時候,并沒有模塊名,所以這里產生的是一個匿名模塊。Module 類,我們可以理解為一個模塊加載器,主要作用是進行依賴的加載,并在依賴加載完畢后,調用回調函數,同時將依賴的模塊逐一作為參數回傳到回調函數中。

  1. // 全局 require 方法 
  2. req = require = (deps, callback) => { 
  3.   if (!deps && !callback) { 
  4.     return 
  5.   } 
  6.   if (!deps) { 
  7.     deps = [] 
  8.   } 
  9.   if (typeof deps === 'function') { 
  10.     callback = deps 
  11.     deps = [] 
  12.   } 
  13.   const mod = getModule() 
  14.   mod.init(deps, callback) 
  15.  
  16. let reqCounter = 0 
  17. const registry = {} // 已注冊的模塊 
  18.  
  19. // 模塊加載器的工廠方法 
  20. const getModule = name => { 
  21.   if (!name) { 
  22.     // 如果模塊名不存在,表示為匿名模塊,自動構造模塊名 
  23.     name = `@mod_${++reqCounter}` 
  24.   } 
  25.   let mod = registry[name
  26.   if (!mod) { 
  27.     mod = registry[name] = new Module(name
  28.   } 
  29.   return mod 

模塊加載器是是整個模塊加載的核心,主要包括 enable 方法和 check 方法。

模塊加載器在完成實例化之后,會首先調用 init 方法進行初始化,初始化的時候傳入模塊的依賴以及回調。

  1. // 模塊加載器 
  2.  
  3. class Module { 
  4.   constructor(name) { 
  5.     this.name = name 
  6.     this.depCount = 0 
  7.     this.depMaps = [] 
  8.     this.depExports = [] 
  9.     this.definedFn = () => {} 
  10.   } 
  11.   init(deps, callback) { 
  12.     this.deps = deps 
  13.     this.callback = callback 
  14.     // 判斷是否存在依賴 
  15.     if (deps.length === 0) { 
  16.       this.check() 
  17.     } else { 
  18.       this.enable() 
  19.     } 
  20.   } 

enable 方法主要用于模塊的依賴加載,該方法的主要邏輯如下:

1.遍歷所有的依賴模塊;

2.記錄已加載模塊數 (this.depCount++),該變量用于判斷依賴模塊是否全部加載完畢;

3.實例化依賴模塊的模塊加載器,并綁定 definedFn 方法;

“definedFn 方法會在依賴模塊加載完畢后調用,主要作用是獲取依賴模塊的內容,并將 depCount 減 1,最后調用 check 方法 (該方法會判斷 depCount 是否已經小于 1,以此來界定依賴全部加載完畢);

4.最后通過依賴模塊名,在配置中獲取依賴模塊的路徑,進行模塊加載。

  1. class Module { 
  2.   ... 
  3.   // 啟用模塊,進行依賴加載 
  4.   enable() { 
  5.     // 遍歷依賴 
  6.     this.deps.forEach((name, i) => { 
  7.       // 記錄已加載的模塊數 
  8.       this.depCount++ 
  9.        
  10.       // 實例化依賴模塊的模塊加載器,綁定模塊加載完畢的回調 
  11.       const mod = getModule(name
  12.       mod.definedFn = exports => { 
  13.         this.depCount-- 
  14.         this.depExports[i] = exports 
  15.         this.check() 
  16.       } 
  17.        
  18.       // 在配置中獲取依賴模塊的路徑,進行模塊加載 
  19.       const url = cfg.paths[name
  20.       loadModule(name, url) 
  21.     }); 
  22.   } 
  23.   ... 

loadModule 的主要作用就是通過 url 去加載一個 js 文件,并綁定一個 onload 事件。onload 會重新獲取依賴模塊已經實例化的模塊加載器,并調用 init 方法。

  1. // 緩存加載的模塊 
  2. const defMap = {} 
  3.  
  4. // 依賴的加載 
  5. const loadModule =  (name, url) => { 
  6.   const head = document.getElementsByTagName('head')[0] 
  7.   const node = document.createElement('script'
  8.   node.type = 'text/javascript' 
  9.   node.async = true 
  10.   // 設置一個 data 屬性,便于依賴加載完畢后拿到模塊名 
  11.   node.setAttribute('data-module'name
  12.   node.addEventListener('load', onScriptLoad, false
  13.   node.src = url 
  14.   head.appendChild(node) 
  15.   return node 
  16.  
  17. // 節點綁定的 onload 事件函數 
  18. const onScriptLoad = evt => { 
  19.   const node = evt.currentTarget 
  20.   node.removeEventListener('load', onScriptLoad, false
  21.   // 獲取模塊名 
  22.   const name = node.getAttribute('data-module'
  23.   const mod = getModule(name
  24.   const def = defMap[name
  25.   mod.init(def.deps, def.callback) 

看到之前的案例,因為只有一個依賴 (jQuery),并且 jQuery 模塊并沒有其他依賴,所以init 方法會直接調用 check 方法。這里也可以思考一下,如果是一個有依賴項的模塊后續的流程是怎么樣的呢?

  1. define( "jquery", [] /* 無其他依賴 */, function() { 
  2.   return jQuery; 
  3. } ); 

check 方法主要用于依賴檢測,以及調用依賴加載完畢后的回調。

  1. // 模塊加載器 
  2. class Module { 
  3.   ... 
  4.   // 檢查依賴是否加載完畢 
  5.   check() { 
  6.     let exports = this.exports 
  7.     //如果依賴數小于1,表示依賴已經全部加載完畢 
  8.     if (this.depCount < 1) {  
  9.       // 調用回調,并獲取該模塊的內容 
  10.       exports = this.callback.apply(null, this.depExports) 
  11.       this.exports = exports 
  12.       //激活 defined 回調 
  13.       this.definedFn(exports) 
  14.     } 
  15.   } 
  16.   ... 

最終通過 definedFn 重新回到被依賴模塊,也就是最初調用 require 方法實例化的匿名模塊加載器中,將依賴模塊暴露的內容存入 depExports 中,然后調用匿名模塊加載器的check 方法,調用回調。

  1. mod.definedFn = exports => { 
  2.   this.depCount-- 
  3.   this.depExports[i] = exports 
  4.   this.check() 

模塊定義

還有一個疑問就是,在依賴模塊加載完畢的回調中,怎么拿到的依賴模塊的依賴和回調呢?

  1. const def = defMap[name
  2. mod.init(def.deps, def.callback) 

答案就是通過全局定義的 define 方法,該方法會將模塊的依賴項還有回調存儲到一個全局變量,后面只要按需獲取即可。

  1. const defMap = {} // 緩存加載的模塊 
  2. define = (name, deps, callback) => { 
  3.   defMap[name] = { name, deps, callback } 

RequireJS 原理總結

最后可以發現,RequireJS 的核心就在于模塊加載器的實現,不管是通過 require 進行依賴加載,還是使用 define 定義模塊,都離不開模塊加載器。

感興趣的可以在我的github上查看關于簡化版 RequrieJS 的完整代碼 。

CMD規范:sea.js

sea.js logo

 

CMD 規范由國內的開發者玉伯提出,盡管在國際上的知名度遠不如 AMD ,但是在國內也算和 AMD 齊頭并進。相比于 AMD 的異步加載,CMD 更加傾向于懶加載,而且 CMD 的規范與 CommonJS 更貼近,只需要在 CommonJS 外增加一個函數調用的包裝即可。

  1. define(function(require, exports, module) { 
  2.   require("./a").doSomething() 
  3.   require("./b").doSomething() 
  4. }) 

作為 CMD 規范的實現 sea.js 也實現了類似于 RequireJS 的 api:

  1. seajs.use('main'function (main) { 
  2.   main.doSomething() 
  3. }) 

sea.js 在模塊加載的方式上與 RequireJS 一致,都是通過在 head 標簽插入 script 標簽進行加載的,但是在加載順序上有一定的區別。要講清楚這兩者之間的差別,我們還是直接來看一段代碼:

RequireJS :

  1. // RequireJS 
  2. define('a'function () { 
  3.   console.log('a load'
  4.   return { 
  5.     run: function () { console.log('a run') } 
  6.   } 
  7. }) 
  8.  
  9. define('b'function () { 
  10.   console.log('b load'
  11.   return { 
  12.     run: function () { console.log('b run') } 
  13.   } 
  14. }) 
  15.  
  16. require(['a''b'], function (a, b) { 
  17.   console.log('main run'
  18.   a.run() 
  19.   b.run() 
  20. }) 

requirejs result

 

sea.js :

  1. // sea.js 
  2. define('a'function (require, exports, module) { 
  3.   console.log('a load'
  4.   exports.run = function () { console.log('a run') } 
  5. }) 
  6.  
  7. define('b'function (require, exports, module) { 
  8.   console.log('b load'
  9.   exports.run = function () { console.log('b run') } 
  10. }) 
  11.  
  12. define('main'function (require, exports, module) { 
  13.   console.log('main run'
  14.   var a = require('a'
  15.   a.run() 
  16.   var b = require('b'
  17.   b.run() 
  18. }) 
  19.  
  20. seajs.use('main'

sea.js result

 

可以看到 sea.js 的模塊屬于懶加載,只有在 require 的地方,才會真正運行模塊。而 RequireJS,會先運行所有的依賴,得到所有依賴暴露的結果后再執行回調。

正是因為懶加載的機制,所以 sea.js 提供了 seajs.use 的方法,來運行已經定義的模塊。所有 define 的回調函數都不會立即執行,而是將所有的回調函數進行緩存,只有 use 之后,以及被 require 的模塊回調才會進行執行。

sea.js 原理

下面簡單講解一下 sea.js 的懶加載邏輯。在調用 define 方法的時候,只是將 模塊放入到一個全局對象進行緩存。

  1. const seajs = {} 
  2. const cache = seajs.cache = {} 
  3.  
  4. define = (id, factory) => { 
  5.   const uri = id2uri(id) 
  6.   const deps = parseDependencies(factory.toString()) 
  7.   const mod = cache[uri] || (cache[uri] = new Module(uri)) 
  8.   mod.deps = deps 
  9.   mod.factory = factory 
  10.    
  11.  
  12. class Module { 
  13.   constructor(uri, deps) { 
  14.     this.status = 0 
  15.     this.uri    = uri 
  16.     this.deps   = deps 
  17.   } 

這里的 Module,是一個與 RequireJS 類似的模塊加載器。后面運行的 seajs.use 就會從緩存取出對應的模塊進行加載。

“注意:這一部分代碼只是簡單介紹 use 方法的邏輯,并不能直接運行。

  1. let cid = 0 
  2. seajs.use = (ids, callback) => { 
  3.   const deps = isArray(ids) ? ids : [ids] 
  4.    
  5.   deps.forEach(async (dep, i) => { 
  6.     const mod = cache[dep] 
  7.     mod.load() 
  8.   }) 

另外 sea.js 的依賴都是在 factory 中聲明的,在模塊被調用的時候,sea.js 會將 factory 轉成字符串,然后匹配出所有的 require('xxx') 中的 xxx ,來進行依賴的存儲。前面代碼中的 parseDependencies 方法就是做這件事情的。

早期 sea.js 是直接通過正則的方式進行匹配的:

  1. const parseDependencies = (code) => { 
  2.   const REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g 
  3.   const SLASH_RE = /\\\\/g 
  4.   const ret = [] 
  5.  
  6.   code 
  7.     .replace(SLASH_RE, ''
  8.     .replace(REQUIRE_RE, function(_, __, id) { 
  9.       if (id) { 
  10.         ret.push(id) 
  11.       } 
  12.     }) 
  13.   return ret 

但是后來發現正則有各種各樣的 bug,并且過長的正則也不利于維護,所以 sea.js 后期舍棄了這種方式,轉而使用狀態機進行詞法分析的方式獲取 require 依賴。

詳細代碼可以查看 sea.js 相關的子項目:crequire。

sea.js 原理總結

其實 sea.js 的代碼邏輯大體上與 RequireJS 類似,都是通過創建 script 標簽進行模塊加載,并且都有實現一個模塊記載器,用于管理依賴。

主要差異在于,sea.js 的懶加載機制,并且在使用方式上,sea.js 的所有依賴都不是提前聲明的,而是 sea.js 內部通過正則或詞法分析的方式將依賴手動進行提取的。

感興趣的可以在我的github上查看關于簡化版 sea.js 的完整代碼 。

 

總結ES6 的模塊化規范已經日趨完善,其靜態化思想也為后來的打包工具提供了便利,并且能友好的支持 tree shaking。了解這些已經過時的模塊化方案看起來似乎有些無趣,但是歷史不能被遺忘,我們應該多了解這些東西出現的背景,以及前人們的解決思路,而不是一直抱怨新東西更迭的速度太快。

本文轉載自微信公眾號「更了不起的前端」,可以通過以下二維碼關注。轉載本文請聯系更了不起的前端公眾號。

 

責任編輯:武曉燕 來源: 更了不起的前端
相關推薦

2022-09-05 09:01:13

前端模塊化

2020-09-18 09:02:32

前端模塊化

2013-08-20 15:31:18

前端模塊化

2022-03-11 13:01:27

前端模塊

2019-12-02 16:05:10

前端模塊化JavaScript

2023-05-24 10:35:11

Node.jsES模塊

2014-04-27 10:16:31

QCon北京2014Andrew Bett

2019-08-28 16:18:39

JavaScriptJS前端

2016-09-23 11:08:35

前端Javascript模塊化

2013-03-19 10:50:38

2013-03-11 10:00:13

前端模塊化

2018-12-18 11:20:28

前端模塊化JavaScript

2016-10-09 11:03:41

Javascript模塊化Web

2013-03-11 10:10:03

2022-09-21 11:51:26

模塊化應用

2015-10-10 10:01:28

前端模塊化webpack

2017-05-18 10:23:55

模塊化開發RequireJsJavascript

2015-10-10 11:29:45

Java模塊化系統初探

2021-07-14 09:26:51

UPS電源模塊化

2010-05-28 10:31:28

模塊化IT
點贊
收藏

51CTO技術棧公眾號

亚洲国产精品视频一区| 欧美黑人性猛交| 中文字幕第38页| av大大超碰在线| 99精品热视频| 国产精品一二三在线| 免费中文字幕在线| 亚洲男人都懂第一日本| 欧美日韩电影在线| 久久黄色片视频| 9191在线| 91小视频在线免费看| 国产精品自拍偷拍| 天天爽夜夜爽夜夜爽精品| 欧美r级电影| 亚洲国内精品在线| 在线a免费观看| 一二区成人影院电影网| 亚洲国产精品精华液网站| 先锋影音亚洲资源| 水莓100在线视频| 国产麻豆精品视频| 国产精品爽黄69天堂a| 国产精品500部| 91成人精品| 在线成人激情视频| 国产麻豆天美果冻无码视频| 国产日韩中文在线中文字幕| 日本电影亚洲天堂一区| 免费在线观看视频a| 国产秀色在线www免费观看| 国产亚洲精品久| 久久国产精品-国产精品| 精品欧美一区二区精品少妇| 麻豆精品视频在线观看免费| 97avcom| 高h视频免费观看| 国产韩国精品一区二区三区| 亚洲新中文字幕| 野花社区视频在线观看| 电影一区二区在线观看| 日韩色视频在线观看| 手机免费看av网站| 国产香蕉久久| 欧美视频一区二区在线观看| 茄子视频成人免费观看| 国产激情在线播放| 亚洲成人激情自拍| 日本香蕉视频在线观看| 香蕉成人app免费看片| 国产精品国产a| 亚洲午夜精品久久久中文影院av| 国产视频在线看| 国产日韩欧美不卡在线| 欧美色欧美亚洲另类七区| 午夜在线视频观看| 99精品欧美一区二区三区小说| 国产高清在线一区| 亚洲免费成人网| 成人精品视频一区| 狠狠色噜噜狠狠色综合久| 欧美熟妇交换久久久久久分类| 福利电影一区二区三区| 翡翠波斯猫1977年美国| 亚洲精品久久久久久久久久久久久久| 国产高清精品网站| 国产精品免费视频一区二区 | 懂色av中文在线| 欧美激情在线看| 国产对白在线播放| 欧美一卡二卡| 欧美日韩精品在线观看| 情侣黄网站免费看| 91成人app| 精品国产91乱码一区二区三区 | 国产精品免费无遮挡| 国产在线看一区| 好吊妞www.84com只有这里才有精品| 天堂在线观看视频| 欧美激情一区三区| 中文字幕一区二区三区四区五区人 | 免费精品一区二区| 精品一区二区三区香蕉蜜桃| 成人激情直播| 日韩精品一二| 国产精品久久久久三级| 国产91porn| 美女高潮在线观看| 欧美日韩久久一区| 任你躁av一区二区三区| 九一精品国产| 欧美成人免费全部观看天天性色| 国产精品免费av一区二区| 日本在线不卡视频一二三区| 亚洲精品免费网站| 头脑特工队2在线播放| 国产精品久线在线观看| 福利视频免费在线观看| 99只有精品| 亚洲精品在线一区二区| 中文字幕黄色网址| 91久久午夜| 国产欧美日韩丝袜精品一区| 久久久久久久久久久人体| 999热精品视频| 日本成人a网站| 日韩一区二区在线视频| 男女视频免费看| 国产在线视视频有精品| 久久久久久国产精品mv| 制服丝袜在线播放| 欧美主播一区二区三区美女| 中文字幕乱视频| 97色伦图片97综合影院| 日本高清+成人网在线观看| 国产欧美日韩综合精品一区二区三区 | 欧美一区二区麻豆红桃视频| 欧美国产激情18| 一本一道人人妻人人妻αv| www.成人在线| 激情视频小说图片| 99久久er| 亚洲老头同性xxxxx| 国产在线观看免费av| 久久91精品国产91久久小草 | 成人做爰69片免费| 9191国语精品高清在线| 国产精品视频一区二区三区四| 亚洲av成人精品毛片| 亚洲在线视频免费观看| 女同激情久久av久久| 国产欧美一区| 国产aⅴ夜夜欢一区二区三区| 欧美亚洲精品在线观看| 一区二区三区在线观看动漫| 在线一区二区不卡| 99re66热这里只有精品8| 国产一区二区中文字幕| 日韩免费观看高清| 五月婷婷六月丁香综合| 亚洲成人tv网| 无码人妻精品一区二区三区99不卡| 久久久9色精品国产一区二区三区| 国产精品视频精品| 成人欧美亚洲| 欧美网站一区二区| 国产激情av在线| 另类综合日韩欧美亚洲| 亚洲精品永久www嫩草| 日本国产欧美| 深夜精品寂寞黄网站在线观看| 国产成人无码专区| 中文字幕乱码久久午夜不卡| 搡女人真爽免费午夜网站| 深爱激情综合| 国产欧美日韩高清| 久cao在线| 日韩午夜中文字幕| 精品无码人妻一区二区三区| 国产99久久久精品| 人妻无码久久一区二区三区免费| 99久久免费精品国产72精品九九| 久久免费视频网| 天天操天天干天天爱| 欧美香蕉大胸在线视频观看| 国产aⅴ激情无码久久久无码| 日本va欧美va瓶| 男插女免费视频| 国产伦乱精品| 国产精品成av人在线视午夜片| 第一福利在线| 欧美一级黄色片| 日韩美女视频网站| 国产无人区一区二区三区| 手机av在线免费| 在线电影一区| 亚洲国产精品一区在线观看不卡 | 国产日韩精品中文字无码| 久久99精品国产麻豆不卡| 成人短视频在线观看免费| 日韩中文av| 成人精品久久av网站| 里番在线播放| 国产一区二区日韩| 91丨porny丨在线中文| 亚洲国产综合在线| 一级黄色毛毛片| 成人av手机在线观看| 99久久久无码国产精品6| 久久久久电影| 蜜桃麻豆91| 日韩欧洲国产| 国产精品video| 男人天堂亚洲天堂 | h网站久久久| 亚洲欧美自拍一区| 精品人妻无码一区二区三区蜜桃一| 婷婷综合五月天| 欧美日韩国产一二三区| www.日韩av| 夜夜爽久久精品91| 日韩国产精品久久| 欧美成人免费在线观看视频| 久久日文中文字幕乱码| 精品国产一区二区三区麻豆免费观看完整版 | 国产精品.xx视频.xxtv| 久久久久久久久综合| wwwww在线观看免费视频| 精品免费国产二区三区| 中国黄色一级视频| 色综合夜色一区| 国产精品第72页| 亚洲色图欧美激情| 成熟人妻av无码专区| 99国产精品久久久久| 日日夜夜精品视频免费观看| 免费精品视频在线| 久久精品99国产| 亚洲一区二区毛片| 黄色激情在线视频| 韩日视频一区| 精品一区二区成人免费视频 | 欧美日韩亚洲一| 欧美精品九九| 免费成人进口网站| 99久久综合狠狠综合久久aⅴ| 日韩.欧美.亚洲| 国产99久久久国产精品成人免费| 成人h在线播放| 亚洲国产中文在线| 亚洲在线视频观看| japansex久久高清精品| 国产一区深夜福利| 999国产精品亚洲77777| 国产精品电影观看| 日韩高清成人| 国产91在线播放九色快色| 蜜桃视频在线网站| 91tv亚洲精品香蕉国产一区7ujn| 国产网红在线观看| 久久免费视频在线| 岛国av免费在线观看| 午夜精品福利在线观看| av中文在线资源| 2018日韩中文字幕| 这里有精品可以观看| 欧美专区在线播放| 美女一区网站| 国产精品成人久久久久| 91九色综合| 成人激情视频网| 视频精品一区二区三区| 99中文字幕| 老牛精品亚洲成av人片| 久久久人人爽| 欧美日韩国产免费观看视频| 天堂社区 天堂综合网 天堂资源最新版| 一道在线中文一区二区三区| 欧美一区国产一区| 欧美码中文字幕在线| 亚洲日本精品| 欧美黄污视频| 免费av手机在线观看| 久久aⅴ乱码一区二区三区| 男女男精品视频站| 韩国三级在线一区| 精品影片一区二区入口| 久久婷婷国产综合国色天香| 亚洲一区二区自偷自拍| 国产精品福利一区二区| 国产一区二区视频在线观看免费| 亚洲综合色丁香婷婷六月图片| 国产特黄大片aaaa毛片| 欧美一a一片一级一片| 国产剧情精品在线| 亚洲国产成人久久综合一区| 国产在线你懂得| 久久不射电影网| 日本不卡网站| 国产精品视频一区国模私拍| 亚洲国产一区二区三区网站| 欧美不卡福利| 中文字幕午夜精品一区二区三区 | 国产91在线播放精品| 亚洲已满18点击进入在线看片| 国产在线播放精品| 亚洲精品乱码久久久久久蜜桃91| 午夜久久tv| www.日本xxxx| 成人中文字幕电影| 1024手机在线观看你懂的| 亚洲国产一区二区视频| 中文字幕视频一区二区| 亚洲成人国产精品| aiai在线| 欧美一级高清免费播放| 99视频这里有精品| 日本不卡二区| 国产尤物精品| 亚洲天堂网2018| 久久久五月婷婷| 久久精品女人毛片国产| 欧美调教femdomvk| 亚洲 欧美 激情 小说 另类| 久久精品中文字幕电影| 日韩欧美另类一区二区| 国产精品10p综合二区| 奇米影视亚洲| 亚洲乱码中文字幕久久孕妇黑人| 国产福利91精品一区二区三区| 奇米网一区二区| 日韩欧美在线视频日韩欧美在线视频 | 亚洲男人天天操| www.8ⅹ8ⅹ羞羞漫画在线看| 91精品免费久久久久久久久| 精品在线观看入口| 奇米影视亚洲色图| 国产精品系列在线观看| 国产123在线| 欧美午夜丰满在线18影院| 亚洲国产精品欧美久久| 久久精品视频va| 欧美激情三区| 亚洲黄色一区二区三区| 亚洲专区免费| 国产熟女高潮一区二区三区| 亚洲激情网站免费观看| 国产又粗又猛又黄又爽| 最近2019年日本中文免费字幕| 色老太综合网| 欧美另类高清视频在线| 国产日韩1区| 人妻在线日韩免费视频| 亚洲国产人成综合网站| www.xxxx国产| 欧美国产精品日韩| 伊人精品综合| 免费高清一区二区三区| 成人午夜电影小说| 国产精品二区一区二区aⅴ| 日韩欧美中文字幕制服| 一二三四区在线观看| 99久久伊人精品影院| 欧美黄色精品| 无码av免费精品一区二区三区| 一区二区三区欧美| 亚洲男人第一天堂| 高清一区二区三区日本久| 精品国产导航| 日本精品久久久久中文字幕| 久久精品欧美日韩精品| 瑟瑟视频在线免费观看| 视频直播国产精品| 北岛玲精品视频在线观看| av磁力番号网| av一区二区不卡| 黄色在线免费观看| 国产亚洲免费的视频看| 亚洲精品777| 男人天堂新网址| 成人99免费视频| 久久中文字幕免费| 丝袜亚洲另类欧美重口| 9999精品| 久久国产午夜精品理论片最新版本| 成人av在线播放网站| wwwxxx亚洲| 色婷婷**av毛片一区| 久久wwww| 岳毛多又紧做起爽| 国产精品的网站| 亚洲av无码乱码国产麻豆| 91av在线国产| 婷婷丁香综合| 一级特黄a大片免费| 欧美视频一区二区三区| 2024最新电影免费在线观看| 国内精品国语自产拍在线观看| 新67194成人永久网站| 91香蕉一区二区三区在线观看| 精品免费视频.| 日韩一级二级| 欧美一级欧美一级| 国产精品久久久久久久久免费桃花| a视频免费在线观看| 日本精品久久中文字幕佐佐木| 99视频精品全国免费| 少妇饥渴放荡91麻豆| 欧美高清hd18日本| 日韩av影片| 欧美精品久久96人妻无码| 91麻豆精东视频| 亚洲av无码国产精品永久一区 | 中文字幕一级片| 久久久久久91| 欧美电影三区| 日韩乱码人妻无码中文字幕久久| 日韩一区二区影院| 成人免费一区| 国产精品无码av在线播放|