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

讓性能提升56%的Vue3.5響應式重構之“版本計數”

開發 前端
版本計數主要有四個版本:全局變量globalVersion、dep.version、link.version和computed.globalVersion。dep.version和link.version如果不相等就說明當前響應式變量的值改變了,就需要讓Sub訂閱者進行更新。

前言

Vue3.5響應式重構主要分為兩部分:雙向鏈表和版本計數。在上一篇文章中我們講了 雙向鏈表 ,這篇文章我們接著來講版本計數。

版本計數

在上篇 雙向鏈表 文章中我們知道了新的響應式模型中主要分為三個部分:Sub訂閱者、Dep依賴、Link節點。

  • Sub訂閱者:主要有watchEffect、watch、render函數、computed等。
  • Dep依賴:主要有ref、reactive、computed等響應式變量。
  • Link節點:連接Sub訂閱者和Dep依賴之間的橋梁,Sub訂閱者想訪問Dep依賴只能通過Link節點,同樣Dep依賴想訪問Sub訂閱者也只能通過Link節點。

細心的小伙伴可能發現了computed計算屬性不僅是Sub訂閱者還是Dep依賴。 原因是computed可以像watchEffect那樣監聽里面的響應式變量,當響應式變量改變后會觸發computed的回調。

還可以將computed的返回值當做ref那樣的普通響應式變量去使用,所以我們才說computed不僅是Sub訂閱者還是Dep依賴。

版本計數中由4個version實現,分別是:全局變量globalVersion、dep.version、link.version和computed.globalVersion。

  • globalVersion是一個全局變量,初始值為0,僅有響應式變量改變后才會觸發globalVersion++。
  • dep.version是在dep依賴上面的一個屬性,初始值是0。當dep依賴是ref這種普通響應式變量,僅有響應式變量改變后才會觸發dep.version++。當computed計算屬性作為dep依賴時,只有等computed最終計算出來的值改變后才會觸發dep.version++。
  • link.version是Link節點上面的一個屬性,初始值是0。每次響應式更新完了后都會保持和dep.version的值相同。在響應式更新前就是通過link.version和dep.version的值是否相同判斷是否需要更新。
  • computed.globalVersion:計算屬性上面的版本,如果computed.globalVersion === globalVersion說明沒有響應式變量改變,計算屬性的回調就不需要重新執行。

而版本計數最大的受益者就是computed計算屬性,這篇文章接下來我們將以computed舉例講解。

看個例子

我們來看個簡單的demo,代碼如下:

<template>
  <p>{{ doubleCount }}</p>
  <button @click="flag = !flag">切換flag</button>
  <button @click="count1++">count1++</button>
  <button @click="count2++">count2++</button>
</template>

<script setup>
import { computed, ref } from "vue";
const count1 = ref(1);
const count2 = ref(10);
const flag = ref(true);

const doubleCount = computed(() => {
  console.log("computed");
  if (flag.value) {
    return count1.value * 2;
  } else {
    return count2.value * 2;
  }
});
</script>

在computed中根據flag.value的值去決定到底返回count1.value * 2還是count2.value * 2。

那么問題來了,當flag的值為true時,點擊count2++按鈕,console.log("computed")會執行打印嗎?也就是doubleCount的值會重新計算嗎?

答案是:不會。雖然count2也是computed中使用到的響應式變量,但是他不參與返回值的計算,所以改變他不會導致computed重新計算。

有的同學想問為什么能夠做到這么精細的控制呢?這就要歸功于版本計數了,我們接下來會細講。

依賴觸發

還是前面那個demo,初始化時flag的值是true,所以在computed中會對count1變量進行讀操作,然后觸發get攔截。count1這個ref響應式變量就是由RefImpl類new出來的一個對象,代碼如下:

class RefImpl {
  dep: Dep = new Dep();
  get value() {
    this.dep.track()
  }
  set value() {
    this.dep.trigger();
  }
}

在get攔截中會執行this.dep.track(),其中dep是由Dep類new出來的對象,代碼如下

class Dep {
  version = 0;
  track() {
    let link = new Link(activeSub, this);
    // ...省略
  }
  trigger() {
    this.version++;
    globalVersion++;
    this.notify();
  }
}

在track方法中使用Link類new出來一個link對象,Link類代碼如下:

class Link {
  version: number

