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

Virtual DOM到底有什么迷人之處?如何搭建一款迷你版Virtual DOM庫?

開發(fā) 前端
手動操作DOM比較麻煩。還需要考慮瀏覽器兼容性問題,雖然有JQuery等庫簡化DOM操作,但是隨著項目的復雜DOM操作復雜提升。

[[406455]]

為什么使用Virtual DOM

  • 手動操作DOM比較麻煩。還需要考慮瀏覽器兼容性問題,雖然有JQuery等庫簡化DOM操作,但是隨著項目的復雜DOM操作復雜提升。
  • 為了簡化DOM的復雜操作于是出現(xiàn)了各種MVVM框架,MVVM框架解決了視圖和狀態(tài)的同步問題
  • 為了簡化視圖的操作我們可以使用模板引擎,但是模板引擎沒有解決跟蹤狀態(tài)變化的問題,于是Virtual DOM出現(xiàn)了
  • Virtual DOM的好處是當狀態(tài)改變時不需要立即更新DOM,只需要創(chuàng)建一個虛擬樹來描述DOM,Virtual DOM內(nèi)部將弄清楚如何有效的更新DOM(利用Diff算法實現(xiàn))。

Virtual DOM的特性

  1. Virtual DOM可以維護程序的狀態(tài),跟蹤上一次的狀態(tài)。
  2. 通過比較前后兩次的狀態(tài)差異更新真實DOM。

實現(xiàn)一個基礎(chǔ)的Virtual DOM庫

我們可以仿照snabbdom庫https://github.com/snabbdom/snabbdom.git自己動手實現(xiàn)一款迷你版Virtual DOM庫。

