설계에 따라 유형 필드는 열거형 값이어야 하며 호출자가 임의로 설정해서는 안 됩니다.
다음은 총 6개 필드로 구성된 Type의 열거형 선언입니다.
enum Type { primary = "primary", success = "success", warning = "warning", warn = "warn", // warning alias danger = "danger", info = "info", }
TypeScript에는 인터페이스와 유형이라는 두 가지 유형 선언 키워드가 있습니다. 정의되지 않은 키 유형으로 필드를 선언할 때 약간의 차이가 있습니다.
유형을 사용하여 선언:
type ColorConfig = { [key in Type]: Colors; };
인터페이스를 사용하지만 다음과 같이만 가능합니다.
interface ColorConfig { [key: string]: Colors; }
인터페이스의 인덱스는 기본 유형만 가능하고 유형 별칭은 사용할 수 없습니다. 유형의 인덱스는 복합 유형일 수 있습니다.
vue3에서는 컴포넌트의 로직을 setup 함수에 배치할 수 있지만 setup에는 더 이상 this가 없으므로 vue2의 this.$refs 사용법을 vue3에서는 사용할 수 없습니다.
새로운 사용법은 다음과 같습니다.
요소에 ref 속성을 추가합니다.
설정의 요소 참조와 동일한 이름을 가진 변수를 선언하세요.
설정의 반환 개체에 ref 변수를 동일한 이름의 속성으로 반환합니다.
요소 인스턴스인 onMounted 라이프 사이클의 ref 변수에 액세스합니다.
1단계:
<div></div>
2단계:
const point = ref<htmldivelement>(null);</htmldivelement>
HTMLDivElement에 유형을 채워야 유형 추론을 즐길 수 있다는 점에 유의하세요.
3단계:
return { point };
이 단계는 필수입니다. 반환된 객체에 동일한 이름의 이 속성이 포함되어 있지 않으면 onMounted에서 액세스된 ref 객체는 null이 됩니다.
4단계:
onMounted(() => { if (point?.value) { // logic } });
JavaScript는 의사 클래스 요소를 가져올 수 없지만 다르게 생각할 수 있습니다. 의사 클래스 스타일은 CSS 변수를 참조한 후 js를 통해 CSS 변수를 제어하여 의사 클래스를 간접적으로 작동시키는 효과를 완성합니다.
예를 들어 다음은 의사 클래스입니다.
.point-flicker:after { background-color: var(--afterBg); }
afterBg 변수에 따라 다릅니다.
콘텐츠를 수정해야 하는 경우 js만 사용하여 afterBg의 콘텐츠를 작동하면 됩니다.
point.value.style.setProperty("--bg", colorConfig[props.type].bg);
구성 요소가 상위 구성 요소에 의해 자체적으로 전달된 Prop을 수정해야 하는 상황은 흔하지 않습니다.
서랍 구성 요소, 모방 상자 구성 요소 등
vue2의 일반적인 사용법은 sync와 v-model입니다.
vue3에서는 v-model:xxx=""만 권장됩니다.
예를 들어 상위 구성 요소는 다음을 전달합니다.
<ws-log></ws-log>
하위 구성 요소:
<template> <div> ... </div> </template> <script> // ... props: { visible: { type: Boolean, }, }, </script>
watch가 더 간단해집니다.
import { watch } from "vue"; watch(source, (currentValue, oldValue) => { // logic });
소스가 변경되면 watch의 두 번째 매개변수에 전달된 함수가 자동으로 실행됩니다.
computed의 계산 사용량 변경도 더 간단해졌습니다.
import { computed } from "vue" const v = computed(() => { return x });
computed에서 반환된 변수는 반응형 개체입니다.
컴포넌트를 개발하는 기술입니다.
깊이가 불확실한 트리 구조의 데이터가 있다고 가정해 보겠습니다.
{ "label": "root", "children": [ { "label": "a", "children": [ { "label": "a1", "children": [] }, { "label": "a2", "children": [] } ] } ] }
타입은 다음과 같이 정의됩니다.
export interface Menu { id: string; label: string; children: Menu | null; }
렌더링하려면 트리 구성 요소를 구현해야 합니다. 이 기술이 유용한 곳입니다.
<template> <div>{{ menu.label }}</div> <menu></menu> </template> <script> import { defineComponent } from "vue"; export default defineComponent({ name: "Menu", props: { menu: { type: Object, }, }, }); </script>
컴포넌트 이름은 컴포넌트에서 선언하지 않고 그 자체로 직접 사용할 수 있습니다.
Vuex에서는 다양한 상태의 모듈(비즈니스 개념)을 저장하는 데이터 구조를 설계했습니다.
type Code = number; export type ModuleState = Map<code>;</code>
그런데 맵의 값에 있는 속성을 수정하면 Vuex의 모니터링이 실행되지 않는 문제를 발견했습니다.
그래서 데이터 구조를 객체 형태로 수정해야 했습니다.
export type ModuleState = { [key in Code]: StateProperty };
ts에서는 인덱스에 유형 별칭을 사용할 수 없지만 다음과 같이 작성할 수 있습니다.
type Code = number; export type ModuleState = { [key in Code]: StateProperty };
게다가 Map에는 또 다른 문제가 있습니다.
Map 유형의 Proxy 객체가 매개변수로 전달되면 get, set,clear 등의 Map 메서드를 사용할 수 없지만 TypeScript에서는 이러한 메서드를 사용할 수 있다는 메시지를 표시합니다. 이러한 방법을 사용하면 Uncaught TypeError가 발생합니다.
Object를 사용하시면 이런 문제는 발생하지 않습니다.
ws 예외는 onerror 및 onclose 이벤트에서만 처리할 수 있으며 try catch는 이를 포착할 수 없습니다.
때때로 onerror 및 onclose가 연속적으로 실행됩니다. 예를 들어 onerror가 트리거되어 연결이 닫히면 onclose가 즉시 트리거됩니다.
vue devtools는 현재 Vue3를 지원하지 못하지만, vue devtools는 개발에 거의 없어서는 안 될 도구입니다. 현재 vue devtools 베타 버전을 사용할 수 있으나 몇 가지 버그가 있습니다.
사용법은 매우 간단합니다. 설치 후 브라우저를 다시 시작하면 됩니다. vue.config.devtools = true로 설정할 필요가 없습니다. devtools 속성은 vue3의 vue.config 인스턴스에 존재하지 않습니다.
vite를 사용하여 서비스를 시작하는 동안 종속성을 설치할 때 오류가 발생하기 매우 쉽습니다.
Error: EBUSY: resource busy or locked, open 'E:\gxt\property-relay-fed\node_modules\esbuild\esbuild.exe'
이 문제의 원인은 vite가 의존하는 컴파일 도구 esbuild.exe가 점유되어 있기 때문입니다. 해결 방법은 매우 간단합니다. 즉, vite를 중지하고 종속성을 설치한 다음 vite를 다시 시작하는 것입니다.
시스템에 앱에 삽입해야 하는 일부 모바일 페이지가 있습니다.
常见的调试 WebView 的方法有两种,一种简单的方式是使用腾讯开源的 vcosnole,另一种麻烦一些的调试方式是使用 Chrome 的 DevTools。
但是 vconsole 并没有想象中那么好用。
所以我选择使用 Chrome 调试,chrome://inspect/#devices
但是在调试过程中我发现 Chrome 调试工具里面竟然运行的是 TS 源码,TS 的语法直接被认为语法错误。(我是使用 Vite 启动的开发服务。)
解决方案很简单,但挺 Low。先使用 vite build 把 TS 代码编译成 JS,再使用 vite preview 启动服务。
websocket 和 Vue3 没什么关系,但是在这里简单提一下。
设备管理系统的核心概念是设备,设备会有很多属性,在硬件上也被称作数据点。这些属性会经历非常长的链路传输到用户界面上。整体流程大概是:硬件通过 tcp 协议上传到接入网关,接入网关处理后再通过 mqtt 协议上传到物联网平台,物联网平台再经过规则引擎处理,通过 webhook restful 的形式发送到业务系统,业务系统再通过 websocket 推送到前端。
虽然数据通过层层编解码、不同的协议绕了非常远的距离呈现到用户面前,但是前端只需要关心 websocket 就足够了。
在做重连时,需要注意 onerror 和 onclose 连续执行的问题,通常是使用类似防抖的方法来解决。
我的做法是增加一个变量来控制重连次数。
let connecting = false; // 断开连接后,先触发 onerror,再触发 onclose,主要用于防止重复触发
conn(); function conn() { connecting = false; if (ctx.state.stateWS.instance && ctx.state.stateWS.instance.close) { ctx.state.stateWS.instance.close(); } const url = ctx.state.stateWS.url + "?Authorization=" + getAuthtication(); ctx.state.stateWS.instance = new WebSocket(url); ctx.state.stateWS.instance.onopen = () => { ctx.commit(ActionType.SUCCESS); }; ctx.state.stateWS.instance.onclose = () => { if (connecting) return; ctx.commit(ActionType.CLOSE); setTimeout(() => { conn(); }, 10 * 1000); connecting = true; }; ctx.state.stateWS.instance.onerror = () => { if (connecting) return; ctx.commit(ActionType.ERROR); setTimeout(() => { conn(); }, 10 * 1000); connecting = true; }; ctx.state.stateWS.instance.onmessage = function ( this: WebSocket, ev: MessageEvent ) { // logic } catch (e) { console.log("e:", e); } }; }
系统是设计成 7*24 小时不间断运行。所以 websocket 很容易受到一些网络因素或者其它因素的影响发生断开,重连是一项非常重要的功能,同时还应该具备重连日志功能。
在用户的不同环境中,排查 WebSocket 的连接状态很麻烦,添加一个连接日志功能是比较不错的方案,这样可以很好的看到不同时间的连接情况。
image.png
需要注意,这些日志是存储在用户的浏览器内存中的,需要设置上限,到达上限要自动清除早期日志。
websocket 的鉴权是很多人容易忽视的一个点。
我在系统设计中,restful API 的鉴权是通过在 request header 上附带 Authorization 字段,设置生成的 JWT 来实现的。
websocket 无法设置 header,但是可以设置 query,实现思路类似 restful 的认证设计。
关于 ws 鉴权的过期、续期、权限等问题,和 restful 保持一致即可。
script setup 至今仍是一个实验性特性,但它确实非常清爽。
单文件组件的 setup 常规用法像下面这样:
<script> import { defineComponent } from 'vue' export default defineComponent({ setup () { return {} } }) </script>
使用 script setup 后,代码变成了下面这样:
<script> </script>
在 sciprt 标签中的顶层变量、函数都会 return 出去。
在这种模式下,减少了大量代码,可以提高开发效率、降低心智负担。
但这时也存在几个问题,比如在 script setup 中怎么使用生命周期和 watch/computed 函数?怎么使用组件?怎么获取 props 和 context?
直接导入组件后,vue 会自动识别,无需使用 component 挂载。
<script> import C from "component" </script>
和标准写法基本无差异。
<script> import { watch, computed, onMounted } from "vue" </script>
由于 setup 被提升到 script 标签上了,自然也就没办法接收 props 和 context 这两个参数。
所以 vue 提供了 defineProps、defineEmit、useContext 函数。
defineProps
defineProps 的用法和 OptionsAPI 中的 props 用法几乎一致。
<script> import { defineProps } from "vue"; interface Props { moduleID: string; } const props = defineProps<Props>(["moduleID"]); console.log(props.moduleID); </script>
defineEmit
defineEmit 的用法和 OptionsAPI 中的 emit 用法也几乎一致。
<script> import { defineEmit } from "vue"; const emit = defineEmit(["select"]); console.log(emit("select")); </script>
emit 的第一个参数是事件名称,后面支持传递不定个数的参数。
useContext
useContext 是一个 hook 函数,返回 context 对象。
const ctx = useContext()
原理相当简单。增加了一层编译过程,将 script setup 编译成标准模式的代码。
但是实现上有非常多的细节,所以导致至今仍未推出正式版。
这套技术栈带给我最深的感受还是开发方式上的变化。
在 Vue2 的开发中,Options API 在面对业务逻辑复杂的页面时非常吃力。当逻辑长达千行时,追踪一个变量的变化是一件非常头痛的事情。
但是有了 Composition API 后,这将不再是问题,它带来了一种全新的开发方式,虽然有种 React 的感觉,但这相比之前已经非常棒了!
这项目中所有的页面,我都使用 hooks 的方式开发。
在设备模块中,我的 js 代码是这样的。
<script> import { defineComponent, toRefs } from "vue"; import { useDeviceCreate } from "./create"; import { useDeviceQuery } from "./query"; import { useDeviceDelete } from "./delete"; import { useUnbind } from "./unbind"; import { useBind } from "./bind"; import { useDeviceEdit } from "./edit"; import { useState } from "./state"; import { useAssign } from "./assign"; export default defineComponent({ setup() { const queryObj = useDeviceQuery(); const { query, devices } = queryObj; const reload = query; return { ...toRefs(useDeviceCreate(reload)), ...toRefs(queryObj), ...toRefs(useDeviceDelete(reload)), ...toRefs(useUnbind(reload)), ...toRefs(useBind(reload)), ...toRefs(useDeviceEdit(reload)), ...toRefs(useState(devices)), ...toRefs(useAssign()), }; }, }); </script>
每个模块各司其职,各自有自己的内部数据,各个模块如果需要共享数据,可以通过 Vuex,或者在顶层组件的 setup 中传递,比如上面的 reload 函数。
我的目录结构是这样的。
위 내용은 Vue3에서 TypeScript를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!