整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          React 的正確使用方法:ref 篇

          React 的正確使用方法:ref 篇

          谷 阿里云開發(fā)者

          2024年08月09日 08:30 浙江

          阿里妹導(dǎo)讀


          你真的用對了 useRef 嗎?在與 TypeScript 一起使用、以及撰寫組件庫的情況下,你的寫法能夠避開以下所有場景的坑嗎?

          說到 useRef,相信你一定不會陌生:你可以用它來獲取 DOM 元素,也可以多次渲染之間保持引用不變……

          然而,你真的用對了 useRef 嗎?在與 TypeScript 一起使用、以及撰寫組件庫的情況下,你的寫法能夠避開以下所有場景的坑嗎?

          場景一:獲取 DOM 元素

          以下幾種寫法,哪種是正確的?

          function MyComponent() {
            // 寫法 1
            const ref=useRef();
          
          
            // 寫法 2
            const ref=useRef(undefined);
          
          
            // 寫法 3
            const ref=useRef(null);
          
          
            // 通過 ref 計算 DOM 元素尺寸
            //  這段代碼故意留了坑,坑在哪里?請看下文。
            useLayoutEffect(()=> {
              const rect=ref.current.getBoundingClientRect();
            }, [ref.current]);
          
          
            return <div ref={ref} />;  
          }

          如果只看 JS,幾種寫法似乎并沒有差別,但如果你開啟了 TS 的類型提示,就能夠發(fā)現(xiàn)其中端倪:

          function MyComponent() {
            // ? 寫法 1
            // 你會得到一個 MutableRefObject<HTMLDivElement | undefined>,
            // 即 ref.current 類型是 HTMLDivElement | undefined,
            // 這導(dǎo)致你每次獲取 DOM 元素都需要判斷是否為 undefined,很是麻煩。
            const ref=useRef<HTMLDivElement>();
          
          
            // ? 寫法 2.1
            // 你可能想得到一個 MutableRefObject<HTMLDivElement>,但初始值傳入的
            // undefined 并不是 HTMLDivElement,所以會 TS 報錯。
            const ref=useRef<HTMLDivElement>(undefined);
          
          
            // ? 寫法 2.2
            // 等價于寫法 1,但需要多打一些字。
            const ref=useRef<HTMLDivElement | undefined>(undefined);
          
          
            // ? 寫法 3
            // 你會得到一個 RefObject<HTMLDivElement>,其中
            // ref.current 類型是 HTMLDivElement | null。
            // 這個 ref 的 current 是不可從外部修改的,更符合使用場景下的語義,
            // 也是 React 推薦的獲取 DOM 元素方式。
            // 注意:如果 tsconfig 沒開 strictNullCheck,則不會匹配到這個定義,
            // 因此請務(wù)必開啟 strictNullCheck。
            const ref=useRef<HTMLDivElement>(null);
          
          
            // 通過 ref 計算 DOM 元素尺寸
            //  這段代碼故意留了坑,坑在哪里?請看下文。
            useLayoutEffect(()=> {
              const rect=ref.current.getBoundingClientRect();
            }, [ref.current]);
          
          
            return <div ref={ref} />;  
          }

          Ref 還可以傳入一個函數(shù),會把被 ref 的對象應(yīng)用作為參數(shù)傳入,因此我們也可以這樣獲取 DOM 元素:

          function MyComponent() {
            const [divEl, setDivEl]=useState<HTMLDivElement | null>(null);
          
          
            // 計算 DOM 元素尺寸
            useEffect(()=> {
              if (divEl) {
                divEl.current.getBoundingClientRect();
              }
            }, [divEl]);
          
          
            return <div ref={setDivEl} />;
          }

          場景二:DOM 元素與 useLayoutEffect

          在場景一中,我們留了一個坑,你能看出以下代碼有什么問題嗎?

          /*  錯誤案例,請勿照抄 */
          
          
          function MyComponent({ visible }: { visible: boolean }) {
            const ref=useRef<HTMLDivElement>(null);
          
          
            useLayoutEffect(()=> {
              const rect=ref.current.getBoundingClientRect();
              // ...
            }, [ref.current]);
          
          
            return <>{visible && <div ref={ref}/>}</>;
          }

          這段代碼有兩個問題:?


          1. useLayoutEffect 中沒有判空

          按照場景一中的分析:

          useRef<HTMLDivElement>(null) 返回的類型是RefObject<HTMLDivELement>,其ref.current 類型為HTMLDivELement | null。因此單從 TS 類型出發(fā),也應(yīng)該判斷ref.current 是否為空。

          你也許會認(rèn)為,我都在 useLayoutEffect 里了,此時組件 DOM 已經(jīng)生成,因而理應(yīng)存在 ref.current,是否可以不用判斷呢?(或用 ! 強制設(shè)為非空)

          上述使用場景中,確實可以這樣做,但如果div 是條件渲染的,則無法保證useLayoutEffect 時組件已被渲染,自然也就不一定存在ref.current。

          2. useLayoutEffect deps 配置錯誤

          這個問題涉及到useLayoutEffect 更本質(zhì)的使用目的。?

          useLayoutEffect 的執(zhí)行時機是:

          ?VDOM 生成后(所有render 執(zhí)行完成);

          ?DOM 生成后(createElement 等 DOM 操作完成);

          ?最終提交渲染之前(同步任務(wù)返回前)。?

          由于其執(zhí)行時機在 repaint 之前,此時對已生成的 DOM 進行更改,用戶不會看到「閃一下」。舉個例子,你可以計算元素的尺寸,如果太大則修改 CSS 使其自動換行,從而實現(xiàn)溢出檢測。?

          另一個常見場景是在useLayoutEffect 中獲取原生組件,用來添加原生 listener、獲取底層HTMLMediaElement 實例來控制播放,或添加ResizeObserver、IntersectionObserver 等。

          這里,由于div 是條件渲染的,我們顯然會希望useLayoutEffect 的操作在每次渲染出來之后都執(zhí)行一遍,因此我們會想把ref.current 寫進useLayoutEffectdependencies,但這是完全錯誤的。

          讓我們盤一下MyComponent 的渲染過程:

          1.visible 變化導(dǎo)致觸發(fā) render。

          2.useRef 執(zhí)行,ref.current 還是上一次的值。

          3.useLayoutEffect 執(zhí)行,對比 dependencies 發(fā)現(xiàn)沒有變化,跳過執(zhí)行。

          4.渲染結(jié)果包含div

          5.由于<div ref={ref}>,React 使用新的 DOM 元素更新ref.current。

          顯然,這里并沒有再次觸發(fā)useLayoutEffect,直到下一次渲染中才會發(fā)現(xiàn)ref.current 有變化,這背離了我們對于 useLayoutEffect 能讓用戶看不到「閃一下」的預(yù)期。

          解決方案是,使用與條件渲染相同的條件作為useLayoutEffect 的 deps:

          function MyComponent({ visible }: { visible: boolean }) {
            const ref=useRef<HTMLDivElement>(null);
          
          
            useLayoutEffect(()=> {
              // 這里不必額外判斷 if (visible),因為只要這里有 ref.current 那就必然是 visible
              if (ref.current) {
                const rect=ref.current.getBoundingClientRect();
              }
            }, [/* ? */ visible]);
            // 這樣,在 visible 變化時,就必然會在同一次渲染內(nèi)觸發(fā) useLayoutEffect
          
          
            return <>{visible && <div ref={ref}/>}</>;
          }
          
          
          // 或者也可以將 <div> 抽取成一個獨立的組件,從而避免上述問題

          最后,如果并非是要在 repaint 之前對 DOM 元素進行操作,更推薦的寫法是用函數(shù)寫法:

          function MyComponent({ visible }: { visible: boolean }) {
            // ? 無需使用 ref
            const [video, setVideo]=useState<Video | null>(null);
          
          
            const play=useCallback(()=> video?.play(), [video]);
          
          
            // ? 使用普通 useEffect 即可
            useEffect(()=> {
              console.log(video.currentTime);
            }, [video]);
          
          
            return <>{visible && <video ref={setVideo}/>}</>;
          }

          場景三:組件中同時傳遞 & 獲取 Ref

          ——你實現(xiàn)了一個組件,想要將傳入的 ref 傳給組件中渲染的根元素,聽起來很簡單!

          哦對了,出于某種原因,你的組件中也需要用到根組件的 ref,于是你寫出了這樣的代碼:

          /*  錯誤案例,請勿照抄 */
          
          
          const MyComponent=forwardRef(
            function (
              props: MyComponentProps, 
              // type ForwardedRef<T>=//   | ((instance: T | null)=> void)
              //   | MutableRefObject<T | null>
              //   | null
              // ? 這個工具類型覆蓋了傳 useRef 和傳 setState 的情況,是正確的寫法
              ref: ForwardedRef<HTMLDivElement>
            ) {
              useLayoutEffect(()=> {
                const rect=ref.current.getBoundingClientRect();
                // 使用 rect 進行計算
              }, []);
              
              return <div ref={ref}>{/* ... */}</div>;
            }
          });

          等等,如果調(diào)用者沒傳ref 怎么辦?想到這里,你把代碼改成了這樣:

          /*  錯誤案例,請勿照抄 */
          
          
          const MyComponent=forwardRef(
            function (
              props: MyComponentProps, 
              ref: ForwardedRef<HTMLDivElement>
          ) {
              const localRef=useRef<HTMLDivElement>(null);
              
              useLayoutEffect(()=> {
                const rect=localRef.current.getBoundingClientRect();
                // 使用 rect 進行計算
              }, []);
          
          
              return <div ref={(el: HTMLDivElement)=> {
                localRef.current=el;
                if (ref) {
                  ref.current=el;
                }
              }}>{/* ... */}</div>;
            }
          });

          這樣的代碼顯然是會 TS 報錯的,因為ref 可能是個函數(shù),本來你只需要把它直接傳給<div> 就好了,因此你需要寫一堆代碼,處理多種可能的情況……

          更好的解決方式是使用 react-merge-refs

          import { mergeRefs } from "react-merge-refs";
          
          
          const MyComponent=forwardRef(
            function (
              props: MyComponentProps, 
              ref: ForwardedRef<HTMLDivElement>
          ) {
              const localRef=React.useRef<HTMLDivElement>(null);
          
          
              useLayoutEffect(()=> {
                const rect=localRef.current.getBoundingClientRect();
                // 使用 rect 進行計算
              }, []);
              
              return <div ref={mergeRefs([localRef, ref])} />;
            }
          );

          場景四:組件透出命令式操作

          Form 和 Table 這種復(fù)雜的組件往往會在組件內(nèi)維護較多狀態(tài),不適合受控操作,當(dāng)調(diào)用者需要控制組件行為時,往往就會采取這樣的模式:

          function MyPage() {
            const ref=useRef<FormRef>(null);
          
          
            return (
              <div>
                <Button onClick={()=> { ref.current.reset(); }}>重置表單</Button>
                <Form actionRef={ref}>{/* ... */}</Form>
              </div>
            );
          }

          這種用法實際上脫胎于 class component 時代,人們使用 ref 來獲取 class 實例,通過調(diào)用實例方法來控制組件。

          現(xiàn)在,你的超級復(fù)雜絕絕子組件也希望通過這種方式與調(diào)用者交互,于是你寫出了以下實現(xiàn):

          /*  錯誤案例,請勿照抄 */
          
          
          interface MySuperDuperComponentAction {
            reset(): void;
          }
          
          
          const MySuperDuperComponent=forwardRef(
            function (
              props: MySuperDuperComponentProps,
              ref: ForwardedRef<MySuperDuperComponentAction>
          ) {
              const action=useMemo((): MySuperDuperComponentAction=> ({
                reset() {
                  // ...
                }
              }), [/* ... */]);
              
              if (ref) {
                ref.current=action;
              }
          
          
              return <div/>;
            }  
          );   

          然而 TS 不會容許這樣的代碼通過類型檢查,因為調(diào)用者可以函數(shù)作為 ref 來接收 action,這與獲取 DOM 元素時類似。?

          正確的做法是,你應(yīng)該使用 React 提供的工具函數(shù)useImperativeHandle

          const MyComponent=forwardRef(
            function (
              props: MyComponentProps, 
              ref: ForwardedRef<MyComponentRefType>
          ) {
              // useImperativeHandle 這個工具函數(shù)會自動處理函數(shù) ref 和對象 ref 的情況,
              // 后兩個參數(shù)基本等于 useMemo
              useImperativeHandle(ref, ()=> ({
                refresh: ()=> {
                  // ...
                },
                // ...
              }), [/* deps */]);
          
          
              // 命令式 + 下傳
              // 如果你的組件內(nèi)部也會用到這個命令式對象,推薦的寫法是:
              const actions=useMemo(()=> ({
                refresh: ()=> {
                  // ...
                },
              }), [/* deps */]);
              useImperativeHandle(ref, ()=> actions, [actions]);
              
              return <div/>;
            }
          );

          場景五:組件 TS 導(dǎo)出如何聲明 Ref 類型

          如果內(nèi)部的組件類型正確,forwardRef 會自動檢測 ref 類型:

          const MyComponent=forwardRef(
            function (
              props: MyComponentProps,
              ref: ForwardedRef<MyComponentRefType>
          ) {
              return <div/>;
            }
          });
          
          
          // 其結(jié)果類型為:
          // const MyComponent: ForwardRefExoticComponent<
          //   PropsWithoutRef<MyComponentProps> & RefAttributes<MyComponentRefType>
          // >
          
          
          // 這里最后導(dǎo)出的 PropsWithoutRef<P> & RefAttributes<T> 就是用戶側(cè)最終可傳的類型,
          // 其中 PropsWithoutRef 會無視你 component 中 props 的 ref。

          這里有一個問題:你的組件導(dǎo)出的 Props 中需要包含 ref 嗎?由于forwardRef 會強行改掉你的 ref,這里有兩種方法:

          1.在MyComponentProps 中寫上 ref,類型為MyComponentRefType,直接導(dǎo)出它作為最終的 Props;

          2.用ComponentProps<typeof MyComponent> 取出最終的 Props。

          然而,當(dāng)組件內(nèi)需要必須層層透傳 ref 的時候,如果把 ref 寫進 Props 里,就需要每層組件都使用 forwardRef,否則就會出現(xiàn)問題:

          /*  錯誤案例,請勿照抄 */
          
          
          interface OtherComponentProps {
            ref?: Ref<OtherComponentActions>;
          }
          
          
          interface MyComponentProps extends OtherComponentProps {
            myAdditionalProp: string;
          }
          
          
          // 這是錯誤的,props 里根本拿不到 ref!
          function MyComponent({ myAdditionalProp, ...props }: MyComponentProps) {
            console.log(myAdditionalProp);
          
          
            return <OtherComponent {...props} />;
          }

          因此,更推薦的方案是不用 ref 這個名字,比如叫 actionRef 等等,這樣也可以毫無痛苦地寫進 props 并導(dǎo)出了。

          Bonus: 與 Ref 相關(guān)的 TS 類型

          • PropsWithoutRef<Props>:從 Props 中刪除 ref,可用于 HOC 等場景。
          • PropsWithRef<Props>:并不會添加 ref,而是保證 ref 里沒有 string。這是因為在古代,可以給 ref 傳 string 來代替?zhèn)骱瘮?shù),現(xiàn)代我們一般不這么做。
          • 如果想要添加 ref,可以仿照forwardRef 里的寫法:PropsWithoutRef<Props> & RefAttribute<RefType>。
          • RefAttribute<RefType>{ ref?: Ref<T> | undefined; }。
          • ForwardedRef<RefType>:組件內(nèi)部拿外部 ref 唯一指定類型。
          • MutableRefObject<RefType>useRefcreateRef 的結(jié)果。
          • RefObject<RefType>useRef(null) 的結(jié)果。
          • Ref<RefType>:傳入的 ref 參數(shù)類型,RefTyperef.current 拿到的類型,會自動加上 null。
          • 注意:這個類型包括 RefObject 與函數(shù)。
          • 注注意:這個類型與 ForwardedRef 的區(qū)別是,它只需要 RefObject 而非 MutableRefObject,因此可以接受 useRef(null) 的結(jié)果并被用于 props。組件內(nèi)部由于需要修改 ref.current,必須使用 MutableRefObject
          • ForwardRefExoticComponentforwardRef 的返回類型。

          ?這些類型類似于 React 提供的類型接口,為了保證你的組件能夠兼容盡可能多的 React 版本,請務(wù)必使用最合適的類型。

          之前學(xué)習(xí)過ref聲明響應(yīng)式對象,前幾天讀代碼遇到了發(fā)懵的地方,詳細(xì)學(xué)習(xí)了一下才發(fā)現(xiàn),用法還有很多,遂總結(jié)一下ref的用法備忘。


          一、組件的ref用法總結(jié)

          Vue3 中的 ref 是一種創(chuàng)建響應(yīng)式引用的方式,它在Vue生態(tài)系統(tǒng)中扮演著重要角色。以下是Vue3中ref屬性及其相關(guān)API的幾個關(guān)鍵點:

          創(chuàng)建響應(yīng)式變量:使用 ref 函數(shù)可以創(chuàng)建一個響應(yīng)式的數(shù)據(jù)引用,返回的對象包含 .value 屬性,該屬性既可以讀取也可以寫入,并且是響應(yīng)式的。例如:

          Javascript
          1import { ref } from 'vue';
          2
          3const count=ref(0); // 創(chuàng)建一個響應(yīng)式引用,初始值為0
          4console.log(count.value); // 輸出0
          5count.value++; // 改變值,這將觸發(fā)視圖更新
          

          在模板中使用 ref:在模板中,可以使用 v-ref 或簡寫 ref 來給 DOM 元素或組件添加引用標(biāo)識符。對于DOM元素:

          <div ref="myDiv">Hello World</div>
          

          然后在組件的 setup 函數(shù)內(nèi)或者生命周期鉤子如 onMounted 中通過 ref 訪問到該元素:

          onMounted(()=> {
           console.log(myDiv.value); // 這將輸出對應(yīng)的DOM元素
          });
          
          // 注意,在setup函數(shù)中使用需要解構(gòu)
          setup() {
            const myDiv=ref<HTMLElement | null>(null);
            // ...
          

          對于子組件,ref 則指向子組件的實例:

          <MyChildComponent ref="childRef" />
          

          動態(tài) refs:在動態(tài)渲染的組件或循環(huán)列表中,可以使用動態(tài) ref 名稱:

          1<component v-for="(item, index) in items" :is="item.component" :key="index" :ref="`child${index}`" />
          然后通過 getCurrentInstance() 獲取這些動態(tài) ref:
          
          Javascript
          1setup() {
          2  const instance=getCurrentInstance();
          3  const childrenRefs=computed(()=> {
          4    return instance.refs;
          5  });
          6  // ...
          7}
          

          組件間通信:通過 ref 可以方便地在組件之間傳遞并操作狀態(tài),尤其適用于父子組件之間的通信。
          (1)創(chuàng)建一個子組件 ChildComponent.vue:

          
          <template>
            <div>
              <h2>{{ childMessage }}</h2>
              <button @click="handleClick">點擊我</button>
            </div>
          </template>
          
          <script>
          import { ref, defineComponent } from 'vue';
          
          export default defineComponent({
            setup(props, { emit }) {
              const childMessage=ref('Hello from Child');
              const handleClick=()=> {
                emit('child-clicked', 'Child component clicked!');
              };
          
              return {
                childMessage,
                handleClick,
              };
            },
          });
          </script>
          

          (2)創(chuàng)建一個父組件 ParentComponent.vue,并使用 ref 屬性訪問子組件實例:

          <!-- ParentComponent.vue --><template>
            <div>
              <h1>Parent Component</h1>
             <ChildComponent ref="childRef" />
             <button @click="callChildMethod">Call Child Method</button>
            </div>
          </template><script>
          import { ref } from 'vue';
          import ChildComponent from './ChildComponent.vue';
          
          export default {
            components: {
              ChildComponent,
            },
            setup() {
              const childRef=ref(null);
          
              function callChildMethod() {
                childRef.value.showMessage();
              }
          
              return {
                childRef,
                callChildMethod,
              };
            },
          };
          </script>
          

          在這個示例中,我們在父組件的模板中使用了 ref 屬性,并將其值設(shè)置為 “childRef”。然后,在組合式 API 的 setup 函數(shù)中,我們創(chuàng)建了一個名為 childRef 的響應(yīng)式引用,并將其初始值設(shè)置為 null。接著,我們定義了一個名為 callChildMethod 的方法,用于調(diào)用子組件的 showMessage 方法。當(dāng)用戶點擊按鈕時,callChildMethod 方法會被調(diào)用,從而觸發(fā)子組件的 showMessage 方法并在控制臺輸出一條消息。

          import { reactive, toRef } from 'vue';
          2
          3const state=reactive({ count: 0 });
          4const countRef=toRef(state, 'count'); // 提取出count屬性的響應(yīng)式引用
          

          總結(jié)

          總之,Vue3 的 ref 功能增強了Vue的響應(yīng)式系統(tǒng),使得開發(fā)者能夠更靈活地處理組件的狀態(tài)及組件間交互,同時提供了對DOM元素的直接訪問能力。

          人總是在接近幸福時倍感幸福,在幸福進行時卻患得患失。

          用ref函數(shù)獲取組件中的標(biāo)簽元素,可以操作該標(biāo)簽身上的屬性,還可以通過ref來取到子組件的數(shù)據(jù),可以修改子組件的數(shù)據(jù)、調(diào)用子組件的方法等、非常方便. 適用于vue3.0版本語法,后續(xù)會講到vue3.2版本setup語法糖有所不同。

          語法示例:

          <input標(biāo)簽 type="text" ref="inputRef">

          <子組件 ref="childRef" />

          const inputRef=ref<HTMLElement|null>(null)

          const childRef=ref<HTMLElement|null>(null)

          父組件代碼:

          <template>
            <div style="font-size: 14px;">
              <h2>測試ref獲取普通標(biāo)簽 讓輸入框自動獲取焦點</h2>
              <input type="text" ref="inputRef">
          
              <h2>測試ref獲取子組件</h2>
              <Child ref="childRef" />
            </div>
          </template>
          
          <script lang="ts">
          // vue3.0版本語法
          import { defineComponent, ref, onMounted } from 'vue'
          import Child from './child.vue'
          export default defineComponent({
            components: {
              Child
            },
            setup() {
              const inputRef=ref<HTMLElement|null>(null)
              const childRef=ref<HTMLElement|null>(null)
                
              onMounted(()=> {
                // ref獲取元素: 利用ref函數(shù)獲取組件中的標(biāo)簽元素
                // 需求實現(xiàn)1: 讓輸入框自動獲取焦點
                inputRef.value && inputRef.value.focus()
                // ref獲取元素: 利用ref函數(shù)獲取組件中的標(biāo)簽元素
                // 需求實現(xiàn)2: 查看子組件的數(shù)據(jù),修改子組件的某個值
                console.log(childRef.value);
                setTimeout(()=> {
                  childRef.value.text='3秒后修改子組件的text值'
                }, 3000)
              })
          
              return {
                inputRef,childRef
              }
            },
          })
          </script>
          

          子組件代碼:

          <template>
            <div>
              <h3>{{ text }}</h3>
            </div>
          </template>
          
          <script lang="ts">
          // vue3.0版本語法
          import { ref, defineComponent } from "vue";
          
          export default defineComponent({
            name: "Child",
            setup() {
              const text=ref('我是子組件');
          
              return {
                text
              };
            },
          });
          </script>

          初始頁面效果-輸入框獲取到了焦點,log打印出了子組件的所有數(shù)據(jù):

          初始3秒后頁面效果,修改了子組件的text數(shù)據(jù):

          vue3.2版本語法:


          主站蜘蛛池模板: 日韩中文字幕一区| 精品国产一区二区三区在线观看 | 福利一区二区在线| 亚洲AV综合色一区二区三区| 一区二区三区无码高清视频| 视频在线一区二区三区| 一区二区三区美女视频| 亚洲爽爽一区二区三区| www亚洲精品少妇裸乳一区二区| 日韩精品一区二三区中文| 福利一区国产原创多挂探花| 无码精品蜜桃一区二区三区WW| 老湿机一区午夜精品免费福利| 午夜视频久久久久一区| 精品一区二区三区免费观看 | 久久精品一区二区| 亚洲AV无码一区东京热久久| 国产乱码一区二区三区爽爽爽| 久久国产精品亚洲一区二区| 人妻AV中文字幕一区二区三区 | 日韩精品一区二区三区毛片| 国产精久久一区二区三区| 国产伦精品一区二区三区视频猫咪 | 中文字幕日韩一区二区三区不| 岛国精品一区免费视频在线观看 | 视频一区精品自拍| 久久se精品动漫一区二区三区| 无码精品视频一区二区三区 | 秋霞鲁丝片一区二区三区| 国产一区二区三区免费观在线| 亚洲av无码一区二区三区网站| 亚洲一区免费观看| 怡红院一区二区三区| 一区二区三区中文字幕| 免费精品一区二区三区第35| 激情内射亚洲一区二区三区爱妻| 国精产品一区一区三区MBA下载| www亚洲精品少妇裸乳一区二区 | 精品国产一区二区三区香蕉事| 一区二区三区在线观看视频| 亚洲不卡av不卡一区二区|