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

深入Node.js的模塊加載機制,手寫require函數

開發 前端
本文會從基本的模塊原理出發,到最后我們會利用這個原理,自己實現一個簡單的模塊加載機制,即自己實現一個require。

 [[340070]]

模塊是Node.js里面一個很基本也很重要的概念,各種原生類庫是通過模塊提供的,第三方庫也是通過模塊進行管理和引用的。本文會從基本的模塊原理出發,到最后我們會利用這個原理,自己實現一個簡單的模塊加載機制,即自己實現一個require。

本文完整代碼已上傳GitHub:https://github.com/dennis-jiang/Front-End-Knowledges/blob/master/Examples/Node.js/Module/MyModule/index.js

簡單例子

老規矩,講原理前我們先來一個簡單的例子,從這個例子入手一步一步深入原理。Node.js里面如果要導出某個內容,需要使用module.exports,使用module.exports幾乎可以導出任意類型的JS對象,包括字符串,函數,對象,數組等等。我們先來建一個a.js導出一個最簡單的hello world: 

  1. // a.js   
  2. module.exports = "hello world"

然后再來一個b.js導出一個函數: 

  1. // b.js  
  2. function add(a, b) {  
  3.   return a + b;  
  4.  
  5. module.exports = add

然后在index.js里面使用他們,即require他們,require函數返回的結果就是對應文件module.exports的值: 

  1. // index.js  
  2. const a = require('./a.js');  
  3. const add = require('./b.js'); 
  4. console.log(a);      // "hello world"  
  5. console.log(add(1, 2));    // b導出的是一個加法函數,可以直接使用,這行結果是3 

require會先運行目標文件

當我們require某個模塊時,并不是只拿他的module.exports,而是會從頭開始運行這個文件,module.exports = XXX其實也只是其中一行代碼,我們后面會講到,這行代碼的效果其實就是修改模塊里面的exports屬性。比如我們再來一個c.js: 

  1. // c.js  
  2. let c = 1
  3. cc = c + 1;  
  4. module.exports = c 
  5. c = 6

在c.js里面我們導出了一個c,這個c經過了幾步計算,當運行到module.exports = c;這行時c的值為2,所以我們require的c.js的值就是2,后面將c的值改為了6并不影響前面的這行代碼: 

  1. const c = require('./c.js');  
  2. console.log(c);  // c的值是2 

前面c.js的變量c是一個基本數據類型,所以后面的c = 6;不影響前面的module.exports,那他如果是一個引用類型呢?我們直接來試試吧: 

  1. // d.js  
  2. let d = {  
  3.   num: 1  
  4. };  
  5. d.num++;  
  6. module.exports = d; 
  7. d.num = 6

然后在index.js里面require他: 

  1. const d = require('./d.js');  
  2. console.log(d);     // { num: 6 } 

我們發現在module.exports后面給d.num賦值仍然生效了,因為d是一個對象,是一個引用類型,我們可以通過這個引用來修改他的值。其實對于引用類型來說,不僅僅在module.exports后面可以修改他的值,在模塊外面也可以修改,比如index.js里面就可以直接改: 

  1. const d = require('./d.js');  
  2. d.num = 7 
  3. console.log(d);     // { num: 7 } 

require和module.exports不是黑魔法

我們通過前面的例子可以看出來,require和module.exports干的事情并不復雜,我們先假設有一個全局對象{},初始情況下是空的,當你require某個文件時,就將這個文件拿出來執行,如果這個文件里面存在module.exports,當運行到這行代碼時將module.exports的值加入這個對象,鍵為對應的文件名,最終這個對象就長這樣: 

  1.  
  2.   "a.js": "hello world",  
  3.   "b.js": function add(){},  
  4.   "c.js": 2,  
  5.   "d.js": { num: 2 }  

當你再次require某個文件時,如果這個對象里面有對應的值,就直接返回給你,如果沒有就重復前面的步驟,執行目標文件,然后將它的module.exports加入這個全局對象,并返回給調用者。這個全局對象其實就是我們經常聽說的緩存。所以require和module.exports并沒有什么黑魔法,就只是運行并獲取目標文件的值,然后加入緩存,用的時候拿出來用就行。再看看這個對象,因為d.js是一個引用類型,所以你在任何地方獲取了這個引用都可以更改他的值,如果不希望自己模塊的值被更改,需要自己寫模塊時進行處理,比如使用Object.freeze(),Object.defineProperty()之類的方法。

模塊類型和加載順序

這一節的內容都是一些概念,比較枯燥,但是也是我們需要了解的。

模塊類型

Node.js的模塊有好幾種類型,前面我們使用的其實都是文件模塊,總結下來,主要有這兩種類型:

  1.   內置模塊:就是Node.js原生提供的功能,比如fs,http等等,這些模塊在Node.js進程起來時就加載了。
  2.   文件模塊:我們前面寫的幾個模塊,還有第三方模塊,即node_modules下面的模塊都是文件模塊。

加載順序

加載順序是指當我們require(X)時,應該按照什么順序去哪里找X,在官方文檔上有詳細偽代碼,總結下來大概是這么個順序:

  1.   優先加載內置模塊,即使有同名文件,也會優先使用內置模塊。
  2.   不是內置模塊,先去緩存找。
  3.   緩存沒有就去找對應路徑的文件。
  4.   不存在對應的文件,就將這個路徑作為文件夾加載。
  5.   對應的文件和文件夾都找不到就去node_modules下面找。
  6.   還找不到就報錯了。

加載文件夾

前面提到找不到文件就找文件夾,但是不可能將整個文件夾都加載進來,加載文件夾的時候也是有一個加載順序的:

  1.   先看看這個文件夾下面有沒有package.json,如果有就找里面的main字段,main字段有值就加載對應的文件。所以如果大家在看一些第三方庫源碼時找不到入口就看看他package.json里面的main字段吧,比如jquery的main字段就是這樣:"main": "dist/jquery.js"。

       2.  如果沒有package.json或者package.json里面沒有main就找index文件。

       3.   如果這兩步都找不到就報錯了。

支持的文件類型

require主要支持三種文件類型:

  1.   .js:.js文件是我們最常用的文件類型,加載的時候會先運行整個JS文件,然后將前面說的module.exports作為require的返回值。
  2.   .json:.json文件是一個普通的文本文件,直接用JSON.parse將其轉化為對象返回就行。
  3.   .node:.node文件是C++編譯后的二進制文件,純前端一般很少接觸這個類型。

手寫require

前面其實我們已經將原理講的七七八八了,下面來到我們的重頭戲,自己實現一個require。實現require其實就是實現整個Node.js的模塊加載機制,我們再來理一下需要解決的問題:

  1.   通過傳入的路徑名找到對應的文件。
  2.   執行找到的文件,同時要注入module和require這些方法和屬性,以便模塊文件使用。
  3.   返回模塊的module.exports

本文的手寫代碼全部參照Node.js官方源碼,函數名和變量名盡量保持一致,其實就是精簡版的源碼,大家可以對照著看,寫到具體方法時我也會貼上對應的源碼地址。總體的代碼都在這個文件里面:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js 

Module類

Node.js模塊加載的功能全部在Module類里面,整個代碼使用面向對象的思想,如果你對JS的面向對象還不是很熟悉可以先看看這篇文章。Module類的構造函數也不復雜,主要是一些值的初始化,為了跟官方Module名字區分開,我們自己的類命名為MyModule: 

  1. function MyModule(id = '') {  
  2.   this.id = id;       // 這個id其實就是我們require的路徑  
  3.   this.path = path.dirname(id);     // path是Node.js內置模塊,用它來獲取傳入參數對應的文件夾路徑  
  4.   this.exports = {};        // 導出的東西放這里,初始化為空對象  
  5.   this.filename = null;     // 模塊對應的文件名  
  6.   this.loaded = false;      // loaded用來標識當前模塊是否已經加載 
  7.  

require方法

我們一直用的require其實是Module類的一個實例方法,內容很簡單,先做一些參數檢查,然后調用Module._load方法,源碼看這里:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L970。精簡版的代碼如下: 

  1. MyModule.prototype.require = function (id) {  
  2.   return Module._load(id);  

MyModule._load

MyModule._load是一個靜態方法,這才是require方法的真正主體,他干的事情其實是:

  1.   先檢查請求的模塊在緩存中是否已經存在了,如果存在了直接返回緩存模塊的exports。
  2.   如果不在緩存中,就new一個Module實例,用這個實例加載對應的模塊,并返回模塊的exports。

我們自己來實現下這兩個需求,緩存直接放在Module._cache這個靜態變量上,這個變量官方初始化使用的是Object.create(null),這樣可以使創建出來的原型指向null,我們也這樣做吧: 

  1. MyModule._cache = Object.create(null);  
  2. MyModule._load = function (request) {    // request是我們傳入的路勁參數  
  3.   const filename = MyModule._resolveFilename(request);  
  4.   // 先檢查緩存,如果緩存存在且已經加載,直接返回緩存  
  5.   const cachedModule = MyModule._cache[filename];  
  6.   if (cachedModule !== undefined) {  
  7.     return cachedModule.exports; 
  8.   }  
  9.   // 如果緩存不存在,我們就加載這個模塊  
  10.   // 加載前先new一個MyModule實例,然后調用實例方法load來加載  
  11.   // 加載完成直接返回module.exports  
  12.   const module = new MyModule(filename);  
  13.   // load之前就將這個模塊緩存下來,這樣如果有循環引用就會拿到這個緩存,但是這個緩存里面的exports可能還沒有或者不完整  
  14.   MyModule._cache[filename] = module;  
  15.   module.load(filename);  
  16.   return module.exports;  

上述代碼對應的源碼看這里:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L735

可以看到上述源碼還調用了兩個方法:MyModule._resolveFilename和MyModule.prototype.load,下面我們來實現下這兩個方法。

MyModule._resolveFilename

MyModule._resolveFilename從名字就可以看出來,這個方法是通過用戶傳入的require參數來解析到真正的文件地址的,源碼中這個方法比較復雜,因為按照前面講的,他要支持多種參數:內置模塊,相對路徑,絕對路徑,文件夾和第三方模塊等等,如果是文件夾或者第三方模塊還要解析里面的package.json和index.js。我們這里主要講原理,所以我們就只實現通過相對路徑和絕對路徑來查找文件,并支持自動添加js和json兩種后綴名: 

  1. MyModule._resolveFilename = function (request) {  
  2.   const filename = path.resolve(request);   // 獲取傳入參數對應的絕對路徑  
  3.   const extname = path.extname(request);    // 獲取文件后綴名 
  4.   // 如果沒有文件后綴名,嘗試添加.js和.json  
  5.   if (!extname) {  
  6.     const exts = Object.keys(MyModule._extensions);  
  7.     for (let i = 0; i < exts.length; i++) {  
  8.       const currentPath = `${filename}${exts[i]}`;  
  9.       // 如果拼接后的文件存在,返回拼接的路徑  
  10.       if (fs.existsSync(currentPath)) {  
  11.         return currentPath;  
  12.       }  
  13.     }  
  14.   }  
  15.   return filename;  

上述源碼中我們還用到了一個靜態變量MyModule._extensions,這個變量是用來存各種文件對應的處理方法的,我們后面會實現他。

MyModule._resolveFilename對應的源碼看這里:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L822

MyModule.prototype.load

MyModule.prototype.load是一個實例方法,這個方法就是真正用來加載模塊的方法,這其實也是不同類型文件加載的一個入口,不同類型的文件會對應MyModule._extensions里面的一個方法: 

  1. MyModule.prototype.load = function (filename) {  
  2.   // 獲取文件后綴名  
  3.   const extname = path.extname(filename);  
  4.   // 調用后綴名對應的處理函數來處理  
  5.   MyModule._extensions[extname](this, filename);  
  6.   this.loaded = true 

注意這段代碼里面的this指向的是module實例,因為他是一個實例方法。對應的源碼看這里: https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L942

加載js文件: MyModule._extensions['.js']

前面我們說過不同文件類型的處理方法都掛載在MyModule._extensions上面的,我們先來實現.js類型文件的加載: 

  1. MyModule._extensions['.js'] = function (module, filename) {  
  2.   const content = fs.readFileSync(filename, 'utf8');  
  3.   module._compile(content, filename); 
  4.  

可以看到js的加載方法很簡單,只是把文件內容讀出來,然后調了另外一個實例方法_compile來執行他。對應的源碼看這里:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L1098

編譯執行js文件:MyModule.prototype._compile

MyModule.prototype._compile是加載JS文件的核心所在,也是我們最常使用的方法,這個方法需要將目標文件拿出來執行一遍,執行之前需要將它整個代碼包裹一層,以便注入exports, require, module, __dirname, __filename,這也是我們能在JS文件里面直接使用這幾個變量的原因。要實現這種注入也不難,假如我們require的文件是一個簡單的Hello World,長這樣: 

  1. module.exports = "hello world"

那我們怎么來給他注入module這個變量呢?答案是執行的時候在他外面再加一層函數,使他變成這樣: 

  1. function (module) { // 注入module變量,其實幾個變量同理  
  2.   module.exports = "hello world" 

所以我們如果將文件內容作為一個字符串的話,為了讓他能夠變成上面這樣,我們需要再給他拼接上開頭和結尾,我們直接將開頭和結尾放在一個數組里面: 

  1. MyModule.wrapper = [  
  2.   '(function (exports, require, module, __filename, __dirname) { ',  
  3.   '\n});'  
  4. ]; 

注意我們拼接的開頭和結尾多了一個()包裹,這樣我們后面可以拿到這個匿名函數,在后面再加一個()就可以傳參數執行了。然后將需要執行的函數拼接到這個方法中間: 

  1. MyModule.wrap = function (script) {  
  2.   return MyModule.wrapper[0] + script + MyModule.wrapper[1];  
  3. }; 

這樣通過MyModule.wrap包裝的代碼就可以獲取到exports, require, module, __filename, __dirname這幾個變量了。知道了這些就可以來寫MyModule.prototype._compile了: 

  1. MyModule.prototype._compile = function (content, filename) {  
  2.   const wrapper = Module.wrap(content);    // 獲取包裝后函數體  
  3.   // vm是nodejs的虛擬機沙盒模塊,runInThisContext方法可以接受一個字符串并將它轉化為一個函數  
  4.   // 返回值就是轉化后的函數,所以compiledWrapper是一個函數  
  5.   const compiledWrapper = vm.runInThisContext(wrapper, {  
  6.     filename,  
  7.     lineOffset: 0,  
  8.     displayErrors: true,  
  9.   });  
  10.   // 準備exports, require, module, __filename, __dirname這幾個參數  
  11.   // exports可以直接用module.exports,即this.exports  
  12.   // require官方源碼中還包裝了一層,其實最后調用的還是this.require  
  13.   // module不用說,就是this了  
  14.   // __filename直接用傳進來的filename參數了  
  15.   // __dirname需要通過filename獲取下  
  16.   const dirname = path.dirname(filename);  
  17.   compiledWrapper.call(this.exports, this.exports, this.require, this,  
  18.     filename, dirname);  

上述代碼要注意我們注入進去的幾個參數和通過call傳進去的this:

  1.   this:compiledWrapper是通過call調用的,第一個參數就是里面的this,這里我們傳入的是this.exports,也就是module.exports,也就是說我們js文件里面this是對module.exports的一個引用。
  2.   exports: compiledWrapper正式接收的第一個參數是exports,我們傳的也是this.exports,所以js文件里面的exports也是對module.exports的一個引用。
  3.   require: 這個方法我們傳的是this.require,其實就是MyModule.prototype.require,也就是MyModule._load。
  4.   module: 我們傳入的是this,也就是當前模塊的實例。
  5.   __filename:文件所在的絕對路徑。
  6.   __dirname: 文件所在文件夾的絕對路徑。

到這里,我們的JS文件其實已經記載完了,對應的源碼看這里:https://github.com/nodejs/node/blob/c6b96895cc74bc6bd658b4c6d5ea152d6e686d20/lib/internal/modules/cjs/loader.js#L1043

加載json文件: MyModule._extensions['.json']

加載json文件就簡單多了,只需要將文件讀出來解析成json就行了: 

  1. MyModule._extensions['.json'] = function (module, filename) {  
  2.   const content = fs.readFileSync(filename, 'utf8');  
  3.   module.exports = JSONParse(content);  

exports和module.exports的區別

網上經常有人問,node.js里面的exports和module.exports到底有什么區別,其實前面我們的手寫代碼已經給出答案了,我們這里再就這個問題詳細講解下。exports和module.exports這兩個變量都是通過下面這行代碼注入的。 

  1. compiledWrapper.call(this.exports, this.exports, this.require, this,  
  2.     filename, dirname); 

初始狀態下,exports === module.exports === {},exports是module.exports的一個引用,如果你一直是這樣使用的: 

  1. exports.a = 1 
  2. module.exports.b = 2
  3. console.log(exports === module.exports);   // true 

上述代碼中,exports和module.exports都是指向同一個對象{},你往這個對象上添加屬性并沒有改變這個對象本身的引用地址,所以exports === module.exports一直成立。

但是如果你哪天這樣使用了: 

  1. exports = {  
  2.   a: 1  

或者這樣使用了: 

  1. module.exports = {  
  2.     b: 2  

那其實你是給exports或者module.exports重新賦值了,改變了他們的引用地址,那這兩個屬性的連接就斷開了,他們就不再相等了。需要注意的是,你對module.exports的重新賦值會作為模塊的導出內容,但是你對exports的重新賦值并不能改變模塊導出內容,只是改變了exports這個變量而已,因為模塊始終是module,導出內容是module.exports。

循環引用

Node.js對于循環引用是進行了處理的,下面是官方例子:

a.js: 

  1. console.log('a 開始');  
  2. exports.done = false 
  3. const b = require('./b.js');  
  4. console.log('在 a 中,b.done = %j', b.done);  
  5. exports.done = true 
  6. console.log('a 結束'); 

b.js: 

  1. console.log('b 開始');  
  2. exports.done = false 
  3. const a = require('./a.js');  
  4. console.log('在 b 中,a.done = %j', a.done);  
  5. exports.done = true 
  6. console.log('b 結束'); 

main.js: 

  1. console.log('main 開始');  
  2. const a = require('./a.js');  
  3. const b = require('./b.js');  
  4. console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done); 

當 main.js 加載 a.js 時, a.js 又加載 b.js。 此時, b.js 會嘗試去加載 a.js。 為了防止無限的循環,會返回一個 a.js 的 exports 對象的 未完成的副本 給 b.js 模塊。 然后 b.js 完成加載,并將 exports 對象提供給 a.js 模塊。

那么這個效果是怎么實現的呢?答案就在我們的MyModule._load源碼里面,注意這兩行代碼的順序: 

  1. MyModule._cache[filename] = module;  
  2. module.load(filename); 

上述代碼中我們是先將緩存設置了,然后再執行的真正的load,順著這個思路我能來理一下這里的加載流程:

  1.   main加載a,a在真正加載前先去緩存中占一個位置
  2.   a在正式加載時加載了b
  3.   b又去加載了a,這時候緩存中已經有a了,所以直接返回a.exports,即使這時候的exports是不完整的。

總結

  1.  require不是黑魔法,整個Node.js的模塊加載機制都是JS實現的。
  2.  每個模塊里面的exports, require, module, __filename, __dirname五個參數都不是全局變量,而是模塊加載的時候注入的。
  3.  為了注入這幾個變量,我們需要將用戶的代碼用一個函數包裹起來,拼一個字符串然后調用沙盒模塊vm來實現。
  4.  初始狀態下,模塊里面的this, exports, module.exports都指向同一個對象,如果你對他們重新賦值,這種連接就斷了。
  5.  對module.exports的重新賦值會作為模塊的導出內容,但是你對exports的重新賦值并不能改變模塊導出內容,只是改變了exports這個變量而已,因為模塊始終是module,導出內容是module.exports。

      6.  為了解決循環引用,模塊在加載前就會被加入緩存,下次再加載會直接返回緩存,如果這時候模塊還沒加載完,你可能拿到未完成的exports。

      7.  Node.js實現的這套加載機制叫CommonJS。

本文完整代碼已上傳GitHub:https://github.com/dennis-jiang/Front-End-Knowledges/blob/master/Examples/Node.js/Module/MyModule/index.js 

 

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

2020-11-02 11:40:24

Node.jsRequire前端

2021-09-26 05:06:04

Node.js模塊機制

2022-02-05 21:15:59

Node.jsrequire函數

2011-09-08 14:07:28

Node.js

2021-10-16 05:00:32

.js Buffer模塊

2021-08-05 05:46:06

Node.jsInspector工具

2015-07-16 09:59:55

PHP Node.js討論

2022-06-04 07:21:19

requireJSNode.js

2020-04-15 15:48:03

Node.jsstream前端

2025-05-26 00:31:31

2019-12-10 10:23:57

Node.jsCluster前端

2019-12-17 11:40:44

Node.js模塊前端

2021-08-26 13:57:56

Node.jsEncodingBuffer

2021-08-12 01:00:29

NodejsAsync

2020-11-09 10:46:35

CommonJS

2023-06-30 23:25:46

HTTP模塊內存

2017-08-16 10:36:10

JavaScriptNode.js事件驅動

2011-12-09 11:16:48

Node.js

2013-11-01 09:34:56

Node.js技術

2015-03-10 10:59:18

Node.js開發指南基礎介紹
點贊
收藏

51CTO技術棧公眾號

宅男66日本亚洲欧美视频| 午夜欧美视频在线观看| 国产一区视频在线播放| 四虎永久免费在线| 成人av影音| 色婷婷国产精品| 亚洲巨乳在线观看| 国产成年妇视频| 国产一区成人| 色综合亚洲精品激情狠狠| 麻豆tv在线观看| 欧美大片免费| 熟女俱乐部一区二区| 色综合一区二区日本韩国亚洲 | 性色av蜜臀av色欲av| 成人性生活视频| 亚洲视频精选在线| 国产亚洲福利社区| 成人午夜精品视频| 亚洲经典视频在线观看| 日韩中文字幕av| 99热超碰在线| 男人天堂久久| 欧美日韩国产丝袜美女| 免费观看黄色的网站| 日韩国产福利| 国产精品综合久久| 国产精品黄视频| 国产精彩视频在线观看| 日韩欧美一区二区三区免费看| 亚洲国产成人精品女人久久久 | 国产一区二区三区黄| 久久久999久久久| 亚洲一级黄色| 日韩亚洲国产中文字幕| 黄色工厂在线观看| 成人av综合网| 日韩一区二区精品葵司在线 | www.超碰在线| 综合av第一页| 日韩区国产区| 亚洲色偷精品一区二区三区| 高清不卡一二三区| 96国产粉嫩美女| 中文字幕免费播放| 久久久亚洲一区| 91精品国产色综合久久不卡98| 波多野结衣在线网址| 热久久天天拍国产| 亚洲区一区二区| 亚洲啪av永久无码精品放毛片 | 番号集在线观看| 99re这里只有精品6| 成人午夜影院在线观看| 国产又粗又黄又爽| 麻豆视频观看网址久久| 国产精品久久久久秋霞鲁丝 | 国产在线精品一区免费香蕉 | av激情久久| 99re只有精品| 国产精品一区二区无线| 亚洲自拍欧美色图| 国产ts人妖调教重口男| 国产精品一二三四| av色综合网| 黑人精品一区二区三区| 日日夜夜精品| 色婷婷综合久久久久中文一区二区| 日韩欧美国产综合在线| 91探花在线观看| 天天做天天摸天天爽国产一区| 又大又硬又爽免费视频| 超碰成人av| 狠狠躁夜夜躁人人爽超碰91| 午夜精品久久久久久久无码 | 欧美亚洲成人精品| caoporn国产| 日本中文字幕一区| 国产日韩欧美视频| 国产av一区二区三区精品| 国产精品99久| 久久99精品国产一区二区三区| 亚洲人成色777777精品音频| 国产日韩欧美精品一区| 亚洲资源在线网| а√资源新版在线天堂| 亚洲国产一区二区在线播放| av在线播放亚洲| 日韩精品三区| 欧美一区三区二区| 岛国精品资源网站| 久久不见久久见国语| 色婷婷**av毛片一区| 青青草成人免费| 伊人狠狠色j香婷婷综合| 青青在线视频一区二区三区| 在线观看免费高清视频| 国产v日产∨综合v精品视频| 久久精品国产综合精品| 在线免费观看的av网站| 悠悠色在线精品| 99精品人妻少妇一区二区| 本网站久久精品| 日韩午夜av一区| 伊人网在线视频观看| 围产精品久久久久久久| 欧美一级大片在线观看| 91麻豆视频在线观看| 99精品一区二区三区| 中文字幕中文字幕在线中一区高清 | 爱搞国产精品| 欧美日韩精品欧美日韩精品一 | 国产精品第72页| 日韩国产高清影视| 97se亚洲综合| 粉嫩一区二区三区国产精品| 亚洲午夜激情av| 亚洲老女人av| 亚洲盗摄视频| 欧美精品激情在线| 一级黄色a毛片| 久久亚洲欧美国产精品乐播| 国产一区二区三区播放| 成人a在线观看高清电影| 精品国产乱子伦一区| 少妇高潮在线观看| 母乳一区在线观看| 国产精品高清一区二区三区| 欧美精品videos另类| 富二代精品短视频| 91精品又粗又猛又爽| 五月开心六月丁香综合色啪| 国产精品91久久久| 天天色棕合合合合合合合| 国产精品九色蝌蚪自拍| 国产精品乱码久久久久| 日韩三级av| 高清一区二区三区日本久| 国产麻豆精品一区| 国产精品欧美一级免费| www黄色在线| 蜜桃视频欧美| 欧美性做爰毛片| 亚洲av成人精品毛片| 亚洲国产成人av| 国产精品99久久久精品无码| 在线中文字幕亚洲| 国产在线精品一区免费香蕉| 五月婷婷在线观看| 欧美日韩成人在线一区| 大胸美女被爆操| 日本不卡视频一二三区| 欧美日韩一区二区三| 手机在线理论片| 亚洲国产一区二区三区四区| 日韩精品成人一区| 99久久综合狠狠综合久久| 黄网站欧美内射| 欧美三级午夜理伦三级小说| 午夜精品久久久久久久99黑人| 高h震动喷水双性1v1| 亚洲自拍偷拍九九九| 日批免费观看视频| 亚洲麻豆视频| 久久久婷婷一区二区三区不卡| 亚洲性受xxx喷奶水| 亚洲欧美日韩在线高清直播| 波多野结衣一本一道| 亚洲国产成人在线| 五月天婷婷亚洲| 天天综合网网欲色| 成人午夜电影免费在线观看| 国产粉嫩在线观看| 亚洲女成人图区| 在线观看黄色国产| 一区二区三区小说| 国产成人精品无码片区在线| 丝袜美腿亚洲色图| 亚洲三级一区| 在线精品国产亚洲| 97久久精品人人澡人人爽缅北| 天堂av手机版| 91福利在线免费观看| 久久国产精品国语对白| 成人黄色大片在线观看| 精品www久久久久奶水| 97精品一区| 成人自拍视频网站| 欧美成人ⅴideosxxxxx| 日韩视频免费在线观看| 粉嫩av一区二区夜夜嗨| 色av一区二区| 小泽玛利亚一区二区免费| 99在线精品观看| 中文字幕视频在线免费观看| 欧美精品色网| 日本一区二区高清视频| 久久久久久爱| 日韩暖暖在线视频| 日p在线观看| 亚洲国产精品成人av| 波多野结衣视频免费观看| 亚洲免费色视频| 在线免费观看黄色小视频| 久久草av在线| 成人免费观看视频在线观看| 国产高清一区| 欧美日韩国产精品一区二区| 国产麻豆一区二区三区| 日本欧美国产在线| 91麻豆免费在线视频| 亚洲天堂成人在线视频| 国产小视频一区| 欧美日韩在线免费视频| 黄色片免费观看视频| 亚洲精品中文字幕在线观看| 人妻av无码一区二区三区| 国产精品 日产精品 欧美精品| 成年人在线看片| 黄色工厂这里只有精品| 中文字幕精品一区日韩| 国产99精品一区| 国产高清一区视频| 四虎地址8848精品| 国产成人在线播放| 国产精品蜜芽在线观看| 欧美成人免费视频| 午夜精品一区| 国产亚洲精品美女久久久久| 黑人精品一区二区三区| 欧美一卡二卡在线| 亚洲在线观看av| 色狠狠综合天天综合综合| 国产精品99精品| 亚洲免费观看高清完整版在线观看熊| 一级黄色录像毛片| 91蜜桃传媒精品久久久一区二区| www日本在线观看| 国产专区综合网| 亚洲一级免费在线观看| 日本少妇一区二区| 日本999视频| 久久午夜视频| 免费日韩视频在线观看| 亚洲欧美日韩一区在线观看| 国产一区二区视频播放| 欧美午夜免费影院| 黄色成人在线免费观看| 欧美不卡一区| 成人在线免费观看网址| 一精品久久久| 国产奶头好大揉着好爽视频| 国产精品99在线观看| 在线免费观看一区二区三区| 日韩欧美网站| 一区二区三区四区视频在线观看| 日韩一区自拍| 一区二区高清视频| 偷偷www综合久久久久久久| 国产高潮呻吟久久久| 91精品高清| 国产欧美久久久久| 亚洲狠狠婷婷| 18禁免费无码无遮挡不卡网站 | 国产一区二区三区精彩视频| 国产日韩一区二区三区在线播放| 少妇无码av无码专区在线观看| 亚洲美女91| 免费在线观看日韩视频| 久久久噜噜噜| 污污网站免费看| 激情综合色丁香一区二区| 一级黄色片在线免费观看| 国产一区二区三区在线观看免费视频| 欧美一区二区三区影院| 99久久久国产精品免费蜜臀| 色婷婷av777| 国产精品卡一卡二卡三| 四虎精品免费视频| 亚洲国产精品久久久久秋霞影院| 欧美啪啪小视频| 欧美日韩精品电影| 国产女无套免费视频| 亚洲成人亚洲激情| 黄网站在线观看| 日韩有码在线电影| 白白色在线观看| 国产精品高潮在线| 精品视频在线观看免费观看 | 91精品国产综合久| 精品福利一区二区三区| 国产视频二区在线观看| 美女撒尿一区二区三区| av最新在线| 91精品美女在线| 久久九九热re6这里有精品| 视频一区二区三| 合欧美一区二区三区| 国产福利一区视频| 国产成人av影院| 日本一道本视频| 亚洲午夜精品在线| 中文字幕观看在线| 亚洲激情第一页| 伦xxxx在线| 日本免费一区二区三区视频观看| 久久伊人精品| 任我爽在线视频精品一| 欧美国产另类| 我要看一级黄色大片| 91玉足脚交白嫩脚丫在线播放| 天天爽天天爽天天爽| 色综合中文字幕| 亚洲精品字幕在线| 色伦专区97中文字幕| xx欧美xxx| 国产成人免费观看| 99热国内精品永久免费观看| 免费在线a视频| 国产精品亚洲视频| 美国一级黄色录像| 色综合久久久久久久| 亚洲黄色一级大片| 久久久999国产| 亚洲日本在线观看视频| 国产一区在线免费观看| 欧美激情第10页| 国产成人美女视频| 欧美极品美女视频| 中文字幕一区在线播放| 亚洲成人精品视频| 亚洲资源一区| 91情侣偷在线精品国产| 欧美日韩中文一区二区| 欧美精品色婷婷五月综合| 成人免费黄色在线| 国产精品白嫩白嫩大学美女| 欧美区一区二区三区| 国产精品影院在线| 日韩av男人的天堂| 色综合久久中文| 久久久久久免费看| 成人黄色一级视频| 欧美一级高潮片| 日韩欧美国产午夜精品| 中日韩高清电影网| 91最新在线免费观看| 1024精品久久久久久久久| 伊人国产在线视频| 国产精品美女久久久久高潮| 中文字幕在线一| www.日韩系列| 粉嫩av国产一区二区三区| 一区二区三区精品国产| 看电视剧不卡顿的网站| 懂色av蜜臀av粉嫩av永久| 欧美日韩精品一区二区天天拍小说 | 日韩一级完整毛片| 超碰人人在线| 成人激情直播| 国产一级久久| 欧美黄色一级生活片| 欧美综合色免费| 男人的天堂在线视频免费观看 | 极品少妇一区二区| 欧美丰满艳妇bbwbbw| 亚洲国产精彩中文乱码av| 91九色在线播放| 噜噜噜噜噜久久久久久91| 久热精品在线| 一区二区三区在线播放视频| 欧美一区二区三区视频免费播放 | 国产一级做a爰片久久毛片男| 国产成人福利片| 毛片基地在线观看| 中文字幕亚洲欧美日韩高清 | 97免费资源站| 夜夜精品视频| 鲁丝一区二区三区| 欧美一区二区日韩| 国产伦子伦对白在线播放观看| 久久亚洲国产精品日日av夜夜| 人人狠狠综合久久亚洲| 澳门黄色一级片| 亚洲精品美女久久| 日本精品网站| 久草视频这里只有精品| 久久综合中文字幕| 一级片免费网站| 久久久久久午夜| 精品国产a一区二区三区v免费| 国内自拍第二页| 午夜精品福利一区二区三区av| 国产youjizz在线| 成人免费在线看片| 日韩精品91亚洲二区在线观看| 欧美人禽zoz0强交| 亚洲美女视频网站| 欧美专区视频| 欧美丰满熟妇xxxxx|