久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何使用React,TypeScript和React测试库创建出色的用户体验

發布時間:2023/11/29 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何使用React,TypeScript和React测试库创建出色的用户体验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

I'm always willing to learn, no matter how much I know. As a software engineer, my thirst for knowledge has increased a lot. I know that I have a lot of things to learn daily.

無論我知道多少,我總是愿意學習。 作為軟件工程師,我對知識的渴望增加了很多。 我知道我每天有很多東西要學習。

But before I could learn more, I wanted to master the fundamentals. To make myself a better developer, I wanted to understand more about how to create great product experiences.

但是在我可以學習更多之前,我想掌握基礎知識。 為了使自己成為一個更好的開發人員,我想更多地了解如何創建出色的產品體驗。

This post is my attempt to illustrate a Proof of Concept (PoC) I built to try out some ideas.

這篇文章旨在說明我為嘗試一些想法而構建的概念驗證(PoC)。

I had some topics in mind for this project. It needed to:

我在這個項目中想到了一些主題。 它需要:

  • Use high-quality software

    使用高質量的軟件
  • Provide a great user experience

    提供出色的用戶體驗

When I say high-quality software, this can mean so many different things. But I wanted to focus on three parts:

當我說高質量軟件時,這可能意味著很多不同的東西。 但是我想重點關注三個部分:

  • Clean Code: Strive to write human-readable code that is easy to read and simple to maintain. Separate responsibility for functions and components.

    干凈的代碼:努力編寫易于閱讀且易于維護的人類可讀代碼。 對功能和組件負責。
  • Good test coverage: It's actually not about coverage. It's about tests that cover important parts of components' behavior without knowing too much about implementation details.

    良好的測試覆蓋率:實際上與覆蓋率無關。 它是關于覆蓋組件行為的重要部分的測試,而又不了解實施細節。
  • Consistent state management: I wanted to build with software that enables the app to have consistent data. Predictability is important.

    一致的狀態管理:我想使用使應用程序具有一致數據的軟件進行構建。 可預測性很重要。

User experience was the main focus of this PoC. The software and techniques would be the foundation that enabled a good experience for users.

用戶體驗是此PoC的主要重點。 軟件和技術將成為為用戶帶來良好體驗的基礎。

To make the state consistent, I wanted a type system. So I chose TypeScript. This was my first time using Typescript with React. This project also allowed me to build custom hooks and test it properly.

為了使狀態一致,我想要一個類型系統。 所以我選擇了TypeScript。 這是我第一次將Typescript與React結合使用。 這個項目還使我能夠構建自定義的鉤子并對其進行正確的測試。

設置項目 (Setting up the project)

I came across this library called tsdx that sets up all the Typescript configuration for you. It's mainly used to build packages. Since this was a simple side project, I didn't mind giving it a try.

我遇到了一個名為tsdx的庫,該庫為您設置了所有Typescript配置。 它主要用于構建軟件包。 由于這是一個簡單的附帶項目,所以我不介意嘗試一下。

After installing it, I chose the React template and I was ready to code. But before the fun part, I wanted to set up the test configuration too. I used the React Testing Library as the main library together with jest-dom to provide some awesome custom methods (I really like the toBeInTheDocument matcher).

安裝后,我選擇了React模板,并且可以編寫代碼了。 但是在有趣的部分之前,我也想設置測試配置。 我將React Testing庫與jest-dom一起用作主庫,以提供一些很棒的自定義方法(我真的很喜歡toBeInTheDocument匹配器)。

With all that installed, I overwrote the jest config by adding a new jest.config.js:

安裝完所有內容后,我通過添加新的jest.config.js了jest配置:

module.exports = {verbose: true,setupFilesAfterEnv: ["./setupTests.ts"], };

And a setupTests.ts to import everything I needed.

和setupTests.ts導入我需要的一切。

import "@testing-library/jest-dom";

In this case, I just had the jest-dom library to import. That way, I didn't need to import this package in my test files. Now it worked out of the box.

在這種情況下,我只有jest-dom庫要導入。 這樣,我就無需在測試文件中導入該軟件包。 現在,它開箱即用。

To test this installation and configuration, I built a simple component:

為了測試此安裝和配置,我構建了一個簡單的組件:

export const Thing = () => <h1>I'm TK</h1>;

In my test, I wanted to render it and see if it was in the DOM.

在測試中,我想渲染它,看看它是否在DOM中。

import React from 'react'; import { render } from '@testing-library/react'; import { Thing } from '../index';describe('Thing', () => {it('renders the correct text in the document', () => {const { getByText } = render(<Thing />);expect(getByText("I'm TK")).toBeInTheDocument();}); });

Now we are ready for the next step.

現在我們已準備好進行下一步。

配置路由 (Configuring routes)

Here I wanted to have only two routes for now. The home page and the search page - even though I'll do nothing about the home page.

我現在只想有兩條路線。 主頁和搜索頁面-即使我不會對主頁進行任何操作。

For this project, I'm using the react-router-dom library to handle all things router-related. It's simple, easy, and fun to work with.

對于這個項目,我正在使用react-router-dom庫來處理所有與路由器相關的事情。 使用起來非常簡單,輕松且有趣。

After installing it, I added the router components in the app.typescript.

安裝后,我在app.typescript添加了路由器組件。

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';export const App = () => (<Router><Switch><Route path="/search"><h1>It's the search!</h1></Route><Route path="/"><h1>It's Home</h1></Route></Switch></Router> );

Now if we enter the localhost:1234, we see the title It's Home. Go to the localhost:1234/search, and we'll see the text It's the search!.

現在,如果我們輸入localhost:1234 ,則會看到標題為It's Home 。 轉到localhost:1234/search ,我們將看到文本It's the search! 。

Before we continue to start implementing our search page, I wanted to build a simple menu to switch between home and search pages without manipulating the URL. For this project, I'm using Material UI to build the UI foundation.

在繼續開始實現搜索頁面之前,我想構建一個簡單的菜單來在主頁和搜索頁面之間切換而不使用URL。 對于此項目,我正在使用Material UI構建UI基礎。

For now, we are just installing the @material-ui/core.

目前,我們僅安裝@material-ui/core 。

To build the menu, we have the button to open the menu options. In this case they're the "home" and "search" options.

要構建菜單,我們有按鈕來打開菜單選項。 在這種情況下,它們是“主頁”和“搜索”選項。

But to build a better component abstraction, I prefer to hide the content (link and label) for the menu items and make the Menu component receive this data as a prop. This way, the menu doesn't know about the items, it will just iterate through the items list and render them.

但是,為了構建更好的組件抽象,我更喜歡隱藏菜單項的內容(鏈接和標簽),并使Menu組件作為道具接收此數據。 這樣,菜單就不會知道項目,它只會遍歷項目列表并呈現它們。

It looks like this:

看起來像這樣:

import React, { Fragment, useState, MouseEvent } from 'react'; import { Link } from 'react-router-dom'; import Button from '@material-ui/core/Button'; import MuiMenu from '@material-ui/core/Menu'; import MuiMenuItem from '@material-ui/core/MenuItem';import { MenuItem } from '../../types/MenuItem';type MenuPropsType = { menuItems: MenuItem[] };export const Menu = ({ menuItems }: MenuPropsType) => {const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {setAnchorEl(event.currentTarget);};const handleClose = (): void => {setAnchorEl(null);};return (<Fragment><Button aria-controls="menu" aria-haspopup="true" onClick={handleClick}>Open Menu</Button><MuiMenuid="simple-menu"anchorEl={anchorEl}keepMountedopen={Boolean(anchorEl)}onClose={handleClose}>{menuItems.map((item: MenuItem) => (<Link to={item.linkTo} onClick={handleClose} key={item.key}><MuiMenuItem>{item.label}</MuiMenuItem></Link>))}</MuiMenu></Fragment>); };export default Menu;

Don't panic! I know it is a huge block of code, but it is pretty simple. the Fragment wrap the Button and MuiMenu (Mui stands for Material UI. I needed to rename the component because the component I'm building is also called menu).

不要驚慌! 我知道這是一個巨大的代碼塊,但是非常簡單。 Fragment包裝Button和MuiMenu ( Mui代表Material UI。我需要重命名該組件,因為我正在構建的組件也稱為menu)。

It receives the menuItems as a prop and maps through it to build the menu item wrapped by the Link component. Link is a component from react-router to link to a given URL.

它接收menuItems作為道具,并通過它進行映射以構建由Link組件包裝的菜單項。 鏈接是從React-Router鏈接到給定URL的組件。

The menu behavior is also simple: we bind the handleClick function to the button's onClick. That way, we can change anchorEl when the button is triggered (or clicked if you prefer). The anchorEl is just a component state that represents the Mui menu element to open the menu switch. So it will open the menu items to let the user chooses one of those.

菜單行為也很簡單:我們將handleClick函數綁定到按鈕的onClick 。 這樣,我們就可以在觸發按鈕時更改anchorEl (或根據需要單擊)。 anchorEl只是表示Mui菜單元素以打開菜單開關的組件狀態。 因此,它將打開菜單項,讓用戶選擇其中一項。

Now, how do we use this component?

現在,我們如何使用該組件?

import { Menu } from './components/Menu'; import { MenuItem } from './types/MenuItem';const menuItems: MenuItem[] = [{linkTo: '/',label: 'Home',key: 'link-to-home',},{linkTo: '/search',label: 'Search',key: 'link-to-search',}, ];<Menu menuItems={menuItems} />

The menuItems is a list of objects. The object has the correct contract expected by the Menu component. The type MenuItem ensures that the contract is correct. It is just a Typescript type:

menuItems是對象列表。 該對象具有Menu組件期望的正確合同。 MenuItem類型可確保合同正確。 它只是一個Typescript type :

export type MenuItem = {linkTo: string;label: string;key: string; };

Now we are ready to build the search page with all the products and a great experience. But before building the list of products, I wanted to create a fetch function to handle the request for products. As I don't have an API of products yet, I can just mock the fetch request.

現在,我們準備好使用所有產品和豐富的經驗來構建搜索頁面。 但是在構建產品列表之前,我想創建一個提取函數來處理對產品的請求。 由于我還沒有產品的API,因此我可以模擬提取請求。

At first, I just built the fetching with useEffect in the Search component. The idea would look like this:

首先,我只是在Search組件中使用useEffect構建了useEffect 。 這個想法看起來像這樣:

import React, { useState, useEffect } from 'react'; import { getProducts } from 'api';export const Search = () => {const [products, setProducts] = useState([]);const [isLoading, setIsLoading] = useState(false);const [hasError, setHasError] = useState(false);useEffect(() => {const fetchProducts = async () => {try {setIsLoading(true);const fetchedProducts = await getProducts();setIsLoading(false);setProducts(fetchedProducts);} catch (error) {setIsLoading(false);setHasError(true);}};fetchProducts();}, []); };

I have:

我有:

  • products initialized as an empty array

    products初始化為空數組

  • isLoading initialized as false

    isLoading初始化為false

  • hasError initialized as false

    hasError初始化為false

  • The fetchProducts is an async function that calls getProducts from the api module. As we don't have a proper API for products yet, this getProducts would return a mock data.

    fetchProducts是一個異步函數,該函數從api模塊調用getProducts 。 由于我們尚無適用于產品的API,因此此getProducts將返回模擬數據。

  • When the fetchProducts is executed, we set the isLoading to true, fetch the products, and then set the isLoading to false, because the fetching finished, and the set the fetched products into products to be used in the component.

    當fetchProducts執行,我們設置了isLoading為true,取產品,然后將isLoading為假,因為取完,一組取出的產品進入products在組件中。

  • If it gets any error in the fetching, we catch them, set the isLoading to false, and the hasError to true. In this context, the component will know that we had an error while fetching and can handle this case.

    如果在獲取時發生任何錯誤,我們將捕獲它們,將isLoading設置為false,將hasError為true。 在這種情況下,組件將知道在提取時發生了錯誤,并且可以處理這種情況。

  • Everything is encapsulated into a useEffect because we are doing a side effect here.

    一切都封裝在useEffect因為我們在這里有副作用。

To handle all the state logic (when to update each part for the specific context), we can extract it to a simple reducer.

為了處理所有狀態邏輯(當針對特定上下文更新每個部分時),我們可以將其提取到簡單的化簡器中。

import { State, FetchActionType, FetchAction } from './types';export const fetchReducer = (state: State, action: FetchAction): State => {switch (action.type) {case FetchActionType.FETCH_INIT:return {...state,isLoading: true,hasError: false,};case FetchActionType.FETCH_SUCCESS:return {...state,hasError: false,isLoading: false,data: action.payload,};case FetchActionType.FETCH_ERROR:return {...state,hasError: true,isLoading: false,};default:return state;} };

The idea here is to separate each action type and handle each state update. So the fetchReducer will receive the state and the action and it will return a new state. This part is interesting because it gets the current state and then returns a new state, but we keep the state contract by using the State type.

這里的想法是分離每種動作類型并處理每種狀態更新。 因此, fetchReducer將接收狀態和操作,并將返回新狀態。 這部分很有趣,因為它獲取當前狀態然后返回新狀態,但是我們通過使用State類型來保持狀態合同。

And for each action type, we will update the state the right way.

對于每種動作類型,我們將以正確的方式更新狀態。

  • FETCH_INIT: isLoading is true and hasError is false.

    FETCH_INIT : isLoading為true, hasError為false。

  • FETCH_SUCCESS: hasError is false, isLoading is false, and the data (products) is updated.

    FETCH_SUCCESS : hasError為false, isLoading為false,并且數據(產品)已更新。

  • FETCH_ERROR: hasError is true and isLoading is false.

    FETCH_ERROR : hasError為true, isLoading為false。

In case it doesn't match any action type, just return the current state.

如果它與任何操作類型都不匹配,則只需返回當前狀態即可。

The FetchActionType is a simple Typescript enum:

FetchActionType是一個簡單的Typescript枚舉:

export enum FetchActionType {FETCH_INIT = 'FETCH_INIT',FETCH_SUCCESS = 'FETCH_SUCCESS',FETCH_ERROR = 'FETCH_ERROR', }

And the State is just a simple type:

State只是一種簡單的類型:

export type ProductType = {name: string;price: number;imageUrl: string;description: string;isShippingFree: boolean;discount: number; };export type Data = ProductType[];export type State = {isLoading: boolean;hasError: boolean;data: Data; };

With this new reducer, now we can useReducer in our fetch. We pass the new reducer and the initial state to it:

有了這個新的reducer,現在我們可以在提取中使用useReducer了。 我們將新的reducer及其初始狀態傳遞給它:

const initialState: State = {isLoading: false,hasError: false,data: fakeData, };const [state, dispatch] = useReducer(fetchReducer, initialState);useEffect(() => {const fetchAPI = async () => {dispatch({ type: FetchActionType.FETCH_INIT });try {const payload = await fetchProducts();dispatch({type: FetchActionType.FETCH_SUCCESS,payload,});} catch (error) {dispatch({ type: FetchActionType.FETCH_ERROR });}};fetchAPI(); }, []);

The initialState has the same contract type. And we pass it to the useReducer together with the fetchReducer we just built. The useReducer provides the state and a function called dispatch to call actions to update our state.

initialState具有相同的合同類型。 我們把它傳遞給useReducer連同fetchReducer我們剛剛建成。 useReducer提供狀態和一個名為dispatch的函數,以調用操作來更新狀態。

  • State fetching: dispatch FETCH_INIT

    狀態獲取:調度FETCH_INIT

  • Finished fetch: dispatch FETCH_SUCCESS with the products payload

    提取完成:使用產品有效負載調度FETCH_SUCCESS

  • Get an error while fetching: dispatch FETCH_ERROR

    提取時發生錯誤:調度FETCH_ERROR