  /**
   * Pointers for doubly-linked lists
   */
  nextDep?: Link
  prevDep?: Link
  nextSub?: Link
  prevSub?: Link
  prevActiveLink?: Link

  constructor(
    public sub: Subscriber,
    public dep: Dep,
  ) {
    this.version = dep.version
    this.nextDep =
      this.prevDep =
      this.nextSub =
      this.prevSub =
      this.prevActiveLink =
        undefined
  }
}

這里我們只關注Link中的version屬性,其他的屬性在上一篇雙向鏈表文章中已經講過了。

在constructor中使用dep.version給link.version賦值,保證dep.version和link.version的值是相等的,也就是等于0。因為dep.version的初始值是0,接著就會講。

當我們點擊count1++按鈕時會讓響應式變量count1的值自增。因為count1是一個ref響應式變量,所以會觸發其set攔截。代碼如下:

class RefImpl {
  dep: Dep = new Dep();
  get value() {
    this.dep.track()
  }
  set value() {
    this.dep.trigger();
  }
}

在set攔截中執行的是this.dep.trigger(),trigger函數代碼如下:

class Dep {
  version = 0;
  track() {
    let link = new Link(activeSub, this);
    // ...省略
  }
  trigger() {
    this.version++;
    globalVersion++;
    this.notify();
  }
}

前面講過了globalVersion是一個全局變量,初始值為0。

dep上面的version屬性初始值也是0。

在trigger中分別執行了this.version++和globalVersion++,這里的this就是指向的dep。執行完后dep.version和globalVersion的值就是1了。而此時link.version的值依然還是0,這個時候dep.version和link.version的值就已經不相等了。

接著就是執行notify方法按照新的響應式模型進行通知訂閱者進行更新,我們這個例子此時新的響應式模型如下圖:

圖片圖片

如果修改的響應式變量會觸發多個訂閱者,比如count1變量被多個watchEffect使用,修改count1變量的值就需要觸發多個訂閱者的更新。notify方法中正是將多個更新操作放到一個批次中處理,從而提高性能。由于篇幅有限我們就不去細講notify方法的內容,你只需要知道執行notify方法就會觸發訂閱者的更新。

(這兩段是notify方法內的邏輯)按照正常的邏輯如果count1變量的值改變,就可以通過Link2節點找到Sub1訂閱者,然后執行訂閱者的notify方法從而進行更新。

如果我們的Sub1訂閱者是render函數,是這個正常的邏輯。但是此時我們的Sub1訂閱者是計算屬性doubleCount,這里會有一個優化,如果訂閱者是一個計算屬性,觸發其更新時不會直接執行計算屬性的回調函數,而是直接去通知計算屬性的訂閱者去更新,在更新前才會去執行計算屬性的回調函數(這個接下來的文章會講)。代碼如下:

if (link.sub.notify()) {
  // if notify() returns `true`, this is a computed. Also call notify
  // on its dep - it's called here instead of inside computed's notify
  // in order to reduce call stack depth.
  link.sub.dep.notify()
}

link.sub.notify()的執行結果是true就代表當前的訂閱者是計算屬性,然后就會觸發計算屬性“作為依賴”時對應的訂閱者。我們這里的計算屬性doubleCount是在template中使用,所以計算屬性doubleCount的訂閱者就是render函數。

所以這里就是調用link.sub.notify()不會觸發計算屬性doubleCount中的回調函數重新執行,而是去觸發計算屬性doubleCount的訂閱者,也就是render函數。在執行render函數之前會再去通過臟檢查(依靠版本計數實現)去判斷是否需要重新執行計算屬性的回調,如果需要執行計算屬性的回調那么就去執行render函數重新渲染。

臟檢查

所有的Sub訂閱者內部都是基于ReactiveEffect類去實現的,調用訂閱者的notify方法通知更新實際底層就是在調用ReactiveEffect類中的runIfDirty方法。代碼如下:

class ReactiveEffect<T = any> implements Subscriber, ReactiveEffectOptions {
  /**
   * @internal
   */
  runIfDirty(): void {
    if (isDirty(this)) {
      this.run();
    }
  }
}

在runIfDirty方法中首先會調用isDirty方法判斷當前是否需要更新,如果返回true,那么就執行run方法去執行Sub訂閱者的回調函數進行更新。如果是computed、watch、watchEffect等訂閱者調用run方法就會執行其回調函數,如果是render函數這種訂閱者調用run方法就會再次執行render函數。

