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

ReactRouter-V4 構建之道與源碼分析

開發 開發工具
本文即是我在構建 React Router V4 過程中的考慮以及所謂路由即組件思想的落地實踐。

[[188988]]

多年之后當我回想起初學客戶端路由的那個下午,滿腦子里充斥著的只是對于單頁應用的驚嘆與漿糊。彼時我還是將應用代碼與路由代碼當做兩個獨立的部分進行處理,就好像同父異母的兄弟盡管不喜歡對方但是不得不在一起。幸而這些年里我能夠和其他優秀的開發者進行交流,了解他們對于客戶端路由的看法。盡管他們中的大部分與我“英雄所見略同”,但是我還是找到了合適的平衡路由的抽象程度與復雜程度的方法。本文即是我在構建 React Router V4 過程中的考慮以及所謂路由即組件思想的落地實踐。首先我們來看下我們在構建路由過程中的測試代碼,你可以用它來測試你的自定義路由:

  1. const Home = () => ( 
  2.   <h2>Home</h2> 
  3.  
  4. const About = () => ( 
  5.   <h2>About</h2> 
  6.  
  7. const Topic = ({ topicId }) => ( 
  8.   <h3>{topicId}</h3> 
  9.  
  10. const Topics = ({ match }) => { 
  11.   const items = [ 
  12.     { name'Rendering with React', slug: 'rendering' }, 
  13.     { name'Components', slug: 'components' }, 
  14.     { name'Props v. State', slug: 'props-v-state' }, 
  15.   ] 
  16.  
  17.   return ( 
  18.     <div> 
  19.       <h2>Topics</h2> 
  20.       <ul> 
  21.         {items.map(({ name, slug }) => ( 
  22.           <li key={name}> 
  23.             <Link to={`${match.url}/${slug}`}>{name}</Link> 
  24.           </li> 
  25.         ))} 
  26.       </ul> 
  27.       {items.map(({ name, slug }) => ( 
  28.         <Route key={name} path={`${match.path}/${slug}`} render={() => ( 
  29.           <Topic topicId={name} /> 
  30.         )} /> 
  31.       ))} 
  32.       <Route exact path={match.url} render={() => ( 
  33.         <h3>Please select a topic.</h3> 
  34.       )}/> 
  35.     </div> 
  36.   ) 
  37.  
  38. const App = () => ( 
  39.   <div> 
  40.     <ul> 
  41.       <li><Link to="/">Home</Link></li> 
  42.       <li><Link to="/about">About</Link></li> 
  43.       <li><Link to="/topics">Topics</Link></li> 
  44.     </ul> 
  45.  
  46.     <hr/> 
  47.  
  48.     <Route exact path="/" component={Home}/> 
  49.     <Route path="/about" component={About}/> 
  50.     <Route path="/topics" component={Topics} /> 
  51.   </div> 

如果你對于 React Router v4 尚不是完全了解,我們先對上述代碼中涉及到的相關關鍵字進行解釋。Route 會在當前 URL 與 path 屬性值相符的時候渲染相關組件,而 Link 提供了聲明式的,易使用的方式來在應用內進行跳轉。換言之,Link 組件允許你更新當前 URL,而 Route 組件則是根據 URL 渲染組件。本文并不專注于講解 RRV4 的基礎概念,你可以前往官方文檔了解更多知識;本文是希望介紹我在構建 React Router V4 過程中的思維考慮過程,值得一提的是,我很欣賞 React Router V4 中的 Just Components 概念,這一點就不同于 React Router 之前的版本中將路由與組件隔離來看,而允許了路由組件像普通組件一樣自由組合。相信對于 React 組件相當熟悉的開發者絕不會陌生于如何將路由組件嵌入到正常的應用中。

Route

我們首先來考量下如何構建Route組件,包括其暴露的 API,即 Props。在我們上面的示例中,我們會發現Route組件包含三個 Props:exact、path 以及 component。這也就意味著我們的propTypes聲明如下:

  1. static propTypes = { 
  2.   exact: PropTypes.bool, 
  3.   path: PropTypes.string, 
  4.   component: PropTypes.func, 

這里有一些微妙的細節需要考慮,首先對于path并沒有設置為必須參數,這是因為我們認為對于沒有指定關聯路徑的Route組件應該自動默認渲染。而component參數也沒有被設置為必須是因為我們提供了其他的方式進行渲染,譬如render函數:

  1. <Route path='/settings' render={({ match }) => { 
  2.   return <Settings authed={isAuthed} match={match} /> 
  3. }} /> 

render 函數允許你方便地使用內聯函數來創建 UI 而不是創建新的組件,因此我們也需要將該函數設置為 propTypes:

  1. static propTypes = { 
  2.   exact: PropTypes.bool, 
  3.   path: PropTypes.string, 
  4.   component: PropTypes.func, 
  5.   render: PropTypes.func, 

在確定了 Route 需要接收的組件參數之后,我們需要來考量其實際功能;Route 核心的功能在于能夠當 URL 與 path 屬性相一致時執行渲染操作。基于這個論斷,我們首先需要實現判斷是否匹配的功能,如果判斷為匹配則執行渲染否則返回空值。我們在這里將該函數命名為 matchPatch,那么此時整個 Route 組件的 render 函數定義如下:

  1. class Route extends Component { 
  2.   static propTypes = { 
  3.     exact: PropTypes.bool, 
  4.     path: PropTypes.string, 
  5.     component: PropTypes.func, 
  6.     render: PropTypes.func, 
  7.   } 
  8.  
  9.   render () { 
  10.     const { 
  11.       path, 
  12.       exact, 
  13.       component, 
  14.       render, 
  15.     } = this.props 
  16.  
  17.     const match = matchPath( 
  18.       location.pathname, // global DOM variable 
  19.       { path, exact } 
  20.     ) 
  21.  
  22.     if (!match) { 
  23.       // Do nothing because the current 
  24.       // location doesn't match the path prop. 
  25.  
  26.       return null 
  27.     } 
  28.  
  29.     if (component) { 
  30.       // The component prop takes precedent over the 
  31.       // render method. If the current location matches 
  32.       // the path prop, create a new element passing in 
  33.       // match as the prop. 
  34.  
  35.       return React.createElement(component, { match }) 
  36.     } 
  37.  
  38.     if (render) { 
  39.       // If there's a match but component 
  40.       // was undefined, invoke the render 
  41.       // prop passing in match as an argument. 
  42.  
  43.       return render({ match }) 
  44.     } 
  45.  
  46.     return null 
  47.   } 

現在的 Route 看起來已經相對明確了,當路徑相匹配的時候才會執行界面渲染,否則返回為空。現在我們再回過頭來考慮客戶端路由中常見的跳轉策略,一般來說用戶只有兩種方式會更新當前 URL。一種是用戶點擊了某個錨標簽或者直接操作 history 對象的 replace/push 方法;另一種是用戶點擊前進/后退按鈕。無論哪一種方式都要求我們的路由系統能夠實時監聽 URL 的變化,并且在 URL 發生變化時及時地做出響應,渲染出正確的頁面。我們首先來考慮下如何處理用戶點擊前進/后退按鈕。React Router 使用 History 的 .listen 方法來監聽當前 URL 的變化,其本質上還是直接監聽 HTML5 的 popstate 事件。popstate 事件會在用戶點擊某個前進/后退按鈕的時候觸發;而在每次重渲染的時候,每個 Route 組件都會重現檢測當前 URL 是否匹配其預設的路徑參數。

  1. class Route extends Component { 
  2.   static propTypes: { 
  3.     path: PropTypes.string, 
  4.     exact: PropTypes.bool, 
  5.     component: PropTypes.func, 
  6.     render: PropTypes.func, 
  7.   } 
  8.  
  9.   componentWillMount() { 
  10.     addEventListener("popstate", this.handlePop) 
  11.   } 
  12.  
  13.   componentWillUnmount() { 
  14.     removeEventListener("popstate", this.handlePop) 
  15.   } 
  16.  
  17.   handlePop = () => { 
  18.     this.forceUpdate() 
  19.   } 
  20.  
  21.   render() { 
  22.     const { 
  23.       path, 
  24.       exact, 
  25.       component, 
  26.       render, 
  27.     } = this.props 
  28.  
  29.     const match = matchPath(location.pathname, { path, exact }) 
  30.  
  31.     if (!match) 
  32.       return null 
  33.  
  34.     if (component) 
  35.       return React.createElement(component, { match }) 
  36.  
  37.     if (render) 
  38.       return render({ match }) 
  39.  
  40.     return null 
  41.   } 

你會發現上面的代碼與之前的相比多了掛載與卸載 popstate 監聽器的功能,其會在組件掛載時添加一個 popstate 監聽器;當監聽到 popstate 事件被觸發時,我們會調用 forceUpdate 函數來強制進行重渲染。總結而言,無論我們在系統中設置了多少的路由組件,它們都會獨立地監聽 popstate 事件并且相應地執行重渲染操作。接下來我們繼續討論 matchPath 這個 Route 組件中至關重要的函數,它負責決定當前路由組件的 path 參數是否與當前 URL 相一致。這里還必須提下我們設置的另一個 Route 的參數 exact,其用于指明路徑匹配策略;當 exact 值被設置為 true 時,僅當路徑完全匹配于 location.pathname 才會被認為匹配成功:

pathlocation.pathnameexactmatches?/one/one/twotrueno/one/one/twofalseyes

讓我們深度了解下 matchPath 函數的工作原理,該函數的簽名如下:

  1. const match = matchPath(location.pathname, { path, exact }) 

其中函數的返回值 match 應該根據路徑是否匹配的情況返回為空或者一個對象。基于這些推導我們可以得出 matchPatch 的原型:

  1. const matchPath = (pathname, options) => { 
  2.   const { exact = false, path } = options 

這里我們使用 ES6 的解構賦值,當某個屬性未定義時我們使用預定義地默認值,即 false。我在上文提及的 path 非必要參數的具體支撐實現就在這里,我們首先進行空檢測,當發現 path 為未定義或者為空時則直接返回匹配成功:

  1. const matchPath = (pathname, options) => { 
  2.   const { exact = false, path } = options 
  3.  
  4.   if (!path) { 
  5.     return { 
  6.       path: null
  7.       url: pathname, 
  8.       isExact: true
  9.     } 
  10.   } 

接下來繼續考慮具體執行匹配的部分,React Router 使用了 pathToRegex 來檢測是否匹配,即可以用簡單的正則表達式:

  1. const matchPath = (pathname, options) => { 
  2.   const { exact = false, path } = options 
  3.  
  4.   if (!path) { 
  5.     return { 
  6.       path: null
  7.       url: pathname, 
  8.       isExact: true
  9.     } 
  10.   } 
  11.  
  12.   const match = new RegExp(`^${path}`).exec(pathname) 
  13.  

這里使用的 .exec 函數,會在包含指定的文本時返回一個數組,否則返回空值;下表即是當我們的路由設置為 /topics/components時具體的返回:

  1. | path                    | location.pathname    | return value             | 
  2. ----------------------- | -------------------- | ------------------------ | 
  3. | `/`                     | `/topics/components` | `['/']`                  | 
  4. | `/about`                | `/topics/components` | `null`                   | 
  5. | `/topics`               | `/topics/components` | `['/topics']`            | 
  6. | `/topics/rendering`     | `/topics/components` | `null`                   | 
  7. | `/topics/components`    | `/topics/components` | `['/topics/components']` | 
  8. | `/topics/props-v-state` | `/topics/components` | `null`                   | 
  9. | `/topics`               | `/topics/components` | `['/topics']`            | 

這里大家就會看出來,我們會為每個 <Route> 實例創建一個 match 對象。在獲取到 match 對象之后,我們需要再做如下判斷是否匹配:

  1. const matchPath = (pathname, options) => { 
  2.   const { exact = false, path } = options 
  3.  
  4.   if (!path) { 
  5.     return { 
  6.       path: null
  7.       url: pathname, 
  8.       isExact: true
  9.     } 
  10.   } 
  11.  
  12.   const match = new RegExp(`^${path}`).exec(pathname) 
  13.  
  14.   if (!match) { 
  15.     // There wasn't a match. 
  16.     return null 
  17.   } 
  18.  
  19.   const url = match[0] 
  20.   const isExact = pathname === url 
  21.  
  22.   if (exact && !isExact) { 
  23.     // There was a match, but it wasn't 
  24.     // an exact match as specified by 
  25.     // the exact prop. 
  26.  
  27.     return null 
  28.   } 
  29.  
  30.   return { 
  31.     path, 
  32.     url, 
  33.     isExact, 
  34.   } 

Link

上文我們已經提及通過監聽 popstate 狀態來響應用戶點擊前進/后退事件,現在我們來考慮通過構建 Link 組件來處理用戶通過點擊錨標簽進行跳轉的事件。Link 組件的 API 應該如下所示:

  1. <Link to='/some-path' replace={false} /> 

其中的 to 是一個指向跳轉目標地址的字符串,而 replace 則是布爾變量來指定當用戶點擊跳轉時是替換 history 棧中的記錄還是插入新的記錄。基于上述的 API 設計,我們可以得到如下的組件聲明:

  1. class Link extends Component { 
  2.   static propTypes = { 
  3.     to: PropTypes.string.isRequired, 
  4.     replace: PropTypes.bool, 
  5.   } 

現在我們已經知道 Link 組件的渲染函數中需要返回一個錨標簽,不過我們的前提是要避免每次用戶切換路由的時候都進行整頁的刷新,因此我們需要為每個錨標簽添加一個點擊事件的處理器:

  1. class Link extends Component { 
  2.   static propTypes = { 
  3.     to: PropTypes.string.isRequired, 
  4.     replace: PropTypes.bool, 
  5.   } 
  6.  
  7.   handleClick = (event) => { 
  8.     const { replaceto } = this.props 
  9.     event.preventDefault() 
  10.  
  11.     // route here. 
  12.   } 
  13.  
  14.   render() { 
  15.     const { to, children} = this.props 
  16.  
  17.     return ( 
  18.       <a href={to} onClick={this.handleClick}> 
  19.         {children} 
  20.       </a> 
  21.     ) 
  22.   } 

這里實際的跳轉操作我們還是執行 History 中的抽象的 push 與 replace 函數,在使用 browserHistory 的情況下我們本質上還是使用 HTML5 中的 pushState 與 replaceState 函數。pushState 與 replaceState 函數都要求輸入三個參數,首先是一個與***的歷史記錄相關的對象,在 React Router 中我們并不需要該對象,因此直接傳入一個空對象;第二個參數是標題參數,我們同樣不需要改變該值,因此直接傳入空即可;***第三個參數則是我們需要的,用于指明新的相對地址的字符串:

  1. const historyPush = (path) => { 
  2.   history.pushState({}, null, path) 
  3.  
  4. const historyReplace = (path) => { 
  5.   history.replaceState({}, null, path) 

而后在 Link 組件內,我們會根據 replace 參數來調用 historyPush 或者 historyReplace 函數:

  1. class Link extends Component { 
  2.   static propTypes = { 
  3.     to: PropTypes.string.isRequired, 
  4.     replace: PropTypes.bool, 
  5.   } 
  6.   handleClick = (event) => { 
  7.     const { replaceto } = this.props 
  8.     event.preventDefault() 
  9.  
  10.     replace ? historyReplace(to) : historyPush(to
  11.   } 
  12.  
  13.   render() { 
  14.     const { to, children} = this.props 
  15.  
  16.     return ( 
  17.       <a href={to} onClick={this.handleClick}> 
  18.         {children} 
  19.       </a> 
  20.     ) 
  21.   } 

組件注冊

現在我們需要考慮如何保證用戶點擊了 Link 組件之后觸發全部路由組件的檢測與重渲染。在我們上面實現的 Link 組件中,用戶執行跳轉之后瀏覽器的顯示地址會發生變化,但是頁面尚不能重新渲染;我們聲明的 Route 組件并不能收到相應的通知。為了解決這個問題,我們需要追蹤那些顯現在界面上實際被渲染的 Route 組件并且當路由變化時調用它們的 forceUpdate 方法。React Router 主要通過有機組合 setState、context 以及 history.listen 方法來實現該功能。每個 Route 組件被掛載時我們會將其加入到某個數組中,然后當位置變化時,我們可以遍歷該數組然后對每個實例調用 forceUpdate 方法:

  1. let instances = [] 
  2.  
  3. const register = (comp) => instances.push(comp) 
  4. const unregister = (comp) => instances.splice(instances.indexOf(comp), 1) 

這里我們創建了兩個函數,當 Route 掛載時調用 register 函數,而卸載時調用 unregister 函數。然后無論何時調用 historyPush 或者 historyReplace 函數時都會遍歷實例數組中的對象的渲染方法,此時我們的 Route 組件就需要聲明為如下樣式:

  1. class Route extends Component { 
  2.   static propTypes: { 
  3.     path: PropTypes.string, 
  4.     exact: PropTypes.bool, 
  5.     component: PropTypes.func, 
  6.     render: PropTypes.func, 
  7.   } 
  8.  
  9.   componentWillMount() { 
  10.     addEventListener("popstate", this.handlePop) 
  11.     register(this) 
  12.   } 
  13.  
  14.   componentWillUnmount() { 
  15.     unregister(this) 
  16.     removeEventListener("popstate", this.handlePop) 
  17.   } 
  18.  
  19.   ... 

然后我們需要更新 historyPush 與 historyReplace 函數:

  1. const historyPush = (path) => { 
  2.   history.pushState({}, null, path) 
  3.   instances.forEach(instance => instance.forceUpdate()) 
  4.  
  5. const historyReplace = (path) => { 
  6.   history.replaceState({}, null, path) 
  7.   instances.forEach(instance => instance.forceUpdate()) 

這樣的話就保證了無論何時用戶點擊 <Link> 組件之后,在位置顯示變化的同時,所有的 <Route> 組件都能夠被通知到并且執行重匹配與重渲染。現在我們完整的路由解決方案就成形了:

  1. import React, { PropTypes, Component } from 'react' 
  2.  
  3. let instances = [] 
  4.  
  5. const register = (comp) => instances.push(comp) 
  6. const unregister = (comp) => instances.splice(instances.indexOf(comp), 1) 
  7.  
  8. const historyPush = (path) => { 
  9.   history.pushState({}, null, path) 
  10.   instances.forEach(instance => instance.forceUpdate()) 
  11.  
  12. const historyReplace = (path) => { 
  13.   history.replaceState({}, null, path) 
  14.   instances.forEach(instance => instance.forceUpdate()) 
  15.  
  16. const matchPath = (pathname, options) => { 
  17.   const { exact = false, path } = options 
  18.  
  19.   if (!path) { 
  20.     return { 
  21.       path: null
  22.       url: pathname, 
  23.       isExact: true 
  24.     } 
  25.   } 
  26.  
  27.   const match = new RegExp(`^${path}`).exec(pathname) 
  28.  
  29.   if (!match) 
  30.     return null 
  31.  
  32.   const url = match[0] 
  33.   const isExact = pathname === url 
  34.  
  35.   if (exact && !isExact) 
  36.     return null 
  37.  
  38.   return { 
  39.     path, 
  40.     url, 
  41.     isExact, 
  42.   } 
  43.  
  44. class Route extends Component { 
  45.   static propTypes: { 
  46.     path: PropTypes.string, 
  47.     exact: PropTypes.bool, 
  48.     component: PropTypes.func, 
  49.     render: PropTypes.func, 
  50.   } 
  51.  
  52.   componentWillMount() { 
  53.     addEventListener("popstate", this.handlePop) 
  54.     register(this) 
  55.   } 
  56.  
  57.   componentWillUnmount() { 
  58.     unregister(this) 
  59.     removeEventListener("popstate", this.handlePop) 
  60.   } 
  61.  
  62.   handlePop = () => { 
  63.     this.forceUpdate() 
  64.   } 
  65.  
  66.   render() { 
  67.     const { 
  68.       path, 
  69.       exact, 
  70.       component, 
  71.       render, 
  72.     } = this.props 
  73.  
  74.     const match = matchPath(location.pathname, { path, exact }) 
  75.  
  76.     if (!match) 
  77.       return null 
  78.  
  79.     if (component) 
  80.       return React.createElement(component, { match }) 
  81.  
  82.     if (render) 
  83.       return render({ match }) 
  84.  
  85.     return null 
  86.   } 
  87.  
  88. class Link extends Component { 
  89.   static propTypes = { 
  90.     to: PropTypes.string.isRequired, 
  91.     replace: PropTypes.bool, 
  92.   } 
  93.   handleClick = (event) => { 
  94.     const { replaceto } = this.props 
  95.  
  96.     event.preventDefault() 
  97.     replace ? historyReplace(to) : historyPush(to
  98.   } 
  99.  
  100.   render() { 
  101.     const { to, children} = this.props 
  102.  
  103.     return ( 
  104.       <a href={to} onClick={this.handleClick}> 
  105.         {children} 
  106.       </a> 
  107.     ) 
  108.   } 

另外,React Router API 中提供了所謂 <Redirect> 組件,允許執行路由跳轉操作:

  1. class Redirect extends Component { 
  2.   static defaultProps = { 
  3.     push: false 
  4.   } 
  5.  
  6.   static propTypes = { 
  7.     to: PropTypes.string.isRequired, 
  8.     push: PropTypes.bool.isRequired, 
  9.   } 
  10.  
  11.   componentDidMount() { 
  12.     const { to, push } = this.props 
  13.  
  14.     push ? historyPush(to) : historyReplace(to
  15.   } 
  16.  
  17.   render() { 
  18.     return null 
  19.   } 

注意這個組件并沒有真實地進行界面渲染,而是僅僅進行了簡單的跳轉操作。到這里本文也就告一段落了,希望能夠幫助你去了解 React Router V4 的設計思想以及 Just Component 的接口理念。我一直說 React 會讓你成為更加優秀地開發者,而 React Router 則會是你不小的助力。

【本文是51CTO專欄作者“張梓雄 ”的原創文章,如需轉載請通過51CTO與作者聯系】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2021-02-16 10:55:02

Nodejs模塊

2016-10-21 13:03:18

androidhandlerlooper

2021-09-08 10:47:33

Flink執行流程

2016-11-29 09:38:06

Flume架構核心組件

2016-11-25 13:26:50

Flume架構源碼

2016-11-25 13:14:50

Flume架構源碼

2020-06-04 12:15:08

Pythonkafka代碼

2021-08-06 15:06:09

騰訊開源Apache

2009-12-22 13:36:39

Linux Sysfs

2009-11-30 17:33:07

微軟

2013-06-26 10:25:39

2016-11-29 16:59:46

Flume架構源碼

2022-04-05 12:59:07

源碼線程onEvent

2014-08-26 11:11:57

AsyncHttpCl源碼分析

2021-10-27 11:29:49

Linux框架內核

2011-03-15 11:33:18

iptables

2009-09-28 10:49:13

ITIL摩卡

2017-10-25 13:20:43

軟件安全模型

2011-06-08 09:22:54

Samba

2009-12-25 15:21:00

WPF構建前臺
點贊
收藏

51CTO技術棧公眾號

国产资源在线看| 国产免费a视频| 欧美激情极品| 在线影院国内精品| 少妇高潮大叫好爽喷水| 天天干免费视频| 日本怡春院一区二区| 久色乳综合思思在线视频| a级一a一级在线观看| 高清电影一区| 亚洲国产三级在线| 午夜精品短视频| 精品人妻伦一二三区久久| 久久一区二区三区四区五区 | 亚洲综合欧美综合| 精品一区二区三区中文字幕 | 国内精品久久久久久久久久久| 性欧美xxxx大乳国产app| 欧美精品日韩www.p站| 99久久久无码国产精品性| 日韩精品一区二区三区中文| 日本韩国欧美一区| 欧美综合在线播放| 在线观看免费视频你懂的| 国产精品乱人伦中文| 欧美lavv| 亚洲欧美色视频| 国产成人av影院| 成人激情视频在线播放| 亚洲男人天堂网址| 欧美一级一区| 亚洲97在线观看| 国产真实夫妇交换视频| 99久久.com| 深夜福利91大全| av男人的天堂av| 亚欧日韩另类中文欧美| 日韩大陆毛片av| 中文字幕国产高清| 久久影视精品| 欧美性大战久久| 久草在在线视频| 成人做爰视频www网站小优视频| 午夜国产不卡在线观看视频| www.18av.com| 免费av网站在线看| 国产精品国产三级国产aⅴ入口| 日本不卡二区| h视频网站在线观看| 国产午夜精品美女毛片视频| 奇米精品在线| 大乳在线免费观看| 国产欧美日韩在线| 日韩亚洲视频在线| 成年人在线看| 亚洲欧美在线观看| 国产一二三四区在线观看| 中文在线手机av| 亚洲黄色av一区| 久久人人爽人人爽人人av| 91九色porn在线资源| 五月天一区二区| 国产l精品国产亚洲区久久| 成人免费看黄| 欧美日韩www| 日韩精品在线播放视频| 91午夜精品| 亚洲精品电影在线观看| 亚洲一区二区三区蜜桃| 国产精品亚洲人成在99www| 伊人成人开心激情综合网| 久久久精品少妇| 欧美日本三区| 26uuu亚洲国产精品| 中文字幕在线天堂| 久久精品国产99| 成人在线视频网址| 精品亚洲综合| 亚洲女同一区二区| 九色自拍视频在线观看| 黄色精品视频| 日韩精品一区二区三区蜜臀| 国产特黄级aaaaa片免| 精品香蕉视频| 欧美激情视频在线观看| 国产又大又黄视频| 国内精品伊人久久久久av一坑| 国产精品国产亚洲精品看不卡15| 激情小说 在线视频| 亚洲精品欧美在线| 成人一级片网站| 国产精品亚洲欧美日韩一区在线| 亚洲第一色中文字幕| 老熟妇一区二区| 欧美国产另类| 国产国语刺激对白av不卡| av网站在线观看免费| 久久色在线观看| 一级特黄妇女高潮| 中文字幕一区久| 欧美岛国在线观看| 天天操天天舔天天射| 欧美三区视频| 国产日韩精品在线观看| 午夜成人免费影院| 亚洲精品视频在线观看网站| 激情综合网俺也去| 美女扒开腿让男人桶爽久久动漫| 日韩视频亚洲视频| 波多野结衣高清视频| 成人网在线播放| 在线精品亚洲一区二区| 在线毛片观看| 亚洲国产精品999| 黄色香蕉视频在线观看| 老牛影视一区二区三区| 国产高清自拍一区| 国产成人在线视频免费观看| 在线视频中文字幕一区二区| 欧美日韩人妻精品一区在线| 午夜精品剧场| 91夜夜揉人人捏人人添红杏| 成人免费高清在线播放| 欧美日韩免费区域视频在线观看| 制服下的诱惑暮生| 国产精品久久天天影视| 国产精品久久在线观看| 国产在线三区| 色网站国产精品| 亚洲欧美视频在线播放| 亚洲手机视频| 国产69精品久久久久9999apgf| 黄色小网站在线观看| 欧美日韩亚洲综合在线| 国产探花视频在线播放| 久久这里有精品15一区二区三区| 免费一区二区三区| 日韩国产激情| 亚洲男人av在线| 久久久久久久久久久久久久av| 成人一级片网址| 日韩亚洲欧美视频| 极品束缚调教一区二区网站| 久久久免费av| 日韩一级片免费看| 天天综合色天天综合| 岛国精品资源网站| 国产日本精品| 美女视频久久| 日产精品一区| 在线播放精品一区二区三区| 中文字幕欧美人妻精品| 国产精品久久久久久户外露出| 视色视频在线观看| 天天综合网91| 成人91视频| 2021天堂中文幕一二区在线观| 欧美精品一区二区在线观看| av中文在线播放| 99精品国产一区二区三区不卡| 干日本少妇首页| 欧美精品乱码| 91精品免费看| 免费在线中文字幕| 日韩精品一区二区三区第95| 日本中文字幕在线观看视频| 中文字幕亚洲综合久久菠萝蜜| 在线观看av免费观看| 在线日韩视频| 欧美日韩在线一二三| 日韩黄色在线| 欧美激情视频三区| 国产中文字幕在线观看| 在线播放亚洲一区| 久青草视频在线观看| 久久先锋资源网| 波多结衣在线观看| 欧美va天堂在线| 久久精品久久精品国产大片| 美女色狠狠久久| 欧美成人精品xxx| 四虎在线免费看| 欧美女优在线| 日韩一区二区三区在线| 97久久久久久久| 中文字幕在线观看一区二区| 午夜男人的天堂| 毛片基地黄久久久久久天堂| 欧美在线一区视频| 色喇叭免费久久综合网| 国模一区二区三区私拍视频| 四虎国产精品成人免费影视| 97视频在线观看免费高清完整版在线观看| 国产在线视频网| 亚洲成人黄色在线| 国产精品一区二区人人爽| 欧美日韩国产在线播放| 男人在线观看视频| 久久综合色鬼综合色| 性久久久久久久久久久久久久| 久久久久久自在自线| 大胆欧美熟妇xx| 欧美一级本道电影免费专区| 国产亚洲欧美一区二区 | 亚洲欧美卡通另类91av| 91免费视频黄| 欧美丝袜一区| 久久亚洲免费| 一区二区三区四区精品视频| 国产专区精品视频| 怡红院成人在线| 欧美怡红院视频一区二区三区| 午夜小视频在线观看| 日韩在线视频国产| 超碰国产在线| 亚洲精品中文字幕有码专区| 人妻少妇精品无码专区久久| 欧美一区二区三区四区五区| 亚洲无码精品国产| 在线欧美日韩精品| 中文字幕激情小说| 亚洲v日本v欧美v久久精品| 午夜国产福利一区二区| 国产精品亲子乱子伦xxxx裸| 亚洲精品国产熟女久久久| 99热精品国产| av av在线| 国产成人av电影在线| 野花视频免费在线观看| 国产在线一区观看| 国内外成人免费在线视频| 久久先锋影音| 国产嫩草在线观看| 丝袜脚交一区二区| 日本成人中文字幕在线| 日韩综合小视频| 亚州精品一二三区| 蜜臀99久久精品久久久久久软件 | 国产一区二区中文字幕| 99精品999| 国产精品自在欧美一区| 91香蕉视频免费看| 国产乱码精品一品二品| 两女双腿交缠激烈磨豆腐| 国产精品一品二品| 极品人妻一区二区| 成人一道本在线| 一本色道综合久久欧美日韩精品| 91在线国内视频| 好吊视频在线观看| 国产日韩av一区| 午夜黄色福利视频| 亚洲视频免费观看| 免费三片在线播放| 亚洲电影一区二区三区| 青青国产在线观看| 在线观看亚洲精品视频| 亚洲一卡二卡在线| 日韩欧美亚洲另类制服综合在线| 亚洲免费国产视频| 亚洲男人的天堂网站| 在线免费看av| 久久视频国产精品免费视频在线| 丝袜在线观看| 国产91精品高潮白浆喷水| 激情开心成人网| 成人免费直播live| 成人福利免费在线观看| 欧美极品一区二区| 99精品视频在线观看免费播放| www.99riav| 亚洲综合社区| www.污污视频| 成人av综合在线| 精品亚洲aⅴ无码一区二区三区| 日韩一区欧美一区| 国产成人精品片| 欧美日韩电影一区| 日韩性xxxx| 日韩色av导航| av日韩电影| 亚洲在线www| 欧美女优在线视频| 91传媒免费视频| 老司机午夜精品视频| 超碰91在线播放| 国产午夜精品美女毛片视频| 欧美日韩亚洲国产另类| 天堂在线中文网| 神马久久桃色视频| 国产网红女主播精品视频| 国产91亚洲精品| av动漫精品一区二区| 视频一区二区三| 亚洲激情婷婷| 欧美精品 - 色网| 久久久久久久久岛国免费| 强行糟蹋人妻hd中文| 欧美午夜精品久久久久久孕妇| 亚洲a视频在线| 中文字幕日韩在线观看| 日本不卡1234视频| 亚洲影院高清在线| 久久国产精品成人免费观看的软件| www.亚洲视频.com| 国内成人精品2018免费看| 天天操天天干天天操天天干| 亚洲第一在线综合网站| 91成人国产综合久久精品| 亚洲欧美999| a国产在线视频| 91观看网站| 亚洲成人精品| 中文字幕国产免费| 国产亚洲精品7777| 亚洲另类欧美日韩| 亚洲韩国青草视频| 大桥未久在线播放| 91久色国产| 亚洲午夜精品一区 二区 三区| 麻豆三级在线观看| 国产亚洲精久久久久久| www欧美在线| 亚洲国产日韩精品在线| 国模雨婷捆绑高清在线| 91热精品视频| 久久久久免费av| 在线观看亚洲色图| 国产精品欧美极品| 中文字幕在线播放不卡| 在线成人一区二区| 黄色欧美视频| 亚洲图片都市激情| 久久精品国产77777蜜臀| 亚洲色图27p| 欧美日韩一区三区| 日本在线免费播放| 国产在线精品一区免费香蕉 | 久久久久久久激情视频| 亚洲一区二区三区中文字幕在线观看 | 日韩免费网站| 91欧美精品午夜性色福利在线| 久久一区91| 亚洲精品中文字幕乱码无线| 综合在线观看色| 国产黄色美女视频| 欧美激情在线视频二区| 澳门久久精品| 日韩国产欧美亚洲| 久久久美女毛片| 狠狠狠狠狠狠狠| 中文字幕日韩免费视频| 国产激情一区| 日本熟妇人妻xxxx| 久久久噜噜噜久久人人看 | 欧美三级日本三级| 狠久久av成人天堂| 国产一区二区在线观看免费播放| 亚洲欧洲日本mm| 大黑人交xxx极品hd| 色就色 综合激情| av在线天堂播放| 91久久久久久久久| 欧美日本一区二区高清播放视频| 扒开伸进免费视频| 日韩欧美a级成人黄色| yes4444视频在线观看| 91免费电影网站| 精品二区久久| 国产高清一区二区三区四区| 欧美夫妻性生活| 1区2区在线| 亚洲精品9999| 国产98色在线|日韩| 在线精品免费视| 久久精品国产96久久久香蕉| 午夜视频在线观看精品中文| 春日野结衣av| 中文字幕在线观看一区二区| 色一情一乱一乱一区91av| 日韩av电影院| 欧美二区视频| 成年人免费观看视频网站| 日韩限制级电影在线观看| 日本不卡免费高清视频在线| 在线不卡视频一区二区| 92精品国产成人观看免费| 亚洲天堂男人网| 97在线观看免费高清| 91精品国产麻豆国产在线观看| 一级欧美一级日韩片| 欧美日本一区二区三区四区| av免费不卡| 亚洲成年人专区| 久久久久久久综合狠狠综合| 午夜久久久久久久久久| 国产精品第2页| 中文日韩欧美| 超碰手机在线观看| 在线电影欧美日韩一区二区私密|