国产无遮挡裸体免费直播视频,久久精品国产蜜臀av,动漫在线视频一区二区,欧亚日韩一区二区三区,久艹在线 免费视频,国产精品美女网站免费,正在播放 97超级视频在线观看,斗破苍穹年番在线观看免费,51最新乱码中文字幕

Vue3源碼通過(guò)render?patch?了解diff

 更新時(shí)間:2022年11月03日 10:51:46   作者:ChrisLey  
這篇文章主要為大家介紹了Vue3源碼系列通過(guò)render及patch了解diff原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

上一篇中,我們理清了createApp走的流程,最后通過(guò)createAppAPI創(chuàng)建了app。雖然app上的各種屬性和方法也都已經(jīng)有所了解,但其中的mountunmount方法,都是通過(guò)調(diào)用render函數(shù)來(lái)完成的。盡管我們很好奇render函數(shù)的故事,可是baseCreateRenderer函數(shù)有2000+行,基本都和render相關(guān),因此拆解到本文里敘述,以下方法都定義在baseCreateRenderer函數(shù)中。

render

render也不神秘了,畢竟在上一篇文章中露過(guò)面,當(dāng)然這里也順帶提一下 baseCreateRenderer從參數(shù)options中解構(gòu)的一些方法,基本都是些增刪改查、復(fù)制節(jié)點(diǎn)的功能,見(jiàn)名知義了。主要看看render,接收vnode、containerisSvg三個(gè)參數(shù)。調(diào)用unmount卸載或者調(diào)用patch進(jìn)行節(jié)點(diǎn)比較從而繼續(xù)下一步。

  • 判斷vnode是否為null。如果對(duì)上一篇文章還有印象,那么就會(huì)知道,相當(dāng)于是判斷調(diào)用的是app.mount還是app.unmount方法,因?yàn)?code>app.unmount方法傳入的vnode就是null。那么這里對(duì)應(yīng)的就是在app.unmount里使用unmount函數(shù)來(lái)卸載;而在app.mount里進(jìn)行patch比較。
  • 調(diào)用flushPostFlushCbs(),其中的單詞Post的含義,看過(guò)第一篇講解watch的同學(xué)也許能猜出來(lái),表示執(zhí)行時(shí)機(jī)是在組件更新后。這個(gè)函數(shù)便是執(zhí)行組件更新后的一些回調(diào)。
  • vnode掛到container上,即舊的虛擬DOM
const {
  insert: hostInsert,
  remove: hostRemove,
  patchProp: hostPatchProp,
  createElement: hostCreateElement,
  createText: hostCreateText,
  createComment: hostCreateComment,
  setText: hostSetText,
  setElementText: hostSetElementText,
  parentNode: hostParentNode,
  nextSibling: hostNextSibling,
  setScopeId: hostSetScopeId = NOOP,
  cloneNode: hostCloneNode,
  insertStaticContent: hostInsertStaticContent
} = options
// render
const render: RootRenderFunction = (vnode, container, isSVG) => {
  if (vnode == null) {
    if (container._vnode) {
      unmount(container._vnode, null, null, true)
    }
  } else {
    // 新舊節(jié)點(diǎn)的對(duì)比
    patch(container._vnode || null, vnode, container, null, null, null, isSVG)
  }
  flushPostFlushCbs()
  // 記錄舊節(jié)點(diǎn)
  container._vnode = vnode
}

patch

patch函數(shù)里主要對(duì)新舊節(jié)點(diǎn)也就是虛擬DOM的對(duì)比,常說(shuō)的vue里的diff算法,便是從patch開(kāi)始。結(jié)合render函數(shù)來(lái)看,我們知道,舊的虛擬DOM存儲(chǔ)在container._vnode上。那么diff的方式就在patch中了:

新舊節(jié)點(diǎn)相同,直接返回;

舊節(jié)點(diǎn)存在,且新舊節(jié)點(diǎn)類(lèi)型不同,則舊節(jié)點(diǎn)不可復(fù)用,將其卸載(unmount),錨點(diǎn)anchor移向下一個(gè)節(jié)點(diǎn);

新節(jié)點(diǎn)是否靜態(tài)節(jié)點(diǎn)標(biāo)記;

根據(jù)新節(jié)點(diǎn)的類(lèi)型,相應(yīng)地調(diào)用不同類(lèi)型的處理方法:

  • 文本:processText;
  • 注釋?zhuān)?code>processCommentNode;
  • 靜態(tài)節(jié)點(diǎn):mountStaticNodepatchStaticNode
  • 文檔片段:processFragment
  • 其它。

在 其它 這一項(xiàng)中,又根據(jù)形狀標(biāo)記 shapeFlag等,判斷是 元素節(jié)點(diǎn)、組件節(jié)點(diǎn),或是TeleportSuspense等,然后調(diào)用相應(yīng)的process去處理。最后處理template中的ref

// Note: functions inside this closure should use `const xxx = () => {}`
// style in order to prevent being inlined by minifiers.
const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  isSVG = false,
  slotScopeIds = null,
  optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
  // 新舊節(jié)點(diǎn)相同,直接返回
  if (n1 === n2) {
    return
  }
  // 舊節(jié)點(diǎn)存在,且新舊節(jié)點(diǎn)類(lèi)型不同,卸載舊節(jié)點(diǎn),錨點(diǎn)anchor后移
  // patching & not same type, unmount old tree
  if (n1 && !isSameVNodeType(n1, n2)) {
    anchor = getNextHostNode(n1)
    unmount(n1, parentComponent, parentSuspense, true)
    n1 = null
  }
  // 是否靜態(tài)節(jié)點(diǎn)優(yōu)化
  if (n2.patchFlag === PatchFlags.BAIL) {
    optimized = false
    n2.dynamicChildren = null
  }
  // 
  const { type, ref, shapeFlag } = n2
  switch (type) {
    case Text:
      processText(n1, n2, container, anchor)
      break
    case Comment:
      processCommentNode(n1, n2, container, anchor)
      break
    case Static:
      if (n1 == null) {
        mountStaticNode(n2, container, anchor, isSVG)
      } else if (__DEV__) {
        patchStaticNode(n1, n2, container, isSVG)
      }
      break
    case Fragment:
      processFragment(
        n1,
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
      break
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) {
        processElement(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else if (shapeFlag & ShapeFlags.COMPONENT) {
        processComponent(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else if (shapeFlag & ShapeFlags.TELEPORT) {
        ;(type as typeof TeleportImpl).process(
          n1 as TeleportVNode,
          n2 as TeleportVNode,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized,
          internals
        )
      } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
        ;(type as typeof SuspenseImpl).process(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized,
          internals
        )
      } else if (__DEV__) {
        warn('Invalid VNode type:', type, `(${typeof type})`)
      }
  }
  // 處理 template 中的 ref 
  // set ref
  if (ref != null && parentComponent) {
    setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
  }
}

processText

文本節(jié)點(diǎn)的處理十分簡(jiǎn)單,沒(méi)有舊節(jié)點(diǎn)則新建并插入新節(jié)點(diǎn);有舊節(jié)點(diǎn),且節(jié)點(diǎn)內(nèi)容不一致,則設(shè)置為新節(jié)點(diǎn)的內(nèi)容。

const processText: ProcessTextOrCommentFn = (n1, n2, container, anchor) => {
  if (n1 == null) {
    hostInsert(
      (n2.el = hostCreateText(n2.children as string)),
      container,
      anchor
    )
  } else {
    const el = (n2.el = n1.el!)
    if (n2.children !== n1.children) {
      hostSetText(el, n2.children as string)
    }
  }
}

 processCommontNode

不支持動(dòng)態(tài)的注視節(jié)點(diǎn),因此只要舊節(jié)點(diǎn)存在,就使用舊節(jié)點(diǎn)的內(nèi)容。

const processCommentNode: ProcessTextOrCommentFn = (
    n1,
    n2,
    container,
    anchor
  ) => {
    if (n1 == null) {
      hostInsert(
        (n2.el = hostCreateComment((n2.children as string) || '')),
        container,
        anchor
      )
    } else {
      // there's no support for dynamic comments
      n2.el = n1.el
    }
  }

mountStaticNode 和 patchStaticNode

事實(shí)上靜態(tài)節(jié)點(diǎn)沒(méi)啥好比較的,畢竟是靜態(tài)的。當(dāng)沒(méi)有舊節(jié)點(diǎn)時(shí),則通過(guò)mountStaticNode創(chuàng)建并插入新節(jié)點(diǎn);即使有舊節(jié)點(diǎn),也僅在_DEV_條件下在hmr,才會(huì)使用patchStaticVnode做一下比較并通過(guò)removeStaticNode移除某些舊節(jié)點(diǎn)。

const mountStaticNode = (
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  isSVG: boolean
) => {
  // static nodes are only present when used with compiler-dom/runtime-dom
  // which guarantees presence of hostInsertStaticContent.
  ;[n2.el, n2.anchor] = hostInsertStaticContent!(
    n2.children as string,
    container,
    anchor,
    isSVG,
    n2.el,
    n2.anchor
  )
}
/**
 * Dev / HMR only
 */
const patchStaticNode = (
  n1: VNode,
  n2: VNode,
  container: RendererElement,
  isSVG: boolean
) => {
  // static nodes are only patched during dev for HMR
  if (n2.children !== n1.children) {
    const anchor = hostNextSibling(n1.anchor!)
    // 移除已有的靜態(tài)節(jié)點(diǎn),并插入新的節(jié)點(diǎn)
    // remove existing
    removeStaticNode(n1)
    // insert new
    ;[n2.el, n2.anchor] = hostInsertStaticContent!(
      n2.children as string,
      container,
      anchor,
      isSVG
    )
  } else {
    n2.el = n1.el
    n2.anchor = n1.anchor
  }
}
// removeStaticNode:從 n1.el 至 n1.anchor 的內(nèi)容被遍歷移除
const removeStaticNode = ({ el, anchor }: VNode) => {
  let next
  while (el && el !== anchor) {
    next = hostNextSibling(el)
    hostRemove(el)
    el = next
  }
  hostRemove(anchor!)
}

processFragment

vue3的單文件組件里,不再需要加一個(gè)根節(jié)點(diǎn),因?yàn)槭褂昧宋臋n片段fragment來(lái)承載子節(jié)點(diǎn),最后再一并添加到文檔中。

若舊的片段節(jié)點(diǎn)為空,則插入起始錨點(diǎn),掛載新的子節(jié)點(diǎn);

舊的片段不為空:

  • 存在優(yōu)化條件時(shí):使用patchBlockChildren優(yōu)化diff
  • 不存在優(yōu)化條件時(shí):使用patchChildren進(jìn)行全量diff。
const processFragment = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) => {
  // 錨點(diǎn)
  const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''))!
  const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))!
  let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2
  // 開(kāi)發(fā)環(huán)境熱更新時(shí),強(qiáng)制全量diff
  if (
    __DEV__ &&
    // #5523 dev root fragment may inherit directives
    (isHmrUpdating || patchFlag & PatchFlags.DEV_ROOT_FRAGMENT)
  ) {
    // HMR updated / Dev root fragment (w/ comments), force full diff
    patchFlag = 0
    optimized = false
    dynamicChildren = null
  }
  // 檢查是否是插槽
  // check if this is a slot fragment with :slotted scope ids
  if (fragmentSlotScopeIds) {
    slotScopeIds = slotScopeIds
      ? slotScopeIds.concat(fragmentSlotScopeIds)
      : fragmentSlotScopeIds
  }
  // 當(dāng)舊的片段為空時(shí),掛載新的片段的子節(jié)點(diǎn)
  if (n1 == null) {
    hostInsert(fragmentStartAnchor, container, anchor)
    hostInsert(fragmentEndAnchor, container, anchor)
    // a fragment can only have array children
    // since they are either generated by the compiler, or implicitly created
    // from arrays.
    mountChildren(
      n2.children as VNodeArrayChildren,
      container,
      fragmentEndAnchor,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  } else {
    // 當(dāng)舊片段不為空時(shí),啟用優(yōu)化則使用patchBlockChildren
    if (
      patchFlag > 0 &&
      patchFlag & PatchFlags.STABLE_FRAGMENT &&
      dynamicChildren &&
      // #2715 the previous fragment could've been a BAILed one as a result
      // of renderSlot() with no valid children
      n1.dynamicChildren
    ) {
      // a stable fragment (template root or <template v-for>) doesn't need to
      // patch children order, but it may contain dynamicChildren.
      patchBlockChildren(
        n1.dynamicChildren,
        dynamicChildren,
        container,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds
      )
      // 開(kāi)發(fā)環(huán)境,熱更新 處理靜態(tài)子節(jié)點(diǎn)
      if (__DEV__ && parentComponent && parentComponent.type.__hmrId) {
        traverseStaticChildren(n1, n2)
      } else if (
        // #2080 if the stable fragment has a key, it's a <template v-for> that may
        //  get moved around. Make sure all root level vnodes inherit el.
        // #2134 or if it's a component root, it may also get moved around
        // as the component is being moved.
        n2.key != null ||
        (parentComponent && n2 === parentComponent.subTree)
      ) {
        traverseStaticChildren(n1, n2, true /* shallow */)
      }
    } else {
      // 不可優(yōu)化時(shí),使用patchChildren處理
      // keyed / unkeyed, or manual fragments.
      // for keyed & unkeyed, since they are compiler generated from v-for,
      // each child is guaranteed to be a block so the fragment will never
      // have dynamicChildren.
      patchChildren(
        n1,
        n2,
        container,
        fragmentEndAnchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
  }
}

patchBlockChildren

在文檔片段中的diff中,當(dāng)符合優(yōu)化條件時(shí),則調(diào)用patchBlockChildren來(lái)進(jìn)行優(yōu)化的diff。這里主要以新節(jié)點(diǎn)的子節(jié)點(diǎn)長(zhǎng)度為準(zhǔn),遍歷新舊節(jié)點(diǎn)的子節(jié)點(diǎn),更新了每個(gè)子節(jié)點(diǎn)的container然后進(jìn)行patch。

// The fast path for blocks.
const patchBlockChildren: PatchBlockChildrenFn = (
  oldChildren,
  newChildren,
  fallbackContainer,
  parentComponent,
  parentSuspense,
  isSVG,
  slotScopeIds
) => {
  for (let i = 0; i < newChildren.length; i++) {
    const oldVNode = oldChildren[i]
    const newVNode = newChildren[i]
    // Determine the container (parent element) for the patch.
    const container =
      // oldVNode may be an errored async setup() component inside Suspense
      // which will not have a mounted element
      oldVNode.el &&
      // - In the case of a Fragment, we need to provide the actual parent
      // of the Fragment itself so it can move its children.
      (oldVNode.type === Fragment ||
        // - In the case of different nodes, there is going to be a replacement
        // which also requires the correct parent container
        !isSameVNodeType(oldVNode, newVNode) ||
        // - In the case of a component, it could contain anything.
        oldVNode.shapeFlag & (ShapeFlags.COMPONENT | ShapeFlags.TELEPORT))
        ? hostParentNode(oldVNode.el)!
        : // In other cases, the parent container is not actually used so we
          // just pass the block element here to avoid a DOM parentNode call.
          fallbackContainer
    patch(
      oldVNode,
      newVNode,
      container,
      null,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      true
    )
  }
}

patchChildren

在沒(méi)有優(yōu)化條件時(shí),使用patchChildren對(duì)子節(jié)點(diǎn)進(jìn)行全量的diff

const patchChildren: PatchChildrenFn = (
  n1,
  n2,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  isSVG,
  slotScopeIds,
  optimized = false
) => {
  const c1 = n1 && n1.children
  const prevShapeFlag = n1 ? n1.shapeFlag : 0
  const c2 = n2.children
  const { patchFlag, shapeFlag } = n2
  // 走綠色通道:用patchFlag來(lái)保證children是數(shù)組
  // fast path
  if (patchFlag > 0) {
    if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
      // 有key屬性的時(shí)候,根據(jù)key來(lái)進(jìn)行diff
      // this could be either fully-keyed or mixed (some keyed some not)
      // presence of patchFlag means children are guaranteed to be arrays
      patchKeyedChildren(
        c1 as VNode[],
        c2 as VNodeArrayChildren,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
      return
    } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
      // 沒(méi)有key
      // unkeyed
      patchUnkeyedChildren(
        c1 as VNode[],
        c2 as VNodeArrayChildren,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
      return
    }
  }
  // 沒(méi)有patchFlag的保證,則children可能為文本、數(shù)組或空
  // 根據(jù)形狀標(biāo)識(shí)來(lái)判斷
  // children has 3 possibilities: text, array or no children.
  if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
    // 文本子節(jié)點(diǎn)的綠色通道
    // text children fast path
    if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      unmountChildren(c1 as VNode[], parentComponent, parentSuspense)
    }
    if (c2 !== c1) {
      hostSetElementText(container, c2 as string)
    }
  } else {
    // 舊的子節(jié)點(diǎn)是數(shù)組
    if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      // prev children was array
      if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
        // 新舊子節(jié)點(diǎn)都是數(shù)組,需要進(jìn)行全量diff
        // two arrays, cannot assume anything, do full diff
        patchKeyedChildren(
          c1 as VNode[],
          c2 as VNodeArrayChildren,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else {
        // 新的子節(jié)點(diǎn)為空,則只需要卸載舊的子節(jié)點(diǎn)
        // no new children, just unmount old
        unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)
      }
    } else {
      // 舊的子節(jié)點(diǎn)為文本節(jié)點(diǎn)或者空,新的為數(shù)組或空
      // prev children was text OR null
      // new children is array OR null
      if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
        // 舊的為文本節(jié)點(diǎn),先將其文本置空
        hostSetElementText(container, '')
      }
      // 新的為數(shù)組,則通過(guò)mountChildren掛載子節(jié)點(diǎn)
      // mount new if array
      if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
        mountChildren(
          c2 as VNodeArrayChildren,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      }
    }
  }
}

patchKeyedChildren

使用patchKeyedChildren來(lái)比較兩組有key,或者有key和沒(méi)有key混合的children,屬于diff的核心內(nèi)容了。

從前往后依次對(duì)比相同索引位置的節(jié)點(diǎn)類(lèi)型,當(dāng)遇到節(jié)點(diǎn)類(lèi)型不同則退出比較;

再?gòu)暮笸皩?duì)比相同倒序位置上的節(jié)點(diǎn)類(lèi)型,遇到不同類(lèi)型則退出比較;

如果舊節(jié)點(diǎn)組遍歷完,而新節(jié)點(diǎn)組還有內(nèi)容,則掛載新節(jié)點(diǎn)組里的剩余內(nèi)容;

如果新節(jié)點(diǎn)組遍歷完,而舊節(jié)點(diǎn)組還有內(nèi)容,則卸載舊節(jié)點(diǎn)組里的剩余內(nèi)容;

如果都沒(méi)有遍歷完:

  • 將新節(jié)點(diǎn)組的剩余內(nèi)容以key=>index的形式存入Map;
  • 遍歷剩余的舊子節(jié)點(diǎn),在Map中找到相同的key對(duì)應(yīng)的index;
  • 如果舊子節(jié)點(diǎn)沒(méi)有key,則找到新子節(jié)點(diǎn)組的剩余子節(jié)點(diǎn)中尚未被匹配到且類(lèi)型相同的節(jié)點(diǎn)對(duì)應(yīng)的index;
  • 求出最大遞增子序列;
  • 卸載不匹配的舊子節(jié)點(diǎn)、掛載未被匹配的新子節(jié)點(diǎn),移動(dòng)需要移動(dòng)的可復(fù)用子節(jié)點(diǎn)。