調用isDirty方法時傳入的是this,值得注意的是this是指向ReactiveEffect實例。而ReactiveEffect又是繼承自Subscriber訂閱者,所以這里的this是指向的是訂閱者。

前面我們講過了,修改響應式變量count1的值時會通知作為訂閱者的doubleCount計算屬性。當通知作為訂閱者的計算屬性更新時不會去像watchEffect這樣的訂閱者一樣去執行其回調,而是去通知計算屬性作為Dep依賴時訂閱他的訂閱者進行更新。在這里計算屬性doubleCount是在template中使用,所以他的訂閱者是render函數。

所以修改count1變量執行runIfDirty時此時觸發的訂閱者是作為Sub訂閱者的render函數,也就是說此時的this是render函數!!

我們來看看isDirty是如何進行臟檢查,代碼如下:

function isDirty(sub: Subscriber): boolean {
  for (let link = sub.deps; link; link = link.nextDep) {
    if (
      link.dep.version !== link.version ||
      (link.dep.computed &&
        (refreshComputed(link.dep.computed) ||
          link.dep.version !== link.version))
    ) {
      return true;
    }
  }
  return false;
}

這里就涉及到我們上一節講過的雙向鏈表了,回顧一下前面講過的響應式模型圖,如下圖:

圖片圖片

此時的sub訂閱者是render函數,也就是圖中的Sub2。sub.deps是指向指向Sub2訂閱者X軸(橫向)上面的Link節點組成的隊列的頭部,link.nextDep就是指向X軸上面下一個Link節點,通過Link節點就可以訪問到對應的Dep依賴。

在這里render函數對應的訂閱者Sub2在X軸上面只有一個節點Link3。

這里的for循環就是去便利Sub訂閱者在X軸上面的所有Link節點,然后在for循環內部去通過Link節點訪問到對應的Dep依賴去做版本計數的判斷。

這里的for循環內部的if語句判斷主要分為兩部分:

if (
  link.dep.version !== link.version ||
  (link.dep.computed &&
    (refreshComputed(link.dep.computed) ||
      link.dep.version !== link.version))
) {
  return true;
}

這兩部分中只要有一個是true,那么就說明當前Sub訂閱者需要更新,也就是執行其回調。

我們來看看第一個判斷:

link.dep.version !== link.version

還記得我們前面講過嗎,初始化時會保持dep.version和link.version的值相同。每次響應式變量改變時走到set攔截中,在攔截中會去執行dep.version++,執行完了后此時dep.version和link.version的值就已經不相同了,在這里就能知道此時響應式變量改變過了,需要通知Sub訂閱者更新執行其回調。

常規情況下Dep依賴是一個ref變量、Sub訂閱者是wachEffect這種確實第一個判斷就可以滿足了。

但是我們這里的link.dep是計算屬性doubleCount,計算屬性是由ComputedRefImpl類new出來的對象,簡化后代碼如下:

class ComputedRefImpl<T = any> implements Subscriber {
  _value: any = undefined;
  readonly dep: Dep = new Dep(this);
  globalVersion: number = globalVersion - 1;
  get value(): T {
    // ...省略
  }
  set value(newValue) {
    // ...省略
  }
}

ComputedRefImpl繼承了Subscriber類,所以說他是一個訂閱者。同時還有get和set攔截,以及初始化一個計算屬性時也會去new一個對應的Dep依賴。

還有一點值得注意的是計算屬性上面的computed.globalVersion屬性初始值為globalVersion - 1,默認是不等于globalVersion的,這是為了第一次執行計算屬性時能夠去觸發執行計算屬性的回調,這個在后面的refreshComputed函數中會講。

我們是直接修改的count1變量,在count1變量的set攔截中觸發了dep.version++,但是并沒有修改計算屬性對應的dep.version。所以當計算屬性作為依賴時單純的使用link.dep.version !== link.version 就不能滿足需求了,需要使用到第二個判斷:

(link.dep.computed &&
    (refreshComputed(link.dep.computed) ||
      link.dep.version !== link.version))