This abstraction got very big and can be very verbose in our component. We could extract it as a separate hook called useProductFetchAPI.

這種抽象很大,在我們的組件中可能非常冗長。 我們可以將其提取為名為useProductFetchAPI的單獨鉤子。

export const useProductFetchAPI = (): State => {const initialState: State = {isLoading: false,hasError: false,data: fakeData,};const [state, dispatch] = useReducer(fetchReducer, initialState);useEffect(() => {const fetchAPI = async () => {dispatch({ type: FetchActionType.FETCH_INIT });try {const payload = await fetchProducts();dispatch({type: FetchActionType.FETCH_SUCCESS,payload,});} catch (error) {dispatch({ type: FetchActionType.FETCH_ERROR });}};fetchAPI();}, []);return state; };

It is just a function that wraps our fetch operation. Now, in the Search component, we can import and call it.

它只是包裝我們的提取操作的函數。 現在,在Search組件中,我們可以導入并調用它。

export const Search = () => {const { isLoading, hasError, data }: State = useProductFetchAPI(); };

We have all the API: isLoading, hasError, and data to use in our component. With this API, we can render a loading spinner or a skeleton based on the isLoading data. We can render an error message based on the hasError value. Or just render the list of products using the data.

我們擁有所有API: isLoading , hasError和要在我們的組件中使用的data 。 使用此API,我們可以基于isLoading數據呈現加載微調器或骨架。 我們可以基于hasError值呈現錯誤消息。 或者只是使用data呈現產品列表。

Before starting implementing our products list, I want to stop and add tests for our custom hook. We have two parts to test here: the reducer and the custom hook.

在開始實施我們的產品列表之前,我想停止并添加針對自定義掛鉤的測試。 這里有兩個要測試的部分:reducer和自定義鉤子。

The reducer is easier as it is just a pure function. It receives value, process, and returns a new value. No side-effect. Everything deterministic.

減速器更簡單,因為它只是一個純函數。 它接收值,處理并返回新值。 無副作用。 一切都是確定性的。

To cover all the possibilities of this reducer, I created three contexts: FETCH_INIT, FETCH_SUCCESS, and FETCH_ERROR actions.

為了涵蓋此reducer的所有可能性,我創建了三個上下文: FETCH_INIT , FETCH_SUCCESS和FETCH_ERROR操作。

Before implementing anything, I set up the initial data to work with.

在實施任何操作之前,我都會設置要使用的初始數據。

const initialData: Data = []; const initialState: State = {isLoading: false,hasError: false,data: initialData, };

Now I can pass this initial state for the reducer together with the specific action I want to cover. For this first test, I wanted to cover the FETCH_INIT action:

現在,我可以將減速器的初始狀態與要覆蓋的特定操作一起傳遞。 對于第一個測試,我想介紹一下FETCH_INIT動作:

describe('when dispatch FETCH_INIT action', () => {it('returns the isLoading as true without any error', () => {const action: FetchAction = {type: FetchActionType.FETCH_INIT,};expect(fetchReducer(initialState, action)).toEqual({isLoading: true,hasError: false,data: initialData,});}); });

It's pretty simple. It receives the initial state and the action, and we expect the proper return value: the new state with the isLoading as true.

很簡單 它接收初始狀態和操作,并且我們期望適當的返回值: isLoading為true的新狀態。

The FETCH_ERROR is pretty similar:

FETCH_ERROR非常相似:

describe('when dispatch FETCH_ERROR action', () => {it('returns the isLoading as true without any error', () => {const action: FetchAction = {type: FetchActionType.FETCH_ERROR,};expect(fetchReducer(initialState, action)).toEqual({isLoading: false,hasError: true,data: [],});}); });

But we pass a different action and expect the hasError to be true.

但是我們通過了一個不同的操作,并期望hasError為true 。

The FETCH_SUCCESS is a bit complex as we just need to build a new state and add it to the payload attribute in the action.

FETCH_SUCCESS有點復雜,因為我們只需要構建一個新狀態并將其添加到操作中的有效負載屬性中。

describe('when dispatch FETCH_SUCCESS action', () => {it('returns the the API data', () => {const product: ProductType = {name: 'iPhone',price: 3500,imageUrl: 'image-url.png',description: 'Apple mobile phone',isShippingFree: true,discount: 0,};const action: FetchAction = {type: FetchActionType.FETCH_SUCCESS,payload: [product],};expect(fetchReducer(initialState, action)).toEqual({isLoading: false,hasError: false,data: [product],});}); });

But nothing too complex here. The new data is there. A list of products. In this case, just one, the iPhone product.

但是這里沒有什么太復雜的。 新數據在那里。 產品清單。 在這種情況下,只有一款iPhone產品。

The second test will cover the custom hook we built. In these tests, I wrote three contexts: a time-out request, a failed network request, and a success request.

第二個測試將涵蓋我們構建的自定義鉤子。 在這些測試中,我編寫了三個上下文:超時請求,失敗的網絡請求和成功的請求。

Here, as I'm using axios to fetch data (when I have an API to fetch the data, I will use it properly), I'm using axios-mock-adapter to mock each context for our tests.

在這里,由于我正在使用axios來獲取數據(當我有一個API來獲取數據時,我會正確使用它),我正在使用axios-mock-adapter來模擬每個上下文以進行測試。

The set up first: Initializing our data and set up an axios mock.

首先設置:初始化數據并設置axios模擬。

const mock: MockAdapter = new MockAdapter(axios); const url: string = '/search'; const initialData: Data = [];

We start implementing a test for the timeout request:

我們開始為超時請求實施測試:

it('handles error on timed-out api request', async () => {mock.onGet(url).timeout();const { result, waitForNextUpdate } = renderHook(() =>useProductFetchAPI(url, initialData));await waitForNextUpdate();const { isLoading, hasError, data }: State = result.current;expect(isLoading).toEqual(false);expect(hasError).toEqual(true);expect(data).toEqual(initialData); });

We set up the mock to return a timeout. The test calls the useProductFetchAPI, wait for an update, and then we can get the state. The isLoading is false, the data is still the same (an empty list), and the hasError is now true as expected.

我們設置了模擬以返回超時。 測試調用useProductFetchAPI ,等待更新,然后我們可以獲取狀態。 isLoading為false, data仍然相同(一個空列表),并且hasError現在為true。

The network request is pretty much the same behavior. The only difference is that the mock will have a network error instead of a timeout.

網絡請求幾乎是相同的行為。 唯一的區別是該模擬將出現網絡錯誤而不是超時。

it('handles error on failed network api request', async () => {mock.onGet(url).networkError();const { result, waitForNextUpdate } = renderHook(() =>useFetchAPI(url, initialData));await waitForNextUpdate();const { isLoading, hasError, data }: State = result.current;expect(isLoading).toEqual(false);expect(hasError).toEqual(true);expect(data).toEqual(initialData); });

And for the success case, we need to create a product object to use it as a request-response data. We also expect the data to be a list of this product object. The hasError and the isLoading are false in this case.

對于成功案例,我們需要創建一個產品對象以將其用作請求-響應數據。 我們還希望data是該產品對象的列表。 在這種情況下, hasError和isLoading為false。

it('gets and updates data from the api request', async () => {const product: ProductType = {name: 'iPhone',price: 3500,imageUrl: 'image-url.png',description: 'Apple mobile phone',isShippingFree: true,discount: 0,};const mockedResponseData: Data = [product];mock.onGet(url).reply(200, mockedResponseData);const { result, waitForNextUpdate } = renderHook(() =>useFetchAPI(url, initialData));await waitForNextUpdate();const { isLoading, hasError, data }: State = result.current;expect(isLoading).toEqual(false);expect(hasError).toEqual(false);expect(data).toEqual([product]); });

Great. We covered everything we needed for this custom hook and the reducer we created. Now we can focus on building the products list.

大。 我們介紹了此自定義鉤子和我們創建的減速器所需的一切。 現在我們可以集中精力構建產品列表。

產品清單 (Products list)

The idea of the products list is to list products that have some information: title, description, price, discount, and if it has free shipping. The final product card would look like this:

產品列表的想法是列出具有以下信息的產品:標題,描述,價格,折扣以及是否可以免費送貨。 最終產品卡如下所示:

To build this card, I created the foundation for the product component:

為了構建此卡,我為產品組件創建了基礎:

const Product = () => (<Box><Image /><TitleDescription/><Price /><Tag /></Box> );

To build the product, we will need to build each component that is inside it.

要構建產品,我們將需要構建產品內部的每個組件。

But before start building the product component, I want to show the JSON data that the fake API will return for us.

但是在開始構建產品組件之前,我想顯示假API為我們返回的JSON數據。

{imageUrl: 'a-url-for-tokyo-tower.png',name: 'Tokyo Tower',description: 'Some description here',price: 45,discount: 20,isShippingFree: true, }

This data is passed from the Search component to the ProductList component:

該數據從Search組件傳遞到ProductList組件:

export const Search = () => {const { isLoading, hasError, data }: State = useProductFetchAPI();if (hasError) {return <h2>Error</h2>;}return <ProductList products={data} isLoading={isLoading} />; };

As I'm using Typescript, I can enforce the static types for the component props. In this case, I have the prop products and the isLoading.

當我使用Typescript時,我可以為組件prop強制使用靜態類型。 在這種情況下,我有prop products和isLoading 。

I built a ProductListPropsType type to handle the product list props.

我建立了一個ProductListPropsType類型來處理產品列表道具。

type ProductListPropsType = {products: ProductType[];isLoading: boolean; };

And the ProductType is a simple type representing the product:

ProductType是表示產品的簡單類型:

export type ProductType = {name: string;price: number;imageUrl: string;description: string;isShippingFree: boolean;discount: number; };

To build the ProductList, I'll use the Grid component from Material UI. First, we have a grid container and then, for each product, we will render a grid item.

要構建ProductList,我將使用Material UI中的Grid組件。 首先,我們有一個網格容器,然后,對于每種產品,我們將渲染一個網格項目。

export const ProductList = ({ products, isLoading }: ProductListPropsType) => (<Grid container spacing={3}>{products.map(product => (<Griditemxs={6}md={3}key={`grid-${product.name}-${product.description}-${product.price}`}><Productkey={`product-${product.name}-${product.description}-${product.price}`}imageUrl={product.imageUrl}name={product.name}description={product.description}price={product.price}discount={product.discount}isShippingFree={product.isShippingFree}isLoading={isLoading}/></Grid>))}</Grid> );

The Grid item will display 2 items per row for mobile as we use the value 6 for each column. And for the desktop version, it will render 4 items per row.

Grid項將為移動設備每行顯示2個項目,因為我們為每列使用值6 。 對于桌面版本,它將每行呈現4個項目。

We iterate through the products list and render the Product component passing all the data it will need.

我們遍歷products列表,并使Product組件傳遞所需的所有數據。

Now we can focus on building the Product component.

現在我們可以集中精力構建Product組件。

Let's start with the easiest one: the Tag. We will pass three data to this component. label, isVisible, and isLoading. When it is not visible, we just return null to don't render it. If it is loading, we will render a Skeleton component from Material UI. But after loading it, we render the tag info with the Free Shipping label.

讓我們從最簡單的一個開始: Tag 。 我們將傳遞三個數據到該組件。 label , isVisible和isLoading 。 當它不可見時,我們只返回null而不渲染它。 如果正在加載,我們將從Material UI渲染一個Skeleton組件。 但是在加載后,我們將使用“ Free Shipping標簽來呈現標簽信息。

export const Tag = ({ label, isVisible, isLoading }: TagProps) => {if (!isVisible) return null;if (isLoading) {return (<Skeleton width="110px" height="40px" data-testid="tag-skeleton-loader" />);}return (<Box mt={1} data-testid="tag-label-wrapper"><span style={tabStyle}>{label}</span></Box>); };

The TagProps is a simple type:

TagProps是一種簡單的類型:

type TagProps = {label: string;isVisible: boolean;isLoading: boolean; };

I'm also using an object to style the span:

我還使用一個對象來設置span樣式:

const tabStyle = {padding: '4px 8px',backgroundColor: '#f2f3fe',color: '#87a7ff',borderRadius: '4px', };

I also wanted to build tests for this component trying to think of its behavior:

我還想為此組件構建測試,以考慮其行為:

  • when it's not visible: the tag will not be in the document.

    當它不可見時:標簽將不在文檔中。
describe('when is not visible', () => {it('does not render anything', () => {const { queryByTestId } = render(<Tag label="a label" isVisible={false} isLoading={false} />);expect(queryByTestId('tag-label-wrapper')).not.toBeInTheDocument();}); });
  • when it's loading: the skeleton will be in the document.

    加載時:骨架將在文檔中。
describe('when is loading', () => {it('renders the tag label', () => {const { queryByTestId } = render(<Tag label="a label" isVisible isLoading />);expect(queryByTestId('tag-skeleton-loader')).toBeInTheDocument();}); });
  • when it's ready to render: the tag will be in the document.

    準備渲染時:標簽將在文檔中。
describe('when is visible and not loading', () => {it('renders the tag label', () => {render(<Tag label="a label" isVisible isLoading={false} />);expect(screen.getByText('a label')).toBeInTheDocument();}); });
  • bonus point: accessibility. I also built an automated test to cover accessibility violations using jest-axe.

    優點:可訪問性。 我還構建了一個自動化測試,以使用jest-axe覆蓋可訪問性沖突。

it('has no accessibility violations', async () => {const { container } = render(<Tag label="a label" isVisible isLoading={false} />);const results = await axe(container);expect(results).toHaveNoViolations(); });

We are ready to implement another component: the TitleDescription. It will work almost similar to the Tag component. It receives some props: name, description, and isLoading.

我們準備實現另一個組件: TitleDescription 。 它的工作原理幾乎類似于Tag組件。 它收到一些道具: name , description和isLoading 。

As we have the Product type with the type definition for the name and the description, I wanted to reuse it. I tried different things - and you can take a look here for more details - and I found the Pick type. With that, I could get the name and the description from the ProductType:

由于我們具有name和description的類型定義的Product類型,因此我想重用它。 我嘗試了不同的方法-您可以在這里查看更多詳細信息 -并且找到了Pick類型。 這樣,我可以從ProductType獲得name和description :

type TitleDescriptionType = Pick<ProductType, 'name' | 'description'>;

With this new type, I could create the TitleDescriptionPropsType for the component:

使用這種新類型,我可以為組件創建TitleDescriptionPropsType :

type TitleDescriptionPropsType = TitleDescriptionType & {isLoading: boolean; };

Now working inside the component, If the isLoading is true, the component renders the proper skeleton component before it renders the actual title and description texts.

現在在組件內部工作,如果isLoading為true,則組件在渲染實際標題和描述文本之前先渲染適當的骨架組件。

if (isLoading) {return (<Fragment><Skeletonwidth="60%"height="24px"data-testid="name-skeleton-loader"/><Skeletonstyle={descriptionSkeletonStyle}height="20px"data-testid="description-skeleton-loader"/></Fragment>); }

If the component is not loading anymore, we render the title and description texts. Here we use the Typography component.

如果該組件不再加載,則呈現標題和描述文本。 在這里,我們使用Typography組件。