// can be all-keyed or mixed
const patchKeyedChildren = (
  c1: VNode[],
  c2: VNodeArrayChildren,
  container: RendererElement,
  parentAnchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) => {
  let i = 0
  const l2 = c2.length
  // 兩組各自的尾節(jié)點(diǎn)
  let e1 = c1.length - 1 // prev ending index
  let e2 = l2 - 1 // next ending index
  // (從前往后)
  // 以?xún)山M中最短的一組為基準(zhǔn)
  // 從頭結(jié)點(diǎn)開(kāi)始,依次比較同一位置的節(jié)點(diǎn)類(lèi)型,若頭節(jié)點(diǎn)類(lèi)型相同,則對(duì)兩個(gè)節(jié)點(diǎn)進(jìn)行patch進(jìn)行比較;
  // 若類(lèi)型不同則退出循環(huán)
  // 1. sync from start
  // (a b) c
  // (a b) d e
  while (i <= e1 && i <= e2) {
    const n1 = c1[i]
    const n2 = (c2[i] = optimized
      ? cloneIfMounted(c2[i] as VNode)
      : normalizeVNode(c2[i]))
    if (isSameVNodeType(n1, n2)) {
      patch(
        n1,
        n2,
        container,
        null,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    } else {
      break
    }
    // 
    i++
  }
  // (從后往前)
  // 從尾節(jié)點(diǎn)開(kāi)始,尾節(jié)點(diǎn)類(lèi)型相同,則通過(guò)patch比較尾節(jié)點(diǎn);
  // 若類(lèi)型不同則退出循環(huán)
  // 2. sync from end
  // a (b c)
  // d e (b c)
  while (i <= e1 && i <= e2) {
    const n1 = c1[e1]
    const n2 = (c2[e2] = optimized
      ? cloneIfMounted(c2[e2] as VNode)
      : normalizeVNode(c2[e2]))
    if (isSameVNodeType(n1, n2)) {
      patch(
        n1,
        n2,
        container,
        null,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    } else {
      break
    }
    e1--
    e2--
  }
  // 經(jīng)過(guò)前后兩輪比較之后,剩下的就是中間那部分類(lèi)型不同的子節(jié)點(diǎn)了
  // 若舊的子節(jié)點(diǎn)組已經(jīng)遍歷完,而新的子節(jié)點(diǎn)組還有剩余內(nèi)容
  // 通過(guò)patch處理剩下的新的子節(jié)點(diǎn)中的內(nèi)容,由于舊的子節(jié)點(diǎn)為空,
  // 因此相當(dāng)于在patch內(nèi)部掛載剩余的新的子節(jié)點(diǎn)
  // 3. common sequence + mount
  // (a b)
  // (a b) c
  // i = 2, e1 = 1, e2 = 2
  // (a b)
  // c (a b)
  // i = 0, e1 = -1, e2 = 0
  if (i > e1) {
    if (i <= e2) {
      const nextPos = e2 + 1
      const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
      while (i <= e2) {
        patch(
          null,
          (c2[i] = optimized
            ? cloneIfMounted(c2[i] as VNode)
            : normalizeVNode(c2[i])),
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
        i++
      }
    }
  }
  // 舊的子節(jié)點(diǎn)還有剩余內(nèi)容而新的子節(jié)點(diǎn)組已經(jīng)遍歷完,則卸載舊子節(jié)點(diǎn)組剩余的那部分
  // 4. common sequence + unmount
  // (a b) c
  // (a b)
  // i = 2, e1 = 2, e2 = 1
  // a (b c)
  // (b c)
  // i = 0, e1 = 0, e2 = -1
  else if (i > e2) {
    while (i <= e1) {
      unmount(c1[i], parentComponent, parentSuspense, true)
      i++
    }
  }
  // 新舊子節(jié)點(diǎn)組都沒(méi)有遍歷完,如下注釋中[]里的部分
  // 5. unknown sequence
  // [i ... e1 + 1]: a b [c d e] f g
  // [i ... e2 + 1]: a b [e d c h] f g
  // i = 2, e1 = 4, e2 = 5
  else {
    // 拿到上次比較完的起點(diǎn)
    const s1 = i // prev starting index
    const s2 = i // next starting index
    // 5.1 build key:index map for newChildren
    const keyToNewIndexMap: Map<string | number | symbol, number> = new Map()
    // 用Map存儲(chǔ)新的子節(jié)點(diǎn)組的key和對(duì)應(yīng)的index, key=>index 并給出重復(fù)的key的警告
    for (i = s2; i <= e2; i++) {
      const nextChild = (c2[i] = optimized
        ? cloneIfMounted(c2[i] as VNode)
        : normalizeVNode(c2[i]))
      if (nextChild.key != null) {
        if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
          warn(
            `Duplicate keys found during update:`,
            JSON.stringify(nextChild.key),
            `Make sure keys are unique.`
          )
        }
        keyToNewIndexMap.set(nextChild.key, i)
      }
    }
    // 5.2 loop through old children left to be patched and try to patch
    // matching nodes & remove nodes that are no longer present
    let j
    // 已比較的數(shù)量
    let patched = 0
    // 未比較的數(shù)量
    const toBePatched = e2 - s2 + 1
    let moved = false
    // used to track whether any node has moved
    let maxNewIndexSoFar = 0
    // works as Map<newIndex, oldIndex>
    // Note that oldIndex is offset by +1
    // and oldIndex = 0 is a special value indicating the new node has
    // no corresponding old node.
    // used for determining longest stable subsequence
    // 以新的子節(jié)點(diǎn)組中未完成比較的節(jié)點(diǎn)為基準(zhǔn)
    const newIndexToOldIndexMap = new Array(toBePatched)
    // 先用0來(lái)填充,標(biāo)記為沒(méi)有key的節(jié)點(diǎn)。 ps:直接fill(0)不就好了么
    for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
    // 處理舊的子節(jié)點(diǎn)組
    for (i = s1; i <= e1; i++) {
      const prevChild = c1[i]
      // 當(dāng)已經(jīng)比較完了(patched >= toBePatched),卸載舊的子節(jié)點(diǎn)
      if (patched >= toBePatched) {
        // all new children have been patched so this can only be a removal
        unmount(prevChild, parentComponent, parentSuspense, true)
        continue
      }
      let newIndex
      // 當(dāng)舊的子節(jié)點(diǎn)的key存在,取出key在新的子節(jié)點(diǎn)組中對(duì)應(yīng)的index
      if (prevChild.key != null) {
        newIndex = keyToNewIndexMap.get(prevChild.key)
      } else {
        // 若舊的子節(jié)點(diǎn)沒(méi)有key,找出沒(méi)有key且類(lèi)型相同的節(jié)點(diǎn)對(duì)應(yīng)在新子節(jié)點(diǎn)組中的index
        // key-less node, try to locate a key-less node of the same type
        for (j = s2; j <= e2; j++) {
          if (
            newIndexToOldIndexMap[j - s2] === 0 &&
            isSameVNodeType(prevChild, c2[j] as VNode)
          ) {
            newIndex = j
            break
          }
        }
      }
      // newIndex不存在,即根據(jù)key來(lái)找,發(fā)現(xiàn)舊的子節(jié)點(diǎn)不可復(fù)用,則卸載舊的子節(jié)點(diǎn)
      if (newIndex === undefined) {
        unmount(prevChild, parentComponent, parentSuspense, true)
      } else {
        // 找到了可復(fù)用的節(jié)點(diǎn),在newIndexToOldIndexMap中標(biāo)記 i+1,
        // 用于最大上升子序列算法
        newIndexToOldIndexMap[newIndex - s2] = i + 1
        // 刷新目前找到的最大的新子節(jié)點(diǎn)的index,做節(jié)點(diǎn)移動(dòng)標(biāo)記
        if (newIndex >= maxNewIndexSoFar) {
          maxNewIndexSoFar = newIndex
        } else {
          moved = true
        }
        // 再遞歸詳細(xì)比較兩個(gè)節(jié)點(diǎn)
        patch(
          prevChild,
          c2[newIndex] as VNode,
          container,
          null,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
        // 已對(duì)比的數(shù)量+1
        patched++
      }
    }
    // 當(dāng)需要移動(dòng)時(shí),采用最大遞增子序列算法,從而最大限度減少節(jié)點(diǎn)移動(dòng)次數(shù)
    // 5.3 move and mount
    // generate longest stable subsequence only when nodes have moved
    const increasingNewIndexSequence = moved
      ? getSequence(newIndexToOldIndexMap)
      : EMPTY_ARR
    j = increasingNewIndexSequence.length - 1
    // 倒序遍歷,好處是可以使用上一次對(duì)比的節(jié)點(diǎn)作為錨點(diǎn)
    // looping backwards so that we can use last patched node as anchor
    for (i = toBePatched - 1; i >= 0; i--) {
      const nextIndex = s2 + i
      const nextChild = c2[nextIndex] as VNode
      const anchor =
        nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor
      if (newIndexToOldIndexMap[i] === 0) {
        // 等于0說(shuō)明未被舊的子節(jié)點(diǎn)匹配到,屬于全新的不可復(fù)用的子節(jié)點(diǎn),則通過(guò)patch進(jìn)行掛載
        // mount new
        patch(
          null,
          nextChild,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
      } else if (moved) {
        // 當(dāng)計(jì)算出來(lái)的最大上升子序列為空數(shù)組,
        // 或者當(dāng)前節(jié)點(diǎn)不處于最大上升子序列中
        // move if:
        // There is no stable subsequence (e.g. a reverse)
        // OR current node is not among the stable sequence
        if (j < 0 || i !== increasingNewIndexSequence[j]) {
          move(nextChild, container, anchor, MoveType.REORDER)
        } else {
          j--
        }
      }
    }
  }
}

patchUnkeyedChildren

沒(méi)有key的時(shí)候就很直接了,只依照最短的那組的長(zhǎng)度,來(lái)按位置進(jìn)行比較。而后該卸載就卸載,該掛載就掛載。

const patchUnkeyedChildren = (
  c1: VNode[],
  c2: VNodeArrayChildren,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) => {
  c1 = c1 || EMPTY_ARR
  c2 = c2 || EMPTY_ARR
  const oldLength = c1.length
  const newLength = c2.length
  const commonLength = Math.min(oldLength, newLength)
  let i
  for (i = 0; i < commonLength; i++) {
    const nextChild = (c2[i] = optimized
      ? cloneIfMounted(c2[i] as VNode)
      : normalizeVNode(c2[i]))
    patch(
      c1[i],
      nextChild,
      container,
      null,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  }
  if (oldLength > newLength) {
    // remove old
    unmountChildren(
      c1,
      parentComponent,
      parentSuspense,
      true,
      false,
      commonLength
    )
  } else {
    // mount new
    mountChildren(
      c2,
      container,
      anchor,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized,
      commonLength
    )
  }
}

mountChildren

mountChildren用于掛載子節(jié)點(diǎn),主要是遍歷子節(jié)點(diǎn),處理每個(gè)子節(jié)點(diǎn),得到復(fù)制的或者標(biāo)準(zhǔn)化的單個(gè)子節(jié)點(diǎn),然后遞歸調(diào)用patch。

const mountChildren: MountChildrenFn = (
  children,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  isSVG,
  slotScopeIds,
  optimized,
  start = 0
) => {
  for (let i = start; i < children.length; i++) {
    const child = (children[i] = optimized
      ? cloneIfMounted(children[i] as VNode)
      : normalizeVNode(children[i]))
    patch(
      null,
      child,
      container,
      anchor,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds,
      optimized
    )
  }
}

unmountChildren

遍歷子節(jié)點(diǎn)組,調(diào)用unmount方法卸載子節(jié)點(diǎn)。

const unmountChildren: UnmountChildrenFn = (
  children,
  parentComponent,
  parentSuspense,
  doRemove = false,
  optimized = false,
  start = 0
) => {
  for (let i = start; i < children.length; i++) {
    unmount(children[i], parentComponent, parentSuspense, doRemove, optimized)
  }
}

move

在有key的子節(jié)點(diǎn)比較中,出現(xiàn)了需要移動(dòng)子節(jié)點(diǎn)的情況,而移動(dòng)就是通過(guò)move來(lái)完成的。按照不同的節(jié)點(diǎn)類(lèi)型,處理方式有所差異。

const move: MoveFn = (
  vnode,
  container,
  anchor,
  moveType,
  parentSuspense = null
) => {
  const { el, type, transition, children, shapeFlag } = vnode
  // 對(duì)于組件節(jié)點(diǎn),遞歸處理subTree
  if (shapeFlag & ShapeFlags.COMPONENT) {
    move(vnode.component!.subTree, container, anchor, moveType)
    return
  }
  // 處理異步組件<Suspense>
  if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
    vnode.suspense!.move(container, anchor, moveType)
    return
  }
  // 處理<Teleport>
  if (shapeFlag & ShapeFlags.TELEPORT) {
    ;(type as typeof TeleportImpl).move(vnode, container, anchor, internals)
    return
  }
  // 文檔片段,處理起始錨點(diǎn)和子節(jié)點(diǎn)
  if (type === Fragment) {
    hostInsert(el!, container, anchor)
    for (let i = 0; i < (children as VNode[]).length; i++) {
      move((children as VNode[])[i], container, anchor, moveType)
    }
    hostInsert(vnode.anchor!, container, anchor)
    return
  }
  // 靜態(tài)節(jié)點(diǎn)
  if (type === Static) {
    moveStaticNode(vnode, container, anchor)
    return
  }
  // 處理<Transition>的鉤子
  // single nodes
  const needTransition =
    moveType !== MoveType.REORDER &&
    shapeFlag & ShapeFlags.ELEMENT &&
    transition
  if (needTransition) {
    if (moveType === MoveType.ENTER) {
      transition!.beforeEnter(el!)
      hostInsert(el!, container, anchor)
      queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
    } else {
      const { leave, delayLeave, afterLeave } = transition!
      const remove = () => hostInsert(el!, container, anchor)
      const performLeave = () => {
        leave(el!, () => {
          remove()
          afterLeave && afterLeave()
        })
      }
      if (delayLeave) {
        delayLeave(el!, remove, performLeave)
      } else {
        performLeave()
      }
    }
  } else {
    hostInsert(el!, container, anchor)
  }
}

processElement

processElement內(nèi)容很簡(jiǎn)單,判斷一下是否要當(dāng)作svg處理;之后,如果舊節(jié)點(diǎn)為空,則直接通過(guò)mountElement掛載新的元素節(jié)點(diǎn),否則通過(guò)patchElement對(duì)元素節(jié)點(diǎn)進(jìn)行對(duì)比。

const processElement = (
    n1: VNode | null,
    n2: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) => {
    isSVG = isSVG || (n2.type as string) === 'svg'
    if (n1 == null) {
      mountElement(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    } else {
      patchElement(
        n1,
        n2,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
  }

mountElement

假如此時(shí)舊節(jié)點(diǎn)為空,那么就會(huì)調(diào)用mountElement,我們來(lái)看看它是怎么做的。

  • vndoe上的el屬性存在,開(kāi)發(fā)環(huán)境下則簡(jiǎn)單對(duì)el進(jìn)行復(fù)制;不存在則新建;
  • 先進(jìn)行子節(jié)點(diǎn)的掛載,因?yàn)槟承?code>props依賴(lài)于子節(jié)點(diǎn)的渲染;
  • 指令的created階段;
  • 處理props并設(shè)置scopeId;
  • 開(kāi)發(fā)環(huán)境下設(shè)置el.__vnodeel.vueParentComponent的取值,并設(shè)置為不可枚舉;
  • 指令的beforeMounted階段;
  • 動(dòng)畫(huà)組件TransitionbeforeEnter鉤子;
  • 執(zhí)行vnode上的鉤子、Transitionenter鉤子、指令的mounted鉤子等
const mountElement = (
    vnode: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) => {
    let el: RendererElement
    let vnodeHook: VNodeHook | undefined | null
    const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode
    if (
      !__DEV__ &&
      vnode.el &&
      hostCloneNode !== undefined &&
      patchFlag === PatchFlags.HOISTED
    ) {
      // vnode的el元素存在,僅在生產(chǎn)環(huán)境下對(duì)可復(fù)用的靜態(tài)節(jié)點(diǎn)進(jìn)行復(fù)制
      // If a vnode has non-null el, it means it's being reused.
      // Only static vnodes can be reused, so its mounted DOM nodes should be
      // exactly the same, and we can simply do a clone here.
      // only do this in production since cloned trees cannot be HMR updated.
      el = vnode.el = hostCloneNode(vnode.el)
    } else {
      // vnode上的元素不存在則新建
      el = vnode.el = hostCreateElement(
        vnode.type as string,
        isSVG,
        props && props.is,
        props
      )
      // 注釋?zhuān)河捎谀承﹑rops依賴(lài)于子節(jié)點(diǎn)的渲染,先掛載子節(jié)點(diǎn)
      // mount children first, since some props may rely on child content
      // being already rendered, e.g. `<select value>`
      if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
        // 設(shè)置元素文本
        hostSetElementText(el, vnode.children as string)
      } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
        // 掛載子節(jié)點(diǎn)
        mountChildren(
          vnode.children as VNodeArrayChildren,
          el,
          null,
          parentComponent,
          parentSuspense,
          isSVG && type !== 'foreignObject',
          slotScopeIds,
          optimized
        )
      }
      // 指令的created階段
      if (dirs) {
        invokeDirectiveHook(vnode, null, parentComponent, 'created')
      }
      // 處理元素的props
      // props
      if (props) {
        for (const key in props) {
          if (key !== 'value' && !isReservedProp(key)) {
            hostPatchProp(
              el,
              key,
              null,
              props[key],
              isSVG,
              vnode.children as VNode[],
              parentComponent,
              parentSuspense,
              unmountChildren
            )
          }
        }
        /**
         * Special case for setting value on DOM elements:
         * - it can be order-sensitive (e.g. should be set *after* min/max, #2325, #4024)
         * - it needs to be forced (#1471)
         * #2353 proposes adding another renderer option to configure this, but
         * the properties affects are so finite it is worth special casing it
         * here to reduce the complexity. (Special casing it also should not
         * affect non-DOM renderers)
         */
        if ('value' in props) {
          hostPatchProp(el, 'value', null, props.value)
        }
        if ((vnodeHook = props.onVnodeBeforeMount)) {
          invokeVNodeHook(vnodeHook, parentComponent, vnode)
        }
      }
      // scopeId
      setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent)
    }
    // __DEV__環(huán)境下處理 __vnode屬性和父組件為不可枚舉
    if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
      Object.defineProperty(el, '__vnode', {
        value: vnode,
        enumerable: false
      })
      Object.defineProperty(el, '__vueParentComponent', {
        value: parentComponent,
        enumerable: false
      })
    }
    // 執(zhí)行指令中的 beforeMount 階段
    if (dirs) {
      invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
    }
    // #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
    // #1689 For inside suspense + suspense resolved case, just call it
    // 是否需要執(zhí)行動(dòng)畫(huà)組件鉤子
    const needCallTransitionHooks =
      (!parentSuspense || (parentSuspense && !parentSuspense.pendingBranch)) &&
      transition &&
      !transition.persisted
    if (needCallTransitionHooks) {
      transition!.beforeEnter(el)
    }
    hostInsert(el, container, anchor)
    if (
      (vnodeHook = props && props.onVnodeMounted) ||
      needCallTransitionHooks ||
      dirs
    ) {
      // 加入組件更新后的副作用執(zhí)行隊(duì)列,在合適的時(shí)機(jī)執(zhí)行入隊(duì)的函數(shù)
      // 這里是一些鉤子函數(shù)、trasition的鉤子、指令在mounted階段的鉤子
      queuePostRenderEffect(() => {
        vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
        needCallTransitionHooks && transition!.enter(el)
        dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
      }, parentSuspense)
    }
  }

patchElement

patchElemengt相當(dāng)重要,因?yàn)槠渌湍愣鄡?nèi)容的patch,最終經(jīng)過(guò)遞歸,依然會(huì)走到patchElement。當(dāng)新舊元素節(jié)點(diǎn)都存在時(shí),就會(huì)調(diào)用patchElement進(jìn)行對(duì)比??梢钥吹巾樞颍?/p>

beforeUpdated -> 子節(jié)點(diǎn) -> class/style -> 其它props/attrs -> updated

關(guān)閉recurse,處理beforeUpdated鉤子;

處理指定的beforeUpdated階段,再啟用recurse;

__DEV__環(huán)境下的熱更新時(shí),則會(huì)清理優(yōu)化標(biāo)記,從而強(qiáng)制對(duì)節(jié)點(diǎn)進(jìn)行全量的比較(full diff);

處理動(dòng)態(tài)子節(jié)點(diǎn):

  • 當(dāng)新節(jié)點(diǎn)中有動(dòng)態(tài)子節(jié)點(diǎn),則通過(guò)patchBlockChildren來(lái)和舊節(jié)點(diǎn)的動(dòng)態(tài)子節(jié)點(diǎn)進(jìn)行對(duì)比;
  • 否則,如果沒(méi)有優(yōu)化(!optimized),則使用patchChildren對(duì)子節(jié)點(diǎn)進(jìn)行全量diff;