在第二個判斷中首先判斷當前當前的Dep依賴是不是計算屬性,如果是就調用refreshComputed函數去執行計算屬性的回調。然后判斷計算屬性的結果是否改變,如果改變了在refreshComputed函數中就會去執行link.dep.version++,所以執行完refreshComputed函數后link.dep.version和link.version的值就不相同了,表示計算屬性的值更新了,當然就需要執行依賴計算屬性的render函數啦。

refreshComputed函數

我們來看看refreshComputed函數的代碼,簡化后的代碼如下:

function refreshComputed(computed: ComputedRefImpl): undefined {
  if (computed.globalVersion === globalVersion) {
    return;
  }
  computed.globalVersion = globalVersion;

  const dep = computed.dep;
  try {
    prepareDeps(computed);
    const value = computed.fn(computed._value);
    if (dep.version === 0 || hasChanged(value, computed._value)) {
      computed._value = value;
      dep.version++;
    }
  } catch (err) {
    dep.version++;
    throw err;
  } finally {
    cleanupDeps(computed);
  }
}

首先會去判斷computed.globalVersion === globalVersion是否相等,如果相等就說明根本就沒有響應式變量改變,那么當然就無需去重新執行計算屬性回調。

還記得我們前面講過每當響應式變量改變后觸發set攔截是都會執行globalVersion++嗎?所以這里就可以通過computed.globalVersion === globalVersion判斷是否有響應式變量改變,如果沒有說明計算屬性的值肯定就沒有改變。

接著就是執行computed.globalVersion = globalVersion將computed.globalVersion的值同步為globalVersion,為了下次判斷是否需要重新執行計算屬性做準備。

在try中會先去執行prepareDeps函數,這個先放放接下來講,先來看看try中其他的代碼。

首先調用const value = computed.fn(computed._value)去重新執行計算屬性的回調函數拿到計算屬性新的返回值value。

接著就是執行if (dep.version === 0 || hasChanged(value, computed._value))

我們前面講過了dep上面的version默認值為0,這里的dep.version === 0說明是第一次渲染計算屬性。接著就是使用hasChanged(value, computed._value)判斷計算屬性新的值和舊的值相比較是否有修改。

上面這兩個條件滿足一個就執行if里面的內容,將新得到的計算屬性的值更新上去,并且執行dep.version++。因為前面講過了在外面會使用link.dep.version !== link.version判斷dep的版本是否和link上面的版本是否相同,如果不相等就執行render函數。

這里由于計算屬性的值確實改變了,所以會執行dep.version++,dep的版本和link上面的版本此時就不同了,所以就會被標記為dirty,從而執行render函數。

如果執行計算屬性的回調函數出錯了,同樣也執行一次dep.version++。

最后就是剩余執行計算屬性回調函數之前調用的prepareDeps和finally調用的cleanupDeps函數沒講了。

更新響應式模型

回顧一下demo的代碼:

<template>
  <p>{{ doubleCount }}</p>
  <button @click="flag = !flag">切換flag</button>
  <button @click="count1++">count1++</button>
  <button @click="count2++">count2++</button>
</template>

<script setup>
import { computed, ref } from "vue";
const count1 = ref(1);
const count2 = ref(10);
const flag = ref(true);

const doubleCount = computed(() => {
  console.log("computed");
  if (flag.value) {
    return count1.value * 2;
  } else {
    return count2.value * 2;
  }
});
</script>

當flag的值為true時,對應的響應式模型前面我們已經講過了,如下圖:

圖片圖片

如果我們將flag的值設置為false呢?此時的計算屬性doubleCount就不再依賴于響應式變量count1,而是依賴于響應式變量count2。小伙伴們猜猜此時的響應式模型應該是什么樣的呢?

圖片圖片

現在多了一個count2變量對應的Link4,原本Link1和Link2之間的連接也因為計算屬性不再依賴于count1變量后,他們倆之間的連接也沒有了,轉而變成了Link1和Link4之間建立連接。

前面沒有講的prepareDeps和cleanupDeps函數就是去掉Link1和Link2之間的連接。

prepareDeps函數代碼如下:

function prepareDeps(sub: Subscriber) {
  // Prepare deps for tracking, starting from the head
  for (let link = sub.deps; link; link = link.nextDep) {
    // set all previous deps' (if any) version to -1 so that we can track
    // which ones are unused after the run
    link.version = -1
    // store previous active sub if link was being used in another context
    link.prevActiveLink = link.dep.activeLink
    link.dep.activeLink = link
  }
}