return (<Fragment><Typography data-testid="product-name">{name}</Typography><Typographydata-testid="product-description"color="textSecondary"variant="body2"style={descriptionStyle}>{description}</Typography></Fragment> );

For the tests, we want three things:

對于測試,我們需要三件事:

  • when it is loading, the component renders the skeletons

    加載時,組件將渲染骨架
  • when it is not loading anymore, the component renders the texts

    當不再加載時,組件將呈現文本
  • make sure the component doesn't violate the accessibility

    確保組件沒有違反可訪問性

We will use the same idea we use for the Tag tests: see if it in the document or not based on the state.

我們將使用與Tag測試相同的想法:根據狀態來查看它是否在文檔中。

When it is loading, we want to see if the skeleton is in the document, but the title and description texts are not.

加載時,我們要查看骨架是否在文檔中,但標題和描述文本不在。

describe('when is loading', () => {it('does not render anything', () => {const { queryByTestId } = render(<TitleDescriptionname={product.name}description={product.description}isLoading/>);expect(queryByTestId('name-skeleton-loader')).toBeInTheDocument();expect(queryByTestId('description-skeleton-loader')).toBeInTheDocument();expect(queryByTestId('product-name')).not.toBeInTheDocument();expect(queryByTestId('product-description')).not.toBeInTheDocument();}); });

When it is not loading anymore, it renders the texts in the DOM:

當不再加載時,它將在DOM中呈現文本:

describe('when finished loading', () => {it('renders the product name and description', () => {render(<TitleDescriptionname={product.name}description={product.description}isLoading={false}/>);expect(screen.getByText(product.name)).toBeInTheDocument();expect(screen.getByText(product.description)).toBeInTheDocument();}); });

And a simple test to cover accessibility issues:

和一個簡單的測試來解決可訪問性問題:

it('has no accessibility violations', async () => {const { container } = render(<TitleDescriptionname={product.name}description={product.description}isLoading={false}/>);const results = await axe(container);expect(results).toHaveNoViolations(); });

The next component is the Price. In this component we will provide a skeleton when it is still loading as we did in the other component, and add three different components here:

下一個組成部分是Price 。 在此組件中,我們將像在其他組件中一樣提供一個仍在加載時的框架,并在此處添加三個不同的組件:

  • PriceWithDiscount: we apply the discount into the original price and render it

    PriceWithDiscount :我們將折扣應用于原始價格并進行渲染

  • OriginalPrice: it just renders the product price

    OriginalPrice :僅呈現產品價格

  • Discount: it renders the discount percentage when the product has a discount

    Discount :當產品有折扣時,它將顯示折扣百分比

But before I start implementing these components, I wanted to structure the data to be used. The price and the discount values are numbers. So let's build a function called getPriceInfo that receives the price and the discount and it will return this data:

但是在開始實現這些組件之前,我想構造要使用的數據。 price和discount值是數字。 因此,讓我們構建一個名為getPriceInfo的函數,該函數接收price和discount ,并將返回此數據:

{priceWithDiscount,originalPrice,discountOff,hasDiscount, };

With this type contract:

使用這種類型的合同:

type PriceInfoType = {priceWithDiscount: string;originalPrice: string;discountOff: string;hasDiscount: boolean; };

In this function, it will get the discount and transform it into a boolean, then apply the discount to build the priceWithDiscount, use the hasDiscount to build the discount percentage, and build the originalPrice with the dollar sign:

在此函數中,它將獲得discount并將其轉換為boolean ,然后應用discount來構建priceWithDiscount ,使用hasDiscount來構建折扣百分比,并使用美元符號來構建originalPrice :

export const applyDiscount = (price: number, discount: number): number =>price - (price * discount) / 100;export const getPriceInfo = (price: number,discount: number ): PriceInfoType => {const hasDiscount: boolean = Boolean(discount);const priceWithDiscount: string = hasDiscount? `$${applyDiscount(price, discount)}`: `$${price}`;const originalPrice: string = `$${price}`;const discountOff: string = hasDiscount ? `${discount}% OFF` : '';return {priceWithDiscount,originalPrice,discountOff,hasDiscount,}; };

Here I also built an applytDiscount function to extract the discount calculation.

在這里,我還構建了applytDiscount函數來提取折扣計算。

I added some tests to cover these functions. As they are pure functions, we just need to pass some values and expect new data.

我添加了一些測試來涵蓋這些功能。 由于它們是純函數,因此我們只需要傳遞一些值并期望有新數據即可。

Test for the applyDiscount:

測試applyDiscount :

describe('applyDiscount', () => {it('applies 20% discount in the price', () => {expect(applyDiscount(100, 20)).toEqual(80);});it('applies 95% discount in the price', () => {expect(applyDiscount(100, 95)).toEqual(5);}); });

Test for the getPriceInfo:

測試getPriceInfo :

describe('getPriceInfo', () => {describe('with discount', () => {it('returns the correct price info', () => {expect(getPriceInfo(100, 20)).toMatchObject({priceWithDiscount: '$80',originalPrice: '$100',discountOff: '20% OFF',hasDiscount: true,});});});describe('without discount', () => {it('returns the correct price info', () => {expect(getPriceInfo(100, 0)).toMatchObject({priceWithDiscount: '$100',originalPrice: '$100',discountOff: '',hasDiscount: false,});});}); });

Now we can use the getPriceInfo in the Price components to get this structure data and pass down for the other components like this:

現在,我們可以在Price組件中使用getPriceInfo來獲取此結構數據,并向下傳遞其他組件,如下所示:

export const Price = ({ price, discount, isLoading }: PricePropsType) => {if (isLoading) {return (<Skeleton width="80%" height="18px" data-testid="price-skeleton-loader" />);}const {priceWithDiscount,originalPrice,discountOff,hasDiscount,}: PriceInfoType = getPriceInfo(price, discount);return (<Fragment><PriceWithDiscount price={priceWithDiscount} /><OriginalPrice hasDiscount={hasDiscount} price={originalPrice} /><Discount hasDiscount={hasDiscount} discountOff={discountOff} /></Fragment>); };

As we talked earlier, when it is loading, we just render the Skeleton component. When it finishes the loading, it will build the structured data and render the price info. Let's build each component now!

如前所述,在加載時,我們僅渲染Skeleton組件。 完成加載后,它將構建結構化數據并呈現價格信息。 讓我們現在構建每個組件!

Let's start with the OriginalPrice. We just need to pass the price as a prop and it renders with the Typography component.

讓我們從OriginalPrice開始。 我們只需要傳遞price作為道具,然后使用Typography組件進行渲染。

type OriginalPricePropsType = {price: string; };export const OriginalPrice = ({ price }: OriginalPricePropsType) => (<Typography display="inline" style={originalPriceStyle} color="textSecondary">{price}</Typography> );

Very simple! Let's add a test now.

很簡單! 現在添加一個測試。

Just pass a price and see it if was rendered in the DOM:

只要傳遞一個價格,看看它是否在DOM中呈現即可:

it('shows the price', () => {const price = '$200';render(<OriginalPrice price={price} />);expect(screen.getByText(price)).toBeInTheDocument(); });

I also added a test to cover accessibility issues:

我還添加了一個測試以解決可訪問性問題:

it('has no accessibility violations', async () => {const { container } = render(<OriginalPrice price="$200" />);const results = await axe(container);expect(results).toHaveNoViolations(); });

The PriceWithDiscount component has a very similar implementation, but we pass the hasDiscount boolean to render this price or not. If it has a discount, render the price with the discount. Otherwise, it won't render anything.

PriceWithDiscount組件具有非常相似的實現,但是我們傳遞hasDiscount布爾值來呈現或不呈現此價格。 如果有折扣,則用折扣呈現價格。 否則,它將不會渲染任何內容。

type PricePropsType = {hasDiscount: boolean;price: string; };

The props type has the hasDiscount and the price. And the component just renders things based on the hasDiscount value.

道具類型具有hasDiscount和price 。 并且該組件僅基于hasDiscount值呈現事物。

export const PriceWithDiscount = ({ price, hasDiscount }: PricePropsType) => {if (!hasDiscount) {return null;}return (<Typography display="inline" style={priceWithDiscountStyle}>{price}</Typography>); };

The tests will cover this logic when it has or doesn't have the discount. If it hasn't the discount, the prices will not be rendered.

當有或沒有折扣時,測試將涵蓋此邏輯。 如果沒有折扣,將不顯示價格。

describe('when the product has no discount', () => {it('shows nothing', () => {const { queryByTestId } = render(<PriceWithDiscount hasDiscount={false} price="" />);expect(queryByTestId('discount-off-label')).not.toBeInTheDocument();}); });

If it has the discount, it will be the rendered in the DOM:

如果有折扣,它將在DOM中呈現:

describe('when the product has a discount', () => {it('shows the price', () => {const price = '$200';render(<PriceWithDiscount hasDiscount price={price} />);expect(screen.getByText(price)).toBeInTheDocument();}); });

And as always, a test to cover accessibility violations:

和往常一樣,涵蓋可訪問性違規的測試:

it('has no accessibility violations', async () => {const { container } = render(<PriceWithDiscount hasDiscount price="$200" />);const results = await axe(container);expect(results).toHaveNoViolations(); });

The Discount component is pretty much the same as the PriceWithDiscount. Render the discount tag if the product has a discount:

Discount組件與PriceWithDiscount幾乎相同。 如果產品有折扣,則顯示折扣標簽:

type DiscountPropsType = {hasDiscount: boolean;discountOff: string; };export const Discount = ({ hasDiscount, discountOff }: DiscountPropsType) => {if (!hasDiscount) {return null;}return (<Typographydisplay="inline"color="secondary"data-testid="discount-off-label">{discountOff}</Typography>); };

And all the tests we did for the other component, we do the same thing for the Discount component:

我們對其他組件所做的所有測試,對Discount組件也做同樣的事情:

describe('Discount', () => {describe('when the product has a discount', () => {it('shows the discount label', () => {const discountOff = '20% OFF';render(<Discount hasDiscount discountOff={discountOff} />);expect(screen.getByText(discountOff)).toBeInTheDocument();});});describe('when the product has no discount', () => {it('shows nothing', () => {const { queryByTestId } = render(<Discount hasDiscount={false} discountOff="" />);expect(queryByTestId('discount-off-label')).not.toBeInTheDocument();});});it('has no accessibility violations', async () => {const { container } = render(<Discount hasDiscount discountOff="20% OFF" />);const results = await axe(container);expect(results).toHaveNoViolations();}); });

Now we will build an Image component. This component has the basic skeleton as any other component we've built. If it is loading, wait to render the image source and render the skeleton instead. When it finishes the loading, we will render the image, but only if the component is in the intersection of the browser window.

現在,我們將構建一個Image組件。 該組件具有基本骨架,就像我們構建的任何其他組件一樣。 如果正在加載,請等待渲染圖像源并渲染骨架。 完成加載后,我們將渲染圖像,但前提是組件位于瀏覽器窗口的交點內。

What does it mean? When you are on a website on your mobile device, you'll probably see the first 4 products. They will render the skeleton and then the image. But below these 4 products, as you're not seeing any of them, it doesn't matter if we are rendering them or not. And we can choose to not render them. Not for now. But on-demand. When you are scrolling, if the product's image is at the intersection of the browser window, we start rendering the image source.

這是什么意思? 當您在移動設備上的網站上時,可能會看到前4種產品。 他們將先渲染骨架,然后再渲染圖像。 但是在這4種產品之下,您可能看不到它們,因此我們是否渲染它們都沒有關系。 我們可以選擇不渲染它們。 現在不行。 但是按需。 滾動時,如果產品的圖像位于瀏覽器窗口的交點處,我們將開始渲染圖像源。

That way we gain performance by speeding up the page load time and reduce the cost by requesting images on demand.

這樣,我們可以通過加快頁面加載時間來提高性能,并通過按需請求圖像來降低成本。

We will use the Intersection Observer API to download images on demand. But before writing any code about this technology, let's start building our component with the image and the skeleton view.

我們將使用Intersection Observer API來按需下載圖像。 但是在編寫有關該技術的任何代碼之前,讓我們開始使用圖像和框架視圖構建組件。

Image props will have this object:

圖像道具將具有以下對象:

{imageUrl,imageAlt,width,isLoading,imageWrapperStyle,imageStyle, }

The imageUrl, imageAlt, and the isLoading props are passed by the product component. The width is an attribute for the skeleton and the image tag. The imageWrapperStyle and the imageStyle are props that have a default value in the image component. We'll talk about this later.

imageUrl , imageAlt和isLoading道具由產品組件傳遞。 width是骨架和圖像標簽的屬性。 imageWrapperStyle和imageStyle是在圖像組件中具有默認值的道具。 我們稍后再討論。

Let's add a type for this props:

讓我們為這個道具添加一個類型:

type ImageUrlType = Pick<ProductType, 'imageUrl'>; type ImageAttrType = { imageAlt: string; width: string }; type ImageStateType = { isLoading: boolean }; type ImageStyleType = {imageWrapperStyle: CSSProperties;imageStyle: CSSProperties; };export type ImagePropsType = ImageUrlType &ImageAttrType &ImageStateType &ImageStyleType;

The idea here is to give meaning for the types and then compose everything. We can get the imageUrl from the ProductType. The attribute type will have the imageAlt and the width. The image state has the isLoading state. And the image style has some CSSProperties.

這里的想法是為類型賦予含義,然后組成所有內容。 我們可以從ProductType獲取imageUrl 。 屬性類型將具有imageAlt和width 。 圖像狀態為isLoading狀態。 并且圖像樣式具有一些CSSProperties 。

At first, the component would like this:

首先,該組件將如下所示:

export const Image = ({imageUrl,imageAlt,width,isLoading,imageWrapperStyle,imageStyle, }: ImagePropsType) => {if (isLoading) {<Skeletonvariant="rect"width={width}data-testid="image-skeleton-loader"/>}return (<imgsrc={imageUrl}alt={imageAlt}width={width}style={imageStyle}/>); };

Let's build the code to make the intersection observer works.

讓我們構建代碼以使相交觀察器起作用。

The idea of the intersection observer is to receive a target to be observed and a callback function that is executed whenever the observed target enters or exits the viewport. So the implementation would be very simple:

相交觀察器的思想是接收要觀察的目標,并在觀察到的目標進入或退出視口時執行回調函數。 因此,實現將非常簡單:

const observer: IntersectionObserver = new IntersectionObserver(onIntersect,options );observer.observe(target);

Instantiate the IntersectionObserver class by passing an options object and the callback function. The observer will observe the target element.

通過傳遞選項對象和回調函數來實例化IntersectionObserver類。 observer將觀察target元素。

As it is an effect in the DOM, we can wrap this into a useEffect.

由于它是DOM中的一種效果,因此我們可以將其包裝到useEffect 。

useEffect(() => {const observer: IntersectionObserver = new IntersectionObserver(onIntersect,options);observer.observe(target);return () => {observer.unobserve(target);}; }, [target]);

Using useEffect, we have two different things here: the dependency array and the returning function. We pass the target as the dependency function to make sure that we will re-run the effect if the target changes. And the returning function is a cleanup function. React performs the cleanup when the component unmounts, so it will clean up the effect before running another effect for every render.

使用useEffect ,我們在這里有兩件事:依賴項數組和返回函數。 我們將target作為依賴項函數傳遞,以確保如果target發生更改,我們將重新運行效果。 返回函數是清理函數。 當組件卸載時,React會執行清理操作,因此它將在為每個渲染運行另一個效果之前清理效果。

In this cleanup function, we just stop observing the target element.

