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

一篇文章教你如何寫成Strview.js之源碼剖析

開發(fā) 開發(fā)工具
前段時(shí)間我自己開發(fā)了一款Strview.js,它是一個可以將字符串轉(zhuǎn)換為視圖的JS庫。在JS代碼中,我們引入了Strview.js,并且我們調(diào)用了它一個createView方法,最后傳入了一個對象。

[[421771]]

前言

前段時(shí)間我自己開發(fā)了一款Strview.js,它是一個可以將字符串轉(zhuǎn)換為視圖的JS庫。什么意思呢?就像下面這段代碼:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3.  
  4. <head> 
  5.     <meta charset="UTF-8"
  6.     <meta http-equiv="X-UA-Compatible" content="IE=edge"
  7.     <meta name="viewport" content="width=device-width, initial-scale=1.0"
  8.     <title>Strview.js</title> 
  9. </head> 
  10.  
  11. <body> 
  12.     <div id="app"></div> 
  13.     <script src="https://cdn.jsdelivr.net/npm/strview@1.9.0/dist/strview.global.js"></script> 
  14.     <script> 
  15.         Strview.createView({ 
  16.             el: "#app"
  17.             data: { 
  18.                 msg: 'Hello World' 
  19.             }, 
  20.             template: `<p>{msg}</p>`, 
  21.         }); 
  22.     </script> 
  23. </body> 
  24.  
  25. </html> 

顯示如下頁面:

你會看到頁面上顯示了一個Hello World字樣,而我們看到HTML代碼中除了一個ID名是app標(biāo)簽之外,其他標(biāo)簽并沒有,更沒有Hello World文本。這時(shí),繼續(xù)往下看,在JS代碼中,我們引入了Strview.js,并且我們調(diào)用了它一個createView方法,最后傳入了一個對象。我們在對象中發(fā)現(xiàn)了Hello World字符串,并且我們在template屬性中看到它多所對應(yīng)的值是一個標(biāo)簽,就是這個標(biāo)簽<p>{msg}</p>,另外,里面我們會看到使用{}包裹的msg字符。與data對象中的msg屬性相對應(yīng),正好它的值為Hello World。我們現(xiàn)在改變下msg屬性對應(yīng)的值來看下頁面是否發(fā)生改變。

果然,發(fā)生了改變,所以我們知道Strview.js就是這么將字符串轉(zhuǎn)換為視圖的。

這里,我們只是簡單介紹了Strview.js的簡單用法,如果想繼續(xù)了解其他用法的話,可以去Strview.js中文官網(wǎng):

https://www.maomin.club/site/strviewjs/zh

下面的內(nèi)容呢,我們將看下Strview.js源碼,看它是如何實(shí)現(xiàn)的。

剖析源碼

本篇分析Strview.js版本為1.9.0

首先,我們獲取到源碼,這里我們使用生產(chǎn)環(huán)境下的Strview.js,也就是上面實(shí)例中的這個地址:

https://cdn.jsdelivr.net/npm/strview@1.9.0/dist/strview.global.js

