日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Vue3+TypeScript復(fù)盤總結(jié)

 [[401627]]

背景

近期在研發(fā)一套物聯(lián)網(wǎng)設(shè)備管理系統(tǒng),其主要用途是將公司旗下所負(fù)責(zé)智能園區(qū)中的硬件設(shè)備通過物聯(lián)網(wǎng)云平臺(tái)來進(jìn)行綜合管控。

創(chuàng)新互聯(lián)公司專注于憑祥網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供憑祥營銷型網(wǎng)站建設(shè),憑祥網(wǎng)站制作、憑祥網(wǎng)頁設(shè)計(jì)、憑祥網(wǎng)站官網(wǎng)定制、小程序制作服務(wù),打造憑祥網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供憑祥網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

由于這個(gè)產(chǎn)品是實(shí)驗(yàn)性項(xiàng)目,沒有合同,沒有明確收益。所以能夠拿到的資源非常少。

產(chǎn)品具體的負(fù)責(zé)人,只有 1.5 人,幾乎只有我自己。所以既要擔(dān)任產(chǎn)品經(jīng)理,又要擔(dān)任開發(fā)者,還要擔(dān)任運(yùn)維。不過從技術(shù)角度而言,選型可以更加自由。

整個(gè)系統(tǒng)在架構(gòu)上設(shè)計(jì)分為 4 層。自底向上分別是設(shè)備硬件、設(shè)備接入網(wǎng)關(guān)、物聯(lián)網(wǎng)平臺(tái)、設(shè)備管理系統(tǒng)。除去設(shè)備硬件,其它 3 層都屬于軟件范疇。

這篇文章主要記錄一下我在開發(fā)最后一層-設(shè)備管理系統(tǒng)的前端開發(fā)過程中的一些總結(jié)。

前端采用 Vite2.x、Vue3.x、Vuex4.x、VueRouter4.x、TypeScript、Element-Plus 進(jìn)行開發(fā)??梢钥吹剑@些框架和庫所采用的版本是比較激進(jìn)的,大部分都是最新版本,以及 rc 和 beta 版本。不過從項(xiàng)目開始到寫這篇總結(jié),其中的一些庫的版本已經(jīng)不是最新的了,不得不感慨前端技術(shù)變化之快。

一個(gè)組件的思考

首先來看一個(gè)組件。

水波紋.gif

這是一個(gè)具有波紋效果、用來表示當(dāng)前 websocket 連接狀態(tài)的小圓點(diǎn)。是一個(gè)非常簡單的純展示組件。樣式效果使用 css3 變量、動(dòng)畫、和 before、after 偽類實(shí)現(xiàn)。

props 設(shè)計(jì)非常簡單,只有一個(gè) type 字段。根據(jù) type 字段的不同,波紋的顏色也不同。

思路有了,下面是實(shí)現(xiàn)上的一些細(xì)節(jié)性問題。

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

根據(jù)設(shè)計(jì),type 字段應(yīng)該是一個(gè)枚舉值,不應(yīng)該由調(diào)用方隨意設(shè)置。

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

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

TypeScript 中聲明類型的關(guān)鍵字有兩個(gè),interface 和 type,在聲明 key 不確定類型的字段時(shí)稍有不同。

使用 type 進(jìn)行聲明: 

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

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

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

因?yàn)?interface 的索引只能是基礎(chǔ)類型,類型別名也不可以。而 type 的索引可以是復(fù)合類型。

Vue 3 如何獲取元素實(shí)例?

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

新的用法是:

給元素添加 ref 屬性。

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

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

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

第一步: 

 
 
 
 
 

第二步: 

 
 
 
 
  1. const point = ref(null); 

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

第三步: 

 
 
 
 
  1. return { point }; 

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

第四步: 

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

如何操作偽類?

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

比如這是一個(gè)偽類: 

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

它依賴了 afterBg 變量。

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

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

API 的變化

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

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

比如抽屜組件、擬態(tài)框組件等。

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

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

比如父組件傳遞: 

 
 
 
 
  1.  

子組件: 

 
 
 
 
  1.   
  2.  

Vue3 中 watch 用法的變化

watch 變得更加簡單。 

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

當(dāng) source 變化時(shí)自動(dòng)執(zhí)行 watch 第二個(gè)參數(shù)所傳入的函數(shù)。