在此清理功能中,我們只是停止觀察target元素。

When the component starts rendering, the target reference is not set yet, so we need to have a guard to not observe an undefined target.

當組件開始渲染時,尚未設置target參考,因此我們需要有保護措施以免觀察undefined目標。

useEffect(() => {if (!target) {return;}const observer: IntersectionObserver = new IntersectionObserver(onIntersect,options);observer.observe(target);return () => {observer.unobserve(target);}; }, [target]);

Instead of using this effect in our component, we could build a custom hook to receive the target, some options to customize the configuration, and it would provide a boolean telling if the target is at the intersection of the viewport or not.

可以在組件中使用一個自定義鉤子來接收目標,而不是在組件中使用此效果,可以使用一些選項來自定義配置,它將提供一個布爾值來告知目標是否在視口的交叉點。

export type TargetType = Element | HTMLDivElement | undefined; export type IntersectionStatus = {isIntersecting: boolean; };const defaultOptions: IntersectionObserverInit = {rootMargin: '0px',threshold: 0.1, };export const useIntersectionObserver = (target: TargetType,options: IntersectionObserverInit = defaultOptions ): IntersectionStatus => {const [isIntersecting, setIsIntersecting] = useState(false);useEffect(() => {if (!target) {return;}const onIntersect = ([entry]: IntersectionObserverEntry[]) => {setIsIntersecting(entry.isIntersecting);if (entry.isIntersecting) {observer.unobserve(target);}};const observer: IntersectionObserver = new IntersectionObserver(onIntersect,options);observer.observe(target);return () => {observer.unobserve(target);};}, [target]);return { isIntersecting }; };

In our callback function, we just set if the entry target is intersecting the viewport or not. The setIsIntersecting is a setter from the useState hook we define at the top of our custom hook.

在回調函數中,我們只是設置輸入目標是否與視口相交。 setIsIntersecting是我們在自定義鉤子頂部定義的useState鉤子中的設置器。

It is initialized as false but will update to true if it is intersecting the viewport.

它初始化為false但如果與視口相交,則將更新為true 。

With this new information in the component, we can render the image or not. If it is intersecting, we can render the image. If not, just render a skeleton until the user gets to the viewport intersection of the product image.

利用組件中的新信息,我們可以渲染圖像或不渲染圖像。 如果相交,則可以渲染圖像。 如果沒有,則只需渲染骨架,直到用戶到達產品圖像的視口相交處。

How does it look in practice?

實際情況如何?

First we define the wrapper reference using useState:

首先,我們使用useState定義包裝器引用:

const [wrapperRef, setWrapperRef] = useState<HTMLDivElement>();

It start as undefined. Then build a wrapper callback to set the element node:

它以undefined開始。 然后構建包裝器回調以設置元素節點:

const wrapperCallback = useCallback(node => {setWrapperRef(node); }, []);

With that, we can use it to get the wrapper reference by using a ref prop in our div.

這樣,我們可以通過在div使用ref prop來使用它獲取包裝器引用。

<div ref={wrapperCallback}>

After setting the wrapperRef, we can pass it as the target for our useIntersectionObserver and expect a isIntersecting status as a result:

設置wrapperRef ,我們可以將其作為useIntersectionObserver的target傳遞,并期望結果為isIntersecting狀態:

const { isIntersecting }: IntersectionStatus = useIntersectionObserver(wrapperRef);

With this new value, we can build a boolean value to know if we render the skeleton or the product image.

使用這個新值,我們可以構建一個布爾值來知道是否渲染骨架或產品圖像。

const showImageSkeleton: boolean = isLoading || !isIntersecting;

So now we can render the appropriate node to the DOM.

因此,現在我們可以將適當的節點呈現給DOM。

<div ref={wrapperCallback} style={imageWrapperStyle}>{showImageSkeleton ? (<Skeletonvariant="rect"width={width}height={imageWrapperStyle.height}style={skeletonStyle}data-testid="image-skeleton-loader"/>) : (<imgsrc={imageUrl}alt={imageAlt}width={width}/>)} </div>

The full component looks like this:

完整的組件如下所示:

export const Image = ({imageUrl,imageAlt,width,isLoading,imageWrapperStyle, }: ImagePropsType) => {const [wrapperRef, setWrapperRef] = useState<HTMLDivElement>();const wrapperCallback = useCallback(node => {setWrapperRef(node);}, []);const { isIntersecting }: IntersectionStatus = useIntersectionObserver(wrapperRef);const showImageSkeleton: boolean = isLoading || !isIntersecting;return (<div ref={wrapperCallback} style={imageWrapperStyle}>{showImageSkeleton ? (<Skeletonvariant="rect"width={width}height={imageWrapperStyle.height}style={skeletonStyle}data-testid="image-skeleton-loader"/>) : (<imgsrc={imageUrl}alt={imageAlt}width={width}/>)}</div>); };

Great, now the loading on-demand works well. But I want to build a slightly better experience. The idea here is to have two different sizes of the same image. The low-quality image is requested and we make it visible, but blur while the high-quality image is requested in the background. When the high-quality image finally finishes loading, we transition from the low-quality to the high-quality image with an ease-in/ease-out transition to make it a smooth experience.

太好了,現在按需加載效果很好。 但是我想建立一個更好的體驗。 這里的想法是使同一圖像具有兩個不同的大小。 我們請求了低質量的圖像,我們將其顯示出來,但是當在后臺請求高質量的圖像時,圖像就會模糊。 當高質量的圖像最終完成加載時,我們通過緩入/緩出過渡從低質量圖像過渡到高質量圖像,以提供流暢的體驗。

Let's build this logic. We could build this into the component, but we could also extract this logic into a custom hook.

讓我們建立這個邏輯。 我們可以將其構建到組件中,但也可以將該邏輯提取到自定義鉤子中。

export const useImageOnLoad = (): ImageOnLoadType => {const [isLoaded, setIsLoaded] = useState(false);const handleImageOnLoad = () => setIsLoaded(true);const imageVisibility: CSSProperties = {visibility: isLoaded ? 'hidden' : 'visible',filter: 'blur(10px)',transition: 'visibility 0ms ease-out 500ms',};const imageOpactity: CSSProperties = {opacity: isLoaded ? 1 : 0,transition: 'opacity 500ms ease-in 0ms',};return { handleImageOnLoad, imageVisibility, imageOpactity }; };

This hook just provides some data and behavior for the component. The handleImageOnLoad we talked earlier, the imageVisibility to make the low-quality image visible or not, and the imageOpactity to make the transition from transparent to opaque, that way we make it visible after loading it.

該掛鉤僅提供組件的一些數據和行為。 該handleImageOnLoad我們之前談到的,在imageVisibility使低質量的圖像可見或不可見,并且imageOpactity使從透明到不透明的過渡,這樣我們做加載它之后它是可見的。

The isLoaded is a simple boolean to handle the visibility of the images. Another small detail is the filter: 'blur(10px)' to make the low-quality-image blur and then slowly focusing while transitioning from the low-quality image to the high-quality image.

isLoaded是一個簡單的布爾值,用于處理圖像的可見性。 另一個小細節是filter: 'blur(10px)'使劣質圖像模糊,然后在從劣質圖像過渡到高質量圖像時緩慢聚焦。

With this new hook, we just import it, and call inside the component:

有了這個新的鉤子,我們只需導入它,然后在組件內部調用:

const {handleImageOnLoad,imageVisibility,imageOpactity, }: ImageOnLoadType = useImageOnLoad();

And start using the data and behavior we built.

并開始使用我們構建的數據和行為。

<Fragment><imgsrc={thumbUrl}alt={imageAlt}width={width}style={{ ...imageStyle, ...imageVisibility }}/><imgonLoad={handleImageOnLoad}src={imageUrl}alt={imageAlt}width={width}style={{ ...imageStyle, ...imageOpactity }}/> </Fragment>

The first one has a low-quality image, the thumbUrl. The second has the original high-quality image, the imageUrl. When the high-quality image is loaded, it calls the handleImageOnLoad function. This function will make the transition between one image to the other.

第一個圖像質量低下, thumbUrl 。 第二個具有原始的高質量圖像imageUrl 。 加載高質量圖像后,它將調用handleImageOnLoad函數。 此功能將使一個圖像與另一個圖像之間過渡。

結語 (Wrapping up)

This is the first part of this project to learn more about user experience, native APIs, typed frontend, and tests.

這是該項目的第一部分,旨在了解有關用戶體驗,本機API,類型化前端和測試的更多信息。

For the next part of this series, we are going to think more in an architectural way to build the search with filters, but keeping the mindset to bring technical solutions to make the user experience as smooth as possible.

對于本系列的下一部分,我們將以一種體系結構的方式進行更多思考,以使用過濾器構建搜索,但要保持思路以帶來技術解決方案,以使用戶體驗盡可能流暢。

You can find other articles like this on TK's blog.

您可以在TK的博客上找到其他類似的文章 。

資源資源 (Resources)

  • Lazy Loading Images and Video

    延遲加載圖像和視頻

  • Functional Uses for Intersection Observer

    交叉口觀察員的功能用途

  • Tips for rolling your own lazy loading

    滾動自己的延遲加載的提示

  • Intersection Observer API - MDN

    交叉路口觀察員API-MDN

  • React Typescript Cheatsheet

    React打字稿備忘單

翻譯自: https://www.freecodecamp.org/news/ux-studies-with-react-typescript-and-testing-library/

總結

以上是生活随笔為你收集整理的如何使用React,TypeScript和React测试库创建出色的用户体验的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

