<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React Hooks 學(xué)習(xí)筆記 | useEffect Hook(二)

          共 27317字,需瀏覽 55分鐘

           ·

          2021-07-09 23:44

          大家好,上一篇文章我們學(xué)習(xí)了 State Hook 的基礎(chǔ)用法,還沒看的同學(xué)們,小編建議你先看下《 React Hooks 學(xué)習(xí)筆記 | State Hook(一)》這篇文章,今天我們一起來了解 useEffect Hook 的基礎(chǔ)用法。

          一、開篇

          一般大多數(shù)的組件都需要特殊的操作,比如獲取數(shù)據(jù)、監(jiān)聽數(shù)據(jù)變化或更改DOM的相關(guān)操作,這些操作被稱作 “side effects(副作用)”。

          在類組件中,我們通常會(huì)在 componentDidMount 和 componentDidUpdate 這兩個(gè)常用的生命鉤子函數(shù)進(jìn)行操作,這些生命周期的相關(guān)方法便于我們?cè)诤线m的時(shí)機(jī)更加精確的控制組件的行為。

          這有一個(gè)簡(jiǎn)單的代碼示例,頁面加載完成后,更改頁面的標(biāo)題

          componentDidMount() {
            document.title = this.state.name + " from " + this.state.location;
          }

          當(dāng)你嘗試更改標(biāo)題對(duì)應(yīng)的狀態(tài)值時(shí),頁面的標(biāo)題不會(huì)發(fā)生任何變化,你還需要添加另一個(gè)生命周期的方法 componentDidUpdate() ,監(jiān)聽狀態(tài)值的變化重新re-render,示例代碼如下:

          componentDidUpdate() {
            document.title = this.state.name + " from " + this.state.location;
          }

          從上述代碼我們可以看出,要實(shí)現(xiàn)動(dòng)態(tài)更改頁面標(biāo)題的方法,我們需要調(diào)用兩個(gè)生命鉤子函數(shù),同樣的方法寫兩遍。但是我們使用 useEffect Hook 函數(shù),就能解決代碼重復(fù)的問題,示例代碼如下:

          import React, { useState, useEffect } from "react";
          //...
          useEffect(() => {
            document.title = name + " from " + location;
          });

          可以看出,使用 useEffect Hook ,我們就實(shí)現(xiàn)了兩個(gè)生命周期函數(shù)等效的目的,節(jié)省了代碼量。


          二、添加清除功能

          還有一個(gè)類組件的例子,在某些情況下,你需要在組件卸載(unmounted)或銷毀(destroyed)之前,做一些有必要的清除的操作,比如timers、interval,或者取消網(wǎng)絡(luò)請(qǐng)求,或者清理任何在componentDidMount()中創(chuàng)建的DOM元素(elements),你可能會(huì)想到類組件中的 componentWillUnmount()這個(gè)鉤子函數(shù),示例代碼如下:

          import React from "react";
          export default class ClassDemo extends React.Component {
            constructor(props) {
              super(props);
              this.state = {
                resolution: {
                  widthwindow.innerWidth,
                  heightwindow.innerHeight
                }
              };
              this.handleResize = this.handleResize.bind(this);
            }
            componentDidMount() {
              window.addEventListener("resize"this.handleResize);
            }
            componentDidUpdate() {
              window.addEventListener("resize"this.handleResize);
            }
            componentWillUnmount() {
              window.removeEventListener('resize'this.handleResize);
            }
            handleResize() {
              this.setState({
                resolution: {
                  widthwindow.innerWidth,
                  heightwindow.innerHeight
                }
              });
            }
            render() {
              return (
                <section>
                  <h3>
                    {this.state.resolution.width} x {this.state.resolution.height}
                  </h3>
                </section>

              )
            }
          }

          上面的代碼將顯示瀏覽器窗口的當(dāng)前分辨率。當(dāng)你調(diào)整窗口大小,您應(yīng)該會(huì)看到自動(dòng)更新窗口的寬和高的值,同時(shí)我們又添加了組件銷毀時(shí),在 componentWillUnmount() 函數(shù)中定義清除監(jiān)聽窗口大小的邏輯。

          如果我們使用 Hook 的方式改寫上述代碼,看起來更加簡(jiǎn)潔,示例代碼如下:

          import React, { useState, useEffect } from "react";
          export default function HookDemo(props{
            ...
            const [resolution, setResolution] = useState({
              widthwindow.innerWidth,
              heightwindow.innerHeight
            });
            useEffect(() => {
              const handleResize = () => {
                setResolution({
                  widthwindow.innerWidth,
                  heightwindow.innerHeight
                });
              };
              window.addEventListener("resize", handleResize);
              // return clean-up function
              return () => {
                document.title = 'React Hooks Demo';
                window.removeEventListener("resize", handleResize);
              };
            });
            ...
            return (
              <section>
                ...
                <h3>
                  {resolution.width} x {resolution.height}
                </h3>
              </section>

            );
          }

          運(yùn)行后的效果如下所示:

          顯而易見,我們使用 hook 代碼完成了同樣的事情,代碼量更少,結(jié)構(gòu)更緊湊。你是否注意到我們?cè)谶@個(gè) useEffect Hook 中調(diào)用了 return 函數(shù)?這種寫法就等同 componentWillUnmount(),你可以在這里做一些和清除邏輯相關(guān)的一些處理邏輯。

          三、關(guān)于 [ ] 依賴數(shù)組參數(shù)的說明

          在開篇的時(shí)候,我們使用  useEffect Hook 實(shí)現(xiàn)了 componentDidMount ,componentDidUpdate 兩個(gè)生命鉤子函數(shù)的一致的效果,這就意味著 DOM 加載完成后,狀態(tài)發(fā)生變化造成的 re-render 都會(huì)執(zhí)行 useEffect Hook 中的邏輯,在一些場(chǎng)景下,我們沒必要在狀態(tài)發(fā)生變化時(shí),調(diào)用此函數(shù)的邏輯,比如我們?cè)谶@里定義數(shù)據(jù)接口更改數(shù)據(jù)狀態(tài),數(shù)據(jù)狀態(tài)發(fā)生變化,會(huì)重新調(diào)用  useEffect Hook 中的請(qǐng)求邏輯,這樣豈不是進(jìn)入了無限循環(huán),數(shù)據(jù)量大的話,說不定就把接口請(qǐng)求死了。

          但是還好, useEffect Hook  提供了依賴使用參數(shù),第一個(gè)參數(shù)是定義方法,第二個(gè)參數(shù)是依賴數(shù)組,用于自定義依賴的參數(shù),是否觸發(fā)再次執(zhí)行,接下來我們來看幾個(gè)示例效果:

          3.1、after every render

          useEffect(() => { 
          // run after every rendering 
          console.log('render'
          })


          如上圖所示,我們每次更改狀態(tài)值導(dǎo)致組件重新渲染時(shí),我們?cè)?useEffect 中定義的輸出將會(huì)反復(fù)的被執(zhí)行。

          3.2、Once(執(zhí)行一次)

          接下來我們可以在第二個(gè)參數(shù)上定義一個(gè)空數(shù)組,解決上述問題,告訴 Hook 組件只執(zhí)行一次(及時(shí)狀態(tài)發(fā)生改變導(dǎo)致的 re-render ),示例代碼如下:

          useEffect(() => {
              // Just run the first time
              console.log('render')
            }, [])


          如上圖運(yùn)行效果所示,你會(huì)發(fā)現(xiàn) Hook 函數(shù)中定義的輸出,無論我們?cè)趺锤臓顟B(tài)值,其只輸出一次。

          3.3、依賴 state/props 的改變?cè)賵?zhí)行

          如果你想依賴特定的狀態(tài)值、屬性,如果其發(fā)生變化時(shí)導(dǎo)致的 re-render ,再次執(zhí)行 Hook 函數(shù)中定義的邏輯,你可以將其寫在數(shù)組內(nèi),示例代碼如下:

          useEffect(() => {
              // When title or name changed will render
              console.log('render')
          }, [title, name])

          四、用一張圖總結(jié)下

          說了這么多,我們做一下總結(jié),說白了就是整合了 componentDidMount,componentDidUpdate,與 componentWillUnmount 這三個(gè)生命鉤子函數(shù),變成了一個(gè)API,其用法可以用如下一張圖進(jìn)行精簡(jiǎn)概括



          五、繼續(xù)完善購(gòu)物清單

          在上一篇系列文章里《 React Hooks 學(xué)習(xí)筆記 | State Hook(一)》,我們通過做一個(gè)簡(jiǎn)單的購(gòu)物清單實(shí)踐了 State Hook,本篇文章我們通過繼續(xù)完善這個(gè)實(shí)例,加深我們對(duì) useEffect Hook 的理解,學(xué)習(xí)之前大家可以先提前下載上一篇文章的源碼。

          本節(jié)案例,為了更加接近實(shí)際應(yīng)用場(chǎng)景,這里我使用了 Firebase 快速構(gòu)建后端的數(shù)據(jù)庫(kù)和其自身的接口服務(wù)。(谷歌的產(chǎn)品,目前需要科學(xué)上網(wǎng)才能使用,F(xiàn)irebase 是 Google Cloud Platform 為應(yīng)用開發(fā)者們推出的應(yīng)用后臺(tái)服務(wù)。借助Firebase,應(yīng)用開發(fā)者們可以快速搭建應(yīng)用后臺(tái),集中注意力在開發(fā) client 上,并且可以享受到 Google Cloud 的穩(wěn)定性和 scalability )。

          5.1、創(chuàng)建Firebase

          1、在 https://firebase.google.com/(科學(xué)上網(wǎng)才能訪問),使用谷歌賬戶登錄 ,進(jìn)入控制臺(tái)創(chuàng)建項(xiàng)目。

          2、這里我新建了一個(gè) react-hook-update 項(xiàng)目,并這個(gè)項(xiàng)目里我們創(chuàng)建了 Realtime Database 實(shí)時(shí)數(shù)據(jù)庫(kù)(非關(guān)系數(shù)據(jù)庫(kù)),用于存儲(chǔ)項(xiàng)目的數(shù)據(jù),其數(shù)據(jù)庫(kù)又提供了相關(guān)的接口用于數(shù)據(jù)的增刪改查。每個(gè)數(shù)據(jù)庫(kù)都會(huì)提供一個(gè)鏈接用于操作,本項(xiàng)目數(shù)據(jù)庫(kù)鏈接為 https://react-hook-update-350d4-default-rtdb.firebaseio.com/



          5.2、添加狀態(tài)加載、錯(cuò)誤提示UI組件

          接下來我們添加進(jìn)度加載組件和錯(cuò)誤提示對(duì)話框組件,分別用于狀態(tài)加載中狀態(tài)提示和系統(tǒng)錯(cuò)誤狀態(tài)提示,代碼比較簡(jiǎn)單,這里就是貼下相關(guān)代碼。

          LoadingIndicator 數(shù)據(jù)加載狀態(tài)提示組件

          import React from 'react';

          import './LoadingIndicator.css';

          const LoadingIndicator = () => (
              <div className="lds-ring">
                  <div />
                  <div />
                  <div />
                  <div />
              </div>

          );

          export default LoadingIndicator;

          // components/UI/LoadingIndicator.js
          .lds-ring {
            display: inline-block;
            position: relative;
            width54px;
            height54px;
          }
          .lds-ring div {
            box-sizing: border-box;
            display: block;
            position: absolute;
            width44px;
            height44px;
            margin6px;
            border6px solid #ff2058;
            border-radius50%;
            animation: lds-ring 1.2s cubic-bezier(0.500.51) infinite;
            border-color#ff2058 transparent transparent transparent;
          }
          .lds-ring div:nth-child(1) {
            animation-delay: -0.45s;
          }
          .lds-ring div:nth-child(2) {
            animation-delay: -0.3s;
          }
          .lds-ring div:nth-child(3) {
            animation-delay: -0.15s;
          }
          @keyframes lds-ring {
            0% {
              transformrotate(0deg);
            }
            100% {
              transformrotate(360deg);
            }
          }

          /*
           components/UI/LoadingIndicator.css
          */

          ErrorModal 系統(tǒng)錯(cuò)誤組件

          import React from "react";
          import './ErrorModal.css'

          const ErrorModal = React.memo(props => {
              return (
                  <React.Fragment>
                      <div className="backdrop" onClick={props.onClose} />
                      <div className="error-modal">
                          <h2>An Error Occurred!</h2>
                          <p>{props.children}</p>
                          <div className="error-modal__actions">
                              <button type="button" onClick={props.onClose}>
                                  Okay
                              </button>
                          </div>
                      </div>
                  </React.Fragment>

              );
          });

          export default ErrorModal;

          // components/UI/ErrorModal.js
          .error-modal {
            position: fixed;
            top30vh;
            leftcalc(50% - 15rem);
            width30rem;
            background: white;
            box-shadow0 2px 8px rgba(0000.26);
            z-index100;
            border-radius7px;
          }

          .error-modal h2 {
            margin0;
            padding1rem;
            background#ff2058;
            color: white;
            border-radius7px 7px 0 0;
          }

          .error-modal p {
              padding1rem;
          }

          .error-modal__actions {
              display: flex;
              justify-content: flex-end;
              padding0 0.5rem;
          }

          .backdrop {
            position: fixed;
            top0;
            left0;
            width100%;
            height100vh;
            backgroundrgba(0000.75);
            z-index50;
          }

          /*
           components/UI/ErrorModal.css
          */

          5.3、增加接口顯示購(gòu)物清單列表

          接下來,我們?cè)谫?gòu)物清單頁 Ingredients 組件里,我們使用今天所學(xué)的知識(shí),在 useEffect() 里添加歷史購(gòu)物清單的列表接口,用于顯示過往的清單信息,這里我們使用 firebase 的提供的API, 請(qǐng)求 https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json  這個(gè)地址,就會(huì)默認(rèn)給你創(chuàng)建 ingredients 的集合,并返回一個(gè) JSON 形式的數(shù)據(jù)集合,示例代碼如下:

          useEffect(() => {
                  fetch('https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json')
                      .then(response => response.json())
                      .then(responseData => {
                          const loadedIngredients = [];
                          for (const key in responseData) {
                              loadedIngredients.push({
                                  id: key,
                                  title: responseData[key].title,
                                  amount: responseData[key].amount
                              });
                          }
                          setUserIngredients(loadedIngredients);
                      })
              }, []);

          // components/Ingredients/Ingredients.js

          上述代碼我們可以看出,我們使用 fetch 函數(shù)請(qǐng)求接口,請(qǐng)求完成后我們更新 UserIngredients 數(shù)據(jù)狀態(tài),最后別忘記了,同時(shí)在 useEffect 函數(shù)中,依賴參數(shù)為空數(shù)組[ ],表示只加載一次,數(shù)據(jù)狀態(tài)更新時(shí)導(dǎo)致的 re-render,就不會(huì)發(fā)生無限循環(huán)的請(qǐng)求接口了,這個(gè)很重要、很重要、很重要!

          5.4 、更新刪除清單的方法

          這里我們要改寫刪除清單的方法,將刪除的數(shù)據(jù)更新到云端數(shù)據(jù)庫(kù) Firebase ,為了顯示更新狀態(tài)和系統(tǒng)的錯(cuò)誤信息,這里我們引入 ErrorModal ,添加數(shù)據(jù)加載狀態(tài)和錯(cuò)誤狀態(tài),示例如下:

          import ErrorModal from '../UI/ErrorModal';
          const Ingredients = () => {
          ...
              const [isLoading, setIsLoading] = useState(false);
              const [error, setError] = useState();
          ...
          }

          // components/Ingredients/Ingredients.js

          接下來我們來改寫刪除方法 removeIngredientHandler

          const removeIngredientHandler = ingredientId => {
                  setIsLoading(true);
                  fetch(`https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients/${ingredientId}.json`,
                      {
                          method'DELETE'
                      }
                  ).then(response => {
                      setIsLoading(false);
                      setUserIngredients(prevIngredients =>
                          prevIngredients.filter(ingredient => ingredient.id !== ingredientId)
                      );
                  }).catch(error => {
                      setError('系統(tǒng)開了個(gè)小差,請(qǐng)稍后再試!');
                      setIsLoading(false);
                  })
              };

          // components/Ingredients/Ingredients.js

          從上述代碼我們可以看出,首先我們先將加載狀態(tài)默認(rèn)為true,接下來請(qǐng)求刪除接口,這里請(qǐng)注意接口地址 ${ingredientId} 這個(gè)變量的使用(當(dāng)前數(shù)據(jù)的 ID 主鍵),刪除成功后,更新加載狀態(tài)為 false 。如果刪除過程中發(fā)生錯(cuò)誤,我們?cè)赾atch 代碼塊里捕捉錯(cuò)誤并調(diào)用錯(cuò)誤提示對(duì)話框(更新錯(cuò)誤狀態(tài)和加載狀態(tài))。

          5.5、更新添加清單的方法

          接著我們改寫添加清單的方式,通過接口請(qǐng)求的方式,將添加的數(shù)據(jù)添加至 Firebase 數(shù)據(jù)庫(kù),代碼比較簡(jiǎn)單,就不多解釋了,示例代碼如下:

          const addIngredientHandler = ingredient => {
                  setIsLoading(true);
                  fetch('https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json',
                      {
                      method'post',
                      bodyJSON.stringify(ingredient),
                      headers: {'Content-Type''application/json'}
                  })
                      .then(response => {
                      setIsLoading(false);
                      return response.json();
                  })
                      .then(responseData => {
                      setUserIngredients(prevIngredients => [
                          ...prevIngredients,
                          {id: responseData.name, ...ingredient}
                      ]);
                  });

              };

          // components/Ingredients/Ingredients.js

          5.5、添加搜索組件功能

          我們繼續(xù)完善購(gòu)物清單的功能,為購(gòu)物清單添加新功能-搜索功能(通過請(qǐng)求接口),方便我們搜索清單的內(nèi)容,界面效果如下圖所示,在中間添加一個(gè)搜索框。

          新建 Search.js 文件,然后在 useEffect 方法內(nèi)通過 Firebase 提供的接口,實(shí)現(xiàn)基于商品名稱搜索購(gòu)物清單,然后定義 onLoadIngredients 方法屬性,用于接收返回的數(shù)據(jù),方便將數(shù)據(jù)通過參數(shù)的形式傳遞給父組件。最后我們定義 enteredFilter 數(shù)據(jù)狀態(tài),用于接收用戶輸入框的輸入內(nèi)容,代碼如下所示:
          import React,{useState,useEffect,useRef} from "react";
          import Card from "../UI/Card";
          import './Search.css';

          const Search = React.memo(props=>{
              const { onLoadIngredients } = props;
              const [enteredFilter,setEnterFilter]=useState('');
              const inputRef = useRef();
              useEffect(() => {
                  const timer = setTimeout(() => {
                      if (enteredFilter === inputRef.current.value) {
                          const query =
                              enteredFilter.length === 0
                                  ? ''
                                  : `?orderBy="title"&equalTo="${enteredFilter}"`;
                          fetch(
                              'https://react-hook-update-350d4-default-rtdb.firebaseio.com/ingredients.json' + query
                          )
                              .then(response => response.json())
                              .then(responseData => {
                                  const loadedIngredients = [];
                                  for (const key in responseData) {
                                      loadedIngredients.push({
                                          id: key,
                                          title: responseData[key].title,
                                          amount: responseData[key].amount
                                      });
                                  }
                                  onLoadIngredients(loadedIngredients);
                              });
                      }
                  }, 500);
                  return () => {
                      clearTimeout(timer);
                  };
              }, [enteredFilter, onLoadIngredients, inputRef]);
              return(
                  <section className="search">
                      <Card>
                          <div className="search-input">
                              <label>搜索商品名稱</label>
                              <input
                                  ref={inputRef}
                                  type="text"
                                  value={enteredFilter}
                                  onChange={event=>
          setEnterFilter(event.target.value)}
                              />
                          </div>
                      </Card>
                  </section>

              )

          });

          export  default Search;

          // components/Ingredients/Search.js

          上述代碼,我們定義為了避免頻繁觸發(fā)接口,定義了一個(gè)定時(shí)器,在用戶輸入500毫秒后在請(qǐng)求接口。你可以看到  useEffect() 里,我們使用了 return 方法,用于清理定時(shí)器,要不會(huì)有很多的定時(shí)器。同時(shí)依賴參數(shù)有三個(gè) [enteredFilter, onLoadIngredients,inputRef],只有用戶的輸入內(nèi)容和事件屬性發(fā)生變化時(shí),才會(huì)再次觸發(fā) useEffect() 中的邏輯。這里我們用到了useRef 方法獲取輸入框的值,關(guān)于其詳細(xì)的介紹,會(huì)在稍后的文章介紹。

          接下來貼上 Search.css 的相關(guān)代碼,由于內(nèi)容比較簡(jiǎn)單,這里就不過多解釋了。

          .search {
            width30rem;
            margin2rem auto;
            max-width80%;
          }

          .search-input {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-direction: column;
          }

          .search-input input {
            font: inherit;
            border1px solid #ccc;
            border-radius5px;
            padding0.15rem 0.25rem;
          }

          .search-input input:focus {
            outline: none;
            border-color#ff2058;
          }

          @media (min-width: 768px) {
            .search-input {
              flex-direction: row;
            }
          }

          /* 
          components/Ingredients/Search.css
          */

          最后我們將 Search 組件添加至清單頁面,在這個(gè)頁面里定義了一個(gè) useCallback 的方法,類似 Vue 的 computed 緩存的特性,避免重復(fù)計(jì)算,這個(gè)方法主要用來接收  Search 子組件傳輸數(shù)據(jù),用于更新 UserIngredients 數(shù)據(jù)中的狀態(tài),在稍后的文章里我會(huì)詳細(xì)介紹,這里只是簡(jiǎn)單的貼下代碼,示例代碼如下:

          const filteredIngredientsHandler = useCallback(filteredIngredients => {
                  setUserIngredients(filteredIngredients)
              }, []);
          // components/Ingredients/Ingredients.js

          接下來在 return 里添加 Search 組件和 ErrorModal 組件,在 Search 組件的 ingredients 屬性里添加上述定義的 filteredIngredientsHandler 方法,用于接收組件搜索接口請(qǐng)求返回的數(shù)據(jù)內(nèi)容,用于更新 UserIngredients 的數(shù)據(jù)狀態(tài),示例代碼如下:

          <div className="App">
               {error && <ErrorModal onClose={clearError}>{error}</ErrorModal>}
               <IngredientForm onAddIngredient={addIngredientHandler} loading={isLoading}/>
                      <section>
                          <Search onLoadIngredients={filteredIngredientsHandler}/>
                          <IngredientList ingredients={userIngredients} onRemoveItem={removeIngredientHandler}/>
                      </section>
                  </div>
          // components/Ingredients/Ingredients.js

          到這里,本節(jié)的實(shí)踐練習(xí)就完了,基本上是一個(gè)基于后端接口的,基礎(chǔ)的增刪改查案例,稍微完善下就可以運(yùn)用到你的實(shí)際案例中。你可以點(diǎn)擊閱讀原文進(jìn)行體驗(yàn)(主要本案例采用了Firebase ,科學(xué)上網(wǎng)才能在線體驗(yàn))。

          六、結(jié)束語

          好了,本篇關(guān)于 useEffect() 的介紹就結(jié)束了,希望你已經(jīng)理解了 useEffect 的基本用法,感謝你的閱讀,你可以點(diǎn)擊閱讀原文體驗(yàn)本文的案例部分,如果你想獲取源碼請(qǐng)回復(fù)"r2",小編建議親自動(dòng)手做一下,這樣才能加深對(duì) useEffect Hook 的認(rèn)知,下一篇本系列文章將會(huì)繼續(xù)介紹 useRef,敬請(qǐng)期待。

          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美精品久久人妻无码免费视频 | 99免费在线观看视频 | 亚洲无码专区日韩 | 亚洲操逼影视 | 少妇高潮日韩 |