国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Vue3 + TypeScript 復盤總結

開發 前端
這篇文章主要記錄一下我在開發最后一層-設備管理系統的前端開發過程中的一些總結。

 [[401627]]

背景

近期在研發一套物聯網設備管理系統,其主要用途是將公司旗下所負責智能園區中的硬件設備通過物聯網云平臺來進行綜合管控。

由于這個產品是實驗性項目,沒有合同,沒有明確收益。所以能夠拿到的資源非常少。

產品具體的負責人,只有 1.5 人,幾乎只有我自己。所以既要擔任產品經理,又要擔任開發者,還要擔任運維。不過從技術角度而言,選型可以更加自由。

整個系統在架構上設計分為 4 層。自底向上分別是設備硬件、設備接入網關、物聯網平臺、設備管理系統。除去設備硬件,其它 3 層都屬于軟件范疇。

這篇文章主要記錄一下我在開發最后一層-設備管理系統的前端開發過程中的一些總結。

前端采用 Vite2.x、Vue3.x、Vuex4.x、VueRouter4.x、TypeScript、Element-Plus 進行開發。可以看到,這些框架和庫所采用的版本是比較激進的,大部分都是最新版本,以及 rc 和 beta 版本。不過從項目開始到寫這篇總結,其中的一些庫的版本已經不是最新的了,不得不感慨前端技術變化之快。

一個組件的思考

首先來看一個組件。

水波紋.gif

這是一個具有波紋效果、用來表示當前 websocket 連接狀態的小圓點。是一個非常簡單的純展示組件。樣式效果使用 css3 變量、動畫、和 before、after 偽類實現。

props 設計非常簡單,只有一個 type 字段。根據 type 字段的不同,波紋的顏色也不同。

思路有了,下面是實現上的一些細節性問題。

如何聲明字段名為枚舉的類型?

根據設計,type 字段應該是一個枚舉值,不應該由調用方隨意設置。