這里使用for循環遍歷計算屬性Sub1在X軸上面的Link節點,也就是Link1和Link2,并且將這些Link節點的version屬性設置為-1。

當flag的值設置為false后,重新執行計算屬性doubleCount中的回調函數時,就會對回調函數中的所有響應式變量進行讀操作。從而再次觸發響應式變量的get攔截,然后執行track方法進行依賴收集。注意此時新收集了一個響應式變量count2。收集完成后響應式模型圖如下圖:

圖片圖片

從上圖中可以看到雖然計算屬性雖然不再依賴count1變量,但是count1變量變量對應的Link2節點還在隊列的連接上。

我們在prepareDeps方法中將計算屬性依賴的所有Link節點的version屬性都設置為-1,在track方法收集依賴時會執行這樣一行代碼,如下:

class Dep {
  track() {
    if (link === undefined || link.sub !== activeSub) {
      // ...省略
    } else if (link.version === -1) {
      link.version = this.version;
      // ...省略
    }
  }
}

如果link.version === -1,那么就將link.version的值同步為dep.version的值。

只有計算屬性最新依賴的響應式變量才會觸發track方法進行依賴收集,從而將對應的link.version從-1更新為dep.version。

而變量count1現在已經不會觸發track方法了,所以變量count1對應的link.version的值還是-1。

最后就是執行cleanupDeps函數將link.version的值還是-1的響應式變量(也就是不再使用的count1變量)對應的Link節點,從雙向鏈表中給干掉。代碼如下:

function cleanupDeps(sub: Subscriber) {
  // Cleanup unsued deps
  let head;
  let tail = sub.depsTail;
  let link = tail;
  while (link) {
    const prev = link.prevDep;
    if (link.version === -1) {
      if (link === tail) tail = prev;
      // unused - remove it from the dep's subscribing effect list
      removeSub(link);
      // also remove it from this effect's dep list
      removeDep(link);
    } else {
      // The new head is the last node seen which wasn't removed
      // from the doubly-linked list
      head = link;
    }

    // restore previous active link if any
    link.dep.activeLink = link.prevActiveLink;
    link.prevActiveLink = undefined;
    link = prev;
  }
  // set the new head & tail
  sub.deps = head;
  sub.depsTail = tail;
}

遍歷Sub1計算屬性橫向隊列(X軸)上面的Link節點,當link.version === -1時,說明這個Link節點對應的Dep依賴已經不被計算屬性所依賴了,所以執行removeSub和removeDep將其從雙向鏈表中移除。

執行完cleanupDeps函數后此時的響應式模型就是我們前面所提到的樣子,如下圖:

圖片圖片

總結

版本計數主要有四個版本:全局變量globalVersion、dep.version、link.version和computed.globalVersion。dep.version和link.version如果不相等就說明當前響應式變量的值改變了,就需要讓Sub訂閱者進行更新。

如果是計算屬性作為Dep依賴時就不能通過dep.version和link.version去判斷了,而是執行refreshComputed函數進行判斷。在refreshComputed函數中首先會判斷globalVersion和computed.globalVersion是否相等,如果相等就說明并沒有響應式變量更新。如果不相等那么就會執行計算屬性的回調函數,拿到最新的值后去比較計算屬性的值是否改變。并且還會執行prepareDeps和cleanupDeps函數將那些計算屬性不再依賴的響應式變量對應的Link節點從雙向鏈表中移除。

最后說一句,版本計數最大的贏家應該是computed計算屬性,雖然引入版本計數后代碼更難理解了。但是整體流程更加優雅,以及現在只需要通過判斷幾個version是否相等就能知道訂閱者是否需要更新,性能當然也更好了。

責任編輯:武曉燕 來源: 前端歐陽
相關推薦

2024-10-14 12:56:28

2024-11-13 09:57:22

2024-09-02 08:48:45

2024-09-04 11:42:17

Vue3.5源碼API

2024-09-05 09:17:14

2025-02-17 08:58:06

2024-07-11 09:00:13

2024-09-02 08:31:46

2025-05-29 01:55:00

Vue3.5API性能

2025-05-15 08:10:00

Vue 3.5Vue

2009-04-20 08:59:49

Firefox性能測試瀏覽器