我們,先大體看下源碼,加上空行,源碼一共125行。不壓縮的話,僅僅4kb。

  1. var Strview = (function (exports) { 
  2.     'use strict'
  3.  
  4.     // global object 
  5.     const globalObj = { 
  6.         _nHtml: [], 
  7.         _oHtml: [], 
  8.         _el: null
  9.         _data: null
  10.         _template: null
  11.         _sourceTemplate: null 
  12.     }; 
  13.  
  14.     // initialization 
  15.     function createView(v) { 
  16.         globalObj._data = v.data; 
  17.         globalObj._template = v.template; 
  18.         globalObj._sourceTemplate = v.template; 
  19.         globalObj._el = v.el; 
  20.         v.el ? document.querySelector(v.el).insertAdjacentHTML("beforeEnd", render(globalObj._template)) : console.error("Error: Please set el property!"); 
  21.     } 
  22.  
  23.     // event listeners 
  24.     function eventListener(el, event, cb) { 
  25.         document.querySelector(el).addEventListener(event, cb); 
  26.     } 
  27.  
  28.     // processing simple values 
  29.     function ref() { 
  30.         return new Proxy(globalObj._data, { 
  31.             get: (target, key) => { 
  32.                 return target[key
  33.             }, 
  34.             set: (target, key, newValue) => { 
  35.                 target[key] = newValue; 
  36.                 setTemplate(); 
  37.                 return true
  38.             } 
  39.         }) 
  40.     } 
  41.  
  42.     // reactiveHandlers 
  43.     const reactiveHandlers = { 
  44.         get: (target, key) => { 
  45.             if (typeof target[key] === 'object' && target[key] !== null) { 
  46.                 return new Proxy(target[key], reactiveHandlers); 
  47.             } 
  48.             return Reflect.get(target, key); 
  49.         }, 
  50.         set: (target, key, value) => { 
  51.             Reflect.set(target, key, value); 
  52.             setTemplate(); 
  53.             return true 
  54.         } 
  55.     }; 
  56.  
  57.     // respond to complex objects 
  58.     function reactive() { 
  59.         return new Proxy(globalObj._data, reactiveHandlers) 
  60.     } 
  61.  
  62.     // update the view 
  63.     function setTemplate() { 
  64.         const oNode = document.querySelector(globalObj._el); 
  65.         const nNode = toHtml(render(globalObj._sourceTemplate)); 
  66.         compile(oNode, 'o'); 
  67.         compile(nNode, 'n'); 
  68.         if (globalObj._oHtml.length === globalObj._nHtml.length) { 
  69.             for (let index = 0; index < globalObj._oHtml.length; index++) { 
  70.                 const element = globalObj._oHtml[index]; 
  71.                 element.textContent !== globalObj._nHtml[index].textContent && (element.textContent = globalObj._nHtml[index].textContent); 
  72.             } 
  73.         } 
  74.     } 
  75.  
  76.     // judge text node 
  77.     function isTextNode(node) { 
  78.         return node.nodeType === 3; 
  79.     } 
  80.  
  81.     // compile DOM 
  82.     function compile(node, type) { 
  83.         const childNodesArr = node.childNodes; 
  84.         for (let index = 0; index < Array.from(childNodesArr).length; index++) { 
  85.             const item = Array.from(childNodesArr)[index]; 
  86.             if (item.childNodes && item.childNodes.length) { 
  87.                 compile(item, type); 
  88.             } else if (isTextNode(item) && item.textContent.trim().length !== 0) { 
  89.                 type === 'o' ? globalObj._oHtml.push(item) : globalObj._nHtml.push(item); 
  90.             } 
  91.         } 
  92.     } 
  93.  
  94.     // string to DOM 
  95.     function toHtml(domStr) { 
  96.         const parser = new DOMParser(); 
  97.         return parser.parseFromString(domStr, "text/html"); 
  98.     } 
  99.  
  100.     // template engine 
  101.     function render(template) { 
  102.         const reg = /\{(.+?)\}/; 
  103.         if (reg.test(template)) { 
  104.             const key = reg.exec(template)[1]; 
  105.             if (globalObj._data.hasOwnProperty(key)) { 
  106.                 template = template.replace(reg, globalObj._data[key]); 
  107.             } else { 
  108.                 template = template.replace(reg, eval(`globalObj._data.${key}`)); 
  109.             } 
  110.             return render(template) 
  111.         } 
  112.  
  113.         return template; 
  114.     } 
  115.  
  116.     // exports 
  117.     exports.createView = createView; 
  118.     exports.eventListener = eventListener; 
  119.     exports.reactive = reactive; 
  120.     exports.ref = ref; 
  121.  
  122.     Object.defineProperty(exports, '__esModule', { value: true }); 
  123.  
  124.     return exports; 
  125. }({})); 

首先,我們會看到最外層定義了一個Strview變量,暴露在外面,并將一個立即執(zhí)行函數(shù)(IIFE)賦予這個變量。

我們先來看下這個立即執(zhí)行函數(shù)。

  1. var Strview = (function (exports) { 
  2. // ... 
  3.  
  4. }({})); 

函數(shù)中需要傳一個形參exports,并且又立即傳入一個空對象。

然后,我們來看下函數(shù)內(nèi)的內(nèi)容。

我們會看到函數(shù)中有很多變量與函數(shù)方法,那么我們就按功能來分析。

首先,我們看到了一個全局對象,全局對象中分別定義了幾個屬性。這樣做是為了減少全局變量污染,JS可以隨意定義保存所有應(yīng)用資源的全局變量,但全局變量可以削弱程序靈活性,增大了模塊之間的耦合性。最小化使用全局變量的一個方法是在你的應(yīng)用中只創(chuàng)建唯一一個全局變量。

  1. // global object 
  2. const globalObj = { 
  3.     _nHtml: [], // 存放新DOM數(shù)組 
  4.     _oHtml: [], // 存放舊DOM數(shù)組 
  5.     _el: null, // 掛載DOM節(jié)點(diǎn) 
  6.     _data: null, // 存放數(shù)據(jù) 
  7.     _template: null, // 模板字符串 
  8.     _sourceTemplate: null // 源模板字符串 
  9. }; 

然后,我們接著看初始化階段,這個階段是將模板字符串轉(zhuǎn)換成視圖。

  1. // initialization 
  2. function createView(v) { 
  3.     globalObj._data = v.data; 
  4.     globalObj._template = v.template; 
  5.     globalObj._sourceTemplate = v.template; 
  6.     globalObj._el = v.el; 
  7.     v.el ? document.querySelector(v.el).insertAdjacentHTML("beforeEnd", render(globalObj._template)) : console.error("Error: Please set el property!"); 

我們看到這個createView方法傳入了一個參數(shù),也就是我們之前傳入的那個對象:

  1. Strview.createView({ 
  2.         el: "#app"
  3.         data: { 
  4.             msg: 'Hello World' 
  5.         }, 
  6.         template: `<p>{msg}</p>`, 
  7.     }); 

 我們看到傳入的對象中的屬性分別賦給全局對象globalObj。在最后一行中通過判斷v.el是否是真值,如果是就執(zhí)行這行代碼:

  1. document.querySelector(v.el).insertAdjacentHTML("beforeEnd", render(globalObj._template))  

這行代碼執(zhí)行了insertAdjacentHTML()方法,這個方法在MDN上是這樣解釋它的。

  • insertAdjacentHTML() 方法將指定的文本解析為 Element 元素,并將結(jié)果節(jié)點(diǎn)插入到DOM樹中的指定位置。它不會重新解析它正在使用的元素,因此它不會破壞元素內(nèi)的現(xiàn)有元素。這避免了額外的序列化步驟,使其比直接使用innerHTML操作更快。

insertAdjacentHTML()方法傳入的第二個參數(shù)是是要被解析為HTML或XML元素,并插入到DOM樹中的DOMString,render(globalObj._template)這個方法就是返回的DOMString。

如果是假,就執(zhí)行console.error("Error: Please set el property!"),在瀏覽器上輸出錯誤。

既然這個用到了render(globalObj._template)這個方法,那么我們下面來看下。

  1. // template engine 
  2. function render(template) { 
  3.     const reg = /\{(.+?)\}/; 
  4.     if (reg.test(template)) { 
  5.         const key = reg.exec(template)[1]; 
  6.         if (globalObj._data.hasOwnProperty(key)) { 
  7.             template = template.replace(reg, globalObj._data[key]); 
  8.         } else { 
  9.             template = template.replace(reg, eval(`globalObj._data.${key}`)); 
  10.         } 
  11.         return render(template) 
  12.     } 
  13.  
  14.     return template; 

首先,這個render(template)方法傳入了一個參數(shù),第一個參數(shù)是模板字符串。

然后,我們進(jìn)入這個方法中看一下,首先,我們定義了正則/\{(.+?)\}/,用于匹配模板字符串中的{}中的內(nèi)容。如果匹配為真,就進(jìn)入這個邏輯:

  1. const key = reg.exec(template)[1]; 
  2. if (globalObj._data.hasOwnProperty(key)) { 
  3.     template = template.replace(reg, globalObj._data[key]); 
  4. else { 
  5.     template = template.replace(reg, eval(`globalObj._data.${key}`)); 
  6. return render(template) 

我們在第一行代碼中看到了這行代碼const key = reg.exec(template)[1],這里使用的是reg.exec()方法,MDN這樣解釋它:

  • exec() 方法在一個指定字符串中執(zhí)行一個搜索匹配。返回一個結(jié)果數(shù)組或 null。在設(shè)置了 global 或 sticky 標(biāo)志位的情況下(如 /foo/g or /foo/y),JavaScript RegExp 對象是有狀態(tài)的。他們會將上次成功匹配后的位置記錄在 lastIndex 屬性中。使用此特性,exec() 可用來對單個字符串中的多次匹配結(jié)果進(jìn)行逐條的遍歷(包括捕獲到的匹配),而相比之下, String.prototype.match() 只會返回匹配到的結(jié)果。

所以,通過這個方法我們?nèi)〉搅四0遄址械膡}中的內(nèi)容,它一般是我們存取數(shù)據(jù)_data中的屬性。首先,我們判斷globalObj._data對象中是否有這個key,如果有我們就使用字符串替換方法replace來把對應(yīng)的占位符key替換成所對應(yīng)的值。下面接著進(jìn)行遞歸,直到reg.test(template)返回為false。最終,render()方法返回處理后的template。

看完render()方法,我們來看下事件處理階段,也就是eventListener()方法。

  1. // event listeners 
  2. function eventListener(el, event, cb) { 
  3.     document.querySelector(el).addEventListener(event, cb); 

這個方法很簡單,第一個參數(shù)傳入DOM選擇器,第二個參數(shù)傳入一個事件名,第三個參數(shù)傳入一個回調(diào)函數(shù)。

最后,我們來看下Strview.js的數(shù)據(jù)響應(yīng)系統(tǒng)。

  1. // processing simple values 
  2. function ref() { 
  3.     return new Proxy(globalObj._data, { 
  4.         get: (target, key) => { 
  5.             return target[key
  6.         }, 
  7.         set: (target, key, newValue) => { 
  8.             target[key] = newValue; 
  9.             setTemplate(); 
  10.             return true
  11.         } 
  12.     }) 
  13.  
  14. // reactiveHandlers 
  15. const reactiveHandlers = { 
  16.     get: (target, key) => { 
  17.         if (typeof target[key] === 'object' && target[key] !== null) { 
  18.             return new Proxy(target[key], reactiveHandlers); 
  19.         } 
  20.         return Reflect.get(target, key); 
  21.     }, 
  22.     set: (target, key, value) => { 
  23.         Reflect.set(target, key, value); 
  24.         setTemplate(); 
  25.         return true 
  26.     } 
  27. }; 
  28.  
  29. // respond to complex objects 
  30. function reactive() { 
  31.     return new Proxy(globalObj._data, reactiveHandlers) 

上面這些代碼主要是reactive()、ref()這兩個方法的實(shí)現(xiàn)。reactive()方法是針對復(fù)雜數(shù)據(jù)的處理,比如嵌套對象以及數(shù)組。ref()方法主要是針對簡單數(shù)據(jù)的處理,像是原始值與單一非嵌套對象。

它們兩個都是基于Proxy代理來實(shí)現(xiàn)數(shù)據(jù)的攔截與響應(yīng),MDN中這樣定義它。

  • Proxy 對象用于創(chuàng)建一個對象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。

它們兩個Proxy對象第一個參數(shù)都是我們在初始化定義的globalObj._data,第二個參數(shù)是一個通常以函數(shù)作為屬性的對象。這里都定義了get()方法、set()方法,get()是屬性讀取操作的捕捉器,set()是屬性設(shè)置操作的捕捉器。

reactive()、ref()這兩個方法實(shí)現(xiàn)不一樣的地方是reactive()方法加上了對嵌套對象判斷來實(shí)現(xiàn)遞歸。

我們在set()方法中看到它們都調(diào)用了setTemplate()方法,下面,我們來看下這個方法。

  1. // update the view 
  2. function setTemplate() { 
  3.     const oNode = document.querySelector(globalObj._el); 
  4.     const nNode = toHtml(render(globalObj._sourceTemplate)); 
  5.     compile(oNode, 'o'); 
  6.     compile(nNode, 'n'); 
  7.     if (globalObj._oHtml.length === globalObj._nHtml.length) { 
  8.         for (let index = 0; index < globalObj._oHtml.length; index++) { 
  9.             const element = globalObj._oHtml[index]; 
  10.             element.textContent !== globalObj._nHtml[index].textContent && (element.textContent = globalObj._nHtml[index].textContent); 
  11.         } 
  12.     } 

首先,我們?nèi)〉匠跏蓟瘯r(shí)掛載的DOM節(jié)點(diǎn),接著我們使用toHtml()方法將render(globalObj._sourceTemplate)方法作為第一個參數(shù)傳入。

我們先來看下toHtml()方法,這里的第一個參數(shù)domStr,也就是render(globalObj._sourceTemplate)。

  1. // string to DOM 
  2. function toHtml(domStr) { 
  3.     const parser = new DOMParser(); 
  4.     return parser.parseFromString(domStr, "text/html"); 

toHtml()方法第一行我們實(shí)例化了一個DOMParser對象。一旦建立了一個解析對象以后,你就可以使用它的parseFromString方法來解析一個html字符串。

然后,我們回到setTemplate()方法中,變量nNode被賦值了toHtml(render(globalObj._sourceTemplate)),這里是被處理成一個DOM對象。

接著,執(zhí)行compile()方法。

  1. compile(oNode, 'o'); 
  2. compile(nNode, 'n'); 

我們來看下這個compile()方法。

  1. // compile DOM 
  2. function compile(node, type) { 
  3.     const childNodesArr = node.childNodes; 
  4.     for (let index = 0; index < Array.from(childNodesArr).length; index++) { 
  5.         const item = Array.from(childNodesArr)[index]; 
  6.         if (item.childNodes && item.childNodes.length) { 
  7.             compile(item, type); 
  8.         } else if (isTextNode(item) && item.textContent.trim().length !== 0) { 
  9.             type === 'o' ? globalObj._oHtml.push(item) : globalObj._nHtml.push(item); 
  10.         } 
  11.     } 

這個方法是將遍歷DOM元素并把每一項(xiàng)存儲到我們初始化定義的數(shù)組里面,分別是globalObj._oHtml和globalObj._nHtml,這個方法中用到了isTextNode()方法。

  1. // judge text node 
  2. function isTextNode(node) { 
  3.     return node.nodeType === 3; 

這個方法第一個參數(shù)是一個Node節(jié)點(diǎn),如果它的nodeType屬性等于3就說明這個節(jié)點(diǎn)是文本節(jié)點(diǎn)。

最后,我們又回到setTemplate()方法中,接著執(zhí)行以下代碼:

  1. if (globalObj._oHtml.length === globalObj._nHtml.length) { 
  2.     for (let index = 0; index < globalObj._oHtml.length; index++) { 
  3.         const element = globalObj._oHtml[index]; 
  4.         element.textContent !== globalObj._nHtml[index].textContent && (element.textContent = globalObj._nHtml[index].textContent); 
  5.     } 

判斷兩個數(shù)組的長度是否一樣,如果一樣就遍歷globalObj._oHtml,最后判斷globalObj._nHtml[index].textContent是否等于globalObj._oHtml[index].textContent,如果不相等,直接將globalObj._nHtml[index].textContent賦于globalObj._OHtml[index].textContent,完成更新。

最后,將這幾個定義的方法賦于傳入的exports對象并返回這個對象。

  1. // exports 
  2. exports.createView = createView; 
  3. exports.eventListener = eventListener; 
  4. exports.reactive = reactive; 
  5. exports.ref = ref; 
  6.  
  7. Object.defineProperty(exports, '__esModule', { value: true }); 
  8.  
  9. return exports; 

這里,有一行代碼Object.defineProperty(exports, '__esModule', { value: true }),這行代碼其實(shí)也可以這么寫exports.__esModule = true。表面上看就是把一個導(dǎo)出對象標(biāo)識為一個 ES 模塊。

隨著 JS 不斷發(fā)展和 Node.js 的出現(xiàn),JS 慢慢有了模塊化方案。在 ES6 之前,最有名的就是 CommonJS / AMD,AMD 就不提了現(xiàn)在基本不用。CommonJS 被 Node.js 采用至今,與 ES 模塊共存。由于 Node.js 早期模塊化方案選擇了 CommonJS,導(dǎo)致現(xiàn)在 NPM 上仍然存在大量的 CommonJS 模塊,JS 圈子一時(shí)半會兒是丟不掉 CommonJS 了。

Webpack 實(shí)現(xiàn)了一套 CommonJS 模塊化方案,支持打包 CommonJS 模塊,同時(shí)也支持打包 ES 模塊。但是兩種模塊格式混用的時(shí)候問題就來了,ES 模塊和 CommonJS 模塊并不完全兼容,CommonJS 的 module.exports 在 ES 模塊中沒有對應(yīng)的表達(dá)方式,和默認(rèn)導(dǎo)出 export default 是不一樣的。

而__esModule則是用來兼容 ES 模塊導(dǎo)入 CommonJS 模塊默認(rèn)導(dǎo)出方案。

結(jié)語

至此,Strview.js的源碼分析完畢。謝謝閱讀~

開發(fā)版本

推薦使用StrviewCLI搭建StrviewApp項(xiàng)目腳手架。

  • https://github.com/maomincoding/strview-app

生產(chǎn)版本

直接引入CDN鏈接,目前版本為1.9.0。

  • https://cdn.jsdelivr.net/npm/strview@1.9.0/dist/strview.global.js

 

責(zé)任編輯:姜華 來源: 前端歷劫之路
相關(guān)推薦

2022-10-08 15:07:06

ChatOps運(yùn)維

2017-09-05 08:52:37

Git程序員命令

2019-07-15 07:58:10

前端開發(fā)技術(shù)

2021-10-15 07:57:04

Docker 日志容器

2021-08-12 00:03:37

JSStrview視圖

2021-03-08 09:15:46

日志Filebeat運(yùn)維

2020-03-31 08:37:31

遞歸單鏈表反轉(zhuǎn)

2020-10-09 08:15:11

JsBridge

2018-01-09 05:39:02

2022-02-21 09:44:45

Git開源分布式

2021-06-30 00:20:12

Hangfire.NET平臺

2019-04-17 15:16:00

Sparkshuffle算法

2023-05-12 08:19:12

Netty程序框架

2024-06-25 08:18:55

2021-04-09 08:40:51

網(wǎng)絡(luò)保險(xiǎn)網(wǎng)絡(luò)安全網(wǎng)絡(luò)風(fēng)險(xiǎn)

2022-02-18 00:13:53

JavaScript編程語言數(shù)組

2021-11-04 10:34:02

JavaScript繼承編程

2023-04-13 08:21:38

DevOpsAPI管理平臺

2019-11-14 15:44:32

系統(tǒng)緩存架構(gòu)

2023-05-08 08:21:15

JavaNIO編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

久久精品国产亚洲a∨麻豆| 黄色一级视频免费观看| 性欧美hd调教| 中文字幕一区二区视频| av资源一区二区| 国产 日韩 欧美 成人| 免费国产自久久久久三四区久久| 欧美日韩国产一区中文午夜| 午夜欧美一区二区三区免费观看| 中文字幕码精品视频网站| 91超碰国产精品| 日韩欧美国产三级| 国产精品入口免费软件| 欧洲一区二区三区| 国产女人aaa级久久久级| 亚洲精品日韩av| 日韩黄色一级视频| 亚洲特级毛片| 亚洲天堂男人天堂| 国产成人av片| 91九色综合| 亚洲va在线va天堂| 免费观看国产视频在线| 国产一二三区在线| 成人毛片视频在线观看| 国产精品吴梦梦| 精品少妇久久久| 日韩免费视频| 亚洲色在线视频| 你懂的在线观看网站| 亚洲精品777| 在线观看一区日韩| 日本一道本久久| 中文在线观看免费| 国产精品国产精品国产专区不蜜| 久久riav二区三区| 国内精品久久久久久久久久| 日韩高清一区在线| 欧美高清视频在线观看| www欧美com| 久久五月天小说| 在线视频免费一区二区| 9.1成人看片| 国产精品男女| 精品乱人伦小说| 国产精品91av| 99re91这里只有精品| 欧美一区二区三区人| 欧美日韩一区二区三区69堂| 亚洲1234区| 在线视频你懂得一区| 国产精品免费观看久久| 日本蜜桃在线观看视频| 欧美日韩在线视频观看| 人妻熟妇乱又伦精品视频| 99爱在线观看| 偷窥少妇高潮呻吟av久久免费| 国内精品国产三级国产99| 国产日产一区二区三区| 亚洲男人的天堂一区二区| 国产91av视频在线观看| 九七电影韩国女主播在线观看| 中文字幕精品三区| 亚洲乱码国产乱码精品天美传媒| 二区在线观看| 国产精品美女www爽爽爽| 一区二区在线高清视频| 欧美激情黑人| 伊人开心综合网| 青青在线免费观看| 欧美大胆a人体大胆做受| 日韩欧美黄色动漫| 黄色在线视频网| 超碰国产精品一区二页| 日韩精品专区在线| 麻豆精品国产传媒av| 综合国产视频| 综合久久五月天| 国产探花在线免费观看| 亚洲无吗在线| 日韩免费精品视频| 一区二区日韩在线观看| 国v精品久久久网| 精品国产乱码久久久久久郑州公司 | 91国偷自产一区二区使用方法| 91香蕉视频导航| 奇米一区二区| 亚洲精品中文字幕有码专区| 国产精品www爽爽爽| 欧美一区免费| 91精品国产高清久久久久久91| 亚洲黄网在线观看| 国产在线视频精品一区| 国产精品日韩一区二区三区 | 久久综合久久99| 亚洲一区二区在线免费观看| 牛牛电影国产一区二区| 一本久久a久久精品亚洲| 天天操天天爱天天爽| 久久wwww| 亚洲午夜性刺激影院| 午夜爽爽爽男女免费观看| 夜夜嗨av一区二区三区网站四季av| 国产999精品视频| 国产成年妇视频| 久久蜜桃一区二区| 日日噜噜夜夜狠狠久久丁香五月| 麻豆mv在线看| 91精品国产麻豆| 91视频免费观看网站| 女人色偷偷aa久久天堂| 日韩美女写真福利在线观看| 精品人妻少妇嫩草av无码专区| 91久色porny| 300部国产真实乱| 嫩草伊人久久精品少妇av杨幂| 精品国产三级电影在线观看| 国产成人精品无码免费看夜聊软件| 亚洲经典一区| 国产成人精品一区| 免费av一级片| 亚洲精品亚洲人成人网| 2025韩国理伦片在线观看| 亚洲va久久久噜噜噜久久| 欧美精品激情在线观看| 国产精品一区二区人人爽| 久久久www成人免费无遮挡大片| 日韩成人三级视频| 国产精品麻豆成人av电影艾秋| 亚洲黄色片网站| 久久久久黄色片| 黑人巨大精品欧美一区| 神马影院一区二区| 韩国久久久久久| 日韩av中文字幕在线免费观看| 欧美成欧美va| 国产激情视频一区二区三区欧美| 亚洲日本精品国产第一区| 97久久香蕉国产线看观看| 亚洲国产精品高清久久久| 久久国产在线观看| 国产精品69毛片高清亚洲| 在线播放 亚洲| 色8久久久久| 色综合伊人色综合网| 影音先锋国产在线| 欧美激情综合网| 香蕉视频网站入口| 国内精品伊人久久久| 日韩女优在线播放| 国产在线一在线二| 欧美性淫爽ww久久久久无| 97人妻人人揉人人躁人人| 日欧美一区二区| 三区精品视频| 色999久久久精品人人澡69| 在线视频日本亚洲性| 一级黄色大片免费观看| 最新欧美精品一区二区三区| 国产精欧美一区二区三区白种人| 国产精品久久久久无码av| 91免费看国产| 黄页网站在线| 亚洲精品久久久久久久久久久久久| 日韩免费不卡视频| 久久一区二区视频| a在线观看免费视频| 永久亚洲成a人片777777| 99久久自偷自偷国产精品不卡| 麻豆av在线免费观看| 亚洲精品久久久一区二区三区| 日韩三级视频在线| 国产日韩欧美激情| 亚洲精品永久视频| 影音先锋中文字幕一区| 久久精品一二三区| 国产精品66| 欧美激情国产精品| 男人天堂网在线观看| 欧美视频完全免费看| 欧美成人精品一区二区免费看片| 成人av电影免费在线播放| 激情网站五月天| 亚洲蜜桃视频| 久久久久久久久一区二区| 黄色成人在线观看网站| 欧美xxxx做受欧美.88| 色网站免费观看| 欧美在线不卡一区| 久一视频在线观看| 国产午夜精品久久久久久免费视| 久久黄色片网站| 亚洲精品日韩久久| 伊甸园精品99久久久久久| 亚洲成人影音| 国产精品扒开腿做爽爽爽男男| 国产三区视频在线观看| 亚洲欧美成人在线| 精品国精品国产自在久不卡| 欧美日韩亚洲精品一区二区三区| 一本一本久久a久久| 成人性生交大片免费看视频在线| avav在线看| 欧美日本一区二区视频在线观看| 欧美主播一区二区三区美女 久久精品人| 婷婷激情成人| 国产mv免费观看入口亚洲| 伊人在我在线看导航| 在线播放国产精品| 色综合久久久久久| 欧美一级久久久| 亚洲综合图片网| 亚洲国产精品久久久久秋霞影院| 91免费在线看片| 2020国产精品自拍| www.四虎在线| 国内精品免费**视频| 久久久久久久少妇| 亚洲区一区二| 97av中文字幕| 久久精品久久久| 日韩videos| 人人精品亚洲| 国产精品久久久久久免费观看| 欧美高清免费| 国产精品h在线观看| 极品视频在线| 性色av一区二区三区在线观看| av在线网址观看| 精品国内产的精品视频在线观看| 久久av少妇| 亚洲精品自产拍| 天天射,天天干| 亚洲国产中文字幕久久网| 国产不卡av在线播放| 91精品国产91久久综合桃花| 在线观看毛片视频| 精品污污网站免费看| 国产91av在线播放| 色婷婷av一区二区三区gif| 久久久久久久久久影院| 偷拍亚洲欧洲综合| 午夜毛片在线观看| 日韩欧美成人精品| 人妻丰满熟妇av无码区| 狠狠色噜噜狠狠狠狠97| 黄色在线视频网址| 色综合咪咪久久| 午夜精品一区二| 欧美亚洲高清一区二区三区不卡| 日韩综合在线观看| 欧美性猛交xxxxxx富婆| 中文字幕资源网| 欧美精品1区2区| 精品久久久久中文慕人妻| 日韩欧美久久久| 免费av一级片| 亚洲色图综合网| 日本美女高清在线观看免费| 久久久精品视频成人| 国产精品va在线观看视色| 欧美成人一二三| av资源中文在线天堂| 欧美最猛性xxxx| 福利视频亚洲| 99re在线视频上| 亚洲aa在线| 在线视频不卡一区二区三区| 欧美黄色一级视频| 欧美视频在线观看网站| 老司机精品视频网站| 免费成年人高清视频| 国产精品18久久久久| 欧美精品欧美极品欧美激情| 久久精品视频免费| 杨钰莹一级淫片aaaaaa播放| 亚洲国产一区二区三区 | 噜噜噜久久亚洲精品国产品小说| 免费日韩视频在线观看| 麻豆成人av在线| 亚洲熟女一区二区三区| 91论坛在线播放| 黄色香蕉视频在线观看| 精品国产乱码久久久久久虫虫漫画| 亚洲天堂五月天| 日韩网站在线看片你懂的| 亚洲日本国产精品| xxx成人少妇69| 天堂中文av在线资源库| 国产日产欧美a一级在线| 第四色在线一区二区| 欧美二区在线看| 亚洲色图国产| 成年人免费大片| 国产不卡视频在线播放| www色com| 午夜视频一区二区三区| 91精品人妻一区二区三区果冻| 亚洲福利在线播放| 欧美激情午夜| 国产98色在线| 久草精品视频| 日韩精品手机在线观看| 日韩精品一二区| 丰满少妇xbxb毛片日本| 亚洲欧美在线aaa| 五月天激情四射| 亚洲第一精品福利| 麻豆影院在线| 国产精品福利无圣光在线一区| 国产自产在线视频一区| 涩涩屋成人免费视频软件| 欧美日本亚洲| 影音国产精品| 国产xxxxhd| 国产精品久久毛片| 无码人妻aⅴ一区二区三区有奶水 无码免费一区二区三区 | 亚洲人成人77777线观看| 国产日韩视频| 在线观看免费视频国产| 亚洲欧美另类图片小说| 影音先锋国产在线| 一区二区三区视频免费| 樱桃视频成人在线观看| 国产欧美一区二区视频| 国产字幕视频一区二区| 99999精品| 亚洲色图制服诱惑| 国产一区二区视频免费观看| 这里只有精品丝袜| 亚洲精品粉嫩美女一区| 美日韩免费视频| 亚洲免费影院| 毛茸茸多毛bbb毛多视频| 亚洲成人av一区二区| 成人午夜精品福利免费| 欧美人与性动交| 视频一区中文字幕精品| 狠狠精品干练久久久无码中文字幕 | 国产91在线高潮白浆在线观看| 日韩成人午夜| 欧美色图另类小说| 久久欧美中文字幕| 亚洲第一网站在线观看| 亚洲乱亚洲乱妇无码| 中文字幕不卡三区视频| 欧美精品尤物在线| 丝袜诱惑亚洲看片| 99在线视频免费| 欧美视频一二三区| 国产1区在线| 成人免费在线一区二区三区| 国自产拍偷拍福利精品免费一| 国产调教打屁股xxxx网站| 亚洲最快最全在线视频| 好吊色一区二区| 欧美一区深夜视频| 国产一区二区三区网| 日韩av手机版| 亚洲欧美激情在线| 人妻va精品va欧美va| 欧美一级成年大片在线观看| 你懂的一区二区三区| 日韩无套无码精品| 成人免费一区二区三区在线观看| 91女人18毛片水多国产| 色综合久久久久久中文网| 精品淫伦v久久水蜜桃| 播放灌醉水嫩大学生国内精品| 国产视频一区在线播放| 国产精品九九九九| 久久久久久久成人| 国语产色综合| 丰满人妻一区二区三区大胸| 天天综合网 天天综合色| 国产亚洲依依| 97人人模人人爽人人喊38tv| 亚洲理伦在线| 殴美一级黄色片| 精品国产一区久久| 欧美成人资源| 欧美a级黄色大片| 久久人人超碰精品| 国产精品久久久久久久久久久久久久久久久久 | 国产色噜噜噜91在线精品| 日韩欧美精品在线观看视频| 国产精品久久三区| 人成网站在线观看| 国产区精品视频| 在线精品在线| www中文在线| 日韩黄色高清视频| 国产一区二区三区黄网站| 无码人妻丰满熟妇区96| 国产精品三级视频| 欧美自拍偷拍一区二区| 国产美女直播视频一区| 9国产精品视频| 国产色无码精品视频国产| 精品视频久久久久久久|