Vue3 中 computed 用法的變化

computed 也變得更加簡單。 

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

computed 返回的變量是一個(gè)響應(yīng)式對象。

Vue3 中組件循環(huán)自身的技巧

這是一種開發(fā)組件的技巧。

假設(shè)你有一個(gè)不確定深度的樹狀結(jié)構(gòu)數(shù)據(jù)。 

 
 
 
 
  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;  

你需要實(shí)現(xiàn)一種樹狀組件來渲染它們。這時(shí)就需要用到這種技巧。 

 
 
 
 
  1.   
  2.  

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

一些坑

Vuex:慎用 Map

在 Vuex 中,我設(shè)計(jì)了一個(gè)數(shù)據(jù)結(jié)構(gòu)用于存儲(chǔ)模塊(業(yè)務(wù)概念)不同的狀態(tài)。 

 
 
 
 
  1. type Code = number;  
  2. export type ModuleState = Map

但是我發(fā)現(xiàn)一個(gè)問題,當(dāng)我修改 Map 中某一個(gè) value 中的屬性時(shí),不會(huì)觸發(fā) Vuex 的監(jiān)聽。

所以我只好將數(shù)據(jù)結(jié)構(gòu)修改為對象的形式。 

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

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

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

除此之外,Map 還存在另外一個(gè)問題。

當(dāng)一個(gè) Map 類型的 Proxy 對象作為參數(shù)被傳遞時(shí),是無法使用 get、set、clear 等 Map 方法的,但是 TypeScript 會(huì)提示這些方法可用。如果使用了這些方法,會(huì)得到一個(gè) Uncaught TypeError。

如果使用 Object 則不會(huì)產(chǎn)生這個(gè)問題。

WebSocket 發(fā)生異常無法被 try catch 監(jiān)聽

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

有些時(shí)候,onerror 和 onclose 會(huì)連續(xù)執(zhí)行,比如觸發(fā) onerror,導(dǎo)致連接關(guān)閉,就會(huì)緊接著觸發(fā) onclose。

Vue Devtools

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

下載地址:

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

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

ESbuild 安裝依賴

在使用 vite 啟動(dòng)服務(wù)的同時(shí)安裝依賴,非常容易碰到一個(gè)錯(cuò)誤。

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

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

Vite 在 Chrome 中調(diào)試的問題

系統(tǒng)中有一些移動(dòng)頁面,需要嵌入在 App 中使用。

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

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

image.png

所以我選擇使用 Chrome 調(diào)試,chrome://inspect/#devices

但是在調(diào)試過程中我發(fā)現(xiàn) Chrome 調(diào)試工具里面竟然運(yùn)行的是 TS 源碼,TS 的語法直接被認(rèn)為語法錯(cuò)誤。(我是使用 Vite 啟動(dòng)的開發(fā)服務(wù)。)

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

WebSocket

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

設(shè)備管理系統(tǒng)的核心概念是設(shè)備,設(shè)備會(huì)有很多屬性,在硬件上也被稱作數(shù)據(jù)點(diǎn)。這些屬性會(huì)經(jīng)歷非常長的鏈路傳輸?shù)接脩艚缑嫔?。整體流程大概是:硬件通過 tcp 協(xié)議上傳到接入網(wǎng)關(guān),接入網(wǎng)關(guān)處理后再通過 mqtt 協(xié)議上傳到物聯(lián)網(wǎng)平臺(tái),物聯(lián)網(wǎng)平臺(tái)再經(jīng)過規(guī)則引擎處理,通過 webhook restful 的形式發(fā)送到業(yè)務(wù)系統(tǒng),業(yè)務(wù)系統(tǒng)再通過 websocket 推送到前端。

雖然數(shù)據(jù)通過層層編解碼、不同的協(xié)議繞了非常遠(yuǎn)的距離呈現(xiàn)到用戶面前,但是前端只需要關(guān)心 websocket 就足夠了。

WebSocket 重連

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

我的做法是增加一個(gè)變量來控制重連次數(shù)。

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

 
 
 
 
  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 連接活動(dòng)日志