2021-01-08 09:40:40

優化VUE性能

2021-03-09 22:29:46

Vue 響應式API

2017-08-30 17:10:43

前端JavascriptVue.js

2009-07-01 09:49:11

Firefox 3.5

2022-04-02 09:56:41

Vue2響應式系統

2022-04-14 08:46:46

響應式系統js

2022-04-06 07:28:47

數組響應式系統

2020-06-09 11:35:30

Vue 3響應式前端

2019-07-01 13:34:22

vue系統數據
點贊
收藏

51CTO技術棧公眾號

国语自产偷拍精品视频偷| 欧美在线视频日韩| 精品久久中出| av首页在线观看| 91成人影院| 日韩国产高清视频在线| 亚洲福利精品视频| 日韩一级片免费看| 美女久久网站| 久久色免费在线视频| 性色av蜜臀av浪潮av老女人| 日本另类视频| 亚洲自拍偷拍综合| 日韩精品电影网站| 人妻妺妺窝人体色www聚色窝| 午夜精品影院| 国产亚洲一区二区在线| 粗大的内捧猛烈进出视频| 不卡一二三区| 国产欧美精品一区| 国产精品亚洲一区| 6—12呦国产精品| 999视频精品| 亚洲精品黄网在线观看| 欧美日韩国产精品激情在线播放| 天堂av网在线| 国产精品99久久久| 97人人做人人爱| 一区视频免费观看| 大胆日韩av| 亚洲开心激情网| 人人干人人干人人| 亚洲天堂电影| 亚洲成av人片一区二区三区| 先锋亚洲精品| 中文字幕在线不卡一区| 久久国产精品一区二区三区| 亚洲一二区视频| 久久一区二区三区四区五区 | 成人看片黄a免费看在线| 国产啪精品视频| 亚洲欧美在线aaa| 亚洲电影天堂av| 日本美女视频一区| 亚洲国产成人精品综合99| 精品乱码一区二区三区四区| 亚洲成人1区2区| 狠狠精品干练久久久无码中文字幕 | 国产精品成人一区二区三区夜夜夜| 亚洲最大激情中文字幕| 亚洲天堂网在线视频| 日韩专区一卡二卡| 555www成人网| 在线观看中文字幕视频| 夜夜嗨av一区二区三区网站四季av| 国产一区二区久久精品| 天堂久久精品忘忧草| 亚洲妇女av| 亚洲精品一区二区久| 毛毛毛毛毛毛毛片123| 欧美系列精品| 欧美一区二区三区在线观看 | 九色综合国产一区二区三区| 91精品国产91久久久久久吃药| 久久久久亚洲AV成人无在| 国产成人一区| 最好看的2019的中文字幕视频| 极品白嫩的小少妇| 成人在线高清| 欧美性xxxx在线播放| 男人的天堂99| 国产一线二线在线观看| 亚洲永久免费视频| 国产午夜大地久久| **在线精品| 欧美在线视频你懂得| 亚洲黄色av片| 成人国产一区| 日韩一区二区三区高清免费看看 | 久久99久久99精品免观看软件| 亚洲一区二区三区四区的 | 杨幂一区二区国产精品| 97人人做人人爽香蕉精品| 欧美电影一区二区三区| www.黄色网| 九九亚洲精品| 久久视频在线播放| 国产无码精品在线播放| 久久久777| 亚洲一区二区中文| 五月天久久久久久| 中文字幕一区二区三区乱码在线| 日韩欧美在线观看强乱免费| 水莓100在线视频| 久久久久久久国产精品影院| 中文字幕一区二区三区四区五区六区| 91在线高清| 亚洲人吸女人奶水| 国产精品欧美激情在线观看| 欧美一级在线| 国产网站欧美日韩免费精品在线观看 | 亚洲午夜天堂| 欧美精品三级日韩久久| 日韩成人av一区二区| 99久久99视频只有精品| 91国产精品91| 国产特级黄色片| 国产在线一区二区综合免费视频| 91欧美精品午夜性色福利在线| 亚洲最大成人av| www.日韩在线| 日本三级中文字幕在线观看| 澳门成人av网| 精品视频一区三区九区| 色噜噜狠狠永久免费| 国产亚洲成av人片在线观黄桃| 亚洲国产精品国自产拍av秋霞| 91av在线免费| 免费欧美一区| 中文字幕av日韩| 国产精品suv一区二区三区| 精品一区二区精品| 香蕉久久夜色| 久久青青视频| 亚洲国产精品悠悠久久琪琪| 黑鬼狂亚洲人videos| 久久精品动漫| 国产在线播放一区二区| 四虎亚洲精品| 91精品国产欧美一区二区| 影音先锋男人在线| 久久久精品五月天| 91九色精品视频| 阿v免费在线观看| 色综合久久九月婷婷色综合| 欧产日产国产精品98| 第四色成人网| 国产精品美女www| 国产高清视频在线观看| 福利微拍一区二区| 精品国产一区在线| 韩日精品视频| 国产传媒欧美日韩| 蜜臀av在线| 精品女同一区二区| 国产一级理论片| 成人午夜视频在线观看| 亚洲 国产 欧美一区| 永久免费网站在线| 日韩一级免费观看| 久草视频在线免费看| 国产福利视频一区二区三区| 欧美一区二区三区在线播放| 老司机免费在线视频| 欧美日韩三级一区| 亚洲波多野结衣| 欧美亚洲三区| 日韩电影天堂视频一区二区| 成人影院在线免费观看| 三级精品视频久久久久| 三级黄色在线视频| 99久免费精品视频在线观看 | 国产精品av久久久久久麻豆网| 欧美中文字幕在线视频| 青青草观看免费视频在线| 色婷婷久久久亚洲一区二区三区| 91传媒理伦片在线观看| 色一区二区三区四区| 91精品国产综合久久久久久久久 | 午夜肉伦伦影院| 精品黄色一级片| 国产三级精品网站| 日本伦理一区二区| 亚洲电影在线观看| 一区二区三区麻豆| 亚洲人精品一区| av网页在线观看| 久久婷婷一区| 91手机视频在线| 国内精品麻豆美女在线播放视频| 欧美俄罗斯乱妇| av女名字大全列表| 精品视频全国免费看| 激情综合网五月天| 国产亚洲欧洲997久久综合| 手机免费看av网站| 国产日韩欧美一区在线 | 亚洲无码精品国产| 亚洲精品乱码久久久久| 中文在线永久免费观看| 日本vs亚洲vs韩国一区三区| 中文字幕人妻熟女人妻洋洋| 思热99re视热频这里只精品| 欧美亚洲视频在线观看| 日本激情在线观看| 亚洲福利精品在线| 亚洲综合精品在线| 欧美日韩国产一区二区| 亚洲人做受高潮| 九色综合国产一区二区三区| 欧美成人三级在线视频| 水蜜桃久久夜色精品一区| 精品在线不卡| 精品视频一二| 国产精品九九九| 91福利在线尤物| 久久精品99久久久香蕉| 免费毛片在线| 欧美丝袜丝交足nylons图片| 制服丨自拍丨欧美丨动漫丨| 精品亚洲成a人在线观看| 无码人妻h动漫| 国产一区美女| 在线视频亚洲自拍| 精品国产一区二区三区久久久蜜臀 | 欧美暴力调教| 国模吧一区二区| 高清免费电影在线观看| 永久免费毛片在线播放不卡| 视频一区二区免费| 日韩免费在线观看| 91国内精品久久久| 欧美系列一区二区| 一级成人黄色片| 五月天激情小说综合| 69av视频在线| 久久午夜免费电影| 国产精品入口麻豆| 国产成人无遮挡在线视频| 99久久99精品| 久久99国产精品久久| 在线免费视频a| 欧美在线免费| 国产精品12p| 天堂美国久久| 久久久精品国产一区二区三区| 色猫猫成人app| 国产脚交av在线一区二区| 九色porny丨入口在线| 久久久久久69| 福利写真视频网站在线| 欧美日本啪啪无遮挡网站| caoporn免费在线视频| 久久九九精品99国产精品| 欧美人xxx| 久久精品亚洲一区| 男人天堂综合| 亚洲欧美另类自拍| 青青草观看免费视频在线| 亚洲美女av在线| 每日更新av在线播放| 亚洲欧美精品在线| a视频网址在线观看| 日韩一二三在线视频播| 婷婷免费在线视频| 久久婷婷国产麻豆91天堂| www.久久久久.com| 欧美精品xxx| 中文在线中文资源| 国产成人精品a视频一区www| 国产精品99| 91久久精品国产91性色| 欧美xxxxxx| 国产精品爽黄69天堂a| 天天综合在线观看| 超碰在线观看97| 日本欧美三级| 欧美一区1区三区3区公司| www.国产精品一区| 久久久久久艹| 成人3d动漫在线观看| 黄色免费高清视频| 亚洲先锋成人| mm1313亚洲国产精品无码试看| 日韩图片一区| 国产天堂在线播放| 美女一区二区视频| 亚洲成人激情小说| 国产一区二区在线观看视频| 国产精品果冻传媒| 久久亚洲影视婷婷| 国产伦精品一区二区三区妓女| 高清不卡一区二区| 欧美熟妇精品黑人巨大一二三区| 99久久精品免费观看| 91视频免费在观看| 亚洲国产精品一区二区www在线| 538精品在线视频| 精品福利一区二区| 亚洲影院一区二区三区| 亚洲第一精品福利| 8888四色奇米在线观看| 欧美激情影音先锋| 91精品国产66| 国产综合久久久久久| 欧美a级网站| 国产高清精品软男同| 99亚洲视频| 亚洲av无码久久精品色欲| 国产寡妇亲子伦一区二区| 熟女人妻在线视频| 久久久91精品国产一区二区精品 | 色www免费视频| av中文字幕亚洲| 99热这里只有精品4| 精品福利在线观看| 国产精品探花视频| 日韩一区二区三区电影在线观看 | 日本在线播放视频| 黑人巨大精品欧美一区二区一视频| 黄色片网站在线免费观看| 日本高清无吗v一区| 黄色av一区二区三区| 色妞一区二区三区| 天堂中文av在线资源库| 岛国一区二区三区高清视频| 日韩欧美综合| 国产a级片免费观看| 成人97人人超碰人人99| 国产人妻精品一区二区三区不卡| 亚洲国产成人av网| 国产一级免费视频| 亚洲大尺度美女在线| 99热国产在线| 成人美女免费网站视频| 国产91精品对白在线播放| 亚洲免费久久| 日韩av电影一区| 丰满少妇高潮一区二区| 欧美日韩精品二区| 老司机午夜福利视频| 欧美劲爆第一页| 亚洲天堂1区| 日韩电影免费观看在| 久久婷婷丁香| 69视频在线观看免费| 91福利在线观看| 国产视频第一页在线观看| 911国产网站尤物在线观看| 欧美日日夜夜| 自拍偷拍视频在线| 狠狠色丁香婷婷综合| 免费观看一级一片| 姬川优奈aav一区二区| 天堂网www中文在线| 国产成人av网址| 成人综合专区| 在线播放免费视频| 亚洲男女毛片无遮挡| 国产成人三级在线播放| 欧美乱妇40p| a级日韩大片| 久久免费视频3| 久久蜜臀中文字幕| 中文永久免费观看| 日韩中文在线中文网三级| 亚洲我射av| 久久综合久久久久| av色综合久久天堂av综合| 精品成人av一区二区在线播放| 欧美一区二区二区| 四虎影视成人| 玖玖玖精品中文字幕| 日日骚欧美日韩| 成年人视频软件| 日韩欧美在线一区二区三区| 黄色影院在线看| 欧美精品一区二区视频| 热久久国产精品| 欧美精品xxxxx| 日韩高清av在线| av成人亚洲| 精品久久久无码人妻字幂| 99在线精品视频| 中文字幕人成人乱码亚洲电影| 亚洲天堂色网站| 99精品女人在线观看免费视频| 亚洲国产精品综合| 国产成人在线网站| 欧美成人综合色| 亚洲欧美成人一区二区在线电影| 国模私拍一区二区国模曼安| 久久久久欧美| 激情欧美一区二区| 国产成人在线免费观看视频| 中文字幕国产亚洲| 成人台湾亚洲精品一区二区| 精品www久久久久奶水| 一区二区三区资源| 国产综合在线观看| 96久久精品| 热久久国产精品| 中文字幕91视频| 亚洲国产精品99久久| 伊人久久大香伊蕉在人线观看热v| 在线精品亚洲一区二区| 99久久er热在这里只有精品66| 久久夜色精品亚洲| 中文字幕欧美日韩| 日韩深夜福利|