判斷patchFlag > 0,大于0時(shí)則元素的render代碼由compiler生成,有優(yōu)化buff

  • 如果props中有動(dòng)態(tài)的key,則優(yōu)化無(wú)效,進(jìn)行全量diff;
  • 處理動(dòng)態(tài)類(lèi)名和動(dòng)態(tài)style,優(yōu)化diff
  • 處理其它的prop/attr,如果其中有動(dòng)態(tài)的key,則優(yōu)化無(wú)效;
  • 處理文本:當(dāng)元素只有文本子節(jié)點(diǎn)時(shí),則將文本子節(jié)點(diǎn)設(shè)置為新的元素節(jié)點(diǎn)的內(nèi)容;

patchFlag <= 0,且沒(méi)有設(shè)置優(yōu)化時(shí),對(duì)props進(jìn)行全量diff

updated階段。

const patchElement = (
  n1: VNode,
  n2: VNode,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) => {
  const el = (n2.el = n1.el!)
  let { patchFlag, dynamicChildren, dirs } = n2
  // #1426 take the old vnode's patch flag into account since user may clone a
  // compiler-generated vnode, which de-opts to FULL_PROPS
  patchFlag |= n1.patchFlag & PatchFlags.FULL_PROPS
  const oldProps = n1.props || EMPTY_OBJ
  const newProps = n2.props || EMPTY_OBJ
  let vnodeHook: VNodeHook | undefined | null
  // 關(guān)閉recurse,在 beforeUpdated 階段不允許自己調(diào)用
  // disable recurse in beforeUpdate hooks
  parentComponent && toggleRecurse(parentComponent, false)
  // beforeUpdated鉤子
  if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
    invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
  }
  // 指令的 beforeUpdated 鉤子
  if (dirs) {
    invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
  }
  // 允許自己調(diào)用
  parentComponent && toggleRecurse(parentComponent, true)
  // 開(kāi)發(fā)環(huán)境呢下,關(guān)閉優(yōu)化,全量diff
  if (__DEV__ && isHmrUpdating) {
    // HMR updated, force full diff
    patchFlag = 0
    optimized = false
    dynamicChildren = null
  }
  const areChildrenSVG = isSVG && n2.type !== 'foreignObject'
  // 新節(jié)點(diǎn)的動(dòng)態(tài)子節(jié)點(diǎn)不為空,則比較新舊節(jié)點(diǎn)的動(dòng)態(tài)子節(jié)點(diǎn)
  if (dynamicChildren) {
    patchBlockChildren(
      n1.dynamicChildren!,
      dynamicChildren,
      el,
      parentComponent,
      parentSuspense,
      areChildrenSVG,
      slotScopeIds
    )
    // 開(kāi)發(fā)環(huán)境  遞歸遍歷靜態(tài)子節(jié)點(diǎn)
    if (__DEV__ && parentComponent && parentComponent.type.__hmrId) {
      traverseStaticChildren(n1, n2)
    }
    // 沒(méi)有優(yōu)化,全量 diff
  } else if (!optimized) {
    // full diff
    patchChildren(
      n1,
      n2,
      el,
      null,
      parentComponent,
      parentSuspense,
      areChildrenSVG,
      slotScopeIds,
      false
    )
  }
  // 注釋?zhuān)簆atchFlag 標(biāo)識(shí)的存在意味著元素的 render 代碼是由 compiler 生成的,
  // 且可以在 patch 時(shí)走快道,此時(shí)能保證新舊節(jié)點(diǎn)形狀相同,即它們?cè)谠茨0逯姓锰幱谙嗤奈恢?
  // 此時(shí)的對(duì)比是有著各種優(yōu)化的
  if (patchFlag > 0) {
    // the presence of a patchFlag means this element's render code was
    // generated by the compiler and can take the fast path.
    // in this path old node and new node are guaranteed to have the same shape
    // (i.e. at the exact same position in the source template)
    if (patchFlag & PatchFlags.FULL_PROPS) {
      // 當(dāng)props中含有動(dòng)態(tài)的key,需要進(jìn)行全量 diff
      // element props contain dynamic keys, full diff needed
      patchProps(
        el,
        n2,
        oldProps,
        newProps,
        parentComponent,
        parentSuspense,
        isSVG
      )
    } else {
      // 處理動(dòng)態(tài)類(lèi)名綁定
      // class
      // this flag is matched when the element has dynamic class bindings.
      if (patchFlag & PatchFlags.CLASS) {
        if (oldProps.class !== newProps.class) {
          hostPatchProp(el, 'class', null, newProps.class, isSVG)
        }
      }
      // 處理動(dòng)態(tài)的 style 綁定
      // style
      // this flag is matched when the element has dynamic style bindings
      if (patchFlag & PatchFlags.STYLE) {
        hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG)
      }
      // 處理動(dòng)態(tài)的 prop/attr 綁定,有迭代緩存,優(yōu)化比較速度
      // 如果 `prop/attr`的 key 是動(dòng)態(tài)的,那么這種優(yōu)化則會(huì)失效
      // props
      // This flag is matched when the element has dynamic prop/attr bindings
      // other than class and style. The keys of dynamic prop/attrs are saved for
      // faster iteration.
      // Note dynamic keys like :[foo]="bar" will cause this optimization to
      // bail out and go through a full diff because we need to unset the old key
      if (patchFlag & PatchFlags.PROPS) {
        // if the flag is present then dynamicProps must be non-null
        const propsToUpdate = n2.dynamicProps!
        for (let i = 0; i < propsToUpdate.length; i++) {
          const key = propsToUpdate[i]
          const prev = oldProps[key]
          const next = newProps[key]
          // value屬性會(huì)被強(qiáng)行對(duì)比
          // #1471 force patch value
          if (next !== prev || key === 'value') {
            hostPatchProp(
              el,
              key,
              prev,
              next,
              isSVG,
              n1.children as VNode[],
              parentComponent,
              parentSuspense,
              unmountChildren
            )
          }
        }
      }
    }
    // 處理文本:僅在元素只有文本子節(jié)點(diǎn)時(shí)觸發(fā)
    // text
    // This flag is matched when the element has only dynamic text children.
    if (patchFlag & PatchFlags.TEXT) {
      if (n1.children !== n2.children) {
        hostSetElementText(el, n2.children as string)
      }
    }
  } else if (!optimized && dynamicChildren == null) {
    // 沒(méi)有優(yōu)化,全量 diff
    // unoptimized, full diff
    patchProps(
      el,
      n2,
      oldProps,
      newProps,
      parentComponent,
      parentSuspense,
      isSVG
    )
  }
  // updated 鉤子 入隊(duì)
  if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {
    queuePostRenderEffect(() => {
      vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
      dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated')
    }, parentSuspense)
  }
}

patchElement中,注意到當(dāng)新節(jié)點(diǎn)具有動(dòng)態(tài)子節(jié)點(diǎn)時(shí),調(diào)用了patchBlockChildren來(lái)進(jìn)行子節(jié)點(diǎn)的比較,而在沒(méi)有動(dòng)態(tài)子節(jié)點(diǎn)且不符合優(yōu)化條件時(shí),則使用patchChildren來(lái)比較。這與processFragment類(lèi)似。

而當(dāng)patchFlag <= 0且沒(méi)有設(shè)置優(yōu)化時(shí),對(duì)props進(jìn)行全量diff。分別遍歷新的props和舊的props,最后刷新value的值。

const patchProps = (
  el: RendererElement,
  vnode: VNode,
  oldProps: Data,
  newProps: Data,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean
) => {
  if (oldProps !== newProps) {
    // 遍歷新的props
    for (const key in newProps) {
      // empty string is not valid prop
      if (isReservedProp(key)) continue
      const next = newProps[key]
      const prev = oldProps[key]
      // 先不比較 value
      // defer patching value
      if (next !== prev && key !== 'value') {
        hostPatchProp(
          el,
          key,
          prev,
          next,
          isSVG,
          vnode.children as VNode[],
          parentComponent,
          parentSuspense,
          unmountChildren
        )
      }
    }
    // 遍歷舊的props
    if (oldProps !== EMPTY_OBJ) {
      for (const key in oldProps) {
        if (!isReservedProp(key) && !(key in newProps)) {
          hostPatchProp(
            el,
            key,
            oldProps[key],
            null,
            isSVG,
            vnode.children as VNode[],
            parentComponent,
            parentSuspense,
            unmountChildren
          )
        }
      }
    }
    // 最后處理 value
    if ('value' in newProps) {
      hostPatchProp(el, 'value', oldProps.value, newProps.value)
    }
  }
}

processComponent

當(dāng)被patch的節(jié)點(diǎn)類(lèi)型是組件時(shí),通過(guò)processComponent來(lái)處理。

當(dāng)舊組件節(jié)點(diǎn)存在時(shí),則調(diào)用updateComponent進(jìn)行更新;

否則:

  • 當(dāng)新組件節(jié)點(diǎn)為KeepAlive時(shí),調(diào)用其上下文對(duì)象上的activate方法;
  • 否則,使用mountComponent掛載新的組件節(jié)點(diǎn);
const processComponent = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  slotScopeIds: string[] | null,
  optimized: boolean
) => {
  n2.slotScopeIds = slotScopeIds
  if (n1 == null) {
    if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
      ;(parentComponent!.ctx as KeepAliveContext).activate(
        n2,
        container,
        anchor,
        isSVG,
        optimized
      )
    } else {
      mountComponent(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        optimized
      )
    }
  } else {
    updateComponent(n1, n2, optimized)
  }
}

mountComponent

mountComponent在舊的組件節(jié)點(diǎn)不存在時(shí)被調(diào)用。所有的mountXXX最常見(jiàn)的調(diào)用時(shí)機(jī)都是首次渲染時(shí),舊節(jié)點(diǎn)都是空的。

const mountComponent: MountComponentFn = (
  initialVNode,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  isSVG,
  optimized
) => {
  // 2.x compat may pre-create the component instance before actually
  // mounting
  const compatMountInstance =
    __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
  const instance: ComponentInternalInstance =
    compatMountInstance ||
    (initialVNode.component = createComponentInstance(
      initialVNode,
      parentComponent,
      parentSuspense
    ))
  // 注冊(cè)熱更新
  if (__DEV__ && instance.type.__hmrId) {
    registerHMR(instance)
  }
  // 掛載性能檢測(cè)
  if (__DEV__) {
    pushWarningContext(initialVNode)
    startMeasure(instance, `mount`)
  }
  // 注入renderer的內(nèi)部?jī)?nèi)容
  // inject renderer internals for keepAlive
  if (isKeepAlive(initialVNode)) {
    ;(instance.ctx as KeepAliveContext).renderer = internals
  }
  /** 這里備注一下 internals 的內(nèi)容
   * const internals: RendererInternals = {
   *   p: patch,
   *   um: unmount,
   *   m: move,
   *   r: remove,
   *   mt: mountComponent,
   *   mc: mountChildren,
   *   pc: patchChildren,
   *   pbc: patchBlockChildren,
   *   n: getNextHostNode,
   *   o: options
   * }
   */
  // 處理props和插槽
  // resolve props and slots for setup context
  if (!(__COMPAT__ && compatMountInstance)) {
    // 檢測(cè)初始化性能
    if (__DEV__) {
      startMeasure(instance, `init`)
    }
    // 處理setup:這個(gè)函數(shù)里使用其它方法,初始化了props和插槽,且調(diào)用了setup
    setupComponent(instance)
    if (__DEV__) {
      endMeasure(instance, `init`)
    }
  }
  // 處理異步的setup
  // setup() is async. This component relies on async logic to be resolved
  // before proceeding
  if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
    parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect)
    // Give it a placeholder if this is not hydration
    // TODO handle self-defined fallback
    if (!initialVNode.el) {
      const placeholder = (instance.subTree = createVNode(Comment))
      processCommentNode(null, placeholder, container!, anchor)
    }
    return
  }
  // 接下來(lái)根據(jù)setup返回內(nèi)容進(jìn)行渲染
  // todo 閱讀該函數(shù)的內(nèi)容
  setupRenderEffect(
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    isSVG,
    optimized
  )
  // mount性能檢測(cè)結(jié)束點(diǎn)
  if (__DEV__) {
    popWarningContext()
    endMeasure(instance, `mount`)
  }
}

updateComponent

當(dāng)舊的組件節(jié)點(diǎn)存在時(shí),對(duì)組件節(jié)點(diǎn)的處理會(huì)進(jìn)入到更新階段,也就是updateComponent。以舊組件為基準(zhǔn)拿到實(shí)例instance,通過(guò)shouldUpdateComponent判斷是否要更新組件。如果不需要更新,則只復(fù)制一下屬性;否則,當(dāng)實(shí)例是異步組件時(shí),則只更新props和插槽;當(dāng)實(shí)例是同步組件時(shí),則設(shè)置next為新的組件節(jié)點(diǎn),并調(diào)用組件的update方法進(jìn)行更新。

const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {
  const instance = (n2.component = n1.component)!
  if (shouldUpdateComponent(n1, n2, optimized)) {
    if (
      __FEATURE_SUSPENSE__ &&
      instance.asyncDep &&
      !instance.asyncResolved
    ) {
      // async & still pending - just update props and slots
      // since the component's reactive effect for render isn't set-up yet
      if (__DEV__) {
        pushWarningContext(n2)
      }
      // 更新組件的預(yù)渲染:即處理props和插槽
      updateComponentPreRender(instance, n2, optimized)
      if (__DEV__) {
        popWarningContext()
      }
      return
    } else {
      // normal update
      instance.next = n2
      // in case the child component is also queued, remove it to avoid
      // double updating the same child component in the same flush.
      invalidateJob(instance.update)
      // instance.update is the reactive effect.
      instance.update()
    }
  } else {
    // no update needed. just copy over properties
    n2.el = n1.el
    instance.vnode = n2
  }
}

updateComponentPreRender

組件的預(yù)渲染,即在這里處理props和插槽。

const updateComponentPreRender = (
  instance: ComponentInternalInstance,
  nextVNode: VNode,
  optimized: boolean
) => {
  nextVNode.component = instance
  const prevProps = instance.vnode.props
  instance.vnode = nextVNode
  instance.next = null
  updateProps(instance, nextVNode.props, prevProps, optimized)
  updateSlots(instance, nextVNode.children, optimized)
  pauseTracking()
  // props update may have triggered pre-flush watchers.
  // flush them before the render update.
  flushPreFlushCbs(undefined, instance.update)
  resetTracking()
}

setupRenderEffect

相當(dāng)重要的一個(gè)函數(shù)。用componentUpdateFn來(lái)創(chuàng)建一個(gè)effect。最后執(zhí)行的update函數(shù)以及實(shí)例的update方法,都是執(zhí)行effect.run。而effect.run內(nèi)部會(huì)進(jìn)行與依賴(lài)收集相關(guān)的操作,還會(huì)調(diào)用新建effect時(shí)傳入的函數(shù)componentUpdateFn。這里可以看到**componentUpdateFn分為掛載和更新兩部分**。

const setupRenderEffect: SetupRenderEffectFn = (
  instance,
  initialVNode,
  container,
  anchor,
  parentSuspense,
  isSVG,
  optimized
) => {
  const componentUpdateFn = () => {
    if (!instance.isMounted) {
      let vnodeHook: VNodeHook | null | undefined
      const { el, props } = initialVNode
      const { bm, m, parent } = instance
      const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
      // 在beforeMounted期間 不允許effect自己調(diào)用
      toggleRecurse(instance, false)
      // beforeMount hook
      if (bm) {
        invokeArrayFns(bm)
      }
      // onVnodeBeforeMount
      if (
        !isAsyncWrapperVNode &&
        (vnodeHook = props && props.onVnodeBeforeMount)
      ) {
        invokeVNodeHook(vnodeHook, parent, initialVNode)
      }
      if (
        __COMPAT__ &&
        isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
      ) {
        instance.emit('hook:beforeMount')
      }
      toggleRecurse(instance, true)
      if (el && hydrateNode) {
        // vnode has adopted host node - perform hydration instead of mount.
        const hydrateSubTree = () => {
          if (__DEV__) {
            startMeasure(instance, `render`)
          }
          instance.subTree = renderComponentRoot(instance)
          if (__DEV__) {
            endMeasure(instance, `render`)
          }
          if (__DEV__) {
            startMeasure(instance, `hydrate`)
          }
          hydrateNode!(
            el as Node,
            instance.subTree,
            instance,
            parentSuspense,
            null
          )
          if (__DEV__) {
            endMeasure(instance, `hydrate`)
          }
        }
        if (isAsyncWrapperVNode) {
          ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
            // note: we are moving the render call into an async callback,
            // which means it won't track dependencies - but it's ok because
            // a server-rendered async wrapper is already in resolved state
            // and it will never need to change.
            () => !instance.isUnmounted && hydrateSubTree()
          )
        } else {
          hydrateSubTree()
        }
      } else {
        if (__DEV__) {
          startMeasure(instance, `render`)
        }
        const subTree = (instance.subTree = renderComponentRoot(instance))
        if (__DEV__) {
          endMeasure(instance, `render`)
        }
        if (__DEV__) {
          startMeasure(instance, `patch`)
        }
        patch(
          null,
          subTree,
          container,
          anchor,
          instance,
          parentSuspense,
          isSVG
        )
        if (__DEV__) {
          endMeasure(instance, `patch`)
        }
        initialVNode.el = subTree.el
      }
      // mounted鉤子入隊(duì)
      // mounted hook
      if (m) {
        queuePostRenderEffect(m, parentSuspense)
      }
      // onVnodeMounted
      if (
        !isAsyncWrapperVNode &&
        (vnodeHook = props && props.onVnodeMounted)
      ) {
        const scopedInitialVNode = initialVNode
        queuePostRenderEffect(
          () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
          parentSuspense
        )
      }
      if (
        __COMPAT__ &&
        isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
      ) {
        queuePostRenderEffect(
          () => instance.emit('hook:mounted'),
          parentSuspense
        )
      }
      // <KeepAlive>組件的activated鉤子,可能包含從子組件注入的鉤子
      // activated hook for keep-alive roots.
      // #1742 activated hook must be accessed after first render
      // since the hook may be injected by a child keep-alive
      if (
        initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
        (parent &&
          isAsyncWrapper(parent.vnode) &&
          parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
      ) {
        instance.a && queuePostRenderEffect(instance.a, parentSuspense)
        // 兼容
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:activated'),
            parentSuspense
          )
        }
      }
      // 變更組件掛載狀態(tài)
      instance.isMounted = true
      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
        devtoolsComponentAdded(instance)
      }
      // #2458: deference mount-only object parameters to prevent memleaks
      initialVNode = container = anchor = null as any
    } else {
      // updateComponent
      // This is triggered by mutation of component's own state (next: null)
      // OR parent calling processComponent (next: VNode)
      let { next, bu, u, parent, vnode } = instance
      let originNext = next
      let vnodeHook: VNodeHook | null | undefined
      if (__DEV__) {
        pushWarningContext(next || instance.vnode)
      }
      // beforeUpdated 期間也不允許effect自調(diào)用
      // Disallow component effect recursion during pre-lifecycle hooks.
      toggleRecurse(instance, false)
      if (next) {
        next.el = vnode.el
        updateComponentPreRender(instance, next, optimized)
      } else {
        next = vnode
      }
      // beforeUpdate hook
      if (bu) {
        invokeArrayFns(bu)
      }
      // onVnodeBeforeUpdate
      if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
        invokeVNodeHook(vnodeHook, parent, next, vnode)
      }
      // 考慮兼容
      if (
        __COMPAT__ &&
        isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
      ) {
        instance.emit('hook:beforeUpdate')
      }
      toggleRecurse(instance, true)
      // render
      if (__DEV__) {
        startMeasure(instance, `render`)
      }
      const nextTree = renderComponentRoot(instance)
      if (__DEV__) {
        endMeasure(instance, `render`)
      }
      const prevTree = instance.subTree
      instance.subTree = nextTree
      if (__DEV__) {
        startMeasure(instance, `patch`)
      }
      // 更新則比較新舊subTree
      patch(
        prevTree,
        nextTree,
        // parent may have changed if it's in a teleport
        hostParentNode(prevTree.el!)!,
        // anchor may have changed if it's in a fragment
        getNextHostNode(prevTree),
        instance,
        parentSuspense,
        isSVG
      )
      if (__DEV__) {
        endMeasure(instance, `patch`)
      }
      next.el = nextTree.el
      if (originNext === null) {
        // self-triggered update. In case of HOC, update parent component
        // vnode el. HOC is indicated by parent instance's subTree pointing
        // to child component's vnode
        updateHOCHostEl(instance, nextTree.el)
      }
      // 處理updated鉤子
      // updated hook
      if (u) {
        queuePostRenderEffect(u, parentSuspense)
      }
      // onVnodeUpdated
      if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
        queuePostRenderEffect(
          () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
          parentSuspense
        )
      }
      if (
        __COMPAT__ &&
        isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
      ) {
        queuePostRenderEffect(
          () => instance.emit('hook:updated'),
          parentSuspense
        )
      }
      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
        devtoolsComponentUpdated(instance)
      }
      if (__DEV__) {
        popWarningContext()
      }
    }
  }
  // 使用componentUpdateFn創(chuàng)建effect
  // create reactive effect for rendering
  const effect = (instance.effect = new ReactiveEffect(
    componentUpdateFn,
    () => queueJob(update),
    instance.scope // track it in component's effect scope
  ))
  const update: SchedulerJob = (instance.update = () => effect.run())
  update.id = instance.uid
  // allowRecurse
  // #1801, #2043 component render effects should allow recursive updates
  toggleRecurse(instance, true)
  // 用于開(kāi)發(fā)調(diào)試
  if (__DEV__) {
    effect.onTrack = instance.rtc
      ? e => invokeArrayFns(instance.rtc!, e)
      : void 0
    effect.onTrigger = instance.rtg
      ? e => invokeArrayFns(instance.rtg!, e)
      : void 0
    update.ownerInstance = instance
  }
  // 調(diào)用一次更新
  update()
}