国产精品免费大片 | 对白脏话肉麻粗话av | 国产两女互慰高潮视频在线观看 | 亚洲精品中文字幕乱码 | 日本免费一区二区三区最新 | 日韩精品无码一区二区中文字幕 | 日本一区二区三区免费高清 | 日本精品人妻无码77777 天堂一区人妻无码 | 国产真实伦对白全集 | 国产人妻人伦精品1国产丝袜 | 蜜臀av无码人妻精品 | 黑人巨大精品欧美一区二区 | 国产猛烈高潮尖叫视频免费 | 丝袜 中出 制服 人妻 美腿 | 国产美女精品一区二区三区 | 少妇人妻av毛片在线看 | 无码人妻出轨黑人中文字幕 | 天天躁日日躁狠狠躁免费麻豆 | 中文字幕 亚洲精品 第1页 | 亚洲熟妇色xxxxx欧美老妇 | 97久久精品无码一区二区 | 免费无码一区二区三区蜜桃大 | 丰满人妻被黑人猛烈进入 | 奇米影视7777久久精品人人爽 | 午夜福利一区二区三区在线观看 | 窝窝午夜理论片影院 | 亚洲精品一区二区三区在线 | 亚洲精品中文字幕乱码 | 偷窥日本少妇撒尿chinese | 老司机亚洲精品影院 | 国产精品成人av在线观看 | 人人澡人人透人人爽 | 99精品国产综合久久久久五月天 | 亲嘴扒胸摸屁股激烈网站 | 久久综合九色综合97网 | 色婷婷综合激情综在线播放 | 精品久久久久久人妻无码中文字幕 | 欧美日韩人成综合在线播放 | 国产精品99久久精品爆乳 | 亚洲小说图区综合在线 | 免费观看的无遮挡av | 国产suv精品一区二区五 | 色欲人妻aaaaaaa无码 | 日本肉体xxxx裸交 | 欧美第一黄网免费网站 | 性史性农村dvd毛片 | 综合激情五月综合激情五月激情1 | 成年美女黄网站色大免费视频 | 久久99精品国产.久久久久 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 久久zyz资源站无码中文动漫 | 亚洲欧洲日本综合aⅴ在线 | 精品熟女少妇av免费观看 | 在线精品国产一区二区三区 | 久久久久国色av免费观看性色 | 欧美刺激性大交 | 亚洲成a人一区二区三区 | 日本一区二区更新不卡 | 久久无码专区国产精品s | 精品无码国产一区二区三区av | 久久精品国产日本波多野结衣 | 国产成人一区二区三区别 | 特级做a爰片毛片免费69 | 久久99精品国产.久久久久 | 国内精品一区二区三区不卡 | 精品人妻人人做人人爽夜夜爽 | 荡女精品导航 | 人人妻人人澡人人爽欧美一区 | 久久精品中文闷骚内射 | 精品无码国产自产拍在线观看蜜 | 99久久人妻精品免费一区 | 国产小呦泬泬99精品 | 亚洲精品鲁一鲁一区二区三区 | 黑森林福利视频导航 | 精品欧洲av无码一区二区三区 | 亚洲欧美精品伊人久久 | 性做久久久久久久久 | 色婷婷综合激情综在线播放 | 午夜理论片yy44880影院 | 亚洲熟女一区二区三区 | 国产亚洲精品精品国产亚洲综合 | 精品久久综合1区2区3区激情 | 99riav国产精品视频 | 理论片87福利理论电影 | 无码精品人妻一区二区三区av | 日本乱人伦片中文三区 | 欧美老人巨大xxxx做受 | 九九热爱视频精品 | 中文字幕无码av激情不卡 | 欧美日韩人成综合在线播放 | 亚洲一区二区观看播放 | 欧美亚洲国产一区二区三区 | 天堂无码人妻精品一区二区三区 | 国产乱人无码伦av在线a | 亚洲国产精品无码久久久久高潮 | 久久久久成人精品免费播放动漫 | 国产午夜无码精品免费看 | www成人国产高清内射 | 亚洲a无码综合a国产av中文 | 性啪啪chinese东北女人 | 日韩成人一区二区三区在线观看 | 成人欧美一区二区三区黑人 | 国产精品va在线播放 | 鲁大师影院在线观看 | 免费人成在线视频无码 | 久久精品国产一区二区三区 | 国产农村乱对白刺激视频 | 麻豆成人精品国产免费 | 18禁黄网站男男禁片免费观看 | 国产国语老龄妇女a片 | 亚洲一区av无码专区在线观看 | 国产人妻大战黑人第1集 | 4hu四虎永久在线观看 | 日本成熟视频免费视频 | 国产精品美女久久久 | 亚洲精品无码人妻无码 | aa片在线观看视频在线播放 | 亚洲日本在线电影 | 亚洲国产av精品一区二区蜜芽 | 日日碰狠狠躁久久躁蜜桃 | 亚洲爆乳大丰满无码专区 | 国产另类ts人妖一区二区 | 亚洲日韩av一区二区三区四区 | 天天摸天天透天天添 | 人人妻在人人 | 中文字幕av日韩精品一区二区 | 久久久精品成人免费观看 | 无码av免费一区二区三区试看 | 亚洲精品一区二区三区大桥未久 | 亚洲va欧美va天堂v国产综合 | 无套内谢老熟女 | 国产精品久久精品三级 | 国产激情一区二区三区 | 亚洲国产一区二区三区在线观看 | 无码人妻久久一区二区三区不卡 | 狠狠色噜噜狠狠狠7777奇米 | a片在线免费观看 | 久久国产精品偷任你爽任你 | 少妇太爽了在线观看 | 欧美第一黄网免费网站 | 大肉大捧一进一出视频出来呀 | 免费人成在线视频无码 | 亚洲成a人片在线观看日本 | av人摸人人人澡人人超碰下载 | 国产激情综合五月久久 | 久久综合香蕉国产蜜臀av | 亚洲gv猛男gv无码男同 | 久久精品人妻少妇一区二区三区 | 一本久道久久综合狠狠爱 | 久久婷婷五月综合色国产香蕉 | 国产精品怡红院永久免费 | 久久人人爽人人人人片 | 内射后入在线观看一区 | 国产成人综合在线女婷五月99播放 | 狠狠色噜噜狠狠狠狠7777米奇 | 日韩成人一区二区三区在线观看 | 亚洲一区二区三区四区 | 无码一区二区三区在线 | 国内少妇偷人精品视频 | 亚洲国产精华液网站w | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 欧美国产日产一区二区 | 成人av无码一区二区三区 | 无人区乱码一区二区三区 | 欧美亚洲日韩国产人成在线播放 | 伊人久久大香线蕉亚洲 | 亚洲日韩av一区二区三区四区 | 中文字幕无码免费久久99 | 国产精品无码久久av | 九月婷婷人人澡人人添人人爽 | 久久www免费人成人片 | 国产日产欧产精品精品app | 国内揄拍国内精品人妻 | 无码国产乱人伦偷精品视频 | 午夜福利试看120秒体验区 | 老熟妇乱子伦牲交视频 | 日韩人妻无码中文字幕视频 | 国内揄拍国内精品少妇国语 | 国产成人精品久久亚洲高清不卡 | 中文字幕无码免费久久9一区9 | 精品一二三区久久aaa片 | 国产色在线 | 国产 | 国产精品久久久久久亚洲影视内衣 | 国产精品人人爽人人做我的可爱 | 国产成人无码a区在线观看视频app | 午夜福利试看120秒体验区 | 国产精品二区一区二区aⅴ污介绍 | 一本加勒比波多野结衣 | 亚洲成a人片在线观看日本 | 国内精品一区二区三区不卡 | 最新版天堂资源中文官网 | 无遮挡啪啪摇乳动态图 | 日本成熟视频免费视频 | 久9re热视频这里只有精品 | 国产两女互慰高潮视频在线观看 | 亚洲欧洲日本无在线码 | 亚洲色无码一区二区三区 | 中国大陆精品视频xxxx | 丰满少妇高潮惨叫视频 | 久久精品99久久香蕉国产色戒 | 亚洲aⅴ无码成人网站国产app | 国产亚洲人成在线播放 | 国产激情综合五月久久 | 97夜夜澡人人爽人人喊中国片 | 精品国产aⅴ无码一区二区 | 亚洲aⅴ无码成人网站国产app | 99国产欧美久久久精品 | 久久国产劲爆∧v内射 | 亚洲人亚洲人成电影网站色 | 亚洲乱码国产乱码精品精 | 国产另类ts人妖一区二区 | 熟女少妇人妻中文字幕 | 色欲av亚洲一区无码少妇 | 久久aⅴ免费观看 | 中文字幕无码视频专区 | 无码国产乱人伦偷精品视频 | aa片在线观看视频在线播放 | 少妇高潮一区二区三区99 | 欧美成人免费全部网站 | 成年美女黄网站色大免费视频 | 亚洲国产精品一区二区美利坚 | v一区无码内射国产 | 一本久道久久综合狠狠爱 | 亚洲国产精品一区二区美利坚 | 日本欧美一区二区三区乱码 | 免费观看激色视频网站 | 性欧美熟妇videofreesex | 日本精品人妻无码免费大全 | 日本饥渴人妻欲求不满 | 亚洲中文字幕av在天堂 | 大屁股大乳丰满人妻 | 国产成人亚洲综合无码 | 成人无码影片精品久久久 | 青草视频在线播放 | 精品无人区无码乱码毛片国产 | 一本色道婷婷久久欧美 | 国产一区二区三区精品视频 | 精品国产一区av天美传媒 | 国产手机在线αⅴ片无码观看 | 中文无码成人免费视频在线观看 | 少妇人妻大乳在线视频 | 久久精品丝袜高跟鞋 | 成年美女黄网站色大免费视频 | 亚洲精品无码国产 | 无码av免费一区二区三区试看 | 亚洲の无码国产の无码步美 | 强奷人妻日本中文字幕 | 人人爽人人澡人人高潮 | 岛国片人妻三上悠亚 | 国产欧美精品一区二区三区 | 午夜福利试看120秒体验区 | 国产三级久久久精品麻豆三级 | 红桃av一区二区三区在线无码av | 精品偷自拍另类在线观看 | 亚洲の无码国产の无码影院 | 97久久国产亚洲精品超碰热 | 久久人人爽人人爽人人片av高清 | 国产精品怡红院永久免费 | 无码国模国产在线观看 | 亚洲精品一区国产 | 久久久久亚洲精品中文字幕 | 欧洲熟妇色 欧美 | 欧美熟妇另类久久久久久不卡 | 久久99精品久久久久婷婷 | 欧美激情一区二区三区成人 | 蜜臀av在线播放 久久综合激激的五月天 | 老司机亚洲精品影院 | 亚洲综合伊人久久大杳蕉 | 国产精品高潮呻吟av久久4虎 | 欧美国产日产一区二区 | 国产成人精品必看 | 强开小婷嫩苞又嫩又紧视频 | 久久午夜无码鲁丝片秋霞 | 国产精品久久久久7777 | 国产精品人人爽人人做我的可爱 | 国产亚洲精品久久久久久大师 | 青春草在线视频免费观看 | 日韩精品成人一区二区三区 | 精品无码国产一区二区三区av | 无码国产色欲xxxxx视频 | 999久久久国产精品消防器材 | 国内少妇偷人精品视频 | 亚洲色成人中文字幕网站 | 熟妇激情内射com | 天堂亚洲2017在线观看 | 妺妺窝人体色www婷婷 | 国产香蕉97碰碰久久人人 | 人人妻人人澡人人爽人人精品 | 免费中文字幕日韩欧美 | 老司机亚洲精品影院 | 7777奇米四色成人眼影 | 天干天干啦夜天干天2017 | 色五月五月丁香亚洲综合网 | 四虎4hu永久免费 | 国产人妻大战黑人第1集 | 亚洲の无码国产の无码步美 | 国产人妻精品一区二区三区 | 一本精品99久久精品77 | 4hu四虎永久在线观看 | 国产精品怡红院永久免费 | 色诱久久久久综合网ywww | 色婷婷综合中文久久一本 | 国产亲子乱弄免费视频 | 国内揄拍国内精品少妇国语 | 亚洲小说春色综合另类 | 少妇性l交大片 | 欧美老妇交乱视频在线观看 | 2020最新国产自产精品 | 国产舌乚八伦偷品w中 | 国产小呦泬泬99精品 | 亚洲精品一区二区三区婷婷月 | 一二三四在线观看免费视频 | 国产真实乱对白精彩久久 | 亚洲色成人中文字幕网站 | 55夜色66夜色国产精品视频 | 日本高清一区免费中文视频 | 人妻熟女一区 | 成人性做爰aaa片免费看 | 欧美三级不卡在线观看 | 国产区女主播在线观看 | 国产精品第一国产精品 | 欧美第一黄网免费网站 | www国产亚洲精品久久网站 | 国产免费久久精品国产传媒 | 国产在热线精品视频 | 免费观看激色视频网站 | 欧美熟妇另类久久久久久不卡 | 国产精品无码一区二区三区不卡 | 18禁止看的免费污网站 | 国产午夜无码视频在线观看 | 亚洲高清偷拍一区二区三区 | 国内精品久久毛片一区二区 | 国产精品沙发午睡系列 | 美女毛片一区二区三区四区 | 激情亚洲一区国产精品 | 国产真人无遮挡作爱免费视频 | 99久久无码一区人妻 | 久久无码中文字幕免费影院蜜桃 | 日韩av无码一区二区三区 | 97夜夜澡人人爽人人喊中国片 | 国产精品内射视频免费 | 99久久精品午夜一区二区 | 国产性生交xxxxx无码 | 日本护士xxxxhd少妇 | 男人和女人高潮免费网站 | 三级4级全黄60分钟 | 欧美国产亚洲日韩在线二区 | 国产成人人人97超碰超爽8 | 免费国产黄网站在线观看 | 国产精品久久久久久亚洲毛片 | 欧美高清在线精品一区 | 初尝人妻少妇中文字幕 | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲日韩一区二区三区 | 欧美猛少妇色xxxxx | 日韩精品无码免费一区二区三区 | 亚洲精品无码人妻无码 | 蜜桃视频韩日免费播放 | 精品乱子伦一区二区三区 | 亚洲国产成人av在线观看 | 一个人免费观看的www视频 | 欧美肥老太牲交大战 | 捆绑白丝粉色jk震动捧喷白浆 | 久久97精品久久久久久久不卡 | 三级4级全黄60分钟 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产人妻大战黑人第1集 | 欧美刺激性大交 | 夜夜躁日日躁狠狠久久av | 全黄性性激高免费视频 | 麻豆果冻传媒2021精品传媒一区下载 | 成人无码视频在线观看网站 | 中文字幕人妻无码一区二区三区 | 久久亚洲国产成人精品性色 | 久久99精品久久久久久动态图 | aⅴ亚洲 日韩 色 图网站 播放 | 国产av久久久久精东av | 熟妇激情内射com | 综合激情五月综合激情五月激情1 | 激情内射亚州一区二区三区爱妻 | 偷窥村妇洗澡毛毛多 | 天堂亚洲2017在线观看 | 2019午夜福利不卡片在线 | 色婷婷av一区二区三区之红樱桃 | 综合人妻久久一区二区精品 | 国产午夜无码精品免费看 | 丰满人妻被黑人猛烈进入 | 人妻有码中文字幕在线 | 88国产精品欧美一区二区三区 | 男人扒开女人内裤强吻桶进去 | 亚洲综合色区中文字幕 | 国产卡一卡二卡三 | 麻豆国产人妻欲求不满 | 国产精品资源一区二区 | а√资源新版在线天堂 | 亚洲 日韩 欧美 成人 在线观看 | 99精品无人区乱码1区2区3区 | 成年美女黄网站色大免费全看 | 一二三四在线观看免费视频 | 久久这里只有精品视频9 | 爱做久久久久久 | 亚洲第一无码av无码专区 | 亚洲乱码中文字幕在线 | 成人综合网亚洲伊人 | 国产亚洲人成在线播放 | 国产一区二区三区四区五区加勒比 | 亚洲成a人片在线观看无码3d | 性色欲网站人妻丰满中文久久不卡 | av无码久久久久不卡免费网站 | 亚洲区小说区激情区图片区 | 天天摸天天透天天添 | 国精产品一区二区三区 | 国产后入清纯学生妹 | 99久久精品日本一区二区免费 | 一本色道久久综合亚洲精品不卡 | 成年美女黄网站色大免费全看 | 人人超人人超碰超国产 | 夫妻免费无码v看片 | 99精品国产综合久久久久五月天 | 在线观看免费人成视频 | 国产成人精品视频ⅴa片软件竹菊 | 国产激情艳情在线看视频 | 色综合视频一区二区三区 | 综合激情五月综合激情五月激情1 | 国产尤物精品视频 | 久久久久久a亚洲欧洲av冫 | 国产一区二区不卡老阿姨 | 亚洲精品鲁一鲁一区二区三区 | 少妇的肉体aa片免费 | 丰满人妻一区二区三区免费视频 | 99久久婷婷国产综合精品青草免费 | 国产精品久久久久久亚洲影视内衣 | 高清国产亚洲精品自在久久 | 妺妺窝人体色www在线小说 | 国产精品美女久久久 | 野狼第一精品社区 | 婷婷色婷婷开心五月四房播播 | 人妻尝试又大又粗久久 | 国产高清av在线播放 | 丰满少妇弄高潮了www | 亚洲 高清 成人 动漫 | 人人妻人人澡人人爽欧美精品 | 激情国产av做激情国产爱 | 377p欧洲日本亚洲大胆 | aa片在线观看视频在线播放 | 77777熟女视频在线观看 а天堂中文在线官网 | 中文无码伦av中文字幕 | 成年美女黄网站色大免费全看 | 亚洲一区二区三区在线观看网站 | 国产亚洲精品久久久久久久 | 精品偷拍一区二区三区在线看 | 人妻少妇精品视频专区 | 欧美日本精品一区二区三区 | 国产精品亚洲а∨无码播放麻豆 | 色一情一乱一伦一视频免费看 | 精品aⅴ一区二区三区 | 精品久久久中文字幕人妻 | 色老头在线一区二区三区 | 精品水蜜桃久久久久久久 | 中文字幕中文有码在线 | 精品人妻人人做人人爽 | 18精品久久久无码午夜福利 | 正在播放老肥熟妇露脸 | 国产热a欧美热a在线视频 | 精品人人妻人人澡人人爽人人 | 日日摸日日碰夜夜爽av | 欧美人与动性行为视频 | 欧美freesex黑人又粗又大 | 无码精品人妻一区二区三区av | 久久国内精品自在自线 | 久久精品99久久香蕉国产色戒 | 九月婷婷人人澡人人添人人爽 | 日日摸日日碰夜夜爽av | 水蜜桃av无码 | 久久97精品久久久久久久不卡 | 日本精品高清一区二区 | 欧美三级a做爰在线观看 | 丰满妇女强制高潮18xxxx | 激情五月综合色婷婷一区二区 | 精品久久综合1区2区3区激情 | 无遮挡国产高潮视频免费观看 | 亚洲国产精品久久人人爱 | 亚洲七七久久桃花影院 | 欧美黑人性暴力猛交喷水 | 日本一卡2卡3卡四卡精品网站 | 欧美xxxx黑人又粗又长 | 亚洲一区二区三区无码久久 | 久久aⅴ免费观看 | 日韩成人一区二区三区在线观看 | 亚洲性无码av中文字幕 | 人妻aⅴ无码一区二区三区 | 久久精品国产大片免费观看 | 青草青草久热国产精品 | 毛片内射-百度 | 久久熟妇人妻午夜寂寞影院 | 国产精品永久免费视频 | 亚洲乱码日产精品bd | 国产亚洲人成a在线v网站 | 日本丰满护士爆乳xxxx | 亚洲精品无码人妻无码 | 精品无码一区二区三区爱欲 | 波多野结衣av一区二区全免费观看 | 青草青草久热国产精品 | 亚洲高清偷拍一区二区三区 | 精品久久久久久人妻无码中文字幕 | 99久久精品国产一区二区蜜芽 | 麻豆国产人妻欲求不满 | 亚洲色大成网站www | 日日躁夜夜躁狠狠躁 | 黑人玩弄人妻中文在线 | 国产人妻精品午夜福利免费 | www国产亚洲精品久久久日本 | 99国产精品白浆在线观看免费 | 亚洲精品中文字幕乱码 | 国产精品成人av在线观看 | 国产艳妇av在线观看果冻传媒 | 日本一本二本三区免费 | 亚洲国产精品久久久久久 | 青春草在线视频免费观看 | 精品乱码久久久久久久 | 日日碰狠狠躁久久躁蜜桃 | 日韩 欧美 动漫 国产 制服 | 亚洲综合在线一区二区三区 | 午夜熟女插插xx免费视频 | 免费国产黄网站在线观看 | 99riav国产精品视频 | 日本一卡2卡3卡四卡精品网站 | 亚洲 日韩 欧美 成人 在线观看 | 亚洲欧洲无卡二区视頻 | 男女爱爱好爽视频免费看 | 欧美丰满熟妇xxxx性ppx人交 | 国产精品无码永久免费888 | 国产真人无遮挡作爱免费视频 | 欧美成人免费全部网站 | 国产成人午夜福利在线播放 | 国内精品久久毛片一区二区 | 国产极品美女高潮无套在线观看 | 97精品国产97久久久久久免费 | 日日摸夜夜摸狠狠摸婷婷 | 亚洲中文字幕va福利 | 久久国产精品偷任你爽任你 | 亚洲精品中文字幕久久久久 | 成人精品视频一区二区三区尤物 | 亚洲国产精品一区二区第一页 | 国产成人综合美国十次 | 久久99热只有频精品8 | 18精品久久久无码午夜福利 | 国精产品一区二区三区 | 给我免费的视频在线观看 | 久久zyz资源站无码中文动漫 | 色综合久久88色综合天天 | 色一情一乱一伦 | 人妻天天爽夜夜爽一区二区 | 国产人妻人伦精品 | 久久精品无码一区二区三区 | 色综合视频一区二区三区 | 纯爱无遮挡h肉动漫在线播放 | 亚洲自偷自拍另类第1页 | 欧美激情一区二区三区成人 | 亚洲а∨天堂久久精品2021 | 人人超人人超碰超国产 | 内射白嫩少妇超碰 | 无码人妻丰满熟妇区五十路百度 | 国产成人无码午夜视频在线观看 | 男人和女人高潮免费网站 | 一本久久a久久精品vr综合 | 99国产精品白浆在线观看免费 | 在线播放免费人成毛片乱码 | 国产国产精品人在线视 | 中文字幕无码免费久久99 | 狂野欧美性猛xxxx乱大交 | 免费无码的av片在线观看 | 日日麻批免费40分钟无码 | 日韩精品a片一区二区三区妖精 | 欧美日本精品一区二区三区 | 亚洲成a人片在线观看日本 | 日韩人妻少妇一区二区三区 | 性生交片免费无码看人 | 成人一区二区免费视频 | 欧美日韩视频无码一区二区三 | 99久久婷婷国产综合精品青草免费 | 乱中年女人伦av三区 | 对白脏话肉麻粗话av | 娇妻被黑人粗大高潮白浆 | 久久午夜无码鲁丝片午夜精品 | 久久久久国色av免费观看性色 | 男人的天堂2018无码 | 亚洲色大成网站www | 亚洲国产午夜精品理论片 | 亚洲一区二区三区国产精华液 | 国产午夜亚洲精品不卡下载 | 亚洲国产欧美日韩精品一区二区三区 | 亚洲精品美女久久久久久久 | 精品偷拍一区二区三区在线看 | 亚洲日韩av一区二区三区中文 | 无码人中文字幕 | 亚洲国产精品无码久久久久高潮 | 国产精品国产三级国产专播 | 樱花草在线播放免费中文 | 国产在线精品一区二区高清不卡 | 亚洲国产成人av在线观看 | av无码不卡在线观看免费 | 亚洲国产综合无码一区 | 日本熟妇乱子伦xxxx | 人人澡人人透人人爽 | 天海翼激烈高潮到腰振不止 | 中文字幕 亚洲精品 第1页 | 亚洲中文无码av永久不收费 | 日日碰狠狠丁香久燥 | 无码午夜成人1000部免费视频 | 乱人伦人妻中文字幕无码久久网 | 国产精品18久久久久久麻辣 | 亚洲人成网站免费播放 | 欧美人与禽猛交狂配 | 国产精品久久久久久无码 | 成人试看120秒体验区 | 午夜性刺激在线视频免费 | 岛国片人妻三上悠亚 | 日本精品人妻无码77777 天堂一区人妻无码 | 天天躁日日躁狠狠躁免费麻豆 | a片免费视频在线观看 | 婷婷丁香六月激情综合啪 | 亚洲精品国产a久久久久久 | 日韩人妻无码一区二区三区久久99 | 亚洲毛片av日韩av无码 | 国产口爆吞精在线视频 | 久久国产精品_国产精品 | 亚洲国产av精品一区二区蜜芽 | 成人无码影片精品久久久 | 丰满少妇高潮惨叫视频 | 精品亚洲韩国一区二区三区 | 欧洲美熟女乱又伦 | 色一情一乱一伦一视频免费看 | 国产午夜精品一区二区三区嫩草 | 精品国精品国产自在久国产87 | 国产婷婷色一区二区三区在线 | 麻豆精品国产精华精华液好用吗 | 18无码粉嫩小泬无套在线观看 | 亚洲 a v无 码免 费 成 人 a v | 国产精品久久久久影院嫩草 | 成人动漫在线观看 | 国产深夜福利视频在线 | 97夜夜澡人人爽人人喊中国片 | 久久久久av无码免费网 | 欧美大屁股xxxxhd黑色 | 国产亲子乱弄免费视频 | 日日摸天天摸爽爽狠狠97 | 日韩在线不卡免费视频一区 | 无码av免费一区二区三区试看 | 未满小14洗澡无码视频网站 | 麻豆精品国产精华精华液好用吗 | 亚洲国产欧美国产综合一区 | 国产国语老龄妇女a片 | 最新版天堂资源中文官网 | 国产精品久久久久久无码 | 欧美日本精品一区二区三区 | 国产极品视觉盛宴 | 久久99热只有频精品8 | аⅴ资源天堂资源库在线 | 内射老妇bbwx0c0ck | 少妇厨房愉情理9仑片视频 | 两性色午夜免费视频 | 久精品国产欧美亚洲色aⅴ大片 | 免费观看的无遮挡av | 人妻有码中文字幕在线 | 男女性色大片免费网站 | 亚洲精品一区二区三区婷婷月 | 激情五月综合色婷婷一区二区 | 国产日产欧产精品精品app | 99久久人妻精品免费二区 | 亚洲综合在线一区二区三区 | 天天躁夜夜躁狠狠是什么心态 | 撕开奶罩揉吮奶头视频 | 婷婷丁香五月天综合东京热 | 97无码免费人妻超级碰碰夜夜 | 精品偷自拍另类在线观看 | 少女韩国电视剧在线观看完整 | 中文字幕精品av一区二区五区 | 国产亚洲tv在线观看 | 性欧美videos高清精品 | 成人片黄网站色大片免费观看 | 少妇人妻av毛片在线看 | 老熟女重囗味hdxx69 | 毛片内射-百度 | 7777奇米四色成人眼影 | 性欧美大战久久久久久久 | 国内精品久久久久久中文字幕 | 国产又爽又黄又刺激的视频 | 亚洲日本va午夜在线电影 | 妺妺窝人体色www在线小说 | 无套内谢的新婚少妇国语播放 | 亚洲精品www久久久 | 性做久久久久久久免费看 | а√资源新版在线天堂 | 男人的天堂av网站 | 亚洲精品午夜无码电影网 | 国产亚洲精品久久久久久国模美 | 亚洲第一无码av无码专区 | 国产精品亚洲lv粉色 | 国产成人亚洲综合无码 | www国产精品内射老师 | 色五月丁香五月综合五月 | 亚洲中文字幕成人无码 | 丰满肥臀大屁股熟妇激情视频 | 欧美午夜特黄aaaaaa片 | 日韩精品无码一本二本三本色 | 国产精品亚洲综合色区韩国 | 日韩少妇白浆无码系列 | 日日碰狠狠丁香久燥 | 久久久久久九九精品久 | 久久精品人人做人人综合试看 | 99精品无人区乱码1区2区3区 | 亚洲综合无码一区二区三区 | 国产舌乚八伦偷品w中 | 久久无码人妻影院 | 久久99精品久久久久婷婷 | 天天爽夜夜爽夜夜爽 | 无套内谢的新婚少妇国语播放 | 国产精品对白交换视频 | 少妇愉情理伦片bd | 欧美成人午夜精品久久久 | 国产人成高清在线视频99最全资源 | 最新国产乱人伦偷精品免费网站 | 亚洲乱亚洲乱妇50p | 国产亚洲视频中文字幕97精品 | 久久综合给久久狠狠97色 | 美女黄网站人色视频免费国产 | 欧美精品无码一区二区三区 | 欧美 日韩 亚洲 在线 | 国产成人一区二区三区别 | 国产网红无码精品视频 | 天堂а√在线地址中文在线 | 伊人色综合久久天天小片 | 国产情侣作爱视频免费观看 | 玩弄人妻少妇500系列视频 | 性生交片免费无码看人 | 7777奇米四色成人眼影 | 欧洲美熟女乱又伦 | 成人影院yy111111在线观看 | 亚洲区小说区激情区图片区 | 波多野结衣一区二区三区av免费 | 精品欧洲av无码一区二区三区 | 亚洲精品国产精品乱码不卡 | 国产激情艳情在线看视频 | 成 人影片 免费观看 | 任你躁国产自任一区二区三区 | 荫蒂添的好舒服视频囗交 | 欧美国产日产一区二区 | 六月丁香婷婷色狠狠久久 | 中文无码精品a∨在线观看不卡 | 国产真人无遮挡作爱免费视频 | 欧美日韩在线亚洲综合国产人 | 装睡被陌生人摸出水好爽 | 日本又色又爽又黄的a片18禁 | 女人被爽到呻吟gif动态图视看 | 欧美自拍另类欧美综合图片区 | 久久精品人人做人人综合试看 | 成人免费无码大片a毛片 | 亚洲精品国产精品乱码不卡 | 一区二区传媒有限公司 | 亚洲熟女一区二区三区 | 亚洲熟悉妇女xxx妇女av | 久在线观看福利视频 | 夜精品a片一区二区三区无码白浆 | 97色伦图片97综合影院 | 国产香蕉97碰碰久久人人 | 色综合久久久久综合一本到桃花网 | 无码国产色欲xxxxx视频 | 亚洲熟悉妇女xxx妇女av | 天干天干啦夜天干天2017 | 大胆欧美熟妇xx | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 亚洲另类伦春色综合小说 | 亚洲成av人片天堂网无码】 | 蜜臀av无码人妻精品 | 一本大道久久东京热无码av | 丰满少妇熟乱xxxxx视频 | 精品人人妻人人澡人人爽人人 | 欧美日韩视频无码一区二区三 | 成熟人妻av无码专区 | 国产一区二区三区日韩精品 | 国产亚洲欧美日韩亚洲中文色 | 乱码午夜-极国产极内射 | 亚洲国产精品一区二区第一页 | 久久久久久久久888 | 大屁股大乳丰满人妻 | 日本熟妇大屁股人妻 | 亚洲乱码中文字幕在线 | 99久久精品无码一区二区毛片 | 99视频精品全部免费免费观看 | 老熟妇仑乱视频一区二区 | 伊人久久大香线焦av综合影院 | 蜜桃视频韩日免费播放 | 国产精品手机免费 | 久久人人爽人人人人片 | 久久精品国产日本波多野结衣 | 亚洲精品中文字幕乱码 | 高中生自慰www网站 | 精品一区二区三区无码免费视频 | 久久国产精品萌白酱免费 | 一本无码人妻在中文字幕免费 | 亲嘴扒胸摸屁股激烈网站 | 国产精品成人av在线观看 | 亚洲一区二区三区无码久久 | 精品一区二区三区无码免费视频 | 领导边摸边吃奶边做爽在线观看 | 国产午夜无码视频在线观看 | 2019午夜福利不卡片在线 | 色欲人妻aaaaaaa无码 | 久久精品视频在线看15 | 欧美性猛交内射兽交老熟妇 | 亚洲 日韩 欧美 成人 在线观看 | 欧美老妇与禽交 | 漂亮人妻洗澡被公强 日日躁 | 国产av一区二区精品久久凹凸 | 欧美野外疯狂做受xxxx高潮 | 欧美三级a做爰在线观看 | 精品久久8x国产免费观看 | 日韩 欧美 动漫 国产 制服 | 亚洲国产精品毛片av不卡在线 | 亚洲成av人片在线观看无码不卡 | 激情五月综合色婷婷一区二区 | 久久婷婷五月综合色国产香蕉 | 久久久久久九九精品久 | 一本久道久久综合婷婷五月 | 亚洲阿v天堂在线 | 夜夜影院未满十八勿进 | 少妇高潮一区二区三区99 | 丰满少妇女裸体bbw | 午夜精品久久久久久久久 | 日韩精品久久久肉伦网站 | 国产成人综合美国十次 | 四十如虎的丰满熟妇啪啪 | 国产精品资源一区二区 | 天天摸天天碰天天添 | 无码吃奶揉捏奶头高潮视频 | 成人试看120秒体验区 | 亚洲色偷偷偷综合网 | 日本熟妇浓毛 | 在线 国产 欧美 亚洲 天堂 | 色综合视频一区二区三区 | 97人妻精品一区二区三区 | 国产精品免费大片 | а√天堂www在线天堂小说 | 国产精品第一国产精品 | 精品久久久无码人妻字幂 | 高清不卡一区二区三区 | 丰满少妇熟乱xxxxx视频 | 亚洲日韩中文字幕在线播放 | 精品国产av色一区二区深夜久久 | 无码帝国www无码专区色综合 | 欧美丰满老熟妇xxxxx性 | 玩弄少妇高潮ⅹxxxyw | 又黄又爽又色的视频 | 国产精品高潮呻吟av久久4虎 | 99久久精品无码一区二区毛片 | 国产精品a成v人在线播放 | 亚洲精品午夜无码电影网 | 国产香蕉尹人综合在线观看 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 亚洲国产一区二区三区在线观看 | 日韩精品无码一本二本三本色 | 国产一区二区三区四区五区加勒比 | 扒开双腿吃奶呻吟做受视频 | 日韩少妇白浆无码系列 | 国产午夜福利亚洲第一 | 国产午夜视频在线观看 | 中文精品无码中文字幕无码专区 | 内射欧美老妇wbb | ass日本丰满熟妇pics | 粗大的内捧猛烈进出视频 | 天堂无码人妻精品一区二区三区 | 人妻无码αv中文字幕久久琪琪布 | 欧洲熟妇精品视频 | 蜜桃视频韩日免费播放 | 夜夜躁日日躁狠狠久久av | 网友自拍区视频精品 | 国产免费久久久久久无码 | 国产精品va在线播放 | 久久无码人妻影院 | 少妇性l交大片欧洲热妇乱xxx | 蜜桃av抽搐高潮一区二区 | 亚洲精品国偷拍自产在线麻豆 | 狠狠亚洲超碰狼人久久 | 精品人妻人人做人人爽夜夜爽 | 伊人久久婷婷五月综合97色 | 亚洲一区二区三区播放 | 九九热爱视频精品 | 亚洲欧洲无卡二区视頻 | 久久人人爽人人爽人人片av高清 | 久久99久久99精品中文字幕 | 精品成人av一区二区三区 | 九九久久精品国产免费看小说 | 亚洲国产精品久久久久久 | 一个人免费观看的www视频 | 亚洲精品久久久久久一区二区 | 一本久久a久久精品亚洲 | 亚洲の无码国产の无码影院 | 国产一区二区三区四区五区加勒比 | 久久亚洲中文字幕无码 | 丰满护士巨好爽好大乳 | 麻豆成人精品国产免费 | 精品久久久无码中文字幕 | 又大又硬又黄的免费视频 | 国产偷国产偷精品高清尤物 | www一区二区www免费 | 老子影院午夜精品无码 | 中文无码成人免费视频在线观看 | 日韩精品无码免费一区二区三区 | 亚洲国产精品无码久久久久高潮 | 人人妻在人人 | 熟妇女人妻丰满少妇中文字幕 | 熟女体下毛毛黑森林 | 中文字幕无码av波多野吉衣 | 久久精品人妻少妇一区二区三区 | 国产绳艺sm调教室论坛 | 宝宝好涨水快流出来免费视频 | 曰韩无码二三区中文字幕 | 欧美性生交xxxxx久久久 | 无码一区二区三区在线 | 野狼第一精品社区 | 国内精品九九久久久精品 | 亚洲国产精品久久久天堂 | 日韩无套无码精品 | 少妇性l交大片欧洲热妇乱xxx | 色欲久久久天天天综合网精品 | 98国产精品综合一区二区三区 | 亚洲综合精品香蕉久久网 | a在线观看免费网站大全 | 亲嘴扒胸摸屁股激烈网站 | 国产精品亚洲а∨无码播放麻豆 | 欧美精品免费观看二区 | 国产成人无码av片在线观看不卡 | 国产精品无码一区二区三区不卡 | 中文字幕 亚洲精品 第1页 | 黑人巨大精品欧美一区二区 | 午夜精品一区二区三区在线观看 | 国产亚洲欧美日韩亚洲中文色 | 97夜夜澡人人双人人人喊 | 精品乱子伦一区二区三区 | 高潮毛片无遮挡高清免费视频 | 99久久久无码国产aaa精品 | 一区二区三区乱码在线 | 欧洲 | 精品国产一区二区三区四区在线看 | 久久精品人妻少妇一区二区三区 | 午夜时刻免费入口 | av人摸人人人澡人人超碰下载 | 精品夜夜澡人妻无码av蜜桃 | yw尤物av无码国产在线观看 | 欧美 日韩 人妻 高清 中文 | 自拍偷自拍亚洲精品被多人伦好爽 | 丰满人妻翻云覆雨呻吟视频 | 狂野欧美性猛xxxx乱大交 | yw尤物av无码国产在线观看 | 亚洲精品国产第一综合99久久 | 在线亚洲高清揄拍自拍一品区 | 少女韩国电视剧在线观看完整 | 午夜无码人妻av大片色欲 | 中文字幕无码av激情不卡 | 中文字幕无线码 | 国产做国产爱免费视频 | 欧美日本日韩 | 漂亮人妻洗澡被公强 日日躁 | 国产9 9在线 | 中文 | 中文字幕久久久久人妻 | 国产精品a成v人在线播放 | 精品亚洲成av人在线观看 | 欧美大屁股xxxxhd黑色 | 免费无码av一区二区 | 天堂在线观看www | 国产在线无码精品电影网 | 亚洲人成影院在线观看 | 中文字幕无线码免费人妻 | 丝袜 中出 制服 人妻 美腿 | 国产无遮挡又黄又爽免费视频 | 日本成熟视频免费视频 | 亚洲欧美日韩综合久久久 | 亚洲 激情 小说 另类 欧美 | 巨爆乳无码视频在线观看 | 日韩少妇白浆无码系列 | a片在线免费观看 | 窝窝午夜理论片影院 | 色诱久久久久综合网ywww | 精品国产成人一区二区三区 | 爽爽影院免费观看 | 国产做国产爱免费视频 | 亚洲综合无码一区二区三区 | 欧美熟妇另类久久久久久多毛 | 日日天日日夜日日摸 | 97夜夜澡人人双人人人喊 | 蜜桃臀无码内射一区二区三区 | 久久精品国产一区二区三区 | 高潮喷水的毛片 | 亚洲国产一区二区三区在线观看 | 色欲av亚洲一区无码少妇 | 亚洲综合伊人久久大杳蕉 | 久久午夜无码鲁丝片秋霞 | 日日躁夜夜躁狠狠躁 | 99久久久国产精品无码免费 | 亚洲va中文字幕无码久久不卡 | 给我免费的视频在线观看 | 欧美放荡的少妇 | 午夜无码区在线观看 | 熟妇女人妻丰满少妇中文字幕 | 国产9 9在线 | 中文 | 成人无码精品1区2区3区免费看 | 扒开双腿吃奶呻吟做受视频 | 3d动漫精品啪啪一区二区中 | 中文字幕无线码免费人妻 | 中文字幕 亚洲精品 第1页 | 欧美 日韩 人妻 高清 中文 | 性色欲网站人妻丰满中文久久不卡 | 中文亚洲成a人片在线观看 | 成在人线av无码免观看麻豆 | 黑人巨大精品欧美一区二区 | 无码免费一区二区三区 | 麻豆精品国产精华精华液好用吗 | 日日麻批免费40分钟无码 | 精品久久综合1区2区3区激情 | 亚洲 欧美 激情 小说 另类 | 国产激情无码一区二区app | 亚洲精品综合一区二区三区在线 | 精品一区二区不卡无码av | 久久午夜夜伦鲁鲁片无码免费 | 成人aaa片一区国产精品 | 国产精品久久国产精品99 | 黄网在线观看免费网站 | 成在人线av无码免费 | 激情内射亚州一区二区三区爱妻 | 国产精品内射视频免费 | 天堂无码人妻精品一区二区三区 | 国产亚洲人成在线播放 | 老熟女重囗味hdxx69 | 久久精品国产大片免费观看 | 日本一区二区三区免费播放 | 丰满人妻被黑人猛烈进入 | 久久 国产 尿 小便 嘘嘘 | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲精品中文字幕乱码 | 国产精品香蕉在线观看 | 久久精品一区二区三区四区 | 久久国产精品二国产精品 | 久久午夜无码鲁丝片秋霞 | 国产成人无码a区在线观看视频app | 强辱丰满人妻hd中文字幕 | 欧美丰满熟妇xxxx | 精品夜夜澡人妻无码av蜜桃 | 熟女体下毛毛黑森林 | 夜先锋av资源网站 | 疯狂三人交性欧美 | 亚洲中文字幕无码中字 | 欧美日韩一区二区三区自拍 | 图片区 小说区 区 亚洲五月 | 无码毛片视频一区二区本码 | 人妻人人添人妻人人爱 | 人人澡人人妻人人爽人人蜜桃 | 久久无码人妻影院 | 女人被男人爽到呻吟的视频 | 国产欧美熟妇另类久久久 | 99久久精品午夜一区二区 | 中文字幕乱码亚洲无线三区 | 国产福利视频一区二区 | 欧美激情综合亚洲一二区 | 青青久在线视频免费观看 | 中文字幕久久久久人妻 | 无码吃奶揉捏奶头高潮视频 | 亚洲经典千人经典日产 | 日韩人妻无码一区二区三区久久99 | 国产精品久久久一区二区三区 | 一本久久a久久精品亚洲 | 无码国产乱人伦偷精品视频 | 人人妻人人澡人人爽精品欧美 | 国产高潮视频在线观看 | 国产人妻人伦精品1国产丝袜 | 熟妇女人妻丰满少妇中文字幕 | 在线成人www免费观看视频 | 久久精品中文闷骚内射 | 水蜜桃亚洲一二三四在线 | 色诱久久久久综合网ywww | 国产精品人妻一区二区三区四 | 熟女俱乐部五十路六十路av | 天天躁日日躁狠狠躁免费麻豆 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 欧美 日韩 人妻 高清 中文 | 国产激情一区二区三区 | 俺去俺来也www色官网 | 欧美大屁股xxxxhd黑色 | 中文毛片无遮挡高清免费 | 国产莉萝无码av在线播放 | 国产综合久久久久鬼色 | 精品无码一区二区三区的天堂 | 亚洲一区二区三区国产精华液 | 欧美黑人性暴力猛交喷水 | 成人欧美一区二区三区 | 伊人久久大香线蕉亚洲 | 日日噜噜噜噜夜夜爽亚洲精品 | 成人精品视频一区二区 | 狠狠噜狠狠狠狠丁香五月 | 精品国产乱码久久久久乱码 | 亚洲精品午夜无码电影网 | 熟妇女人妻丰满少妇中文字幕 | 久久久久久久久888 | 日本护士xxxxhd少妇 | 国产成人综合美国十次 | 国产精品无码mv在线观看 | av无码电影一区二区三区 | 色噜噜亚洲男人的天堂 | 久久国语露脸国产精品电影 | 三上悠亚人妻中文字幕在线 | 成年美女黄网站色大免费全看 | 日本一本二本三区免费 | 日本精品久久久久中文字幕 | 国产高清av在线播放 | 妺妺窝人体色www在线小说 | 老熟妇乱子伦牲交视频 | 国产成人精品优优av | 99精品国产综合久久久久五月天 | 国产明星裸体无码xxxx视频 | 精品久久综合1区2区3区激情 | 国产精品亚洲综合色区韩国 | 欧美熟妇另类久久久久久不卡 | 99久久人妻精品免费一区 | 内射欧美老妇wbb | 激情五月综合色婷婷一区二区 | 精品国精品国产自在久国产87 | 国产精品高潮呻吟av久久 | 成人免费视频在线观看 | 国产成人无码区免费内射一片色欲 | 欧美 日韩 亚洲 在线 | 欧美精品无码一区二区三区 | 超碰97人人射妻 | 国产精品亚洲一区二区三区喷水 | 97色伦图片97综合影院 | 国产在线精品一区二区三区直播 | 波多野结衣av在线观看 | av无码电影一区二区三区 | 中文字幕乱码中文乱码51精品 | 少妇无套内谢久久久久 | 乱人伦人妻中文字幕无码久久网 | 色婷婷香蕉在线一区二区 | 国产精品无码一区二区桃花视频 | 无码精品人妻一区二区三区av | 无码av免费一区二区三区试看 | 国产精品久久精品三级 | 久久久久久久人妻无码中文字幕爆 | 十八禁视频网站在线观看 | 国产精品亚洲专区无码不卡 | 麻豆蜜桃av蜜臀av色欲av | 蜜桃av抽搐高潮一区二区 | 在线亚洲高清揄拍自拍一品区 | 午夜精品一区二区三区的区别 | 一本色道久久综合亚洲精品不卡 | 国产精品久久久久久亚洲影视内衣 | 日韩少妇白浆无码系列 | 少妇人妻偷人精品无码视频 | 色妞www精品免费视频 | 国产精品久久久久无码av色戒 | 久久亚洲日韩精品一区二区三区 | 久久精品人妻少妇一区二区三区 | 国产手机在线αⅴ片无码观看 | 九月婷婷人人澡人人添人人爽 | 久久精品国产一区二区三区 | 国产小呦泬泬99精品 | 亚洲自偷自拍另类第1页 | 丰满人妻被黑人猛烈进入 | 亚洲大尺度无码无码专区 | 巨爆乳无码视频在线观看 | 98国产精品综合一区二区三区 | 精品偷自拍另类在线观看 | 亚洲の无码国产の无码步美 | 性欧美牲交在线视频 | 亚洲一区二区三区播放 | 亚洲精品中文字幕 | 亚洲熟妇色xxxxx亚洲 | 国产精品嫩草久久久久 | 天天燥日日燥 | 国产精品久久久久久久影院 | 日日碰狠狠丁香久燥 | 好屌草这里只有精品 | 久久天天躁狠狠躁夜夜免费观看 | 国产成人精品一区二区在线小狼 | 一本色道久久综合亚洲精品不卡 | 中文字幕无码免费久久99 | 三上悠亚人妻中文字幕在线 | 日日噜噜噜噜夜夜爽亚洲精品 | 夜夜夜高潮夜夜爽夜夜爰爰 | 狠狠cao日日穞夜夜穞av | 国产肉丝袜在线观看 | 未满成年国产在线观看 | 亚洲精品一区三区三区在线观看 | 99久久精品午夜一区二区 | 强开小婷嫩苞又嫩又紧视频 | 欧美高清在线精品一区 | 久久人妻内射无码一区三区 | 亚洲欧美精品aaaaaa片 | 国产精华av午夜在线观看 | 99久久婷婷国产综合精品青草免费 | 国产熟妇高潮叫床视频播放 | 少妇厨房愉情理9仑片视频 | 在线亚洲高清揄拍自拍一品区 | 久久国产精品精品国产色婷婷 | 我要看www免费看插插视频 | 中文字幕精品av一区二区五区 | 亚洲国产欧美日韩精品一区二区三区 | 国产成人人人97超碰超爽8 | 麻豆国产人妻欲求不满 | 国产成人人人97超碰超爽8 | 又大又硬又爽免费视频 | 亚洲精品一区三区三区在线观看 | 男人和女人高潮免费网站 | 熟妇激情内射com | 麻豆国产丝袜白领秘书在线观看 | 99精品国产综合久久久久五月天 | 精品熟女少妇av免费观看 | 国产乱人无码伦av在线a | 国产精品久久久久久久影院 | 无遮挡啪啪摇乳动态图 | 国产三级精品三级男人的天堂 | 色狠狠av一区二区三区 | 免费中文字幕日韩欧美 | 日韩少妇内射免费播放 | 学生妹亚洲一区二区 | 亚洲色大成网站www国产 | 我要看www免费看插插视频 | 国产真人无遮挡作爱免费视频 | 婷婷五月综合缴情在线视频 | 伊在人天堂亚洲香蕉精品区 | 亚洲熟妇自偷自拍另类 | 精品国产aⅴ无码一区二区 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 性色欲情网站iwww九文堂 | 成 人 网 站国产免费观看 | 久久综合色之久久综合 | www国产精品内射老师 | 亚洲va中文字幕无码久久不卡 | 日韩精品无码一本二本三本色 | 日日碰狠狠躁久久躁蜜桃 | 亚洲人亚洲人成电影网站色 | 亚洲国产欧美日韩精品一区二区三区 | 午夜无码人妻av大片色欲 | 国产精品国产三级国产专播 | 永久免费观看美女裸体的网站 | 人妻夜夜爽天天爽三区 | 国产精品高潮呻吟av久久4虎 | 午夜福利试看120秒体验区 | 中文字幕无码av波多野吉衣 | 全球成人中文在线 | 国产亲子乱弄免费视频 | 国产高清av在线播放 | 亚洲人亚洲人成电影网站色 | 男女性色大片免费网站 | 欧美熟妇另类久久久久久不卡 | 欧美亚洲日韩国产人成在线播放 | 欧美激情一区二区三区成人 | 少妇性l交大片欧洲热妇乱xxx | 中文字幕无线码 | 久久熟妇人妻午夜寂寞影院 | 久久天天躁狠狠躁夜夜免费观看 | 亚洲国产欧美国产综合一区 | 2019nv天堂香蕉在线观看 | 成 人影片 免费观看 | 18精品久久久无码午夜福利 | 成人亚洲精品久久久久 | 日韩人妻无码一区二区三区久久99 | 久久久久久久女国产乱让韩 | 精品人妻人人做人人爽 | 99视频精品全部免费免费观看 | 免费人成在线观看网站 | 奇米影视888欧美在线观看 | 美女极度色诱视频国产 | 亚洲国产一区二区三区在线观看 | 日本欧美一区二区三区乱码 | 网友自拍区视频精品 | 欧美成人家庭影院 | 亚洲精品中文字幕乱码 | 丰满人妻一区二区三区免费视频 | 亚洲欧洲无卡二区视頻 | 国产精品.xx视频.xxtv | 少妇无套内谢久久久久 | 亚洲色欲色欲欲www在线 | 精品国产一区二区三区四区在线看 | 日本精品高清一区二区 | 欧美熟妇另类久久久久久多毛 | 亚洲国产av美女网站 | 5858s亚洲色大成网站www | 国产人妻精品一区二区三区 | 国产人妻精品午夜福利免费 | 亚洲 激情 小说 另类 欧美 | 久久久国产一区二区三区 | 国产精品鲁鲁鲁 | а天堂中文在线官网 | 国产情侣作爱视频免费观看 | av无码不卡在线观看免费 | ass日本丰满熟妇pics | 国产成人综合在线女婷五月99播放 | 在线播放无码字幕亚洲 | 亚洲色www成人永久网址 | 无码成人精品区在线观看 | 无码人妻黑人中文字幕 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 成人综合网亚洲伊人 | 97色伦图片97综合影院 | 国产无遮挡吃胸膜奶免费看 | 久久久久亚洲精品中文字幕 | 四虎永久在线精品免费网址 | 88国产精品欧美一区二区三区 | 2019午夜福利不卡片在线 | 97精品国产97久久久久久免费 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 粉嫩少妇内射浓精videos | 大胆欧美熟妇xx | 精品水蜜桃久久久久久久 | 欧美精品无码一区二区三区 | 亚洲乱码国产乱码精品精 | 76少妇精品导航 | 免费人成在线观看网站 | 亚洲国产精品成人久久蜜臀 | 国产成人无码av片在线观看不卡 | 亚洲一区二区三区偷拍女厕 | 亚洲欧美日韩国产精品一区二区 | 国产亚洲视频中文字幕97精品 | 欧美人与动性行为视频 | 中文字幕人成乱码熟女app | 欧美日韩久久久精品a片 | 桃花色综合影院 | 丰满人妻一区二区三区免费视频 | 亚洲精品国产a久久久久久 | 亚洲精品久久久久avwww潮水 | 亚洲日本va中文字幕 | 久久久国产精品无码免费专区 | 正在播放老肥熟妇露脸 | 国产无遮挡又黄又爽又色 | 全球成人中文在线 | 久久精品人人做人人综合试看 | 午夜精品久久久久久久久 | 极品尤物被啪到呻吟喷水 | 亚洲成av人片在线观看无码不卡 | 疯狂三人交性欧美 | 人妻有码中文字幕在线 | 国产成人综合在线女婷五月99播放 | 4hu四虎永久在线观看 | 在线a亚洲视频播放在线观看 | 亚洲精品综合五月久久小说 | 18禁黄网站男男禁片免费观看 | 日韩人妻无码一区二区三区久久99 | 国内精品人妻无码久久久影院蜜桃 | 国产69精品久久久久app下载 | 国内揄拍国内精品少妇国语 | 97精品国产97久久久久久免费 | 欧美丰满熟妇xxxx性ppx人交 | 在线播放无码字幕亚洲 | 亚洲中文字幕va福利 | 国产成人一区二区三区在线观看 | 亚洲午夜无码久久 | 亚洲乱码中文字幕在线 | 蜜桃视频韩日免费播放 | 国产乱码精品一品二品 | 高清无码午夜福利视频 | 欧美日韩亚洲国产精品 | 中文字幕人妻无码一夲道 | 丁香啪啪综合成人亚洲 | 亚洲色偷偷偷综合网 | 亚洲欧洲日本综合aⅴ在线 | 久久人人爽人人爽人人片av高清 | 久久精品视频在线看15 | 2020久久超碰国产精品最新 | 国产美女极度色诱视频www | 午夜精品一区二区三区的区别 | 十八禁视频网站在线观看 | 欧美人妻一区二区三区 | 免费观看又污又黄的网站 | 中文字幕人妻丝袜二区 | 一本加勒比波多野结衣 | 国产精品沙发午睡系列 | 国产精华av午夜在线观看 | 欧美阿v高清资源不卡在线播放 | 日本精品久久久久中文字幕 | 少妇人妻av毛片在线看 | 成人亚洲精品久久久久 | 日韩精品无码一本二本三本色 | 沈阳熟女露脸对白视频 | 天堂无码人妻精品一区二区三区 | 国产真实伦对白全集 | 性色欲网站人妻丰满中文久久不卡 | 久久久久av无码免费网 | 国产成人综合在线女婷五月99播放 | 老头边吃奶边弄进去呻吟 | 伊人久久大香线蕉午夜 | 大肉大捧一进一出好爽视频 | 成人无码精品1区2区3区免费看 | 亚洲综合在线一区二区三区 | 免费乱码人妻系列无码专区 | 欧美日韩一区二区综合 | 国产成人亚洲综合无码 | 2020久久香蕉国产线看观看 | 亚洲精品美女久久久久久久 | 国产激情一区二区三区 | 草草网站影院白丝内射 | 无码av岛国片在线播放 | 国产成人精品久久亚洲高清不卡 | 少妇人妻偷人精品无码视频 | 欧洲熟妇色 欧美 | 麻豆国产人妻欲求不满谁演的 | 亚洲aⅴ无码成人网站国产app | 亚洲第一网站男人都懂 | 性欧美疯狂xxxxbbbb | 99久久人妻精品免费二区 | 蜜桃av抽搐高潮一区二区 | 人人妻人人澡人人爽欧美一区九九 | 午夜无码区在线观看 | 一个人看的视频www在线 | 国产 浪潮av性色四虎 | 亚洲s码欧洲m码国产av | 乱人伦人妻中文字幕无码 | 国产亚洲日韩欧美另类第八页 | 久久午夜无码鲁丝片 | 天海翼激烈高潮到腰振不止 | 人人澡人人透人人爽 | 玩弄中年熟妇正在播放 | 亚洲の无码国产の无码影院 | 亚洲中文字幕无码一久久区 | 日日摸夜夜摸狠狠摸婷婷 | 无码精品国产va在线观看dvd | 欧美午夜特黄aaaaaa片 | 久久久久久a亚洲欧洲av冫 | 少妇性l交大片 | 伊人久久婷婷五月综合97色 | 久久久精品欧美一区二区免费 | 国产午夜精品一区二区三区嫩草 | 亚洲国产成人av在线观看 | 国精产品一品二品国精品69xx | 国内丰满熟女出轨videos | 久久天天躁夜夜躁狠狠 | 精品国产一区二区三区四区 | 亚洲热妇无码av在线播放 | 动漫av网站免费观看 | www国产亚洲精品久久久日本 | 色妞www精品免费视频 | 狂野欧美性猛交免费视频 | 国产综合色产在线精品 | 亚洲色偷偷偷综合网 | 妺妺窝人体色www在线小说 | 玩弄人妻少妇500系列视频 | 日韩欧美群交p片內射中文 | 伊人久久大香线蕉亚洲 | 国产精品毛片一区二区 | 一本大道伊人av久久综合 | 小鲜肉自慰网站xnxx | 亚洲日韩av片在线观看 | 久久亚洲精品成人无码 | 无码国产色欲xxxxx视频 | 久久国产精品二国产精品 | 国产成人精品视频ⅴa片软件竹菊 | 影音先锋中文字幕无码 | 亚洲精品中文字幕久久久久 | 自拍偷自拍亚洲精品被多人伦好爽 | 成人无码精品1区2区3区免费看 | 久久www免费人成人片 | 亚洲va中文字幕无码久久不卡 | 一本色道久久综合亚洲精品不卡 | 曰本女人与公拘交酡免费视频 | 欧美成人家庭影院 | 亚洲の无码国产の无码影院 | 搡女人真爽免费视频大全 | 荡女精品导航 | 国产真人无遮挡作爱免费视频 | 国语精品一区二区三区 | 好爽又高潮了毛片免费下载 | 日韩亚洲欧美精品综合 | 强辱丰满人妻hd中文字幕 | 东北女人啪啪对白 | 少妇被黑人到高潮喷出白浆 | 久久99精品国产.久久久久 | 亚洲国产成人a精品不卡在线 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 人妻体内射精一区二区三四 | 亚洲熟妇色xxxxx欧美老妇 | 国产激情综合五月久久 | 久久久久久a亚洲欧洲av冫 | 国产黄在线观看免费观看不卡 | 丰满人妻一区二区三区免费视频 | 国产人成高清在线视频99最全资源 | 国产精品无码成人午夜电影 | 少妇高潮喷潮久久久影院 | 久久99精品久久久久久动态图 | 麻豆国产人妻欲求不满谁演的 | 日韩精品无码一本二本三本色 | 国产精品人人妻人人爽 | 粉嫩少妇内射浓精videos | 无码人妻久久一区二区三区不卡 | 300部国产真实乱 | 日日干夜夜干 | 一区二区三区高清视频一 | 欧美日韩一区二区免费视频 | 精品人妻av区 | 成熟人妻av无码专区 | 亚洲va中文字幕无码久久不卡 | 国产人妻精品一区二区三区 | 人妻无码αv中文字幕久久琪琪布 | 国产内射爽爽大片视频社区在线 | 天堂久久天堂av色综合 | 国産精品久久久久久久 | 国产亚洲精品久久久久久久久动漫 | 丝袜足控一区二区三区 | 日本乱偷人妻中文字幕 | 又湿又紧又大又爽a视频国产 | 377p欧洲日本亚洲大胆 | 无码中文字幕色专区 | 激情内射亚州一区二区三区爱妻 | 人妻无码久久精品人妻 | 中文字幕日韩精品一区二区三区 | 精品无码国产自产拍在线观看蜜 | 国产精品沙发午睡系列 | 国产明星裸体无码xxxx视频 | 精品无码一区二区三区的天堂 | 沈阳熟女露脸对白视频 | 色情久久久av熟女人妻网站 | 熟女少妇在线视频播放 | 久久久久成人精品免费播放动漫 | 老熟女乱子伦 | 无码免费一区二区三区 | 夜夜夜高潮夜夜爽夜夜爰爰 | 亚洲熟妇色xxxxx欧美老妇 | 久久久久久久女国产乱让韩 | 久久人妻内射无码一区三区 | 亚洲最大成人网站 | 超碰97人人做人人爱少妇 | 午夜无码人妻av大片色欲 | 露脸叫床粗话东北少妇 | 天天拍夜夜添久久精品大 | 图片区 小说区 区 亚洲五月 | 国产国产精品人在线视 | 性色欲情网站iwww九文堂 | 丰满人妻被黑人猛烈进入 | 国产午夜无码精品免费看 | 国产成人无码av片在线观看不卡 | 天下第一社区视频www日本 | 99精品无人区乱码1区2区3区 | 乱码av麻豆丝袜熟女系列 | 久久久久se色偷偷亚洲精品av | 国产女主播喷水视频在线观看 | 国产香蕉97碰碰久久人人 | 欧美丰满老熟妇xxxxx性 | 日本丰满熟妇videos | 成人综合网亚洲伊人 | 久久久久99精品国产片 | 激情内射亚州一区二区三区爱妻 | 18禁止看的免费污网站 | 欧美熟妇另类久久久久久不卡 | 日本饥渴人妻欲求不满 | 成人综合网亚洲伊人 | 自拍偷自拍亚洲精品被多人伦好爽 | aa片在线观看视频在线播放 | 中文字幕无码视频专区 | 久久伊人色av天堂九九小黄鸭 | 久久精品女人的天堂av | 亚洲国精产品一二二线 | 在线视频网站www色 | 荫蒂添的好舒服视频囗交 | 久久天天躁夜夜躁狠狠 | 久久综合九色综合欧美狠狠 | 最近中文2019字幕第二页 | 精品乱子伦一区二区三区 | 亚洲午夜福利在线观看 | 日本一区二区三区免费高清 | 大地资源网第二页免费观看 | 天天躁夜夜躁狠狠是什么心态 | 亚洲日韩一区二区 | 成人精品视频一区二区三区尤物 | 国产成人无码一二三区视频 | 欧洲精品码一区二区三区免费看 | 人人爽人人澡人人高潮 | www国产精品内射老师 | 无码av最新清无码专区吞精 | 久久国产精品偷任你爽任你 | 国产三级久久久精品麻豆三级 | 无码av中文字幕免费放 | 亚洲精品国产第一综合99久久 | 亚洲国产精品一区二区第一页 | 色综合久久久无码网中文 | 日日噜噜噜噜夜夜爽亚洲精品 | 免费无码一区二区三区蜜桃大 | 日本免费一区二区三区最新 | 黄网在线观看免费网站 | 99久久亚洲精品无码毛片 | 亚洲日本va午夜在线电影 | 国产精品久久久久无码av色戒 | 娇妻被黑人粗大高潮白浆 | 精品国产一区二区三区四区 | 内射后入在线观看一区 | 日韩少妇白浆无码系列 | 成人试看120秒体验区 | 激情内射亚州一区二区三区爱妻 | 国产av无码专区亚洲awww | 国产午夜亚洲精品不卡下载 | 麻豆精品国产精华精华液好用吗 | 亚洲欧美日韩综合久久久 | 日日鲁鲁鲁夜夜爽爽狠狠 | 亚洲 欧美 激情 小说 另类 | 性欧美牲交xxxxx视频 | 日韩少妇白浆无码系列 | 精品人人妻人人澡人人爽人人 | 久久亚洲中文字幕精品一区 | 无码成人精品区在线观看 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 成在人线av无码免观看麻豆 | 亚洲色大成网站www国产 | 国产极品美女高潮无套在线观看 | 久久久久久国产精品无码下载 | 强辱丰满人妻hd中文字幕 | 久久99国产综合精品 | 亚洲人成影院在线无码按摩店 | 亚洲日韩一区二区 | 亚洲aⅴ无码成人网站国产app | 国产sm调教视频在线观看 | 成人无码精品1区2区3区免费看 | 亚洲国产一区二区三区在线观看 | 国产综合色产在线精品 | 婷婷丁香六月激情综合啪 | 东京一本一道一二三区 | 国产av无码专区亚洲a∨毛片 | аⅴ资源天堂资源库在线 | 日欧一片内射va在线影院 | 狠狠噜狠狠狠狠丁香五月 | 精品一区二区三区无码免费视频 | 啦啦啦www在线观看免费视频 | 老子影院午夜伦不卡 | 超碰97人人做人人爱少妇 | 草草网站影院白丝内射 | 美女黄网站人色视频免费国产 | 成人片黄网站色大片免费观看 | 欧美人与善在线com | 一本色道婷婷久久欧美 | 国产香蕉尹人综合在线观看 | 亚洲s码欧洲m码国产av | 国产成人无码a区在线观看视频app | 性欧美疯狂xxxxbbbb | 亚洲阿v天堂在线 | 日韩在线不卡免费视频一区 | 疯狂三人交性欧美 | 西西人体www44rt大胆高清 | 少妇性俱乐部纵欲狂欢电影 | 国产性生交xxxxx无码 | 欧美乱妇无乱码大黄a片 | 亚洲欧美精品aaaaaa片 | 精品偷自拍另类在线观看 | 一区二区三区乱码在线 | 欧洲 | 久久综合九色综合欧美狠狠 | 无码播放一区二区三区 | 熟妇人妻中文av无码 | 国产精品久久久久7777 | 无码人妻精品一区二区三区下载 | 成人试看120秒体验区 | 人人爽人人澡人人人妻 | aⅴ在线视频男人的天堂 | 国产人妻人伦精品 | 又大又硬又爽免费视频 | 精品国产一区av天美传媒 | 无码午夜成人1000部免费视频 | 国产深夜福利视频在线 | 欧美性生交活xxxxxdddd | 亚洲乱码中文字幕在线 | 2019午夜福利不卡片在线 | 久久99国产综合精品 | 亚洲一区二区三区在线观看网站 | 亚无码乱人伦一区二区 | 欧美 丝袜 自拍 制服 另类 | 97夜夜澡人人双人人人喊 | 国产特级毛片aaaaaaa高清 | 大地资源网第二页免费观看 | 宝宝好涨水快流出来免费视频 | 国产亚洲tv在线观看 | 丰满少妇高潮惨叫视频 | 久久久久av无码免费网 | 亚洲综合色区中文字幕 | 无码毛片视频一区二区本码 |