下面是 Type 的枚舉聲明,共有 6 個字段。 

  1. enum Type {  
  2.   primary = "primary" 
  3.   success = "success" 
  4.   warning = "warning" 
  5.   warn = "warn", // warning alias  
  6.   danger = "danger" 
  7.   info = "info" 

TypeScript 中聲明類型的關鍵字有兩個,interface 和 type,在聲明 key 不確定類型的字段時稍有不同。

使用 type 進行聲明: 

  1. type ColorConfig = {  
  2.   [key in Type]: Colors;  
  3. }; 

使用 interface 卻只能像下面這樣: 

  1. interface ColorConfig {  
  2.   [key: string]: Colors;  

因為 interface 的索引只能是基礎類型,類型別名也不可以。而 type 的索引可以是復合類型。

Vue 3 如何獲取元素實例?

在 vue3 中,組件的邏輯可以放在 setup 函數里面,但是 setup 中不再有 this,所以 vue2 中的 this.$refs 的用法在 vue3 中無法使用。

新的用法是:

給元素添加 ref 屬性。

在 setup 中聲明與元素 ref 同名的變量。

在 setup 的 return 對象中將 ref 變量作為同名屬性返回。

在 onMounted 生命周期中訪問 ref 變量,既是元素實例。

第一步: 

  1. <div class="point point-flicker" ref="point"></div> 

第二步: 

  1. const point = ref<HTMLDivElement | null>(null); 

注意類型要填寫 HTMLDivElement,這樣才能享受類型推斷。

第三步: 

  1. return { point }; 

這一步必不可少,如果返回對象中不包含這個同名屬性,onMounted 中訪問的 ref 對象會是 null。

第四步: 

  1. onMounted(() => {  
  2.   if (point?.value) {  
  3.     // logic  
  4.   }  
  5. }); 

如何操作偽類?

JavaScript 無法獲取到偽類元素,但是可以換一種思路。偽類樣式引用 css 變量,再通過 js 控制 css 變量來完成間接操作偽類的效果。

比如這是一個偽類: 

  1. .point-flicker:after {  
  2.   background-color: var(--afterBg);  

它依賴了 afterBg 變量。

如果需要修改它的內容,只需要使用 js 操作 afterBg 的內容即可。 

  1. point.value.style.setProperty("--bg", colorConfig[props.type].bg); 

API 的變化

Vue3 中組件如何修改自身的 props?

有一種不是很常見的情況,需要組件修改父組件傳遞給自己的 Props。

比如抽屜組件、擬態框組件等。

在 vue2 中常見的用法是 sync 和 v-model。

vue3 中只推薦使用 v-model:xxx="" 的方式。

比如父組件傳遞: 

  1. <ws-log v-model="wsLogVisible" /> 

子組件: 

  1. <template>  
  2.     <div v-model:visible="visible">  
  3.     ... 
  4.     </div>  
  5. </template>  
  6. <script>  
  7. // ...  
  8.  props: {  
  9.     visible: {  
  10.       type: Boolean,  
  11.     },  
  12.   },  
  13. </script> 

Vue3 中 watch 用法的變化

watch 變得更加簡單。 

  1. import { watch } from "vue";  
  2. watch(source, (currentValue, oldValue) => {  
  3.     // logic  
  4. }); 

當 source 變化時自動執行 watch 第二個參數所傳入的函數。

Vue3 中 computed 用法的變化

computed 也變得更加簡單。 

  1. import { computed } from "vue"  
  2. const v = computed(() => {  
  3.     return x  
  4. }); 

computed 返回的變量是一個響應式對象。

Vue3 中組件循環自身的技巧

這是一種開發組件的技巧。

假設你有一個不確定深度的樹狀結構數據。 

  1.  
  2.   "label": "root",  
  3.   "children": [  
  4.     {  
  5.       "label": "a",  
  6.       "children": [  
  7.         {  
  8.           "label": "a1",  
  9.           "children": []  
  10.         },  
  11.         {  
  12.           "label": "a2",  
  13.           "children": []  
  14.         }  
  15.       ]  
  16.     }  
  17.   ]  

它的類型定義如下: 

  1. export interface Menu {  
  2.   id: string;  
  3.   label: string;  
  4.   children: Menu | null;  

你需要實現一種樹狀組件來渲染它們。這時就需要用到這種技巧。 

  1. <template>  
  2.     <div>{{ menu.label }}</div>  
  3.     <Menu  
  4.       @select="select"  
  5.       v-for="item in menu.children"  
  6.       :key="item.id"  
  7.       :menu="item"  
  8.     />  
  9. </template>  
  10. <script  lang="ts">  
  11. import { defineComponent } from "vue";  
  12. export default defineComponent({  
  13.   name: "Menu",  
  14.   props: {  
  15.     menu: {  
  16.       type: Object,  
  17.     },  
  18.   },  
  19. });  
  20. </script> 

組件的 name 可以在自身中直接使用,而不需要在 component 中聲明。

一些坑

Vuex:慎用 Map

在 Vuex 中,我設計了一個數據結構用于存儲模塊(業務概念)不同的狀態。 

  1. type Code = number 
  2. export type ModuleState = Map<Code, StateProperty>

但是我發現一個問題,當我修改 Map 中某一個 value 中的屬性時,不會觸發 Vuex 的監聽。

所以我只好將數據結構修改為對象的形式。 

  1. export type ModuleState = { [key in Code]: StateProperty }; 

ts 中索引不可以使用類型別名,但是可以寫成下面這樣: 

  1. type Code = number 
  2. export type ModuleState = { [key in Code]: StateProperty }; 

除此之外,Map 還存在另外一個問題。

當一個 Map 類型的 Proxy 對象作為參數被傳遞時,是無法使用 get、set、clear 等 Map 方法的,但是 TypeScript 會提示這些方法可用。如果使用了這些方法,會得到一個 Uncaught TypeError。

如果使用 Object 則不會產生這個問題。

WebSocket 發生異常無法被 try catch 監聽

ws 的異常只能在 onerror 和 onclose 兩個事件中進行處理,try catch 是無法捕獲的。

有些時候,onerror 和 onclose 會連續執行,比如觸發 onerror,導致連接關閉,就會緊接著觸發 onclose。

Vue Devtools

vue devtools 目前無法支持 Vue3,但是 vue devtools 幾乎是開發中必不可少的工具,目前可以使用 vue devtools beta 版本,但存在一些 Bug。

下載地址:

https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg?utm_source=chrome-ntp-icon

用法非常簡單,安裝后重啟瀏覽器就可以。不需要設置 vue.config.devtools = true,在 vue3 中 vue.config 實例不存在 devtools 屬性。

ESbuild 安裝依賴

在使用 vite 啟動服務的同時安裝依賴,非常容易碰到一個錯誤。

 

  1. Error: EBUSY: resource busy or locked, open 'E:\gxt\property-relay-fed\node_modules\esbuild\esbuild.exe' 

這個問題的原因是 vite 依賴的編譯工具 esbuild.exe 被占用所導致的,解決方法很簡單,就是停掉 vite,安裝完依賴后再重新啟動 vite。

Vite 在 Chrome 中調試的問題

系統中有一些移動頁面,需要嵌入在 App 中使用。

常見的調試 WebView 的方法有兩種,一種簡單的方式是使用騰訊開源的 vcosnole,另一種麻煩一些的調試方式是使用 Chrome 的 DevTools。

但是 vconsole 并沒有想象中那么好用。

image.png

所以我選擇使用 Chrome 調試,chrome://inspect/#devices

但是在調試過程中我發現 Chrome 調試工具里面竟然運行的是 TS 源碼,TS 的語法直接被認為語法錯誤。(我是使用 Vite 啟動的開發服務。)

解決方案很簡單,但挺 Low。先使用 vite build 把 TS 代碼編譯成 JS,再使用 vite preview 啟動服務。

WebSocket

websocket 和 Vue3 沒什么關系,但是在這里簡單提一下。

設備管理系統的核心概念是設備,設備會有很多屬性,在硬件上也被稱作數據點。這些屬性會經歷非常長的鏈路傳輸到用戶界面上。整體流程大概是:硬件通過 tcp 協議上傳到接入網關,接入網關處理后再通過 mqtt 協議上傳到物聯網平臺,物聯網平臺再經過規則引擎處理,通過 webhook restful 的形式發送到業務系統,業務系統再通過 websocket 推送到前端。

雖然數據通過層層編解碼、不同的協議繞了非常遠的距離呈現到用戶面前,但是前端只需要關心 websocket 就足夠了。

WebSocket 重連

在做重連時,需要注意 onerror 和 onclose 連續執行的問題,通常是使用類似防抖的方法來解決。

我的做法是增加一個變量來控制重連次數。

let connecting = false; // 斷開連接后,先觸發 onerror,再觸發 onclose,主要用于防止重復觸發 

  1. conn();  
  2.  function conn() {  
  3.    connecting = false 
  4.    if (ctx.state.stateWS.instance && ctx.state.stateWS.instance.close) {  
  5.      ctx.state.stateWS.instance.close();  
  6.    }  
  7.    const url = ctx.state.stateWS.url + "?Authorization=" + getAuthtication();  
  8.    ctx.state.stateWS.instance = new WebSocket(url);  
  9.    ctx.state.stateWS.instance.onopen = () => {  
  10.      ctx.commit(ActionType.SUCCESS);  
  11.    };  
  12.    ctx.state.stateWS.instance.onclose = () => {  
  13.      if (connecting) return;  
  14.      ctx.commit(ActionType.CLOSE);  
  15.      setTimeout(() => {  
  16.        conn();  
  17.      }, 10 * 1000);  
  18.      connecting = true 
  19.    }; 
  20.    ctx.state.stateWS.instance.onerror = () => {  
  21.      if (connecting) return;  
  22.      ctx.commit(ActionType.ERROR);  
  23.      setTimeout(() => {  
  24.        conn();  
  25.      }, 10 * 1000);  
  26.      connecting = true 
  27.    };  
  28.    ctx.state.stateWS.instance.onmessage = function (  
  29.      this: WebSocket,  
  30.      ev: MessageEvent  
  31.    ) {  
  32.      // logic  
  33.      } catch (e) {  
  34.        console.log("e:", e);  
  35.      }  
  36.    };  
  37.  } 

WebSocket 連接活動日志

系統是設計成 7*24 小時不間斷運行。所以 websocket 很容易受到一些網絡因素或者其它因素的影響發生斷開,重連是一項非常重要的功能,同時還應該具備重連日志功能。

在用戶的不同環境中,排查 WebSocket 的連接狀態很麻煩,添加一個連接日志功能是比較不錯的方案,這樣可以很好的看到不同時間的連接情況。

image.png

需要注意,這些日志是存儲在用戶的瀏覽器內存中的,需要設置上限,到達上限要自動清除早期日志。

WebSocket 鑒權

websocket 的鑒權是很多人容易忽視的一個點。

我在系統設計中,restful API 的鑒權是通過在 request header 上附帶 Authorization 字段,設置生成的 JWT 來實現的。

websocket 無法設置 header,但是可以設置 query,實現思路類似 restful 的認證設計。

關于 ws 鑒權的過期、續期、權限等問題,和 restful 保持一致即可。

script setup:更加清爽的 API

script setup 至今仍是一個實驗性特性,但它確實非常清爽。

單文件組件的 setup 常規用法像下面這樣: 

  1. <script lang="ts"> 
  2. import { defineComponent } from 'vue'  
  3. export default defineComponent({ 
  4.   setup () {  
  5.     return {}  
  6.   }  
  7. })  
  8. </script> 

使用 script setup 后,代碼變成了下面這樣: 

  1. <script setup lang="ts">    
  2. </script> 

在 sciprt 標簽中的頂層變量、函數都會 return 出去。

在這種模式下,減少了大量代碼,可以提高開發效率、降低心智負擔。

但這時也存在幾個問題,比如在 script setup 中怎么使用生命周期和 watch/computed 函數?怎么使用組件?怎么獲取 props 和 context?

使用組件

直接導入組件后,vue 會自動識別,無需使用 component 掛載。 

  1. <script setup lang="ts">  
  2.   import C from "component"  
  3. </script> 

使用生命周期和監聽計算函數

和標準寫法基本無差異。 

  1. <script setup lang="ts">  
  2.   import { watch, computed, onMounted } from "vue"  
  3. </script> 

使用 props 和 context

由于 setup 被提升到 script 標簽上了,自然也就沒辦法接收 props 和 context 這兩個參數。

所以 vue 提供了 defineProps、defineEmit、useContext 函數。

defineProps

defineProps 的用法和 OptionsAPI 中的 props 用法幾乎一致。 

  1. <script setup lang="ts">  
  2. import { defineProps } from "vue";  
  3. interface Props {  
  4.   moduleID: string;  
  5.  
  6. const props = defineProps<Props>(["moduleID"]);  
  7. console.log(props.moduleID);  
  8. </script> 

defineEmit

defineEmit 的用法和 OptionsAPI 中的 emit 用法也幾乎一致。 

  1. <script setup lang="ts">  
  2. import { defineEmit } from "vue";  
  3. const emit = defineEmit(["select"]);  
  4. console.log(emit("select"));  
  5. </script> 

emit 的第一個參數是事件名稱,后面支持傳遞不定個數的參數。

useContext

useContext 是一個 hook 函數,返回 context 對象。 

  1. const ctx = useContext() 

原理

原理相當簡單。增加了一層編譯過程,將 script setup 編譯成標準模式的代碼。

但是實現上有非常多的細節,所以導致至今仍未推出正式版。

Vue3 Composition 所帶來的模塊化開發方式

這套技術棧帶給我最深的感受還是開發方式上的變化。

在 Vue2 的開發中,Options API 在面對業務邏輯復雜的頁面時非常吃力。當邏輯長達千行時,追蹤一個變量的變化是一件非常頭痛的事情。

但是有了 Composition API 后,這將不再是問題,它帶來了一種全新的開發方式,雖然有種 React 的感覺,但這相比之前已經非常棒了!

這項目中所有的頁面,我都使用 hooks 的方式開發。

在設備模塊中,我的 js 代碼是這樣的。 

  1. <script lang="ts">  
  2. import { defineComponent, toRefs } from "vue";  
  3. import { useDeviceCreate } from "./create";  
  4. import { useDeviceQuery } from "./query";  
  5. import { useDeviceDelete } from "./delete";  
  6. import { useUnbind } from "./unbind";  
  7. import { useBind } from "./bind";  
  8. import { useDeviceEdit } from "./edit";  
  9. import { useState } from "./state";  
  10. import { useAssign } from "./assign";  
  11. export default defineComponent({  
  12.   setup() {  
  13.     const queryObj = useDeviceQuery();  
  14.     const { query, devices } = queryObj;  
  15.     const reload = query 
  16.     return {  
  17.       ...toRefs(useDeviceCreate(reload)),  
  18.       ...toRefs(queryObj),  
  19.       ...toRefs(useDeviceDelete(reload)),  
  20.       ...toRefs(useUnbind(reload)),  
  21.       ...toRefs(useBind(reload)),  
  22.       ...toRefs(useDeviceEdit(reload)),  
  23.       ...toRefs(useState(devices)),  
  24.       ...toRefs(useAssign()),  
  25.     };  
  26.   },  
  27. });  
  28. </script> 

每個模塊各司其職,各自有自己的內部數據,各個模塊如果需要共享數據,可以通過 Vuex,或者在頂層組件的 setup 中傳遞,比如上面的 reload 函數。

我的目錄結構是這樣的。

image.png

整體上非常清爽,工程化的感覺越來越強。

前端架構不同于后端架構。

后端考慮的更多是高可用、高性能、可擴展。前端考慮的問題更多是如何實現高內聚低耦合的分層設計,架構即設計。

良好的架構設計能夠極大的開發效率,降低開發人員的心智負擔。

這也是我們一直以來所關注的問題。     

 

責任編輯:龐桂玉 來源: 前端教程
相關推薦

2020-09-17 07:08:04

TypescriptVue3前端

2021-12-01 08:11:44

Vue3 插件Vue應用

2021-11-30 08:19:43

Vue3 插件Vue應用

2023-11-28 09:03:59

Vue.jsJavaScript

2021-11-19 09:29:25

項目技術開發

2020-09-28 06:45:42

故障復盤修復

2021-09-13 07:58:52

考試算法PAT

2020-09-19 21:15:26

Composition

2021-12-02 05:50:35

Vue3 插件Vue應用

2025-11-19 08:23:42

2025-10-17 07:10:00

前端開發Vue

2022-03-10 11:04:04

Vue3Canvas前端

2021-11-16 08:50:29

Vue3 插件Vue應用

2021-12-08 09:09:33

Vue 3 Computed Vue2

2025-10-17 07:10:00

2025-08-22 09:47:44

2020-10-25 18:43:20

VueTypeScript前端

2024-11-06 10:16:22

2022-06-21 12:09:18

Vue差異

2024-09-05 08:50:11

點贊
收藏

51CTO技術棧公眾號

亚洲欧洲国产专区| 欧美日韩国产a| 国产aⅴ精品一区二区三区黄| 成人av免费| 久久精品视频免费| 久久精品国产99精品国产亚洲性色| 91国拍精品国产粉嫩亚洲一区| 一本久道久久综合中文字幕| 国产女女做受ⅹxx高潮| 欧美日韩免费| 国语自产精品视频在免费| 超碰资源在线| 色激情天天射综合网| 国产真实乱子伦| 美洲天堂一区二卡三卡四卡视频| 国产在线精品播放| 精品在线网站观看| 久久精品视频在线| 亚洲午夜天堂| 日韩午夜精品视频| 黄色片免费在线| 中文字幕在线不卡| 欧美一级片免费播放| 裸体一区二区| 99久久99久久精品国产片| 亚洲品质自拍| 欧美日韩福利在线观看| 国产v综合v| 亚洲精品成人久久久| 91.xxx.高清在线| 欧美色图在线视频| 人成福利视频在线观看| 亚洲日本欧美天堂| 午夜影视日本亚洲欧洲精品| 在线免费看黄| 亚洲私人影院在线观看| 国产伦精品一区二区三区四区视频_| 日韩专区中文字幕一区二区| 51漫画成人app入口| 亚洲777理论| 四虎永久在线| 五月天中文字幕一区二区| 国产91精品捆绑调教| 亚洲青青青在线视频| 四虎免费av| 亚洲精品成人a在线观看| 国产专区视频| 亚洲综合在线第一页| 最新天堂资源在线| 色综合久久66| 国产一二三区在线观看| 日韩视频免费直播| free性欧美| 国产午夜精品一区理论片飘花| 超碰国产一区| 久久久精品电影| 另类图片第一页| 欧美激情国产精品| 久久久免费毛片| 国产成人精品免高潮在线观看| 琪琪久久久久日韩精品| 国产精品视频26uuu| 狠色狠色综合久久| 色一情一区二区三区四区| 久久99久国产精品黄毛片色诱| 91午夜在线观看| 亚洲天堂精品在线观看| 日本成人一区| 精品国产麻豆免费人成网站| 亚洲成人激情社区| 欧美精品久久久久| 色天天综合网| 日本视频精品一区| 91久色porny| 超碰在线公开超碰在线| 69久久99精品久久久久婷婷| 精品视频高清无人区区二区三区| 精品国产乱码久久久久久影片| 国产乱码一区二区三区| 欧洲精品一区二区三区| 亚洲一区二区久久久久久久| 国产不卡免费视频| 青草视频在线免费直播 | 91精品免费看| 国产日韩高清一区二区三区在线| 亚洲欧洲精品在线观看| xnxx国产精品| 嫩草研究院在线观看| 日韩精品视频免费专区在线播放 | 亚洲一级特黄| 特级西西444| 亚洲激情网站免费观看| a视频在线观看免费| 日韩在线视频中文字幕| 久久久久免费av| 午夜一区二区三视频在线观看| 91一区在线观看| 岛国视频免费在线观看| 中文字幕成人精品久久不卡| 91久久国产| 男人日女人下面视频| 色诱亚洲精品久久久久久| 毛片无码国产| 999视频在线观看| 99国产一区二区三精品乱码| 国产在线观看免费网站| 久久久久999| 男人的天堂亚洲在线| 欧美18—20岁hd第一次| 亚洲成年人在线播放| 自拍自偷一区二区三区| 五月天色一区| 午夜精品久久久久久久| 成人网ww555视频免费看| 成人精品水蜜桃| 国产视频一区二区三区在线观看| 69av亚洲| 日韩美女激情视频| 91麻豆成人久久精品二区三区| 国产在线激情视频| 欧美一区在线直播| 成人免费视频app| 日本大胆在线观看| 国产成人手机高清在线观看网站| 国产精品久久久久久久app| 国产91综合网| 午夜av在线播放| 91精品国产综合久久久久久蜜臀| 国产激情视频一区二区三区欧美| 成人动漫在线视频| 亚洲午夜精品网| 偷拍一区二区三区| 国产精品主播直播| 激情久久中文字幕| 三级黄色的网站| 亚洲日本成人女熟在线观看| 国产欧美日本| 美女免费免费看网站| 亚洲精品无码专区在线播放| 亚洲一区三区视频在线观看| 国产精品视频自拍| 亚洲精品久久久一区二区三区 | www.久久色.com| 日韩欧美在线一区| 国产一区日韩欧美| 欧美日韩五区| 一本一道久久久a久久久精品91 | 成年人免费在线视频| 久久久精品国产网站| 黄网站免费久久| 国产精品一卡二卡三卡| 99re热精品| 亚洲日本va在线观看| av男人一区| 一区二区三区入口| 欧美激情a在线| 国产亚洲精品aa午夜观看| 亚洲欧美se| 欧美 另类 交| 亚洲黄色av女优在线观看| 性8sex亚洲区入口| 国产cdts系列另类在线观看| 国语精品中文字幕| 91精品国产91综合久久蜜臀| 香蕉成人久久| 久久天天躁日日躁| 日韩欧美另类中文字幕| 妞干网在线免费视频| 国内精品久久久久影院优| 国产精品灌醉下药二区| 精品视频高潮| 黄色毛片av| 成人国产精品色哟哟| 色一情一乱一乱一91av| 亚洲一区日本| 全亚洲第一av番号网站| 国产无套内射久久久国产| 午夜精品久久久99热福利| 亚洲资源在线观看| 国产精品www.| 日韩经典中文字幕在线观看| 成人精品久久av网站| 亚洲a∨日韩av高清在线观看| 91热福利电影| 91精品国产综合久久香蕉麻豆 | 国产日韩专区在线| 欧美午夜宅男影院| 在线综合欧美| 日韩脚交footjobhd| 国产精品免费入口| 国产精品电影网| 欧美精品在线观看播放| 国产成人精品一区二| 久久中文资源| 天堂а√在线资源在线| 久久国产精品视频在线观看| 国产精品久久久久久久久男| 一本久道久久综合中文字幕| 国产乱码精品一区二区三区av| 欧美交a欧美精品喷水|