unmount

舊節(jié)點(diǎn)的卸載通過(guò)unmount來(lái)處理,其中根據(jù)節(jié)點(diǎn)類(lèi)型不同,又有著不同的函數(shù)來(lái)實(shí)施卸載。

經(jīng)過(guò)置空ref、判斷與處理KeepAlive、beforeUnmounted的鉤子函數(shù)和指令、判斷組件的類(lèi)型并相應(yīng)卸載、處理unmounted鉤子和指令等過(guò)程。

const unmount: UnmountFn = (
  vnode,
  parentComponent,
  parentSuspense,
  doRemove = false,
  optimized = false
) => {
  const {
    type,
    props,
    ref,
    children,
    dynamicChildren,
    shapeFlag,
    patchFlag,
    dirs
  } = vnode
  // 置空ref
  // unset ref
  if (ref != null) {
    setRef(ref, null, parentSuspense, vnode, true)
  }
  // 組件被緩存,則調(diào)用<KeepAlive>的失活方法 deactivate
  if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
    ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
    return
  }
  // 是否調(diào)用指令和鉤子
  const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
  const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
  // beforeUnmounted 鉤子
  let vnodeHook: VNodeHook | undefined | null
  if (
    shouldInvokeVnodeHook &&
    (vnodeHook = props && props.onVnodeBeforeUnmount)
  ) {
    invokeVNodeHook(vnodeHook, parentComponent, vnode)
  }
  if (shapeFlag & ShapeFlags.COMPONENT) {
    // 卸載組件
    unmountComponent(vnode.component!, parentSuspense, doRemove)
  } else {
    // 卸載異步組件
    if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
      vnode.suspense!.unmount(parentSuspense, doRemove)
      return
    }
    // 處理指令的 beforeUnmounted 階段
    if (shouldInvokeDirs) {
      invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
    }
    // 卸載 Teleport
    if (shapeFlag & ShapeFlags.TELEPORT) {
      ;(vnode.type as typeof TeleportImpl).remove(
        vnode,
        parentComponent,
        parentSuspense,
        optimized,
        internals,
        doRemove
      )
    } else if (
      dynamicChildren &&
      // #1153: fast path should not be taken for non-stable (v-for) fragments
      (type !== Fragment ||
        (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
    ) {
      // 對(duì)于優(yōu)化過(guò)的塊狀節(jié)點(diǎn),僅需移除動(dòng)態(tài)子節(jié)點(diǎn)
      // fast path for block nodes: only need to unmount dynamic children.
      unmountChildren(
        dynamicChildren,
        parentComponent,
        parentSuspense,
        false,
        true
      )
    } else if (
      // 文檔片段  移除其子節(jié)點(diǎn)
      (type === Fragment &&
        patchFlag &
          (PatchFlags.KEYED_FRAGMENT | PatchFlags.UNKEYED_FRAGMENT)) ||
      (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN)
    ) {
      unmountChildren(children as VNode[], parentComponent, parentSuspense)
    }
    // 處理節(jié)點(diǎn)自身
    if (doRemove) {
      remove(vnode)
    }
  }
  // 處理 unmounted 鉤子以及指令中的 unmounted 階段
  if (
    (shouldInvokeVnodeHook &&
      (vnodeHook = props && props.onVnodeUnmounted)) ||
    shouldInvokeDirs
  ) {
    queuePostRenderEffect(() => {
      vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
      shouldInvokeDirs &&
        invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
    }, parentSuspense)
  }
}

remove

使用remove來(lái)移除一個(gè)節(jié)點(diǎn)。根據(jù)節(jié)點(diǎn)類(lèi)型與環(huán)境,執(zhí)行的邏輯也稍有差別。

const remove: RemoveFn = vnode => {
  const { type, el, anchor, transition } = vnode
  if (type === Fragment) {
    if (
      __DEV__ &&
      vnode.patchFlag > 0 &&
      vnode.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT &&
      transition &&
      !transition.persisted
    ) {
      // __DEV__環(huán)境
      // 遍歷移除子節(jié)點(diǎn)
      ;(vnode.children as VNode[]).forEach(child => {
        if (child.type === Comment) {
          hostRemove(child.el!)
        } else {
          remove(child)
        }
      })
    } else {
      // 移除片段
      removeFragment(el!, anchor!)
    }
    return
  }
  // 移除靜態(tài)節(jié)點(diǎn)
  if (type === Static) {
    removeStaticNode(vnode)
    return
  }
  /** 遍歷移除靜態(tài)節(jié)點(diǎn)
   *  const removeStaticNode = ({ el, anchor }: VNode) => {
   *    let next
   *    while (el && el !== anchor) {
   *      next = hostNextSibling(el)
   *      hostRemove(el)
   *      el = next
   *    }
   *    hostRemove(anchor!)
   *  }
   */
  const performRemove = () => {
    // 移除el
    hostRemove(el!)
    if (transition && !transition.persisted && transition.afterLeave) {
      // 動(dòng)畫(huà)的 afterLeave 鉤子
      transition.afterLeave()
    }
  }
  if (
    vnode.shapeFlag & ShapeFlags.ELEMENT &&
    transition &&
    !transition.persisted
  ) {
    const { leave, delayLeave } = transition
    const performLeave = () => leave(el!, performRemove)
    // 推遲 leave 動(dòng)畫(huà)
    if (delayLeave) {
      delayLeave(vnode.el!, performRemove, performLeave)
    } else {
      performLeave()
    }
  } else {
    // 執(zhí)行
    performRemove()
  }
}

removeFragment

直接遍歷移除所有包含的節(jié)點(diǎn),這一點(diǎn)與移除靜態(tài)節(jié)點(diǎn)十分相似。

const removeFragment = (cur: RendererNode, end: RendererNode) => {
  // For fragments, directly remove all contained DOM nodes.
  // (fragment child nodes cannot have transition)
  let next
  while (cur !== end) {
    next = hostNextSibling(cur)!
    hostRemove(cur)
    cur = next
  }
  hostRemove(end)
}

unmountComponent

對(duì)于組件的卸載,步驟稍微多一點(diǎn)。畢竟除了要遍歷卸載子組件樹(shù),要處理組件的鉤子函數(shù),甚至考慮異步組件。

const unmountComponent = (
  instance: ComponentInternalInstance,
  parentSuspense: SuspenseBoundary | null,
  doRemove?: boolean
) => {
  if (__DEV__ && instance.type.__hmrId) {
    unregisterHMR(instance)
  }
  const { bum, scope, update, subTree, um } = instance
  // 調(diào)用 beforeUnmounted 鉤子
  // beforeUnmount hook
  if (bum) {
    invokeArrayFns(bum)
  }
  if (
    __COMPAT__ &&
    isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  ) {
    instance.emit('hook:beforeDestroy')
  }
  // 停止副作用
  // stop effects in component scope
  scope.stop()
  // 關(guān)閉 update,卸載子組件樹(shù)
  // update may be null if a component is unmounted before its async
  // setup has resolved.
  if (update) {
    // so that scheduler will no longer invoke it
    update.active = false
    unmount(subTree, instance, parentSuspense, doRemove)
  }
  // 調(diào)用unmounted鉤子
  // unmounted hook
  if (um) {
    queuePostRenderEffect(um, parentSuspense)
  }
  // 向后兼容:destroyed 鉤子
  if (
    __COMPAT__ &&
    isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  ) {
    queuePostRenderEffect(
      () => instance.emit('hook:destroyed'),
      parentSuspense
    )
  }
  // 更改狀態(tài)為已卸載
  queuePostRenderEffect(() => {
    instance.isUnmounted = true
  }, parentSuspense)
  // 處理<Suspense>
  // A component with async dep inside a pending suspense is unmounted before
  // its async dep resolves. This should remove the dep from the suspense, and
  // cause the suspense to resolve immediately if that was the last dep.
  if (
    __FEATURE_SUSPENSE__ &&
    parentSuspense &&
    parentSuspense.pendingBranch &&
    !parentSuspense.isUnmounted &&
    instance.asyncDep &&
    !instance.asyncResolved &&
    instance.suspenseId === parentSuspense.pendingId
  ) {
    parentSuspense.deps--
    if (parentSuspense.deps === 0) {
      parentSuspense.resolve()
    }
  }
  if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
    devtoolsComponentRemoved(instance)
  }
}

unmountChildren

卸載子節(jié)點(diǎn),遍歷遞歸unmount方法進(jìn)行卸載。

const unmountChildren: UnmountChildrenFn = (
  children,
  parentComponent,
  parentSuspense,
  doRemove = false,
  optimized = false,
  start = 0
) => {
  for (let i = start; i < children.length; i++) {
    unmount(children[i], parentComponent, parentSuspense, doRemove, optimized)
  }
}

小結(jié)

render只是個(gè)引子,絕大部分功能如節(jié)點(diǎn)掛載、節(jié)點(diǎn)更新都被patch涵蓋了。diff算法在同層級(jí)進(jìn)行遍歷比較,核心內(nèi)容都在patchKeyedChildren中,首尾節(jié)點(diǎn)各自循環(huán)一輪,對(duì)于中間的節(jié)點(diǎn),則利用Map來(lái)映射key和節(jié)點(diǎn)在新子節(jié)點(diǎn)組中的index,再遍歷剩余的舊子節(jié)點(diǎn)組,在Map中找相同的key里確定這個(gè)舊節(jié)點(diǎn)是否可復(fù)用。沒(méi)有key的情況則使用patchUnkeyedChildren進(jìn)行diff,簡(jiǎn)單粗暴。

以上就是Vue3源碼通過(guò)render patch 了解diff的詳細(xì)內(nèi)容,更多關(guān)于Vue3 render patch了解diff的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue3中實(shí)現(xiàn)雙向數(shù)據(jù)綁定的方法

    vue3中實(shí)現(xiàn)雙向數(shù)據(jù)綁定的方法

    Vue3中,雙向數(shù)據(jù)綁定主要通過(guò)v-model指令實(shí)現(xiàn),v-model是一個(gè)語(yǔ)法糖,結(jié)合了v-bind和v-on指令來(lái)實(shí)現(xiàn)數(shù)據(jù)的雙向綁定,它在內(nèi)部做了綁定數(shù)據(jù)和監(jiān)聽(tīng)輸入事件兩件事,感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • 詳解vue-validator(vue驗(yàn)證器)

    詳解vue-validator(vue驗(yàn)證器)

    本篇文章主要介紹了vue-validator(vue驗(yàn)證器),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • Vue使用mixins實(shí)現(xiàn)壓縮圖片代碼

    Vue使用mixins實(shí)現(xiàn)壓縮圖片代碼

    本篇文章主要介紹了Vue使用mixins實(shí)現(xiàn)壓縮圖片代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 關(guān)于Element-ui中el-table出現(xiàn)的表格錯(cuò)位問(wèn)題解決

    關(guān)于Element-ui中el-table出現(xiàn)的表格錯(cuò)位問(wèn)題解決

    使用ElementUI的el-table后,偶然發(fā)現(xiàn)出現(xiàn)行列錯(cuò)位、對(duì)不齊問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Element-ui中el-table出現(xiàn)的表格錯(cuò)位問(wèn)題解決的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Vue el使用el-checkbox-group復(fù)選框進(jìn)行單選框方式

    Vue el使用el-checkbox-group復(fù)選框進(jìn)行單選框方式

    這篇文章主要介紹了Vue el使用el-checkbox-group復(fù)選框進(jìn)行單選框方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue如何自定義事件傳參

    vue如何自定義事件傳參

    這篇文章主要介紹了vue如何自定義事件傳參,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • uniapp頁(yè)面完成水印添加功能代碼示例(自定義文字)

    uniapp頁(yè)面完成水印添加功能代碼示例(自定義文字)

    這篇文章主要介紹了uniapp頁(yè)面完成水印添加功能(自定義文字)的相關(guān)資料,包括如何編寫(xiě)頁(yè)面代碼、數(shù)據(jù)代碼以及如何展示效果圖,通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-01-01
  • vue自定義標(biāo)簽和單頁(yè)面多路由的實(shí)現(xiàn)代碼

    vue自定義標(biāo)簽和單頁(yè)面多路由的實(shí)現(xiàn)代碼

    這篇文章主要介紹了vue自定義標(biāo)簽和單頁(yè)面多路由的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • keep-Alive搭配vue-router實(shí)現(xiàn)緩存頁(yè)面效果的示例代碼

    keep-Alive搭配vue-router實(shí)現(xiàn)緩存頁(yè)面效果的示例代碼

    這篇文章主要介紹了keep-Alive搭配vue-router實(shí)現(xiàn)緩存頁(yè)面效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • vue中el-tab如何點(diǎn)擊不同標(biāo)簽觸發(fā)不同函數(shù)的實(shí)現(xiàn)

    vue中el-tab如何點(diǎn)擊不同標(biāo)簽觸發(fā)不同函數(shù)的實(shí)現(xiàn)

    el-tab本身的功能是點(diǎn)擊之后切換不同頁(yè),本文主要介紹了vue中el-tab如何點(diǎn)擊不同標(biāo)簽觸發(fā)不同函數(shù)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03

最新評(píng)論