系統(tǒng)是設(shè)計(jì)成 7*24 小時(shí)不間斷運(yùn)行。所以 websocket 很容易受到一些網(wǎng)絡(luò)因素或者其它因素的影響發(fā)生斷開,重連是一項(xiàng)非常重要的功能,同時(shí)還應(yīng)該具備重連日志功能。

在用戶的不同環(huán)境中,排查 WebSocket 的連接狀態(tài)很麻煩,添加一個(gè)連接日志功能是比較不錯(cuò)的方案,這樣可以很好的看到不同時(shí)間的連接情況。

image.png

需要注意,這些日志是存儲(chǔ)在用戶的瀏覽器內(nèi)存中的,需要設(shè)置上限,到達(dá)上限要自動(dòng)清除早期日志。

WebSocket 鑒權(quán)

websocket 的鑒權(quán)是很多人容易忽視的一個(gè)點(diǎn)。

我在系統(tǒng)設(shè)計(jì)中,restful API 的鑒權(quán)是通過在 request header 上附帶 Authorization 字段,設(shè)置生成的 JWT 來實(shí)現(xiàn)的。

websocket 無法設(shè)置 header,但是可以設(shè)置 query,實(shí)現(xiàn)思路類似 restful 的認(rèn)證設(shè)計(jì)。

關(guān)于 ws 鑒權(quán)的過期、續(xù)期、權(quán)限等問題,和 restful 保持一致即可。

script setup:更加清爽的 API

script setup 至今仍是一個(gè)實(shí)驗(yàn)性特性,但它確實(shí)非常清爽。

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

 
 
 
 
  1.  

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

 
 
 
 
  1.  

在 sciprt 標(biāo)簽中的頂層變量、函數(shù)都會(huì) return 出去。

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

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

使用組件

直接導(dǎo)入組件后,vue 會(huì)自動(dòng)識(shí)別,無需使用 component 掛載。 

 
 
 
 
  1.  

使用生命周期和監(jiān)聽計(jì)算函數(shù)

和標(biāo)準(zhǔn)寫法基本無差異。 

 
 
 
 
  1.  

使用 props 和 context

由于 setup 被提升到 script 標(biāo)簽上了,自然也就沒辦法接收 props 和 context 這兩個(gè)參數(shù)。

所以 vue 提供了 defineProps、defineEmit、useContext 函數(shù)。

defineProps

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

 
 
 
 
  1.  

defineEmit

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

 
 
 
 
  1.  

emit 的第一個(gè)參數(shù)是事件名稱,后面支持傳遞不定個(gè)數(shù)的參數(shù)。

useContext

useContext 是一個(gè) hook 函數(shù),返回 context 對象。 

 
 
 
 
  1. const ctx = useContext() 

原理

原理相當(dāng)簡單。增加了一層編譯過程,將 script setup 編譯成標(biāo)準(zhǔn)模式的代碼。

但是實(shí)現(xiàn)上有非常多的細(xì)節(jié),所以導(dǎo)致至今仍未推出正式版。

Vue3 Composition 所帶來的模塊化開發(fā)方式

這套技術(shù)棧帶給我最深的感受還是開發(fā)方式上的變化。

在 Vue2 的開發(fā)中,Options API 在面對業(yè)務(wù)邏輯復(fù)雜的頁面時(shí)非常吃力。當(dāng)邏輯長達(dá)千行時(shí),追蹤一個(gè)變量的變化是一件非常頭痛的事情。

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

這項(xiàng)目中所有的頁面,我都使用 hooks 的方式開發(fā)。

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

 
 
 
 
  1.  

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

我的目錄結(jié)構(gòu)是這樣的。

image.png

整體上非常清爽,工程化的感覺越來越強(qiáng)。

前端架構(gòu)不同于后端架構(gòu)。

后端考慮的更多是高可用、高性能、可擴(kuò)展。前端考慮的問題更多是如何實(shí)現(xiàn)高內(nèi)聚低耦合的分層設(shè)計(jì),架構(gòu)即設(shè)計(jì)。

良好的架構(gòu)設(shè)計(jì)能夠極大的開發(fā)效率,降低開發(fā)人員的心智負(fù)擔(dān)。

這也是我們一直以來所關(guān)注的問題。   


當(dāng)前標(biāo)題:Vue3+TypeScript復(fù)盤總結(jié)
分享網(wǎng)址:http://m.5511xx.com/article/djochdi.html