首先,我們創(chuàng)建一個index.html文件,寫一下我們需要展示的內(nèi)容,內(nèi)容如下:

  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>vdom</title> 
  9.     <style> 
  10.         .main { 
  11.             color: #00008b; 
  12.         } 
  13.         .main1{ 
  14.             font-weight: bold; 
  15.         } 
  16.     </style> 
  17. </head> 
  18.  
  19. <body> 
  20.     <div id="app"></div> 
  21.     <script src="./vdom.js"></script> 
  22.     <script> 
  23.         function render() { 
  24.             return h('div', { 
  25.                 style: useObjStr({ 
  26.                     'color''#ccc'
  27.                     'font-size''20px' 
  28.                 }) 
  29.             }, [ 
  30.                 h('div', {}, [h('span', { 
  31.                     onClick: () => { 
  32.                         alert('1'); 
  33.                     } 
  34.                 }, '文本'), h('a', { 
  35.                     href: 'https://www.baidu.com'
  36.                     class: 'main main1' 
  37.                 }, '點擊'
  38.                 ]), 
  39.             ]) 
  40.         } 
  41.          
  42.         // 頁面改變 
  43.         function render1() { 
  44.             return h('div', { 
  45.                 style: useStyleStr({ 
  46.                     'color''#ccc'
  47.                     'font-size''20px' 
  48.                 }) 
  49.             }, [ 
  50.                 h('div', {}, [h('span', { 
  51.                     onClick: () => { 
  52.                         alert('1'); 
  53.                     } 
  54.                 }, '文本改變了'
  55.                 ]), 
  56.             ]) 
  57.         } 
  58.  
  59.         // 首次加載 
  60.         mountNode(render, '#app'); 
  61.  
  62.         // 狀態(tài)改變 
  63.         setTimeout(()=>{ 
  64.             mountNode(render1, '#app'); 
  65.         },3000) 
  66.     </script> 
  67. </body> 
  68.  
  69. </html>

我們在body標簽內(nèi)創(chuàng)建了一個id是app的DOM元素,用于被掛載節(jié)點。接著我們引入了一個vdom.js文件,這個文件就是我們將要實現(xiàn)的迷你版Virtual DOM庫。最后,我們在script標簽內(nèi)定義了一個render方法,返回為一個h方法。調(diào)用mountNode方法掛載到id是app的DOM元素上。h方法中數(shù)據(jù)結(jié)構(gòu)我們是借鑒snabbdom庫,第一個參數(shù)是標簽名,第二個參數(shù)是屬性,最后一個參數(shù)是子節(jié)點。還有,你可能會注意到在h方法中我們使用了useStyleStr方法,這個方法主要作用是將style樣式轉(zhuǎn)化成頁面能識別的結(jié)構(gòu),實現(xiàn)代碼我會在最后給出。

思路理清楚了,展示頁面的代碼也寫完了。下面我們將重點看下vdom.js,如何一步一步地實現(xiàn)它。

第一步

我們看到index.html文件中首先需要調(diào)用mountNode方法,所以,我們先在vdom.js文件中定義一個mountNode方法。

  1. // Mount node 
  2. function mountNode(render, selector) { 
  3.  

接著,我們會看到mountNode方法第一個參數(shù)是render方法,render方法返回了h方法,并且看到第一個參數(shù)是標簽,第二個參數(shù)是屬性,第三個參數(shù)是子節(jié)點。

那么,我們接著在vdom.js文件中再定義一個h方法。

  1.  function h(tag, props, children) { 
  2.     return { tag, props, children }; 

還沒有結(jié)束,我們需要根據(jù)傳入的三個參數(shù)tag、props、children來掛載到頁面上。

我們需要這樣操作。我們在mountNode方法內(nèi)封裝一個mount方法,將傳給mountNode方法的參數(shù)經(jīng)過處理傳給mount方法。

  1. // Mount node 
  2. function mountNode(render, selector) { 
  3.   mount(render(), document.querySelector(selector)) 

接著,我們定義一個mount方法。

  1. function mount(vnode, container) { 
  2.     const el = document.createElement(vnode.tag); 
  3.     vnode.el = el; 
  4.     // props 
  5.     if (vnode.props) { 
  6.         for (const key in vnode.props) { 
  7.             if (key.startsWith('on')) { 
  8.                 el.addEventListener(key.slice(2).toLowerCase(), vnode.props[key],{ 
  9.                     passive:true 
  10.                 }) 
  11.             } else { 
  12.                 el.setAttribute(key, vnode.props[key]); 
  13.             } 
  14.         } 
  15.     } 
  16.     if (vnode.children) { 
  17.         if (typeof vnode.children === "string") { 
  18.             el.textContent = vnode.children; 
  19.         } else { 
  20.             vnode.children.forEach(child => { 
  21.                 mount(child, el); 
  22.             }); 
  23.         } 
  24.     } 
  25.      
  26.     container.appendChild(el); 

第一個參數(shù)是調(diào)用傳進來的render方法,它返回的是h方法,而h方返回一個同名參數(shù)的對象{ tag, props, children },那么我們就可以通過vnode.tag、vnode.props、vnode.children取到它們。

我們看到先是判斷屬性,如果屬性字段開頭含有,on標識就是代表事件,那么就從屬性字段第三位截取,利用addEventListenerAPI創(chuàng)建一個監(jiān)聽事件。否則,直接利用setAttributeAPI設(shè)置屬性。

接著,再判斷子節(jié)點,如果是字符串,我們直接將字符串賦給文本節(jié)點。否則就是節(jié)點,我們就遞歸調(diào)用mount方法。

最后,我們將使用appendChildAPI把節(jié)點內(nèi)容掛載到真實DOM中。

頁面正常顯示。

第二步

我們知道Virtual DOM有以下兩個特性:

  1. Virtual DOM可以維護程序的狀態(tài),跟蹤上一次的狀態(tài)。
  2. 通過比較前后兩次的狀態(tài)差異更新真實DOM。

這就利用到了我們之前提到的diff算法。

我們首先定義一個patch方法。因為要對比前后狀態(tài)的差異,所以第一個參數(shù)是舊節(jié)點,第二個參數(shù)是新節(jié)點。

  1. function patch(n1, n2) { 
  2.     

下面,我們還需要做一件事,那就是完善mountNode方法,為什么這樣操作呢?是因為當狀態(tài)改變時,只更新狀態(tài)改變的DOM,也就是我們所說的差異更新。這時就需要配合patch方法做diff算法。

相比之前,我們加上了對是否掛載節(jié)點進行了判斷。如果沒有掛載的話,就直接調(diào)用mount方法掛載節(jié)點。否則,調(diào)用patch方法進行差異更新。

  1. let isMounted = false
  2. let oldTree; 
  3.  
  4. // Mount node 
  5. function mountNode(render, selector) { 
  6.     if (!isMounted) { 
  7.         mount(oldTree = render(), document.querySelector(selector)); 
  8.         isMounted = true
  9.     } else { 
  10.         const newTree = render(); 
  11.         patch(oldTree, newTree); 
  12.         oldTree = newTree; 
  13.     } 
  14.  

那么下面我們將主動看下patch方法,這也是在這個庫中最復雜的方法。

  1. function patch(n1, n2) { 
  2.     // Implement this 
  3.     // 1. check if n1 and n2 are of the same type 
  4.     if (n1.tag !== n2.tag) { 
  5.         // 2. if notreplace 
  6.         const parent = n1.el.parentNode; 
  7.         const anchor = n1.el.nextSibling; 
  8.         parent.removeChild(n1.el); 
  9.         mount(n2, parent, anchor); 
  10.         return 
  11.     } 
  12.  
  13.     const el = n2.el = n1.el; 
  14.  
  15.     // 3. if yes 
  16.     // 3.1 diff props 
  17.     const oldProps = n1.props || {}; 
  18.     const newProps = n2.props || {}; 
  19.     for (const key in newProps) { 
  20.         const newValue = newProps[key]; 
  21.         const oldValue = oldProps[key]; 
  22.         if (newValue !== oldValue) { 
  23.             if (newValue != null) { 
  24.                 el.setAttribute(key, newValue); 
  25.             } else { 
  26.                 el.removeAttribute(key); 
  27.             } 
  28.         } 
  29.     } 
  30.     for (const key in oldProps) { 
  31.         if (!(key in newProps)) { 
  32.             el.removeAttribute(key); 
  33.         } 
  34.     } 
  35.     // 3.2 diff children 
  36.     const oc = n1.children; 
  37.     const nc = n2.children; 
  38.     if (typeof nc === 'string') { 
  39.         if (nc !== oc) { 
  40.             el.textContent = nc; 
  41.         } 
  42.     } else if (Array.isArray(nc)) { 
  43.         if (Array.isArray(oc)) { 
  44.             // array diff 
  45.             const commonLength = Math.min(oc.length, nc.length); 
  46.             for (let i = 0; i < commonLength; i++) { 
  47.                 patch(oc[i], nc[i]); 
  48.             } 
  49.             if (nc.length > oc.length) { 
  50.                 nc.slice(oc.length).forEach(c => mount(c, el)); 
  51.             } else if (oc.length > nc.length) { 
  52.                 oc.slice(nc.length).forEach(c => { 
  53.                     el.removeChild(c.el); 
  54.                 }) 
  55.             } 
  56.         } else { 
  57.             el.innerHTML = ''
  58.             nc.forEach(c => mount(c, el)); 
  59.         } 
  60.     } 

我們從patch方法入?yún)㈤_始,兩個參數(shù)分別是在mountNode方法中傳進來的舊節(jié)點oldTree和新節(jié)點newTree,首先我們進行對新舊節(jié)點的標簽進行對比。

如果新舊節(jié)點的標簽不相等,就移除舊節(jié)點。另外,利用nextSiblingAPI取指定節(jié)點之后緊跟的節(jié)點(在相同的樹層級中)。然后,傳給mount方法第三個參數(shù)。這時你可能會有疑問,mount方法不是有兩個參數(shù)嗎?對,但是這里我們需要傳進去第三個參數(shù),主要是為了對同級節(jié)點進行處理。

  1. if (n1.tag !== n2.tag) { 
  2.       // 2. if notreplace 
  3.       const parent = n1.el.parentNode; 
  4.       const anchor = n1.el.nextSibling; 
  5.       parent.removeChild(n1.el); 
  6.       mount(n2, parent, anchor); 
  7.       return 
  8.   } 

所以,我們重新修改下mount方法。我們看到我們只是加上了對anchor參數(shù)是否為空的判斷。

如果anchor參數(shù)不為空,我們使用insertBeforeAPI,在參考節(jié)點之前插入一個擁有指定父節(jié)點的子節(jié)點。insertBeforeAPI第一個參數(shù)是用于插入的節(jié)點,第二個參數(shù)將要插在這個節(jié)點之前,如果這個參數(shù)為 null 則用于插入的節(jié)點將被插入到子節(jié)點的末尾。

如果anchor參數(shù)為空,直接在父節(jié)點下的子節(jié)點列表末尾添加子節(jié)點。

  1. function mount(vnode, container, anchor) { 
  2.     const el = document.createElement(vnode.tag); 
  3.     vnode.el = el; 
  4.     // props 
  5.     if (vnode.props) { 
  6.         for (const key in vnode.props) { 
  7.             if (key.startsWith('on')) { 
  8.                 el.addEventListener(key.slice(2).toLowerCase(), vnode.props[key],{ 
  9.                     passive:true 
  10.                 }) 
  11.             } else { 
  12.                 el.setAttribute(key, vnode.props[key]); 
  13.             } 
  14.         } 
  15.     } 
  16.     if (vnode.children) { 
  17.         if (typeof vnode.children === "string") { 
  18.             el.textContent = vnode.children; 
  19.         } else { 
  20.             vnode.children.forEach(child => { 
  21.                 mount(child, el); 
  22.             }); 
  23.         } 
  24.     } 
  25.     if (anchor) { 
  26.         container.insertBefore(el, anchor); 
  27.     } else { 
  28.         container.appendChild(el); 
  29.     } 

下面,我們再回到patch方法。如果新舊節(jié)點的標簽相等,我們首先要遍歷新舊節(jié)點的屬性。我們先遍歷新節(jié)點的屬性,判斷新舊節(jié)點的屬性值是否相同,如果不相同,再進行進一步處理。判斷新節(jié)點的屬性值是否為null,否則直接移除屬性。然后,遍歷舊節(jié)點的屬性,如果屬性名不在新節(jié)點屬性表中,則直接移除屬性。

分析完了對新舊節(jié)點屬性的對比,接下來,我們來分析第三個參數(shù)子節(jié)點。

首先,我們分別定義兩個變量oc、nc,分別賦予舊節(jié)點的children屬性和新節(jié)點的children屬性。如果新節(jié)點的children屬性是字符串,并且新舊節(jié)點的內(nèi)容不相同,那么就直接將新節(jié)點的文本內(nèi)容賦予即可。

接下來,我們看到利用Array.isArray()方法判斷新節(jié)點的children屬性是否是數(shù)組,如果是數(shù)組的話,就執(zhí)行下面這些代碼。

  1. else if (Array.isArray(nc)) { 
  2.         if (Array.isArray(oc)) { 
  3.             // array diff 
  4.             const commonLength = Math.min(oc.length, nc.length); 
  5.             for (let i = 0; i < commonLength; i++) { 
  6.                 patch(oc[i], nc[i]); 
  7.             } 
  8.             if (nc.length > oc.length) { 
  9.                 nc.slice(oc.length).forEach(c => mount(c, el)); 
  10.             } else if (oc.length > nc.length) { 
  11.                 oc.slice(nc.length).forEach(c => { 
  12.                     el.removeChild(c.el); 
  13.                 }) 
  14.             } 
  15.         } else { 
  16.             el.innerHTML = ''
  17.             nc.forEach(c => mount(c, el)); 
  18.         } 
  19.     } 

我們看到里面又判斷舊節(jié)點的children屬性是否是數(shù)組。

如果是,我們?nèi)⌒屡f子節(jié)點數(shù)組的長度兩者的最小值。然后,我們將其循環(huán)遞歸patch方法。為什么取最小值呢?是因為如果取的是他們共有的長度。然后,每次遍歷遞歸時,判斷nc.length和oc.length的大小,循環(huán)執(zhí)行對應(yīng)的方法。

如果不是,直接將節(jié)點內(nèi)容清空,重新循環(huán)執(zhí)行mount方法。

這樣,我們搭建的迷你版Virtual DOM庫就這樣完成了。

頁面如下所示。

源碼

index.html

  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>vdom</title> 
  9.     <style> 
  10.         .main { 
  11.             color: #00008b; 
  12.         } 
  13.         .main1{ 
  14.             font-weight: bold; 
  15.         } 
  16.     </style> 
  17. </head> 
  18.  
  19. <body> 
  20.     <div id="app"></div> 
  21.     <script src="./vdom.js"></script> 
  22.     <script> 
  23.         function render() { 
  24.             return h('div', { 
  25.                 style: useObjStr({ 
  26.                     'color''#ccc'
  27.                     'font-size''20px' 
  28.                 }) 
  29.             }, [ 
  30.                 h('div', {}, [h('span', { 
  31.                     onClick: () => { 
  32.                         alert('1'); 
  33.                     } 
  34.                 }, '文本'), h('a', { 
  35.                     href: 'https://www.baidu.com'
  36.                     class: 'main main1' 
  37.                 }, '點擊'
  38.                 ]), 
  39.             ]) 
  40.         } 
  41.          
  42.         // 頁面改變 
  43.         function render1() { 
  44.             return h('div', { 
  45.                 style: useStyleStr({ 
  46.                     'color''#ccc'
  47.                     'font-size''20px' 
  48.                 }) 
  49.             }, [ 
  50.                 h('div', {}, [h('span', { 
  51.                     onClick: () => { 
  52.                         alert('1'); 
  53.                     } 
  54.                 }, '文本改變了'
  55.                 ]), 
  56.             ]) 
  57.         } 
  58.  
  59.         // 首次加載 
  60.         mountNode(render, '#app'); 
  61.  
  62.         // 狀態(tài)改變 
  63.         setTimeout(()=>{ 
  64.             mountNode(render1, '#app'); 
  65.         },3000) 
  66.     </script> 
  67. </body> 
  68.  
  69. </html> 

vdom.js

  1.  // vdom --- 
  2.  function h(tag, props, children) { 
  3.     return { tag, props, children }; 
  4.  
  5. function mount(vnode, container, anchor) { 
  6.     const el = document.createElement(vnode.tag); 
  7.     vnode.el = el; 
  8.     // props 
  9.     if (vnode.props) { 
  10.         for (const key in vnode.props) { 
  11.             if (key.startsWith('on')) { 
  12.                 el.addEventListener(key.slice(2).toLowerCase(), vnode.props[key],{ 
  13.                     passive:true 
  14.                 }) 
  15.             } else { 
  16.                 el.setAttribute(key, vnode.props[key]); 
  17.             } 
  18.         } 
  19.     } 
  20.     if (vnode.children) { 
  21.         if (typeof vnode.children === "string") { 
  22.             el.textContent = vnode.children; 
  23.         } else { 
  24.             vnode.children.forEach(child => { 
  25.                 mount(child, el); 
  26.             }); 
  27.         } 
  28.     } 
  29.     if (anchor) { 
  30.         container.insertBefore(el, anchor); 
  31.     } else { 
  32.         container.appendChild(el); 
  33.     } 
  34.  
  35. // processing strings 
  36. function useStyleStr(obj) { 
  37.     const reg = /^{|}/g; 
  38.     const reg1 = new RegExp('"',"g"); 
  39.     const str = JSON.stringify(obj); 
  40.     const ustr = str.replace(reg, '').replace(','';').replace(reg1,''); 
  41.     return ustr; 
  42.  
  43. function patch(n1, n2) { 
  44.     // Implement this 
  45.     // 1. check if n1 and n2 are of the same type 
  46.     if (n1.tag !== n2.tag) { 
  47.         // 2. if notreplace 
  48.         const parent = n1.el.parentNode; 
  49.         const anchor = n1.el.nextSibling; 
  50.         parent.removeChild(n1.el); 
  51.         mount(n2, parent, anchor); 
  52.         return 
  53.     } 
  54.  
  55.     const el = n2.el = n1.el; 
  56.  
  57.     // 3. if yes 
  58.     // 3.1 diff props 
  59.     const oldProps = n1.props || {}; 
  60.     const newProps = n2.props || {}; 
  61.     for (const key in newProps) { 
  62.         const newValue = newProps[key]; 
  63.         const oldValue = oldProps[key]; 
  64.         if (newValue !== oldValue) { 
  65.             if (newValue != null) { 
  66.                 el.setAttribute(key, newValue); 
  67.             } else { 
  68.                 el.removeAttribute(key); 
  69.             } 
  70.         } 
  71.     } 
  72.     for (const key in oldProps) { 
  73.         if (!(key in newProps)) { 
  74.             el.removeAttribute(key); 
  75.         } 
  76.     } 
  77.     // 3.2 diff children 
  78.     const oc = n1.children; 
  79.     const nc = n2.children; 
  80.     if (typeof nc === 'string') { 
  81.         if (nc !== oc) { 
  82.             el.textContent = nc; 
  83.         } 
  84.     } else if (Array.isArray(nc)) { 
  85.         if (Array.isArray(oc)) { 
  86.             // array diff 
  87.             const commonLength = Math.min(oc.length, nc.length); 
  88.             for (let i = 0; i < commonLength; i++) { 
  89.                 patch(oc[i], nc[i]); 
  90.             } 
  91.             if (nc.length > oc.length) { 
  92.                 nc.slice(oc.length).forEach(c => mount(c, el)); 
  93.             } else if (oc.length > nc.length) { 
  94.                 oc.slice(nc.length).forEach(c => { 
  95.                     el.removeChild(c.el); 
  96.                 }) 
  97.             } 
  98.         } else { 
  99.             el.innerHTML = ''
  100.             nc.forEach(c => mount(c, el)); 
  101.         } 
  102.     } 
  103.  
  104. let isMounted = false
  105. let oldTree; 
  106.  
  107. // Mount node 
  108. function mountNode(render, selector) { 
  109.     if (!isMounted) { 
  110.         mount(oldTree = render(), document.querySelector(selector)); 
  111.         isMounted = true
  112.     } else { 
  113.         const newTree = render(); 
  114.         patch(oldTree, newTree); 
  115.         oldTree = newTree; 
  116.     } 
  117.  

 

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

2022-05-06 19:42:53

DOM

2020-10-12 08:56:47

Virtual dom

2021-05-26 05:22:09

Virtual DOMSnabbdom虛擬DOM

2021-07-04 10:07:04

Virtual DO閱讀源碼虛擬DOM

2024-10-15 09:48:56

2021-06-25 06:47:38

VueVue2.x迷你版響應(yīng)式原理

2021-01-11 07:51:16

DOM對象節(jié)點樹

2025-07-21 09:30:35

2022-01-26 14:29:04

區(qū)塊鏈加密貨幣技術(shù)

2021-01-18 07:15:22

虛擬DOM真實DOMJavaScript

2011-12-18 12:36:59

摩托

2014-05-26 16:16:59

Shadow DomWeb Compone

2021-02-02 07:37:39

NextTickvueDOM

2018-06-26 14:29:44

LinuxUnix不同

2023-12-01 15:39:13

Linux操作系統(tǒng)

2019-10-14 10:09:33

Wi-Fi 6Wi-Fi無線網(wǎng)絡(luò)

2021-09-06 10:45:18

XDRMDR

2022-09-14 09:45:15

指標標簽

2024-02-26 07:36:09

lockJava語言

2012-07-25 15:45:28

ERPSCM
點贊
收藏

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

日本一区二区三区四区视频| 日韩一区二区视频| 欧洲久久久久久| 成人免费看黄网站| 久久精品成人av| 国产资源一区| 亚洲一卡二卡三卡四卡 | 人人妻人人澡人人爽精品欧美一区| 国产又粗又黄又爽| 在线精品福利| 永久免费精品影视网站| 日韩av福利在线观看| av在线最新| 亚洲国产高清在线| 国产欧美丝袜| 国产女人18毛片18精品| 欧美一级播放| 欧美激情免费看| 亚洲无人区码一码二码三码的含义 | 国偷自产av一区二区三区| 91久久奴性调教| 欧美日韩福利在线| 米奇精品一区二区三区| 91在线国内视频| 亚洲影院色在线观看免费| 波多野结衣家庭主妇| 影音先锋中文字幕一区| www亚洲欧美| 在哪里可以看毛片| youjizz欧美| 91精品欧美福利在线观看| 成人在线免费观看av| 日本高清在线观看视频| 亚洲欧洲在线观看av| 欧美在线视频二区| 欧美1o一11sex性hdhd| 日韩字幕在线观看| 中文字幕日韩一区二区不卡 | 在线观看亚洲一区| 缅甸午夜性猛交xxxx| 最新国产露脸在线观看| 国产精品国产三级国产普通话蜜臀 | 日韩在线综合网| 青草在线视频在线观看| 亚洲女子a中天字幕| 亚洲狠狠婷婷综合久久久| 国产日产精品久久久久久婷婷| 99视频精品免费视频| 国产精品免费看一区二区三区| 国产高清精品软件丝瓜软件| 男人天堂综合网| 亚洲综合社区| 97国产精品视频| 国产网友自拍视频| 亚洲欧洲日本mm| 久久久人成影片一区二区三区观看| 国产中文av在线| 99精品在线| 久久久av免费| 成人在线观看高清| 午夜日韩视频| 欧美激情a在线| 久久久精品国产sm调教| 国内精品99| 久久久久国产精品免费| 天堂资源在线播放| 在线综合亚洲| 国产成人精品电影| a片在线免费观看| 美国十次了思思久久精品导航| 国产va免费精品高清在线| 成人av网站在线播放| 视频一区二区国产| 国产在线观看精品| 性色av蜜臀av| 91色porny在线视频| 欧美高清性xxxxhdvideosex| melody高清在线观看| 国产精品成人网| 女女百合国产免费网站| 97久久人人超碰caoprom| 亚洲福利国产精品| 韩国一区二区av| 醉酒壮男gay强迫野外xx| 999国产精品一区| 亚洲成人精品视频| 中国毛片在线观看| 色婷婷一区二区三区| 久青草国产97香蕉在线视频| 国产一级片免费看| 美女精品一区| 国产在线拍偷自揄拍精品| 草逼视频免费看| 久久这里只有精品6| 一本久久a久久精品vr综合| av网站网址在线观看| 欧美日韩色婷婷| 亚洲综合激情视频| 欧美人体视频| 日韩中文字幕网址| 五月天婷婷丁香| 青青草97国产精品免费观看无弹窗版| 51国产成人精品午夜福中文下载| 欧洲av在线播放| 中文字幕第一区第二区| 人体内射精一区二区三区| 日韩不卡在线| 精品久久久久久久久久久久久久久| 精品人妻一区二区三区日产乱码卜| 极品美女一区二区三区| 欧美日本啪啪无遮挡网站| 亚洲无码精品一区二区三区| 国产成人亚洲综合a∨婷婷| 日本视频精品一区| 1区2区3区在线| 欧美日韩在线播放| 人体私拍套图hdxxxx| 欧美第一精品| 日本sm极度另类视频| 国产成人精品无码高潮| 国产欧美日产一区| 日本a视频在线观看| 四虎影视国产精品| 亚洲社区在线观看| av资源吧首页| 国产在线国偷精品产拍免费yy| 欧美日韩一区在线观看视频| 麻豆av在线免费观看| 欧美精品日日鲁夜夜添| 亚洲精品乱码久久久久久久久久久久 | 成人在线高清免费| 欧美一区二区在线播放| 四虎成人免费影院| 亚洲欧美不卡| 精品国产一区二区三| 怡红院av在线| 6080午夜不卡| 亚洲aaa视频| 天堂成人免费av电影一区| 久久大片网站| 2001个疯子在线观看| 日韩欧美的一区| 天天鲁一鲁摸一摸爽一爽| 蜜臀av一级做a爰片久久| 麻豆成人在线播放| 26uuu亚洲电影| 日韩国产精品亚洲а∨天堂免| 久操视频免费在线观看| 国产福利一区在线| 国产精品啪啪啪视频| 秋霞影院一区| 欧美精品在线第一页| 99久久精品免费看国产交换| 中文字幕一区二区在线观看| 777一区二区| 婷婷亚洲综合| 亚洲综合在线中文字幕| 成人在线观看亚洲| 日韩欧美一二三区| 久久综合色综合| 成人av网站免费| 丁香花在线影院观看在线播放| 国产精品17p| 欧美亚洲国产日本| 欧美人体大胆444www| 在线精品视频一区二区| 四虎成人免费影院| 国内精品伊人久久久久av一坑 | 成人午夜两性视频| a免费在线观看| 欧美成人免费网站| 日本熟妇毛耸耸xxxxxx| 久久夜色精品国产噜噜av| 男女污污的视频| 99精品视频在线观看播放| 91社区国产高清| 在线观看的网站你懂的| 亚洲高清在线观看| 免费精品一区二区| 亚洲视频免费在线| 黄色网址在线视频| 免费看欧美女人艹b| 中文字幕在线乱| 97se亚洲| 国产成人精品午夜| а√中文在线8| 日韩成人在线观看| 亚洲在线观看av| 一区二区三区久久| 欧美狂猛xxxxx乱大交3| 久久激情五月激情| 激情深爱综合网| 欧美日一区二区| 粉嫩av免费一区二区三区| 中文在线а√天堂| 两个人的视频www国产精品| 天天色棕合合合合合合合| 精品视频在线免费观看| 国产一级一片免费播放| 国产清纯白嫩初高生在线观看91| 91插插插影院| 狂野欧美一区| 国产91视频一区| 精品成人影院| 电影午夜精品一区二区三区| 日韩欧美精品电影| 欧美福利视频网站| 91短视频版在线观看www免费| 日韩欧美第一区| 中文字幕在线天堂| 亚洲高清免费视频| 五月婷婷六月香| 91毛片在线观看| 国产传媒免费观看| 日精品一区二区三区| 婷婷无套内射影院| 国产精品久久久久久久久妇女| 久久久久一区二区三区| 97久久综合区小说区图片区| 国产精品精品久久久久久| 黄色aa久久| 久久精品国产99国产精品澳门| 日韩在线无毛| 精品剧情在线观看| 97在线播放免费观看| 日本福利一区二区| 日韩精品在线免费看| 亚洲美女在线国产| 日日操免费视频| 国产片一区二区| 中文在线一区二区三区| 国产99久久精品| 日本网站在线看| 奇米在线7777在线精品| 国产xxxxx视频| 亚洲一区激情| 亚洲国产精品无码av| 欧美.www| 六月婷婷激情网| 一个色综合网| 一区二区三区四区视频在线| 欧美天天综合| 色噜噜一区二区| 免费欧美激情| 欧美日韩国产精品一区二区| 欧美色图五月天| 久久久久天天天天| 欧美重口另类| 久久天天狠狠| 九一精品国产| 欧美在线一区二区三区四区| 国产亚洲一区二区三区啪| 欧美亚洲精品日韩| 九九久久电影| 日本不卡在线观看| gogogo高清在线观看一区二区| 欧美二级三级| 精品国产一区二区三区噜噜噜| 欧美最大成人综合网| 欧美亚洲国产精品久久| 亚洲午夜精品福利| 国产精品毛片一区二区在线看| 做爰高潮hd色即是空| 一区二区三区国产精华| 日本男女交配视频| 红桃视频亚洲| 成人在线免费观看av| 久久久久久穴| 不用播放器的免费av| 国产酒店精品激情| 人妻av一区二区| 久久综合999| 中国特黄一级片| 亚洲欧美国产77777| 国产一二三四在线| 疯狂做受xxxx高潮欧美日本| 伊人久久久久久久久久久久| 欧美精品一二三四| 亚洲欧美高清视频| 亚洲人成在线观看| 免费成人黄色| 国产做受69高潮| 日韩欧美一区二区三区在线观看| 国产日韩综合一区二区性色av| 久久九九精品视频| 久久综合九色欧美狠狠| 欧美成人精品一区二区三区在线看| 免费观看亚洲视频| 久久久久91| 99久久99精品| 久久老女人爱爱| 91高清免费看| 懂色av中文一区二区三区天美| 亚洲视频中文字幕在线观看| 日韩精品一区二区三区四区视频| 在线观看xxx| 俺也去精品视频在线观看| а√在线中文在线新版| 国产乱人伦真实精品视频| 88久久精品| 日韩免费电影一区二区三区| 午夜国产欧美理论在线播放| 不卡av免费在线| 粉嫩高潮美女一区二区三区| 神马久久久久久久久久久| 亚洲国产精品一区二区久久| 中文字幕永久在线| 亚洲电影中文字幕| 黄色网页在线看| 欧美在线视频一区| 亚洲一区电影| 天堂√在线观看一区二区| 99成人在线| 久久久久亚洲av片无码v| 国产视频视频一区| 日本少妇性高潮| 欧美一二三区在线| 国产高清自拍视频在线观看| 91av福利视频| 日韩亚洲精品在线观看| 亚洲精品在线观看免费| 欧美亚洲一级| 特级西西人体wwwww| 亚洲免费在线电影| 91成年人视频| 亚洲色图18p| 三级在线观看视频| av资源站久久亚洲| 伊人久久大香线蕉精品组织观看| www.色偷偷.com| 久久夜色精品一区| 天堂网av手机版| 亚洲国产精品999| 女人天堂av在线播放| 91人人爽人人爽人人精88v| 日本久久精品| 国产97色在线 | 日韩| 久久综合九色综合97婷婷女人 | 成人免费黄色网页| 日本久久久久久久久久久| 日韩av不卡一区| 蜜臀av无码一区二区三区| 国产成都精品91一区二区三| 紧身裙女教师波多野结衣| 91精品免费在线| 羞羞网站在线看| 91九色在线观看| 女人色偷偷aa久久天堂| 极品人妻一区二区| 悠悠色在线精品| 黄色aaa大片| 久久久噜噜噜久久久| www.丝袜精品| 亚洲美免无码中文字幕在线 | 91成人在线免费视频| 色婷婷久久久久swag精品| 久草视频在线看| 国产欧美日韩高清| 久久精品影视| 日本55丰满熟妇厨房伦| 亚洲综合区在线| 亚洲 欧美 激情 小说 另类| 2018中文字幕一区二区三区| 免费电影一区二区三区| 一区二区三区韩国| 亚洲免费色视频| 亚洲精品一区二区口爆| 性欧美办公室18xxxxhd| 中文字幕av一区二区三区人| 久久九九国产视频| 亚洲欧美偷拍另类a∨色屁股| 99国产精品欲| 97久久伊人激情网| 国产成人高清| 91丨九色丨蝌蚪| 一区二区免费看| 天天摸夜夜添狠狠添婷婷| 国产精品日韩欧美大师| 亚洲一区二区| 国产麻豆天美果冻无码视频| 在线观看网站黄不卡| 成人在线观看亚洲| 国内成+人亚洲| 男男成人高潮片免费网站| 九九热最新地址| 亚洲精品www久久久久久广东| 欧美一级二级视频| 永久免费在线看片视频| 99re这里只有精品视频首页| 中文字幕在线观看视频一区| 欧美成人在线免费视频| 亚洲国产国产| 制服下的诱惑暮生| 日韩欧美国产一区二区| 91一区二区三区在线| 日本一区视频在线观看| 处破女av一区二区| 最近中文字幕av| 97涩涩爰在线观看亚洲| 国产精品久久久久蜜臀|