大屁股肉感人妻中文字幕在线| 国产精品人妻一区二区三区网站| 四川乱子伦视频国产vip| 欧美老鸡巴日小嫩逼| 色伦色伦777国产精品| 欧美视频中文一区二区三区| av在线播放国产不卡| 亚洲在线免费h观看网站| 香蕉片在线观看av| 天美传媒mv视频在线观看| 大鸡巴操b视频在线| 日韩精品一区二区三区在线播放| 亚洲精品 日韩电影| 黄色黄色黄片78在线| 欧美美女人体视频一区| 成人乱码一区二区三区av| 色婷婷综合激情五月免费观看| 大鸡巴后入爆操大屁股美女| 欧美韩国日本国产亚洲| 亚洲推理片免费看网站| 青青草国内在线视频精选| 亚洲一区自拍高清免费视频| 成人H精品动漫在线无码播放| 91片黄在线观看喷潮| nagger可以指黑人吗| 适合午夜一个人看的视频| 亚洲精品精品国产综合| 国产在线一区二区三区麻酥酥| 视频一区二区在线免费播放| 中文字幕av第1页中文字幕| ka0ri在线视频| 日韩加勒比东京热二区| 在线观看911精品国产| 在线国产中文字幕视频| 888亚洲欧美国产va在线播放| aiss午夜免费视频| 黄色视频在线观看高清无码| 国产美女午夜福利久久| 黄片色呦呦视频免费看| 人妻激情图片视频小说| 亚洲免费在线视频网站| 免费黄页网站4188| 亚洲的电影一区二区三区| 视频久久久久久久人妻| 高潮喷水在线视频观看| 在线观看成人国产电影| 亚洲久久午夜av一区二区| av视屏免费在线播放| 免费在线黄色观看网站| 国产精品日韩欧美一区二区| 亚洲欧美人精品高清| 日韩中文字幕精品淫| 九色视频在线观看免费| 国产精品黄页网站视频| 国产欧美日韩在线观看不卡| 久久丁香花五月天色婷婷| 日本高清撒尿pissing| 岛国青草视频在线观看| 日韩伦理短片在线观看| 日韩美在线观看视频黄| 青青青视频手机在线观看| 污污小视频91在线观看| 在线视频自拍第三页| 91免费放福利在线观看| 一级A一级a爰片免费免会员| okirakuhuhu在线观看| 成人在线欧美日韩国产| 亚洲图片偷拍自拍区| www日韩a级s片av| 欧美偷拍亚洲一区二区| 女人精品内射国产99| 99热国产精品666| 久久久久久久99精品| 91免费观看国产免费| 欧美麻豆av在线播放| 欧美精产国品一二三产品区别大吗| 欧美亚洲少妇福利视频| 国产亚洲视频在线观看| 好了av中文字幕在线| 99久久久无码国产精品性出奶水| 亚洲欧美综合另类13p| 亚洲人一区二区中文字幕| 青草久久视频在线观看| 亚洲一区二区久久久人妻| 偷青青国产精品青青在线观看| 成年人黄色片免费网站| 91精品国产麻豆国产| 欧美日韩精品永久免费网址| 无码国产精品一区二区高潮久久4| 噜噜色噜噜噜久色超碰| 青青青青在线视频免费观看| 色秀欧美视频第一页| 国产自拍在线观看成人| 亚洲欧美福利在线观看| 经典国语激情内射视频| 大鸡巴操娇小玲珑的女孩逼| 欧美黑人性暴力猛交喷水| 88成人免费av网站| 激情五月婷婷综合色啪| 北条麻妃高跟丝袜啪啪| 天天干天天操天天插天天日| 中文字幕日韩精品就在这里| 天天干天天插天天谢| 国产一区二区欧美三区 | 少妇人妻二三区视频| 在线观看免费岛国av| 在线观看av亚洲情色| 老鸭窝日韩精品视频观看| 天堂女人av一区二区| 毛片av在线免费看| 边摸边做超爽毛片18禁色戒| 国产伦精品一区二区三区竹菊| 日韩av免费观看一区| 亚洲va欧美va人人爽3p| 日韩av有码中文字幕| 亚洲av无女神免非久久| 日本最新一二三区不卡在线| 亚洲免费va在线播放| 国产极品精品免费视频| 天天日天天干天天要| 家庭女教师中文字幕在线播放| 男人的天堂av日韩亚洲| 2017亚洲男人天堂| 日本韩国免费一区二区三区视频| 亚洲青青操骚货在线视频| 老司机欧美视频在线看| 亚洲色偷偷综合亚洲AV伊人| 国产福利在线视频一区| 亚洲特黄aaaa片| 新婚人妻聚会被中出| 亚洲一区制服丝袜美腿| 任我爽精品视频在线播放| 国产高潮无码喷水AV片在线观看| 亚洲激情av一区二区| 亚洲av无女神免非久久| 日本人妻少妇18—xx| aiss午夜免费视频| 国产综合视频在线看片| 日曰摸日日碰夜夜爽歪歪| 40道精品招牌菜特色| 亚洲少妇人妻无码精品| 六月婷婷激情一区二区三区| 成人av在线资源网站| 91麻豆精品传媒国产黄色片| 偷拍自拍亚洲美腿丝袜| 免费观看成年人视频在线观看| 久久久久久性虐视频| 国产亚洲天堂天天一区| 精品乱子伦一区二区三区免费播| 大胸性感美女羞爽操逼毛片| 亚洲中文字幕综合小综合| 不戴胸罩引我诱的隔壁的人妻| 国产欧美精品免费观看视频| 免费看美女脱光衣服的视频| 夜鲁夜鲁狠鲁天天在线| 边摸边做超爽毛片18禁色戒| 超碰97免费人妻麻豆| 亚洲偷自拍高清视频| 制服丝袜在线人妻中文字幕| 亚洲女人的天堂av| 日本丰满熟妇大屁股久久| 777奇米久久精品一区| 午夜精品一区二区三区更新| 欧美viboss性丰满| 日噜噜噜夜夜噜噜噜天天噜噜噜| 欧美专区第八页一区在线播放| 大鸡巴操娇小玲珑的女孩逼| 新婚人妻聚会被中出| 亚洲嫩模一区二区三区| 一区二区三区精品日本| avjpm亚洲伊人久久| 搡老妇人老女人老熟女| 国产之丝袜脚在线一区二区三区| 91色秘乱一区二区三区| 国产 在线 免费 精品| 啪啪啪啪啪啪啪免费视频| 亚洲精品ww久久久久久| 韩国女主播精品视频网站| 黄色男人的天堂视频| 小泽玛利亚视频在线观看| 日本欧美视频在线观看三区| 啊用力插好舒服视频| 人妻丰满熟妇综合网| 亚洲另类伦春色综合小| 人人人妻人人澡人人| 丝袜肉丝一区二区三区四区在线| 懂色av蜜桃a v| weyvv5国产成人精品的视频| 国产精品一二三不卡带免费视频| 亚洲一区二区三区精品乱码| 2022中文字幕在线| 狠狠地躁夜夜躁日日躁| 91九色国产porny蝌蚪| 75国产综合在线视频| 色97视频在线播放| 亚洲蜜臀av一区二区三区九色 | 亚洲av无女神免非久久| 国产内射中出在线观看| 欧美在线一二三视频| 精品一区二区三区在线观看| 日本一区精品视频在线观看| 中文字幕亚洲中文字幕| 在线观看黄色成年人网站| 中文字幕人妻av在线观看| 欧美爆乳肉感大码在线观看| 亚欧在线视频你懂的| 伊人开心婷婷国产av| 国产污污污污网站在线| 最新91九色国产在线观看| 动漫黑丝美女的鸡巴| 人人妻人人爱人人草| 色av色婷婷人妻久久久精品高清| 欧美黄色录像免费看的| 精品91高清在线观看| 美女日逼视频免费观看| 亚洲高清视频在线不卡| 亚洲 欧美 自拍 偷拍 在线| 成年美女黄网站18禁久久| 精品91高清在线观看| 亚洲Av无码国产综合色区| 天天艹天天干天天操| 色综合久久久久久久久中文| sw137 中文字幕 在线| 成人色综合中文字幕| 黄色录像鸡巴插进去| 精品人妻每日一部精品| 91免费观看国产免费| 亚洲国产精品中文字幕网站| 亚洲av无女神免非久久| 一二三中文乱码亚洲乱码one| 888欧美视频在线| 日韩av中文在线免费观看| 欧美一区二区三区激情啪啪啪 | 欧美va不卡视频在线观看 | 五月色婷婷综合开心网4438| 精品成人啪啪18免费蜜臀| 91人妻精品一区二区在线看| 一级黄片久久久久久久久| 日本后入视频在线观看| 一区二区视频在线观看免费观看 | 亚洲无码一区在线影院| 2021最新热播中文字幕| 亚洲一区久久免费视频| 成人性黑人一级av| 免费在线观看视频啪啪| 99热99re在线播放| 天堂中文字幕翔田av| 骚货自慰被发现爆操| 91免费观看国产免费| 亚洲熟妇无码一区二区三区| 亚洲av极品精品在线观看| 国产一区二区神马久久| 97年大学生大白天操逼| 亚洲激情唯美亚洲激情图片| 欧美中国日韩久久精品| 爆乳骚货内射骚货内射在线| 绯色av蜜臀vs少妇| 欧美日韩国产一区二区三区三州| 少妇人妻真实精品视频| 又粗又硬又猛又黄免费30| 天天日天天摸天天爱| 91九色porny国产蝌蚪视频| 初美沙希中文字幕在线| 日韩欧美亚洲熟女人妻| 最后99天全集在线观看| 亚洲久久午夜av一区二区| 99热99re在线播放| 1区2区3区4区视频在线观看| 一区二区三区蜜臀在线| 久久久久91精品推荐99| 亚洲精品午夜久久久久| 亚洲日产av一区二区在线 | wwwxxx一级黄色片| 成人网18免费视频版国产| 欧美地区一二三专区| 动漫精品视频在线观看| 97精品综合久久在线| 任你操任你干精品在线视频| 中国熟女一区二区性xx| 超pen在线观看视频公开97| 国产精品熟女久久久久浪潮| 最新的中文字幕 亚洲| 国产精品自拍在线视频| 国产成人精品午夜福利训2021| 激情综合治理六月婷婷| 大鸡巴操娇小玲珑的女孩逼| 丝袜肉丝一区二区三区四区在线 | 91久久国产成人免费网站| 91成人精品亚洲国产| 2020中文字幕在线播放| 好了av中文字幕在线| 亚洲成人av一区在线| 欧美色婷婷综合在线| 亚洲综合在线视频可播放| 中文字幕在线一区精品| 欧美一级视频一区二区| 非洲黑人一级特黄片| 日本乱人一区二区三区| 日韩av中文在线免费观看| 欧美日本在线观看一区二区| 国产第一美女一区二区三区四区| 天天插天天狠天天操| 夜夜操,天天操,狠狠操| 青青青青青青青青青青草青青| 日韩无码国产精品强奸乱伦| 骚逼被大屌狂草视频免费看| 中文字幕一区二区亚洲一区| 天天操天天操天天碰| 高清一区二区欧美系列| 大香蕉玖玖一区2区| 免费看美女脱光衣服的视频| 五月天久久激情视频| 80电影天堂网官网| 中文字幕av第1页中文字幕| 黄色无码鸡吧操逼视频| 影音先锋女人av噜噜色| 国产实拍勾搭女技师av在线| 91在线免费观看成人| 欧美成一区二区三区四区| av视屏免费在线播放| 成年美女黄网站18禁久久| 日本av高清免费网站| 日韩欧美中文国产在线| 99久久久无码国产精品性出奶水| 午夜毛片不卡免费观看视频| 偷青青国产精品青青在线观看| 在线观看av观看av| 久久精品在线观看一区二区 | eeuss鲁片一区二区三区| 精品乱子伦一区二区三区免费播| 91破解版永久免费| 成人伊人精品色xxxx视频| 人妻少妇一区二区三区蜜桃| 日比视频老公慢点好舒服啊| 国产大学生援交正在播放| 日韩精品中文字幕在线| 男女啪啪视频免费在线观看| 97香蕉碰碰人妻国产樱花| aiss午夜免费视频| 好吊操视频这里只有精品| 少妇一区二区三区久久久| 啪啪啪操人视频在线播放| 久久久人妻一区二区| 91麻豆精品秘密入口在线观看| 在线视频国产欧美日韩| 51国产成人精品视频| 日韩写真福利视频在线观看| 日韩欧美一级aa大片| 九九热99视频在线观看97| 人妻素人精油按摩中出| 91久久综合男人天堂| 老师让我插进去69AV| 国产老熟女伦老熟妇ⅹ| 日本美女性生活一级片| 日本熟妇色熟妇在线观看| 亚洲一区二区三区精品视频在线| 日本韩国亚洲综合日韩欧美国产| 久草视频首页在线观看| 中文字幕熟女人妻久久久| 无套猛戳丰满少妇人妻| 美女吃鸡巴操逼高潮视频| 青青青青爽手机在线| 老司机欧美视频在线看| 亚洲伊人久久精品影院一美女洗澡 | 国产免费av一区二区凹凸四季| 欧美地区一二三专区| 91麻豆精品传媒国产黄色片| 国产精品女邻居小骚货| 久草视频在线看免费| 91老师蜜桃臀大屁股| av在线资源中文字幕| 色婷婷精品大在线观看| 国产老熟女伦老熟妇ⅹ| jiujiure精品视频在线| 亚洲福利天堂久久久久久| 国产成人精品福利短视频| 韩国爱爱视频中文字幕| 亚洲午夜福利中文乱码字幕| jiuse91九色视频| 国产精品久久综合久久| 一级黄片久久久久久久久| 欧美3p在线观看一区二区三区| 亚洲一级美女啪啪啪| 国产实拍勾搭女技师av在线| 97国产在线观看高清| 日本www中文字幕| 只有精品亚洲视频在线观看| 精品少妇一二三视频在线| 最新激情中文字幕视频| 天堂中文字幕翔田av| 精品久久久久久久久久久a√国产| 麻豆性色视频在线观看| 综合激情网激情五月天| 一区二区三区四区视频在线播放| 人妻另类专区欧美制服| 91色九色porny| 欧美3p在线观看一区二区三区| 黑人性生活视频免费看| 不戴胸罩引我诱的隔壁的人妻| av手机在线观播放网站| 男生用鸡操女生视频动漫| 亚洲午夜福利中文乱码字幕| 天天射夜夜操狠狠干| 2020韩国午夜女主播在线| avjpm亚洲伊人久久| 美女小视频网站在线| av久久精品北条麻妃av观看 | 国产综合视频在线看片| 777奇米久久精品一区| 天天日天天爽天天干| 亚洲精品乱码久久久久久密桃明 | 大学生A级毛片免费视频| 精品国产午夜视频一区二区| 国产午夜无码福利在线看| 国产午夜福利av导航| 97a片免费在线观看| 国产高清女主播在线| 中国产一级黄片免费视频播放| 欧美日本在线观看一区二区| 91p0rny九色露脸熟女| 精品一区二区三区三区88| 亚洲2021av天堂| 精品一区二区亚洲欧美| 亚洲蜜臀av一区二区三区九色| 国产av自拍偷拍盛宴| asmr福利视频在线观看| 国产精品伦理片一区二区| 大胆亚洲av日韩av| 天天干天天搞天天摸| 中国熟女一区二区性xx| 日韩成人性色生活片| 天天射夜夜操综合网| 精品欧美一区二区vr在线观看| 班长撕开乳罩揉我胸好爽| 亚洲国产第一页在线观看| 巨乳人妻日下部加奈被邻居中出| 国产密臀av一区二区三| 无码日韩人妻精品久久| 国产精品久久久久久久久福交| 国产日韩精品电影7777| 大鸡巴操b视频在线| 日本特级片中文字幕| 亚洲欧美清纯唯美另类 | 在线免费观看黄页视频| 91亚洲手机在线视频播放| 91国产资源在线视频| 日韩一区二区电国产精品| 一区二区三区的久久的蜜桃的视频| 亚洲男人的天堂a在线| 淫秽激情视频免费观看| 99久久成人日韩欧美精品| 日韩激情文学在线视频| 亚洲 中文字幕在线 日韩| 日本一本午夜在线播放| 亚洲第一黄色在线观看| 大香蕉玖玖一区2区| 国产性色生活片毛片春晓精品| 国产剧情演绎系列丝袜高跟| 一色桃子久久精品亚洲| 蜜桃视频17c在线一区二区| 亚洲伊人色一综合网| 欧美区一区二区三视频| 午夜蜜桃一区二区三区| av在线播放国产不卡| 免费观看理论片完整版| 97人妻色免费视频| 91精品啪在线免费| 高潮喷水在线视频观看| 丰满的子国产在线观看| 天天日天天天天天天天天天天| 黄色片年轻人在线观看| 自拍偷拍亚洲欧美在线视频| av天堂加勒比在线| 久久农村老妇乱69系列| 亚洲午夜伦理视频在线| 75国产综合在线视频| 99久久超碰人妻国产| 99热这里只有国产精品6| 精品91高清在线观看| 日本乱人一区二区三区| 成年人午夜黄片视频资源| 国产九色91在线观看精品| 懂色av蜜桃a v| 亚洲欧美激情中文字幕| 在线视频国产欧美日韩| 韩国黄色一级二级三级| 色偷偷伊人大杳蕉综合网| 国产97视频在线精品| 最新国产精品网址在线观看| 肏插流水妹子在线乐播下载| 51国产偷自视频在线播放| 视频在线免费观看你懂得| 久久久久久久99精品| 亚洲av极品精品在线观看| 天天草天天色天天干| 亚洲最大黄了色网站| 黑人巨大精品欧美视频| 搡老熟女一区二区在线观看| 国产精品精品精品999| 毛茸茸的大外阴中国视频| 扒开让我视频在线观看| 大鸡八强奸视频在线观看| 成人性黑人一级av| 色呦呦视频在线观看视频| 少妇系列一区二区三区视频| 91久久国产成人免费网站| 2021久久免费视频| 91超碰青青中文字幕| 91人妻精品久久久久久久网站| 久青青草视频手机在线免费观看| 色呦呦视频在线观看视频| 人妻少妇性色欲欧美日韩| 在线免费观看欧美小视频| 又大又湿又爽又紧A视频| 人人超碰国字幕观看97| 精品少妇一二三视频在线| 亚洲综合在线观看免费| 99热国产精品666| 久碰精品少妇中文字幕av| 日韩三级黄色片网站| 九九热99视频在线观看97| 中文字幕 亚洲av| 国产视频精品资源网站| 成年午夜免费无码区| 亚洲一级av无码一级久久精品| 91精品激情五月婷婷在线| 国产揄拍高清国内精品对白| 岛国av高清在线成人在线| 国产无遮挡裸体免费直播视频| 伊人成人综合开心网| 九九视频在线精品播放| 9久在线视频只有精品| 亚洲 中文 自拍 另类 欧美| 熟女人妻一区二区精品视频| av天堂中文字幕最新| 99久久中文字幕一本人| 欧美日韩国产一区二区三区三州| 无码国产精品一区二区高潮久久4| 国产又色又刺激在线视频| 男人的天堂在线黄色| 男人插女人视频网站| 91试看福利一分钟| 福利视频网久久91| 高清成人av一区三区| 无码国产精品一区二区高潮久久4| 91大屁股国产一区二区| 在线观看911精品国产| 亚洲av无乱一区二区三区性色 | 色秀欧美视频第一页| 97人妻总资源视频| 国产精品一区二区三区蜜臀av| 成人30分钟免费视频| 人妻丝袜榨强中文字幕| 国产精品久久久久久久女人18| 2020av天堂网在线观看| 蜜桃视频17c在线一区二区| 欧亚日韩一区二区三区观看视频| 亚洲欧洲一区二区在线观看| 老司机免费视频网站在线看| 女警官打开双腿沦为性奴| 黑人解禁人妻叶爱071| 亚洲人妻视频在线网| 亚洲自拍偷拍精品网| 小泽玛利亚视频在线观看| 亚洲欧美激情国产综合久久久| 99婷婷在线观看视频| 欧洲日韩亚洲一区二区三区| 日本特级片中文字幕| 婷婷综合亚洲爱久久| 国产高潮无码喷水AV片在线观看| 日韩美女综合中文字幕pp| 97a片免费在线观看| 国产免费av一区二区凹凸四季| 久久综合老鸭窝色综合久久 | 欧美精品一区二区三区xxxx| 精品少妇一二三视频在线| 国产麻豆剧果冻传媒app| 亚洲第一黄色在线观看| 一区二区免费高清黄色视频| 中文字幕一区二 区二三区四区| 在线免费观看国产精品黄色| 午夜在线观看一区视频| 天天日天天干天天舔天天射| 国产 在线 免费 精品| 亚洲一级美女啪啪啪| 亚洲天堂精品福利成人av| 人妻素人精油按摩中出| 1024久久国产精品| 日韩精品激情在线观看| 美女在线观看日本亚洲一区| 内射久久久久综合网| 黄色录像鸡巴插进去| 一本久久精品一区二区| av天堂中文免费在线| 天天综合天天综合天天网| 黄色男人的天堂视频| 色婷婷六月亚洲综合香蕉| 日本一本午夜在线播放| 视频二区在线视频观看| 污污小视频91在线观看| 宅男噜噜噜666免费观看| 偷拍自拍亚洲视频在线观看| 黑人3p华裔熟女普通话| 精品美女福利在线观看| 日韩欧美制服诱惑一区在线| 欧洲黄页网免费观看| 欧美精产国品一二三产品价格| 韩国男女黄色在线观看| 欧美xxx成人在线| 97人妻总资源视频| 日韩精品二区一区久久| 视频久久久久久久人妻| 国产性色生活片毛片春晓精品| 成人精品视频99第一页| 福利视频广场一区二区| 经典av尤物一区二区| 熟女俱乐部一二三区| 久久这里只有精品热视频| 在线免费91激情四射| 秋霞午夜av福利经典影视| 大香蕉福利在线观看| av无限看熟女人妻另类av| av老司机精品在线观看| 美女骚逼日出水来了| 日韩成人免费电影二区| 国产露脸对白在线观看| 久久久精品欧洲亚洲av| 久久热这里这里只有精品| 亚洲人人妻一区二区三区| 2020国产在线不卡视频| 天天色天天操天天舔| 五十路息与子猛烈交尾视频 | 91传媒一区二区三区| 中文字幕av男人天堂| 在线免费观看靠比视频的网站 | 午夜频道成人在线91| 888欧美视频在线| 播放日本一区二区三区电影| 亚洲精品国产在线电影| 熟女91pooyn熟女| 中国把吊插入阴蒂的视频| 美女av色播在线播放| 超碰97人人澡人人| 日本少妇在线视频大香蕉在线观看| 91国偷自产一区二区三区精品| 国产成人自拍视频在线免费观看| 中文字幕之无码色多多| 欧美一区二区三区啪啪同性| 一区二区三区国产精选在线播放| 亚洲中文字幕国产日韩| 精品一区二区三区午夜| 亚洲精品高清自拍av| 日韩欧美一级aa大片| 国产又大又黄免费观看| 久草福利电影在线观看| 自拍偷拍亚洲另类色图| 9色精品视频在线观看| 青青草亚洲国产精品视频| 日韩美在线观看视频黄| 美女大bxxxx内射| 日本a级视频老女人| 国产精品国产三级国产午| 成年人免费看在线视频| 亚洲精品av在线观看| 亚洲在线一区二区欧美| 老司机免费福利视频网| 激情啪啪啪啪一区二区三区| 日本美女性生活一级片| 国产av自拍偷拍盛宴| 韩国三级aaaaa高清视频| 人妻丝袜诱惑我操她视频| 亚洲精品精品国产综合| 国产一区二区神马久久| 绯色av蜜臀vs少妇| 欧美另类一区二区视频| 成人高潮aa毛片免费| 在线免费观看99视频| 亚洲丝袜老师诱惑在线观看| 日本一道二三区视频久久| 超污视频在线观看污污污 | 亚洲自拍偷拍精品网| 在线播放一区二区三区Av无码| 精品欧美一区二区vr在线观看 | 91国产在线免费播放| 视频一区二区三区高清在线| 国产亚洲欧美45p| 中文字幕一区二区三区人妻大片 | 欧洲精品第一页欧洲精品亚洲| 亚洲伊人久久精品影院一美女洗澡 | www,久久久,com| 18禁精品网站久久| 中文字幕乱码人妻电影| 亚洲 中文 自拍 另类 欧美| 少妇被强干到高潮视频在线观看 | 国语对白xxxx乱大交| 亚洲一区二区三区uij| 久青青草视频手机在线免费观看| 日韩欧美高清免费在线 | 人妻少妇亚洲一区二区| 国产午夜亚洲精品麻豆| 色婷婷精品大在线观看| 成人性爱在线看四区| 亚洲中文字幕乱码区| 在线播放 日韩 av| 国产普通话插插视频| 可以免费看的www视频你懂的| 五月天久久激情视频| 亚洲区美熟妇久久久久| 午夜激情久久不卡一区二区| yy6080国产在线视频| 性感美女高潮视频久久久| 人妻自拍视频中国大陆| 精品人妻每日一部精品| 日韩欧美中文国产在线| 亚洲欧美成人综合视频| 国产亚洲四十路五十路| weyvv5国产成人精品的视频| 白白操白白色在线免费视频| 亚洲欧美清纯唯美另类| 66久久久久久久久久久| 99热碰碰热精品a中文| 欧美一级视频一区二区| 日本美女性生活一级片| 亚洲一级特黄特黄黄色录像片| 中文字幕第一页国产在线| 水蜜桃一区二区三区在线观看视频| 春色激情网欧美成人| 亚洲美女高潮喷浆视频| 成人精品在线观看视频| 五月天中文字幕内射| 日本特级片中文字幕| a v欧美一区=区三区| 久久精品国产999| 93人妻人人揉人人澡人人| 中文字幕一区二 区二三区四区| 欧美日本在线观看一区二区| 97超碰国语国产97超碰| 一本久久精品一区二区| 伊人综合免费在线视频| 国产黄色片在线收看| 蜜桃精品久久久一区二区| 日韩人妻xxxxx| 亚洲偷自拍高清视频| 亚洲一区二区三区偷拍女厕91| 欧美一区二区三区久久久aaa| 天天干夜夜操天天舔| 少妇人妻100系列| 国产精品一二三不卡带免费视频| mm131美女午夜爽爽爽| 天天摸天天干天天操科普| 欧美日韩人妻久久精品高清国产| 欧美在线偷拍视频免费看| 黄色片黄色片wyaa| 午夜在线一区二区免费| 中文字幕中文字幕人妻| 国产麻豆国语对白露脸剧情| 日本欧美视频在线观看三区| 亚洲熟女综合色一区二区三区四区| 九一传媒制片厂视频在线免费观看| 欧美va不卡视频在线观看| 国产精品亚洲а∨天堂免| 性色av一区二区三区久久久| 韩国亚洲欧美超一级在线播放视频| 国产日本欧美亚洲精品视| av网站色偷偷婷婷网男人的天堂| 丝袜美腿欧美另类 中文字幕| 熟女人妻一区二区精品视频| 99re6热在线精品| 最新91精品视频在线| 免费看国产av网站| 欧洲国产成人精品91铁牛tv| 97超碰最新免费在线观看| 欧美精产国品一二三区| 人人妻人人澡欧美91精品| 亚洲欧美久久久久久久久| 2025年人妻中文字幕乱码在线| 99精品视频之69精品视频| 岛国青草视频在线观看| 岛国免费大片在线观看| 中文字幕高清免费在线人妻| 久久久久久九九99精品| 日韩在线视频观看有码在线| 国产亚洲视频在线二区| 激情啪啪啪啪一区二区三区| 国产午夜无码福利在线看| 青青青青青青青青青国产精品视频| 不戴胸罩引我诱的隔壁的人妻| 天天日天天做天天日天天做| 亚洲av一妻不如妾| 亚洲综合自拍视频一区| 久久久精品国产亚洲AV一| 91高清成人在线视频| eeuss鲁片一区二区三区| 日韩近亲视频在线观看| 偷青青国产精品青青在线观看| sspd152中文字幕在线| 天美传媒mv视频在线观看| 精品91高清在线观看 | 天天艹天天干天天操| 国产第一美女一区二区三区四区| 午夜久久久久久久99| 美女av色播在线播放| 操的小逼流水的文章| 国产精品福利小视频a| 天天操夜夜操天天操天天操| 中文字幕av第1页中文字幕| 综合激情网激情五月五月婷婷| 99re6热在线精品| 国产成人自拍视频播放 | 亚洲中文精品字幕在线观看| 午夜蜜桃一区二区三区| 中文字幕在线观看极品视频| 日韩午夜福利精品试看| 青青青青青青草国产| 国产高清精品极品美女| 天天操天天干天天艹| 顶级尤物粉嫩小尤物网站| 熟女妇女老妇一二三区| 久久久精品欧洲亚洲av| 国产清纯美女al在线| 国产成人精品一区在线观看| 全国亚洲男人的天堂| 一区二区三区四区五区性感视频| 99精品视频之69精品视频 | 日本熟女50视频免费| 日韩在线视频观看有码在线| 午夜免费体验区在线观看| 2012中文字幕在线高清| 亚洲免费成人a v| 日本韩国免费一区二区三区视频 | 日韩欧美一级黄片亚洲| 欧美熟妇一区二区三区仙踪林| 日噜噜噜夜夜噜噜噜天天噜噜噜| 免费在线观看视频啪啪| 11久久久久久久久久久| 天天日夜夜操天天摸| 538精品在线观看视频| 欲满人妻中文字幕在线| 国产精品日韩欧美一区二区| 午夜av一区二区三区| 男人在床上插女人视频| 日韩精品中文字幕福利| 色婷婷六月亚洲综合香蕉| 欧洲黄页网免费观看| 黑人巨大精品欧美视频| 国产自拍在线观看成人| 九色视频在线观看免费| 色婷婷六月亚洲综合香蕉| 色在线观看视频免费的| 精品suv一区二区69| 538精品在线观看视频| 婷婷色中文亚洲网68| 天天射夜夜操综合网| 欧美另类z0z变态| 亚洲国产香蕉视频在线播放| 搡老妇人老女人老熟女| 黄色在线观看免费观看在线| 欧亚乱色一区二区三区| 操操网操操伊剧情片中文字幕网| 91片黄在线观看喷潮| 风流唐伯虎电视剧在线观看| 97国产在线av精品| 大香蕉大香蕉在线看| 欧美成人小视频在线免费看 | 国产福利在线视频一区| 99精品国自产在线人| 日本xx片在线观看| 性感美女诱惑福利视频| 天天艹天天干天天操| 日本成人不卡一区二区| 夜色17s精品人妻熟女| 91精品国产高清自在线看香蕉网| 欧美美女人体视频一区| 看一级特黄a大片日本片黑人| 久久精品亚洲成在人线a| 欧美一区二区三区久久久aaa| a v欧美一区=区三区| 亚洲无线观看国产高清在线| 97精品人妻一区二区三区精品| 经典av尤物一区二区| 亚洲av日韩高清hd| 天天综合天天综合天天网| 2020中文字幕在线播放| 亚洲av男人天堂久久| 日韩欧美高清免费在线| 不戴胸罩引我诱的隔壁的人妻| 国产揄拍高清国内精品对白| 欧洲亚洲欧美日韩综合| 国产熟妇一区二区三区av| 沙月文乃人妻侵犯中文字幕在线| 狍和女人的王色毛片| 18禁美女黄网站色大片下载| 中文字幕奴隷色的舞台50| heyzo蜜桃熟女人妻| 日本成人一区二区不卡免费在线| 国产精品一二三不卡带免费视频 | 日本免费视频午夜福利视频| 精品一区二区三四区| 国产大鸡巴大鸡巴操小骚逼小骚逼| 青青青视频自偷自拍38碰| 欧美视频中文一区二区三区| 成人av电影免费版| 大香蕉伊人国产在线| 人妻丰满熟妇综合网| 国产精品自拍在线视频| 精品一区二区三四区| 亚洲 中文 自拍 另类 欧美| 天堂av中文在线最新版| 日本一区二区三区免费小视频| 91啪国自产中文字幕在线| 欧亚日韩一区二区三区观看视频 | 亚洲女人的天堂av| 这里有精品成人国产99| 97精品综合久久在线| 特级无码毛片免费视频播放| 国产一线二线三线的区别在哪| 欧美乱妇无乱码一区二区| 大陆胖女人与丈夫操b国语高清| 亚洲天天干 夜夜操| 精品亚洲中文字幕av| 2020av天堂网在线观看| 国产成人精品亚洲男人的天堂| 日韩无码国产精品强奸乱伦| 成年人黄色片免费网站| 最近的中文字幕在线mv视频| 大陆av手机在线观看| 青青青青草手机在线视频免费看| 视频久久久久久久人妻| www天堂在线久久| 亚洲福利精品视频在线免费观看| 久久这里有免费精品| 2020韩国午夜女主播在线| 大香蕉福利在线观看| 国产高清精品极品美女| 亚洲欧美日韩视频免费观看| 一区二区三区日韩久久| 亚洲免费va在线播放| 国产在线一区二区三区麻酥酥| 男人天堂av天天操| 日韩北条麻妃一区在线| 久久丁香婷婷六月天| 97人人模人人爽人人喊| 亚洲成人三级在线播放| av中文字幕在线导航| 亚洲av日韩精品久久久| 晚上一个人看操B片| 色伦色伦777国产精品| 黑人解禁人妻叶爱071| 第一福利视频在线观看| 日韩视频一区二区免费观看| 国产第一美女一区二区三区四区| 精品高跟鞋丝袜一区二区| 婷婷综合亚洲爱久久| 亚洲变态另类色图天堂网| 日本黄在免费看视频| 亚洲欧美另类自拍偷拍色图| 绯色av蜜臀vs少妇| 男女第一次视频在线观看| 日本中文字幕一二区视频| 午夜在线观看一区视频| 天天草天天色天天干| 美女视频福利免费看| 最新91精品视频在线| 亚洲一级 片内射视正片| 人人妻人人澡欧美91精品| av新中文天堂在线网址| 成人免费做爰高潮视频| 日本美女性生活一级片| 2017亚洲男人天堂| 真实国产乱子伦一区二区| 亚洲 色图 偷拍 欧美| 日韩美女综合中文字幕pp| 五月激情婷婷久久综合网| 国产成人精品亚洲男人的天堂| 一区二区在线观看少妇| 福利视频一区二区三区筱慧| 欧美久久一区二区伊人| 91免费放福利在线观看| 动漫av网站18禁| 亚洲区美熟妇久久久久| 亚洲精品无码久久久久不卡| 欧美日韩高清午夜蜜桃大香蕉| 成人高潮aa毛片免费| 一区二区视频视频视频| 在线网站你懂得老司机| 偷拍自拍 中文字幕| 精品国产乱码一区二区三区乱| 非洲黑人一级特黄片| 国产午夜男女爽爽爽爽爽视频| 毛片av在线免费看| 91中文字幕免费在线观看| 馒头大胆亚洲一区二区| 视频 国产 精品 熟女 | 成人色综合中文字幕| 黑人性生活视频免费看| 九九热99视频在线观看97| 亚洲欧美综合在线探花| 丝袜亚洲另类欧美变态| 国产黑丝高跟鞋视频在线播放 | 亚洲天堂有码中文字幕视频| 国产综合高清在线观看| 精品亚洲中文字幕av| 青青青青青青青青青国产精品视频 | 99久久久无码国产精品性出奶水| 欧美精品 日韩国产| 亚洲成人av一区久久| 精品日产卡一卡二卡国色天香| 999热精品视频在线| 中文字幕日韩精品日本| 天天日天天添天天爽| 国产精品久久久久久久久福交| 国产精品系列在线观看一区二区| 在线免费观看靠比视频的网站 | 抽查舔水白紧大视频| 2018在线福利视频| 欧美熟妇一区二区三区仙踪林| 大鸡巴后入爆操大屁股美女| asmr福利视频在线观看| 亚洲av自拍天堂网| av完全免费在线观看av| 偷拍自拍亚洲美腿丝袜| 粉嫩av懂色av蜜臀av| 青青青青在线视频免费观看| 精品老妇女久久9g国产| 亚洲中文字幕校园春色| av网址在线播放大全| 日本人妻少妇18—xx| 青青青青青免费视频| 亚洲一区制服丝袜美腿| 在线观看操大逼视频| 亚洲中文字幕乱码区| 亚洲蜜臀av一区二区三区九色 | 免费观看国产综合视频| 欧美麻豆av在线播放| 天天日天天干天天干天天日| 性欧美激情久久久久久久 | 大香蕉福利在线观看| 国产之丝袜脚在线一区二区三区 | 手机看片福利盒子日韩在线播放 | 白白操白白色在线免费视频 | 这里只有精品双飞在线播放| 亚洲女人的天堂av| 亚洲最大黄 嗯色 操 啊| 国产黄色高清资源在线免费观看| 欧美日韩高清午夜蜜桃大香蕉 | 亚洲欧美国产综合777| 亚洲综合在线观看免费| 日韩美女综合中文字幕pp| 国产97在线视频观看| 天天干天天操天天玩天天射| av中文字幕在线观看第三页| 国产品国产三级国产普通话三级| 欧美日韩一级黄片免费观看| 国产精品国色综合久久| 欧美viboss性丰满| 亚洲成人黄色一区二区三区| 成人av天堂丝袜在线观看| 中文字幕视频一区二区在线观看| 国产女人露脸高潮对白视频| 中文字幕日韩无敌亚洲精品| 天天艹天天干天天操| 熟女在线视频一区二区三区| 国产三级片久久久久久久| 黄色视频成年人免费观看| 国产在线拍揄自揄视频网站| 中国把吊插入阴蒂的视频| 在线视频自拍第三页| 日本少妇高清视频xxxxx| 欧美成人黄片一区二区三区| 区一区二区三国产中文字幕| 亚洲av一妻不如妾| 一区二区在线视频中文字幕 | 亚洲黄色av网站免费播放| 日本18禁久久久久久| 亚洲一区二区三区久久受| 91精品国产麻豆国产| 精品高潮呻吟久久av| 亚洲自拍偷拍综合色| 99热久久这里只有精品8| 青青青青青手机视频| 91人妻精品一区二区在线看| 91色老99久久九九爱精品| 国产精品国色综合久久 | 漂亮 人妻被中出中文| 直接能看的国产av| 亚洲国产第一页在线观看| 日韩欧美一级精品在线观看| 国产极品美女久久久久久| 2020国产在线不卡视频| 99av国产精品欲麻豆| 国产真实灌醉下药美女av福利| 国产一区二区三免费视频| 91大神福利视频网| 99热这里只有精品中文| 中文字幕在线欧美精品| 精品人人人妻人人玩日产欧| 欧美黑人与人妻精品| 亚洲变态另类色图天堂网| 91超碰青青中文字幕| 成人亚洲国产综合精品| 日美女屁股黄邑视频| 亚洲一区二区三区久久午夜| 久久三久久三久久三久久| 国产三级精品三级在线不卡| 日韩欧美高清免费在线| 天天操天天干天天插| 国产精品久久久久久久精品视频| av中文字幕福利网| 国产视频在线视频播放| 久久久精品999精品日本| 动色av一区二区三区| 欧美成人综合视频一区二区| 久久三久久三久久三久久| 亚国产成人精品久久久| 高潮视频在线快速观看国家快速| 国产91嫩草久久成人在线视频| 五月精品丁香久久久久福利社| 香蕉av影视在线观看| 啊用力插好舒服视频| 亚洲va国产va欧美va在线| 二区中出在线观看老师| 美女被肏内射视频网站| 3D动漫精品啪啪一区二区下载| 午夜在线观看一区视频| 国产高清精品极品美女| 夫妻在线观看视频91| caoporm超碰国产| 欧美精品国产综合久久| 综合激情网激情五月天| 国产精品系列在线观看一区二区| 国产 在线 免费 精品| 性色蜜臀av一区二区三区| 亚洲免费在线视频网站| 色综合久久无码中文字幕波多| 免费看美女脱光衣服的视频| 超污视频在线观看污污污| 青青草原网站在线观看| 在线免费观看日本片| 欧美美女人体视频一区| 久久这里只有精品热视频 | 一区二区三区综合视频| 成人激情文学网人妻 | 精品乱子伦一区二区三区免费播| 天堂av在线播放免费| 欧美黑人性暴力猛交喷水| 欧美亚洲免费视频观看| okirakuhuhu在线观看| 美女福利视频导航网站| 欧美一区二区三区在线资源| 国产午夜男女爽爽爽爽爽视频 | 久久这里有免费精品| 国产97视频在线精品| xxx日本hd高清| 久久久久久久亚洲午夜综合福利| 92福利视频午夜1000看| 久草视频中文字幕在线观看| 年轻的人妻被夫上司侵犯| 偷拍自拍亚洲美腿丝袜| 91福利视频免费在线观看| 欧美精品 日韩国产| 女生被男生插的视频网站| 11久久久久久久久久久| av黄色成人在线观看| av日韩在线观看大全| wwwxxx一级黄色片| 日本性感美女写真视频| 久久国产精品精品美女| 亚洲欧美在线视频第一页| 91精品视频在线观看免费| 人妻在线精品录音叫床| 不卡日韩av在线观看| av在线播放国产不卡| 91麻豆精品传媒国产黄色片| 98视频精品在线观看| 精品美女在线观看视频在线观看| 久精品人妻一区二区三区| chinese国产盗摄一区二区| 成人亚洲国产综合精品| 91色网站免费在线观看| 和邻居少妇愉情中文字幕| 制服丝袜在线人妻中文字幕| 97人妻无码AV碰碰视频| 日韩中文字幕精品淫| 自拍 日韩 欧美激情| 韩国AV无码不卡在线播放| 一区二区麻豆传媒黄片| 久久一区二区三区人妻欧美| 女生被男生插的视频网站| 特一级特级黄色网片| 男人的天堂在线黄色| 国产日韩精品一二三区久久久| 深夜男人福利在线观看| 国产精品人妻熟女毛片av久| 国产三级精品三级在线不卡| 在线免费观看欧美小视频| 日本中文字幕一二区视频| 少妇人妻久久久久视频黄片| 自拍偷拍vs一区二区三区| 美女被肏内射视频网站| 亚洲视频在线观看高清| 亚洲图片偷拍自拍区| 天天射,天天操,天天说| 亚洲麻豆一区二区三区| 日韩黄色片在线观看网站| 色婷婷六月亚洲综合香蕉| 国产日韩欧美视频在线导航 | 韩国黄色一级二级三级| 亚洲综合另类欧美久久| 2022天天干天天操| 亚洲激情偷拍一区二区| 视频在线亚洲一区二区| aaa久久久久久久久| aiss午夜免费视频| 天天射,天天操,天天说| okirakuhuhu在线观看| 2012中文字幕在线高清| 18禁美女黄网站色大片下载| 国产午夜亚洲精品麻豆| 中文字幕 人妻精品| 欧洲黄页网免费观看| 欧美另类一区二区视频| 亚洲av日韩精品久久久久久hd| 2021久久免费视频| 久草视频在线免播放| 1024久久国产精品| caoporn蜜桃视频| 黄色大片男人操女人逼| 久草视频 久草视频2| 性感美女高潮视频久久久| 日本最新一二三区不卡在线| 亚洲精品亚洲人成在线导航| 不戴胸罩引我诱的隔壁的人妻| 三级av中文字幕在线观看| chinese国产盗摄一区二区| 亚洲一区二区三区精品乱码| 亚洲成a人片777777| 精品久久久久久久久久久久人妻| 国产精品一区二区av国| 98视频精品在线观看| 绝色少妇高潮3在线观看| 喷水视频在线观看这里只有精品| av在线播放国产不卡| 一级黄片大鸡巴插入美女| 精品suv一区二区69| 99av国产精品欲麻豆| 成人蜜桃美臀九一一区二区三区| 99av国产精品欲麻豆| 天堂av在线官网中文| 亚洲午夜电影之麻豆| 亚洲一区二区激情在线| 黑人变态深video特大巨大| 亚洲最大免费在线观看| 春色激情网欧美成人| 亚洲精品乱码久久久本| 天美传媒mv视频在线观看| 亚洲在线免费h观看网站| 风流唐伯虎电视剧在线观看 | av在线免费观看亚洲天堂| 亚洲天堂精品福利成人av| 青青青国产片免费观看视频| 亚洲精品 日韩电影| 日本脱亚入欧是指什么| 可以在线观看的av中文字幕| 日韩视频一区二区免费观看| 激情五月婷婷免费视频| 久久艹在线观看视频| 免费成人av中文字幕| 538精品在线观看视频| 97资源人妻免费在线视频| 人妻激情图片视频小说| 伊拉克及约旦宣布关闭领空| 免费在线观看视频啪啪| 欧美成人猛片aaaaaaa| yy96视频在线观看| 日本熟女精品一区二区三区| 99re久久这里都是精品视频| 一区二区三区欧美日韩高清播放| 大香蕉福利在线观看| 免费成人av中文字幕| 红杏久久av人妻一区| 91国产资源在线视频| 18禁污污污app下载| 9色在线视频免费观看| 激情图片日韩欧美人妻| 久久丁香婷婷六月天| 欧美黑人性猛交xxxxⅹooo| 免费在线看的黄片视频| 亚洲另类综合一区小说| 亚洲av无硬久久精品蜜桃| 黄色av网站免费在线| 中文字幕日韩精品日本| 五十路熟女人妻一区二区9933| 日韩欧美国产一区不卡| 99re国产在线精品| 在线观看的黄色免费网站| 亚洲国产精品中文字幕网站| 久青青草视频手机在线免费观看 | 亚洲久久午夜av一区二区| 成人性黑人一级av| weyvv5国产成人精品的视频| 国产精品国产三级国产精东 | 国产内射中出在线观看| 激情五月婷婷综合色啪| 99的爱精品免费视频| 免费在线播放a级片| 国产91久久精品一区二区字幕| 成熟丰满熟妇高潮xx×xx| 日韩在线视频观看有码在线| 青青青艹视频在线观看| 久久农村老妇乱69系列| 欧美精品资源在线观看| 久久久久久久亚洲午夜综合福利| 久久99久久99精品影院| 中文字幕av一区在线观看| 亚洲色偷偷综合亚洲AV伊人| 66久久久久久久久久久| 国产熟妇乱妇熟色T区| 99re国产在线精品| 日本熟妇色熟妇在线观看| 超碰97人人澡人人| 国产成人小视频在线观看无遮挡| 超碰97免费人妻麻豆| 午夜美女少妇福利视频| 大香蕉伊人国产在线| 天堂va蜜桃一区入口| 午夜国产福利在线观看| 久久精品亚洲成在人线a| 天天干天天日天天谢综合156| 男人的天堂在线黄色| 青青色国产视频在线| 亚洲 国产 成人 在线| 最新激情中文字幕视频| 在线免费观看av日韩| 熟女国产一区亚洲中文字幕| 成年人啪啪视频在线观看| 成人亚洲精品国产精品| 国产亚洲视频在线观看| 大屁股肉感人妻中文字幕在线| 丰满少妇人妻xxxxx| 北条麻妃av在线免费观看| 欧美偷拍亚洲一区二区| 99精品免费观看视频| 亚洲福利精品视频在线免费观看| 日韩成人性色生活片| 美女小视频网站在线| 日本免费视频午夜福利视频| 国产高清精品极品美女| 青青青视频手机在线观看| 美女 午夜 在线视频| 久久午夜夜伦痒痒想咳嗽P| 熟女妇女老妇一二三区| 免费69视频在线看| 一区二区三区国产精选在线播放| 玩弄人妻熟妇性色av少妇| 国产精品黄色的av| 成人av免费不卡在线观看| 中文字幕在线观看国产片| 九色精品视频在线播放| 高潮视频在线快速观看国家快速| 抽查舔水白紧大视频| 国产又粗又黄又硬又爽| 丰满少妇人妻xxxxx| 亚洲中文字幕校园春色| 一色桃子人妻一区二区三区| 亚洲高清自偷揄拍自拍| 精品91自产拍在线观看一区| 久久精品视频一区二区三区四区| 人人在线视频一区二区| 性欧美日本大妈母与子| 亚洲精品久久视频婷婷| 黑人解禁人妻叶爱071| 国产一区二区久久久裸臀| 亚洲高清国产拍青青草原| 久久国产精品精品美女| 欧美交性又色又爽又黄麻豆| 亚洲欧美国产麻豆综合| 91色秘乱一区二区三区| 国产精品久久久久久久女人18| 沙月文乃人妻侵犯中文字幕在线 | 日本熟妇丰满厨房55| 中文字幕 人妻精品| 中国熟女一区二区性xx| 日韩欧美在线观看不卡一区二区| 91人妻精品一区二区在线看| 40道精品招牌菜特色| 国产亚洲精品欧洲在线观看| 中文字幕第一页国产在线| 国产精彩福利精品视频| 19一区二区三区在线播放| 亚洲av成人免费网站| 黑人3p华裔熟女普通话| 亚洲美女高潮喷浆视频| 97青青青手机在线视频| 操日韩美女视频在线免费看| 九九热99视频在线观看97| 国产美女一区在线观看| 换爱交换乱高清大片| 免费啪啪啪在线观看视频| 男人和女人激情视频| 国产精品大陆在线2019不卡| 国产一级麻豆精品免费| 色哟哟在线网站入口| 亚洲一区av中文字幕在线观看| 日本免费午夜视频网站| 97成人免费在线观看网站| 不卡一区一区三区在线| 玖玖一区二区在线观看| 91精品国产综合久久久蜜| 日韩影片一区二区三区不卡免费| av在线播放国产不卡| 人妻久久久精品69系列| 大香蕉伊人中文字幕| 中英文字幕av一区| 青青青国产片免费观看视频| 亚洲成人熟妇一区二区三区| 中文字幕网站你懂的| 亚洲av日韩精品久久久| 自拍偷拍日韩欧美亚洲| 亚洲成人国产综合一区| 免费手机黄页网址大全| 亚洲精品无码色午夜福利理论片| 把腿张开让我插进去视频| 精品av久久久久久久| 高潮喷水在线视频观看| 欧美日本在线观看一区二区| 啊慢点鸡巴太大了啊舒服视频| 欧美日韩精品永久免费网址| 天天射夜夜操狠狠干| 极品丝袜一区二区三区| aⅴ五十路av熟女中出| 亚洲国产欧美一区二区三区久久| 黄色片一级美女黄色片| 夜色福利视频在线观看| 日本中文字幕一二区视频| 天堂中文字幕翔田av| 中文字幕熟女人妻久久久| av手机免费在线观看高潮| 欧美80老妇人性视频| 人妻另类专区欧美制服| 快插进小逼里大鸡吧视频| 都市家庭人妻激情自拍视频| 青青青青操在线观看免费| 国产视频网站一区二区三区 | 国产黄色高清资源在线免费观看| 日韩剧情片电影在线收看| 亚洲av人人澡人人爽人人爱 | 国产大学生援交正在播放| 亚洲 图片 欧美 图片| 狠狠躁狠狠爱网站视频| 亚洲欧美另类手机在线 | 丰满少妇人妻xxxxx| 午夜极品美女福利视频| 青青草成人福利电影| 国产97在线视频观看| 在线观看的黄色免费网站| 最新国产精品网址在线观看| 一区二区三区日韩久久| 成人国产影院在线观看| 成年人啪啪视频在线观看| av视屏免费在线播放| 久久久精品999精品日本| 女蜜桃臀紧身瑜伽裤| 欧美另类一区二区视频| 97超碰最新免费在线观看| 午夜久久香蕉电影网| 欧美黑人与人妻精品| 亚洲 欧美 自拍 偷拍 在线| 98视频精品在线观看| 亚洲欧美成人综合视频| 午夜福利资源综合激情午夜福利资 | 美女福利视频导航网站| 精品久久久久久久久久久99| 制丝袜业一区二区三区| 午夜精品在线视频一区| 国产成人精品av网站| 日韩加勒比东京热二区| 亚洲成人黄色一区二区三区| avjpm亚洲伊人久久| 香蕉片在线观看av| 最新日韩av传媒在线| 在线观看911精品国产 | 午夜极品美女福利视频| 国产高潮无码喷水AV片在线观看| 激情小视频国产在线| 班长撕开乳罩揉我胸好爽| 少妇ww搡性bbb91| 日韩午夜福利精品试看| 欧洲精品第一页欧洲精品亚洲| 亚洲av无女神免非久久| 欧美成人黄片一区二区三区 | www骚国产精品视频| 91精品国产黑色丝袜| 黄色中文字幕在线播放| 国产a级毛久久久久精品| 喷水视频在线观看这里只有精品| 国产精品成人xxxx| 免费福利av在线一区二区三区| 青青青国产免费视频| av乱码一区二区三区| 一区二区三区四区视频在线播放| 亚洲av自拍天堂网| 涩爱综合久久五月蜜臀| 免费观看国产综合视频| 日韩精品中文字幕在线| 999热精品视频在线| av一本二本在线观看| 91中文字幕免费在线观看| 日韩精品激情在线观看| 东京干手机福利视频| 成人伊人精品色xxxx视频| 在线 中文字幕 一区| 护士特殊服务久久久久久久 | 午夜蜜桃一区二区三区| 人妻少妇av在线观看| 1769国产精品视频免费观看| 家庭女教师中文字幕在线播放| 日韩精品一区二区三区在线播放| 人人妻人人人操人人人爽| 国产精品成人xxxx| 欧美国产亚洲中英文字幕| 黄色成年网站午夜在线观看| 久久久久久cao我的性感人妻| 精品一区二区三区午夜| 日韩欧美高清免费在线| gogo国模私拍视频| 免费岛国喷水视频在线观看 | 日本精品一区二区三区在线视频。| 污污小视频91在线观看| 亚洲人妻30pwc| 97人人模人人爽人人喊| 日韩人妻丝袜中文字幕| 天天干天天日天天干天天操| 国产在线一区二区三区麻酥酥| 精品一区二区亚洲欧美| 中文字幕日韩精品日本| 天天躁日日躁狠狠躁av麻豆| 国产午夜亚洲精品麻豆| 在线免费91激情四射| 国产一线二线三线的区别在哪 | 青青社区2国产视频| 一区二区三区蜜臀在线| 天天草天天色天天干| 国产精品国产三级国产精东| 动色av一区二区三区| 中文字幕中文字幕人妻| 51国产偷自视频在线播放| av俺也去在线播放| 免费十精品十国产网站| 黄片三级三级三级在线观看| 51精品视频免费在线观看| 久草视频在线免播放| 97国产在线av精品| 亚洲 清纯 国产com| 老司机在线精品福利视频| 日本成人一区二区不卡免费在线| 最新国产精品拍在线观看| gav成人免费播放| 国产成人精品av网站| 亚洲精品成人网久久久久久小说| 欧美aa一级一区三区四区| 久久久久久久亚洲午夜综合福利| 日本啪啪啪啪啪啪啪| 亚洲精品亚洲人成在线导航| 欧美一区二区三区乱码在线播放| 亚洲人成精品久久久久久久| 欧美日韩国产一区二区三区三州 | 欧美交性又色又爽又黄麻豆| 久草视频中文字幕在线观看| 爱有来生高清在线中文字幕| 丁香花免费在线观看中文字幕| 中文字幕高清免费在线人妻| 人妻激情图片视频小说| 日韩a级黄色小视频| 又色又爽又黄的美女裸体| 亚洲一级av无码一级久久精品| 亚洲午夜在线视频福利| 换爱交换乱高清大片| 美日韩在线视频免费看| 亚洲免费av在线视频| 视频 一区二区在线观看| 欧美色婷婷综合在线| 欧美一区二区三区在线资源| 欧美麻豆av在线播放| 亚洲综合乱码一区二区| 97精品视频在线观看| 最近的中文字幕在线mv视频| 无码中文字幕波多野不卡| 精品美女久久久久久| 亚洲在线观看中文字幕av| 毛片av在线免费看| 欧美亚洲免费视频观看| 一区二区久久成人网| 狠狠操操操操操操操操操| 精品久久久久久久久久久a√国产| 亚洲av成人免费网站| 班长撕开乳罩揉我胸好爽| 天天躁夜夜躁日日躁a麻豆| 亚洲综合一区成人在线| 日本欧美视频在线观看三区| 亚洲免费国产在线日韩| 亚洲天天干 夜夜操| 又大又湿又爽又紧A视频| 亚洲免费成人a v| 婷婷五月亚洲综合在线| 制丝袜业一区二区三区| 日韩中文字幕在线播放第二页| 黄色片黄色片wyaa| 最新日韩av传媒在线| 99精品视频之69精品视频 | 国产福利小视频免费观看| 国产真实乱子伦a视频| 国产精品久久久久久美女校花| 免费费一级特黄真人片| 丁香花免费在线观看中文字幕| 人妻丰满熟妇综合网| 啪啪啪啪啪啪啪啪啪啪黄色| 沙月文乃人妻侵犯中文字幕在线 | 粉嫩av蜜乳av蜜臀| 亚洲福利天堂久久久久久| 成年女人免费播放视频| 中文字幕,亚洲人妻| 欧美老妇精品另类不卡片| 无码日韩人妻精品久久| 国产97在线视频观看| 精品suv一区二区69| 日韩一区二区三区三州| 一级黄片大鸡巴插入美女| 欧美在线一二三视频| 91‖亚洲‖国产熟女| 亚洲综合一区二区精品久久| 欧美特级特黄a大片免费| 蜜臀成人av在线播放| 91九色porny蝌蚪国产成人| 91国产资源在线视频| 97人妻夜夜爽二区欧美极品| 国产内射中出在线观看| 国产片免费观看在线观看| 国产亚洲精品视频合集| 成人性爱在线看四区| 2018最新中文字幕在线观看| 青青青青青免费视频| 色婷婷精品大在线观看| 91极品新人『兔兔』精品新作| 免费黄页网站4188| 欧美日本在线视频一区| 精品91高清在线观看| 一色桃子久久精品亚洲| 日本精品视频不卡一二三| 888亚洲欧美国产va在线播放| 美洲精品一二三产区区别| 一区二区在线视频中文字幕| 93人妻人人揉人人澡人人| 一区二区在线观看少妇| 91极品新人『兔兔』精品新作| 午夜毛片不卡免费观看视频| 动漫美女的小穴视频| 国产日韩精品电影7777| 热久久只有这里有精品| 日本熟女精品一区二区三区| 一级黄色片夫妻性生活| 国产亚洲欧美另类在线观看| 免费在线观看视频啪啪| 欧美黑人性暴力猛交喷水| 99热这里只有国产精品6| 啪啪啪啪啪啪啪免费视频| 一区二区三区 自拍偷拍| 欧美aa一级一区三区四区| 国际av大片在线免费观看| 色综合久久五月色婷婷综合| 全国亚洲男人的天堂| 亚洲精品乱码久久久久久密桃明 | 人妻另类专区欧美制服| 欧美xxx成人在线| 人妻无码中文字幕专区| 自拍 日韩 欧美激情| 亚洲青青操骚货在线视频| 中文字幕人妻熟女在线电影| 青青青青操在线观看免费| 97青青青手机在线视频| 九色精品视频在线播放| 91 亚洲视频在线观看| 日韩欧美一级aa大片| 亚洲欧美激情中文字幕| 任你操视频免费在线观看| 一区二区熟女人妻视频| 天天干天天日天天干天天操| 亚洲欧美一区二区三区电影| 视频在线免费观看你懂得| 亚洲美女高潮喷浆视频| 天堂女人av一区二区| 天天干夜夜操天天舔| 桃色视频在线观看一区二区| 99精品免费观看视频| 抽查舔水白紧大视频| 国产丰满熟女成人视频| 午夜精品福利91av| 日韩一区二区电国产精品| 天天夜天天日天天日| 亚洲欧美成人综合视频| 夜色撩人久久7777| 自拍偷拍 国产资源| 亚洲国产中文字幕啊啊啊不行了| 亚洲最大免费在线观看| 精品一区二区三区欧美| 秋霞午夜av福利经典影视| 大香蕉日本伊人中文在线| 日韩不卡中文在线视频网站| 成年人午夜黄片视频资源| 人人妻人人澡欧美91精品 | 唐人色亚洲av嫩草| 白嫩白嫩美女极品国产在线观看| 日本欧美视频在线观看三区| 老司机深夜免费福利视频在线观看| 亚洲粉嫩av一区二区三区| 国产精品亚洲а∨天堂免| 最新国产精品拍在线观看| 天天日天天日天天射天天干| 日韩中文字幕在线播放第二页| 韩国三级aaaaa高清视频| 免费观看丰满少妇做受| 欧美色呦呦最新网址| 成年人啪啪视频在线观看| 男人的网址你懂的亚洲欧洲av| 黄色视频在线观看高清无码| 国产美女一区在线观看| 日本阿v视频在线免费观看| 在线免费观看av日韩| 在线播放国产黄色av| av男人天堂狠狠干| 肏插流水妹子在线乐播下载| 欧美另类重口味极品在线观看| 黄色录像鸡巴插进去| 99久久99久国产黄毛片| 日韩影片一区二区三区不卡免费| 天天日天天干天天爱| 99国产精品窥熟女精品| av亚洲中文天堂字幕网| 91破解版永久免费| 自拍偷拍亚洲另类色图| 一区二区三区综合视频| 亚洲最大黄了色网站| 国产97在线视频观看| 国产成人精品一区在线观看 | 久久久麻豆精亚洲av麻花| 亚洲欧美色一区二区| 国产一区二区神马久久| 三级av中文字幕在线观看| 91中文字幕最新合集| 国产成人一区二区三区电影网站| 亚洲一级av无码一级久久精品| 五色婷婷综合狠狠爱| 国产又粗又硬又大视频| 18禁精品网站久久| 老司机免费福利视频网| 亚洲欧美精品综合图片小说 | 天堂女人av一区二区| 亚洲av成人免费网站| 男大肉棒猛烈插女免费视频| 91一区精品在线观看| 天天日天天爽天天爽| 亚洲av男人天堂久久| 欧美亚洲中文字幕一区二区三区| 亚洲中文字幕综合小综合| 日本韩国免费福利精品| 九色porny九色9l自拍视频| 午夜国产免费福利av| 亚洲欧美在线视频第一页| 粉嫩小穴流水视频在线观看| 99精品视频在线观看婷婷| 在线观看视频网站麻豆| 一区二区三区国产精选在线播放| 亚洲另类综合一区小说| 涩涩的视频在线观看视频| 国产成人精品av网站| 超黄超污网站在线观看| 色哟哟国产精品入口| 日韩一区二区三区三州| 久草福利电影在线观看| 中文字幕在线观看极品视频| aiss午夜免费视频| 日本黄在免费看视频| 97色视频在线观看| 伊人网中文字幕在线视频| 亚洲综合一区成人在线| 久久这里只有精品热视频| 免费在线观看污污视频网站| 91极品大一女神正在播放| 免费一级特黄特色大片在线观看| 久久这里有免费精品| 国产性生活中老年人视频网站| 日本一区美女福利视频| 亚国产成人精品久久久| 日韩中文字幕福利av| 大胆亚洲av日韩av| 2018最新中文字幕在线观看| 亚洲人成精品久久久久久久| 青青草视频手机免费在线观看| 自拍偷拍 国产资源| nagger可以指黑人吗| 亚洲另类伦春色综合小| 中文字幕在线观看极品视频| 视频在线亚洲一区二区| 2025年人妻中文字幕乱码在线| 成人蜜臀午夜久久一区| 97精品视频在线观看| 亚洲天堂精品久久久| 日本人妻少妇18—xx| 动漫av网站18禁| 亚洲中文精品字幕在线观看| 99热久久这里只有精品| 天天想要天天操天天干| 93精品视频在线观看| 国产亚州色婷婷久久99精品| 欧洲日韩亚洲一区二区三区| chinese国产盗摄一区二区 | 亚洲成人国产av在线| 99国内小视频在现欢看| 天天日天天鲁天天操| 黄色三级网站免费下载| 国产免费高清视频视频| 久久99久久99精品影院| 亚洲成人国产综合一区| 夫妻在线观看视频91| av破解版在线观看| 成年人免费看在线视频| 岛国青草视频在线观看| 亚洲国产免费av一区二区三区| 偷拍自拍 中文字幕| 亚洲av人人澡人人爽人人爱| 中文字幕—97超碰网| 日韩美在线观看视频黄| 亚洲午夜福利中文乱码字幕| 青青草原色片网站在线观看| 91欧美在线免费观看| 国产精品久久久久久久久福交| 边摸边做超爽毛片18禁色戒 | 男人操女人逼逼视频网站| 春色激情网欧美成人| 亚洲午夜精品小视频| 自拍偷拍日韩欧美亚洲| 中文字幕日本人妻中出| 国产黄色高清资源在线免费观看| 精品黑人巨大在线一区| 欧美韩国日本国产亚洲| 91久久国产成人免费网站| 亚洲av日韩高清hd| 青草青永久在线视频18| 北条麻妃肉色丝袜视频| 91she九色精品国产| 天天插天天狠天天操| 888欧美视频在线| 男生用鸡操女生视频动漫| 香蕉av影视在线观看| 日韩欧美亚洲熟女人妻| 二区中出在线观看老师| 久草极品美女视频在线观看| 天堂av在线播放免费| 亚洲午夜高清在线观看| 亚洲av在线观看尤物| 天天日天天敢天天干| 狠狠鲁狠狠操天天晚上干干| 抽查舔水白紧大视频| 99久久99一区二区三区| 激情啪啪啪啪一区二区三区| 一区二区三区麻豆福利视频| 青青青青青青青青青青草青青| 91自产国产精品视频| 少妇深喉口爆吞精韩国| 亚洲少妇人妻无码精品| 人妻在线精品录音叫床| 天天日天天爽天天干| 91国产在线免费播放| 任你操视频免费在线观看| 小穴多水久久精品免费看| 午夜美女少妇福利视频| 日韩av熟妇在线观看| 宅男噜噜噜666免费观看| 日韩一区二区三区三州| 99re6热在线精品| 青草青永久在线视频18| 成人乱码一区二区三区av| 春色激情网欧美成人| 欧美色呦呦最新网址| 亚洲中文精品字幕在线观看| 精品黑人巨大在线一区| 成年美女黄网站18禁久久| 免费看国产av网站| 97青青青手机在线视频| 亚洲麻豆一区二区三区| 亚洲 清纯 国产com| 综合国产成人在线观看| 香蕉91一区二区三区| 不卡一不卡二不卡三| 啊啊好大好爽啊啊操我啊啊视频 | 亚洲综合另类精品小说| 国产精品国产三级国产精东| 日本精品一区二区三区在线视频。| 欧美日韩人妻久久精品高清国产| 男生舔女生逼逼的视频| 国产日本欧美亚洲精品视| 大胸性感美女羞爽操逼毛片| 国产成人一区二区三区电影网站| 一区二区三区毛片国产一区| 国产三级片久久久久久久| 中文字幕国产专区欧美激情| 红杏久久av人妻一区| 91麻豆精品久久久久| 欧美日本在线视频一区| 国产视频一区二区午夜| 天天日天天爽天天干| 香蕉91一区二区三区| 精品一区二区三四区| 99热久久极品热亚洲| 2019av在线视频| 97资源人妻免费在线视频| 久久99久久99精品影院| 精品美女在线观看视频在线观看 | 蜜桃久久久久久久人妻| 青青青青青青青在线播放视频| 午夜在线精品偷拍一区二| 亚洲女人的天堂av| 岛国青草视频在线观看| 性感美女福利视频网站| 人妻凌辱欧美丰满熟妇| 成人性爱在线看四区| 成熟熟女国产精品一区| 2020久久躁狠狠躁夜夜躁| 国产又色又刺激在线视频| 在线新三级黄伊人网| 在线观看日韩激情视频| 国产精品欧美日韩区二区| 自拍偷拍一区二区三区图片| 亚洲精品国品乱码久久久久| 美女吃鸡巴操逼高潮视频| 日韩欧美亚洲熟女人妻| 国产成人精品久久二区91| 国产成人精品久久二区91| 一区二区熟女人妻视频| 亚洲一区二区三区在线高清| 97超碰免费在线视频| 五十路老熟女码av| 亚洲 中文 自拍 无码| 在线视频国产欧美日韩| 91人妻精品一区二区久久| 日韩精品二区一区久久| 亚洲 欧美 精品 激情 偷拍| 天天操天天干天天艹| 欧美视频一区免费在线| 亚洲国产欧美一区二区三区久久| 2022精品久久久久久中文字幕| 国产福利小视频大全| 欧洲精品第一页欧洲精品亚洲 | 亚洲一区二区激情在线| 最近中文字幕国产在线| 18禁免费av网站| 国产欧美精品不卡在线| 亚洲美女高潮喷浆视频| 天堂资源网av中文字幕| 77久久久久国产精产品| 蜜桃视频在线欧美一区| 中文字幕高清在线免费播放| 少妇露脸深喉口爆吞精| 午夜久久久久久久精品熟女| 在线观看国产免费麻豆| 极品性荡少妇一区二区色欲| 久久热这里这里只有精品| 色综合色综合色综合色| av男人天堂狠狠干| 亚洲欧美福利在线观看| 国产黑丝高跟鞋视频在线播放| 欧美韩国日本国产亚洲| 久久这里只有精品热视频| 亚洲精品福利网站图片| 国产三级片久久久久久久| 2020国产在线不卡视频| 桃色视频在线观看一区二区 | av网址国产在线观看| 大香蕉福利在线观看| 麻豆精品成人免费视频| 999久久久久999| 亚洲男人的天堂a在线| 巨乳人妻日下部加奈被邻居中出| 岛国免费大片在线观看| 蜜桃专区一区二区在线观看| 精品国产污污免费网站入口自| 黄色三级网站免费下载| 韩国女主播精品视频网站| 日日爽天天干夜夜操| 夜夜骑夜夜操夜夜奸| 亚洲免费视频欧洲免费视频| 欧美在线偷拍视频免费看 | 久久久精品999精品日本| 在线观看操大逼视频| 成人久久精品一区二区三区| 国产a级毛久久久久精品| 在线播放一区二区三区Av无码| 韩国男女黄色在线观看| 北条麻妃高跟丝袜啪啪| 视频在线亚洲一区二区| 久久久精品欧洲亚洲av| 日韩激情文学在线视频| 国产亚洲精品欧洲在线观看| 可以在线观看的av中文字幕| 天天干天天操天天玩天天射 | 婷婷久久久久深爱网| 天天夜天天日天天日| 日本高清成人一区二区三区| 综合一区二区三区蜜臀| 亚洲国产免费av一区二区三区| 亚洲人妻30pwc| 蜜桃视频17c在线一区二区| 亚洲欧美成人综合视频| 国产实拍勾搭女技师av在线| 国内精品在线播放第一页| 大陆胖女人与丈夫操b国语高清| 亚洲精品国产久久久久久| 亚洲视频在线观看高清| 中文字幕人妻熟女在线电影| 国产1区,2区,3区| av天堂加勒比在线| 亚洲精品在线资源站| 女生被男生插的视频网站| 成年人该看的视频黄免费| 中文字幕1卡1区2区3区| 欧美韩国日本国产亚洲| av成人在线观看一区| 人妻3p真实偷拍一二区| 在线成人日韩av电影| 亚洲欧美激情人妻偷拍| 亚洲成人激情视频免费观看了 | 欧洲日韩亚洲一区二区三区| 成人av在线资源网站| 久久永久免费精品人妻专区| 欧美成一区二区三区四区| 国产久久久精品毛片| 午夜激情久久不卡一区二区 | 男人天堂最新地址av| 国产精品入口麻豆啊啊啊| 都市激情校园春色狠狠| 密臀av一区在线观看| 国产精彩对白一区二区三区| 中文字幕最新久久久| heyzo蜜桃熟女人妻| 国产成人自拍视频在线免费观看| 最近中文字幕国产在线| 国产伊人免费在线播放| 国产97在线视频观看| 又粗又长 明星操逼小视频 | 最近中文2019年在线看| 欧美综合婷婷欧美综合| 欧美在线精品一区二区三区视频| 国产福利小视频免费观看| 最后99天全集在线观看| 在线制服丝袜中文字幕| jiujiure精品视频在线| 9色精品视频在线观看| yy96视频在线观看| 国产一线二线三线的区别在哪| 欧美3p在线观看一区二区三区| 直接能看的国产av| 亚洲在线观看中文字幕av| 国产精品入口麻豆啊啊啊| 嫩草aⅴ一区二区三区| 国产性感美女福利视频| 好男人视频在线免费观看网站| 欧美日本在线视频一区| gogo国模私拍视频| 国产精品黄大片在线播放| 2025年人妻中文字幕乱码在线| 人妻丝袜诱惑我操她视频| 黄网十四区丁香社区激情五月天| 一区二区三区欧美日韩高清播放| 日辽宁老肥女在线观看视频| 搡老妇人老女人老熟女| 孕妇奶水仑乱A级毛片免费看| 日本一本午夜在线播放| 日韩三级黄色片网站| 欧美香蕉人妻精品一区二区| 2022精品久久久久久中文字幕| 国产视频一区在线观看| 欧美专区第八页一区在线播放| 女同性ⅹxx女同hd| 女生自摸在线观看一区二区三区 | 夜色17s精品人妻熟女| 三级黄色亚洲成人av| 亚洲少妇人妻无码精品| 亚洲特黄aaaa片| 日韩欧美制服诱惑一区在线| 一区二区在线视频中文字幕| 91老熟女连续高潮对白| 日本又色又爽又黄又粗| 三级等保密码要求条款| 日韩欧美国产一区不卡| 51精品视频免费在线观看| 天天日天天摸天天爱| 特级欧美插插插插插bbbbb| 国产揄拍高清国内精品对白| 99热国产精品666| 国产伦精品一区二区三区竹菊| 福利视频一区二区三区筱慧| 福利一二三在线视频观看| 欧美viboss性丰满| 欧美交性又色又爽又黄麻豆| 伊人成人综合开心网| 播放日本一区二区三区电影| 无套猛戳丰满少妇人妻| 熟女91pooyn熟女| 一区二区三区久久中文字幕| 视频一区 二区 三区 综合| 3344免费偷拍视频| 在线观看成人国产电影| 一区二区三区久久久91| 在线视频国产欧美日韩| 涩涩的视频在线观看视频| 欧美黑人巨大性xxxxx猛交| 免费人成黄页网站在线观看国产| 自拍偷区二区三区麻豆| sejizz在线视频| 国产麻豆国语对白露脸剧情 | 天天日天天干天天插舔舔| 夜夜嗨av蜜臀av| 久久久久久九九99精品| 午夜精品亚洲精品五月色| 亚洲无码一区在线影院| 亚洲国产40页第21页| 超级福利视频在线观看| 日视频免费在线观看| 又大又湿又爽又紧A视频| 日韩激情文学在线视频| 最后99天全集在线观看| 一级黄色av在线观看| 欧美精品国产综合久久| 美女福利写真在线观看视频| 天天日天天日天天射天天干| 最新国产精品网址在线观看| 高潮视频在线快速观看国家快速| 六月婷婷激情一区二区三区| 亚洲欧美久久久久久久久| 欧美地区一二三专区| 成年女人免费播放视频| 天堂va蜜桃一区入口| 人妻丝袜诱惑我操她视频| 国产亚洲天堂天天一区| 97黄网站在线观看| 日本三极片视频网站观看| 一区二区视频视频视频| av亚洲中文天堂字幕网| 天天干天天操天天爽天天摸| 亚洲国产美女一区二区三区软件| 18禁美女黄网站色大片下载| 韩国三级aaaaa高清视频| 亚洲 中文 自拍 无码| 欧美视频中文一区二区三区| 天天躁夜夜躁日日躁a麻豆| 丰满的子国产在线观看| 骚货自慰被发现爆操| 91九色porny国产在线| 阿v天堂2014 一区亚洲| 六月婷婷激情一区二区三区| 特级无码毛片免费视频播放| 97青青青手机在线视频| 亚洲av日韩av网站| 欧洲黄页网免费观看| 亚洲av无码成人精品区辽| 日本一二三区不卡无| 老有所依在线观看完整版| 日本xx片在线观看| 日本熟妇色熟妇在线观看| 偷拍自拍亚洲视频在线观看| 免费高清自慰一区二区三区网站| 国产麻豆剧果冻传媒app| 青青青青青免费视频| 黄色资源视频网站日韩| 精彩视频99免费在线| 11久久久久久久久久久| 欧美日韩高清午夜蜜桃大香蕉| 在线亚洲天堂色播av电影| 天天干天天操天天摸天天射| 亚洲精品av在线观看| 91天堂精品一区二区| 国产精品亚洲а∨天堂免| 亚洲欧美国产综合777| 中文字幕高清在线免费播放| 国产九色91在线视频| 欧美国产亚洲中英文字幕| 日本免费午夜视频网站| 久久精品国产23696| 一二三区在线观看视频| 激情人妻校园春色亚洲欧美| 成人久久精品一区二区三区| 丁香花免费在线观看中文字幕| 伊人开心婷婷国产av| gogo国模私拍视频| 男人操女人的逼免费视频| 国产精品精品精品999| 红杏久久av人妻一区| 日韩精品二区一区久久| 中文字幕第1页av一天堂网| 欧美va不卡视频在线观看| 亚洲欧美国产麻豆综合| 天天干天天搞天天摸| 视频 国产 精品 熟女 | 91成人在线观看免费视频| 在线观看操大逼视频| 在线免费91激情四射 | 欧美成人小视频在线免费看| 欧美黄片精彩在线免费观看| 天天躁日日躁狠狠躁躁欧美av| 日本一二三中文字幕| 中文字幕高清在线免费播放| 青青青青青手机视频| 91免费放福利在线观看| 精品老妇女久久9g国产| 91 亚洲视频在线观看| 亚洲成人av在线一区二区| 欧美日韩v中文在线| 亚洲一区二区三区uij| 国产一区二区火爆视频| 青青青青青操视频在线观看| 国产伊人免费在线播放| 国产品国产三级国产普通话三级| 美女小视频网站在线| 欧美成人小视频在线免费看| 99婷婷在线观看视频| 在线新三级黄伊人网| 亚洲av男人的天堂你懂的| 美女av色播在线播放| 精品区一区二区三区四区人妻| av在线免费资源站| 91九色porny国产蝌蚪视频| 成人av电影免费版| 亚洲成人熟妇一区二区三区| 欧美亚洲自偷自拍 在线| a v欧美一区=区三区| 久久久久久九九99精品| 国产实拍勾搭女技师av在线| 中文亚洲欧美日韩无线码| 经典av尤物一区二区| 大屁股肉感人妻中文字幕在线| 人妻熟女在线一区二区| 粉嫩欧美美人妻小视频| 国产自拍在线观看成人| 国产九色91在线视频| 天天射,天天操,天天说| 嫩草aⅴ一区二区三区| 极品粉嫩小泬白浆20p主播| 国产午夜男女爽爽爽爽爽视频| 人妻久久无码中文成人| 亚洲精品午夜aaa久久| 99热久久极品热亚洲| 国产精品午夜国产小视频| 亚洲av色图18p| 99精品视频之69精品视频| 四川五十路熟女av| 国产自拍黄片在线观看| 黄色av网站免费在线| 亚洲偷自拍高清视频| 91人妻精品久久久久久久网站| 欧美日韩在线精品一区二区三| 欧美国产亚洲中英文字幕| 欧美精产国品一二三区| 精品一区二区三区在线观看| 亚洲美女高潮喷浆视频| 欧美亚洲国产成人免费在线| 肏插流水妹子在线乐播下载| 91中文字幕免费在线观看| 老司机福利精品免费视频一区二区| 99热99这里精品6国产| 99精品视频在线观看免费播放| 91破解版永久免费| 精品成人啪啪18免费蜜臀| 欧美地区一二三专区| 1区2区3区4区视频在线观看| 不卡一不卡二不卡三| 2022天天干天天操| 天天草天天色天天干| 国产高清在线观看1区2区| 又粗又长 明星操逼小视频| 一区二区三区四区中文| 果冻传媒av一区二区三区| 精品suv一区二区69| 中文字幕av男人天堂| 亚洲欧美一卡二卡三卡| 免费黄页网站4188| 日韩欧美高清免费在线| 熟女俱乐部一二三区| 午夜在线一区二区免费| 好太好爽好想要免费| 国产精品亚洲在线观看| 一区二区在线视频中文字幕| 亚洲中文精品人人免费| av中文字幕网址在线| 91精品综合久久久久3d动漫| 国产又粗又猛又爽又黄的视频美国| 亚洲欧美成人综合在线观看| 国产熟妇一区二区三区av| jul—619中文字幕在线| 91精品视频在线观看免费| 亚洲狠狠婷婷综合久久app | 99人妻视频免费在线| 涩爱综合久久五月蜜臀| 免费av岛国天堂网站| 国产大鸡巴大鸡巴操小骚逼小骚逼| 亚洲国产成人av在线一区| aiss午夜免费视频| 国产九色91在线视频| 国产精品久久久久网| 日韩三级黄色片网站| 久久精品国产23696| 国产精品自拍偷拍a| 大香蕉伊人中文字幕| 中文字幕综合一区二区| 特级欧美插插插插插bbbbb| 五十路老熟女码av| 这里有精品成人国产99| 亚洲精品无码色午夜福利理论片| 天天通天天透天天插| 中文字幕第一页国产在线| 熟女人妻三十路四十路人妻斩| 亚洲午夜电影之麻豆| 大香蕉大香蕉在线看| 一区二区在线观看少妇| 美女少妇亚洲精选av| 97色视频在线观看| 蜜桃专区一区二区在线观看| 中文字幕中文字幕人妻| 亚洲推理片免费看网站| 国产精品女邻居小骚货| 国产精品黄大片在线播放| 91 亚洲视频在线观看| 毛片av在线免费看| 黄色无码鸡吧操逼视频| 男人的天堂av日韩亚洲| 在线观看免费岛国av| 桃色视频在线观看一区二区| 欧美中国日韩久久精品| 韩国女主播精品视频网站| 欧美久久一区二区伊人| 亚洲偷自拍高清视频| 亚洲特黄aaaa片| 男女之间激情网午夜在线| 91自产国产精品视频| 欧美成人一二三在线网| 一区二区视频在线观看免费观看 | 久久久麻豆精亚洲av麻花| 亚洲男人在线天堂网| 日本少妇精品免费视频| 亚洲午夜伦理视频在线| 在线观看av观看av| 91啪国自产中文字幕在线| 亚洲视频在线观看高清| 日本黄色三级高清视频| 在线视频国产欧美日韩| 天天日夜夜干天天操| 亚洲欧美综合在线探花| 好吊视频—区二区三区| 亚洲欧美激情中文字幕| 久久人人做人人妻人人玩精品vr| 亚洲欧美综合另类13p| 91人妻精品久久久久久久网站 | 欧美精品 日韩国产| 91欧美在线免费观看| 国产丰满熟女成人视频| 久久久人妻一区二区| 亚洲国产精品美女在线观看| 中文字幕一区二区亚洲一区| 亚洲av男人天堂久久| 大鸡八强奸视频在线观看| 在线观看的a站 最新| 噜噜色噜噜噜久色超碰| 久久三久久三久久三久久| 中文字幕一区二 区二三区四区| 大香蕉大香蕉在线看| 91免费福利网91麻豆国产精品 | 亚洲欧美自拍另类图片| 91色老99久久九九爱精品| 天天日天天干天天插舔舔| 狠狠的往里顶撞h百合| av高潮迭起在线观看| 美女福利视频网址导航| aⅴ五十路av熟女中出| 欧美性受xx黑人性猛交| 欧美伊人久久大香线蕉综合| av中文字幕国产在线观看| 精品成人啪啪18免费蜜臀| 亚洲一区久久免费视频| 国产老熟女伦老熟妇ⅹ| 97精品综合久久在线| 久久久久只精品国产三级| 精品人妻一二三区久久| 91精品国产黑色丝袜| 福利视频一区二区三区筱慧| 天堂v男人视频在线观看| 亚洲国际青青操综合网站| 91she九色精品国产| 日视频免费在线观看| 亚洲一级 片内射视正片| 91免费观看国产免费| 男人天堂av天天操| 搡老熟女一区二区在线观看| av中文在线天堂精品| 男人的天堂av日韩亚洲| 人妻另类专区欧美制服| 国产成人精品av网站| 天天日夜夜操天天摸| 国产高潮无码喷水AV片在线观看| 久久久久五月天丁香社区| 中文字幕1卡1区2区3区| 亚洲av自拍偷拍综合| 中文字幕乱码人妻电影| 亚洲av香蕉一区区二区三区犇| 天天操天天操天天碰| 国产性色生活片毛片春晓精品| 在线新三级黄伊人网| 欧美美女人体视频一区| 国产av自拍偷拍盛宴| 蝴蝶伊人久久中文娱乐网| 欧美一级色视频美日韩| 一区二区三区av高清免费| 久久人人做人人妻人人玩精品vr | 99精品视频之69精品视频| 福利国产视频在线观看| 自拍偷拍亚洲欧美在线视频| 57pao国产一区二区| 国产成人精品av网站| 国产欧美日韩第三页| 欧美麻豆av在线播放| 福利在线视频网址导航| 国产精品熟女久久久久浪潮|