最近在用 Vue 3 + TypeScript 写代码项目了。来说说自己的一些感受:本身 JavaScript 是弱语言类型,在简单易上手的同时,也带来了类型混乱的问题。TypeScript 应运而生,可以看成是 JavaScript 的超集,强制语言类型的 “JavaScript”。在写 TypeScript 代码的时候,要先定义类型;用写好的类型来定义变量这样达到强类型的目的。
关于 TypeScript 入门的文章:juejin.cn/post/690313…
关于 Vue 3 的入门文章:juejin.cn/post/690818…
ref reactive 传入类型
interface LeaveInfoForm {
userId: number | undefined,
startDate: string,
endDate: string,
attendanceEventTypeCode: string | undefined,
}
interface DatePartEnum{
code: string
name: string
}
const switchOptions = ref<DatePartEnum[]>([])
const formState = reactive<LeaveInfoForm>({
userId: undefined,
startDate: '',
endDate: '',
attendanceEventTypeCode: undefined,
})
ref reactive 的泛型传入具体的类型后,在使用的时候一方面增强 VS Code 的提示,另一方面利用到了 typescript 的类型检查,避免类型错误。
枚举类型的应用
在项目中,会遇到上述 tab 进行切换,可以使用枚举类型增强代码的可读性
enum EventType{
StatutoryAnnualLeave = 11,
WelfareLeave = 12,
FullPaySickLeave = 13,
TransferVacation = 14,
}
switch (holidaysRecordParams.typeCode) {
case EventType.StatutoryAnnualLeave:
break
...
default:
break
}
编写项目的声明文件
编写自己项目的声明文件主要是将一些全局类型暴露出来,方便统一管理,同时减少重复代码的编写。
- 声明文件的位置:可以在项目任何地方,并且该位置在 tsconfig.json 的 include 或 files 有配置编译路径
- declare 声明的类型、变量是全局的,需要在 .eslintrc.js 中添加进去
defineAsyncComponent 导入动态组件在 keep-alive 缓存组件的问题
const Home = defineAsyncComponent(() => import('../Home/Home.vue'))
Home 组件的 name 已经不是 Home 组件中定义的 homeNoKeep 了,而是 AsyncComponentWrapper 我们知道 keep-alive 的 exclude/include 属性是根据组件的 name 来识别的,所以下面这么写并不能实现 Home 组件的不缓存,原因在于 Home 组件的 name 已经被修改了。
解决方法:手动修改 Home 组件的 name 属性:
const Home: any = defineAsyncComponent(() => import('../Home/Home.vue'))
Home.name = 'homeNoKeep'
这样才能实现 Home 组件的不缓存,满足业务需求
Composition Api
在移动项目中需要实现一个向上滑动隐藏筛选条件,向下滑动出现筛选条件的功能。这里将手指的上滑和下滑事件抽出来:
// useMouseMove.ts
import { reactive, onMounted, onBeforeUnmount } from 'vue'
const MIN_DISTANCE = 10
interface Position{
x: number
y: number
}
type FnArgus = ()=> void
function useMouseMove(onMoveDown: FnArgus = () => {}, onMoveUp: FnArgus = () => {}) {
const startPoint = reactive<Position>({
x: 0,
y: 0,
})
const touchStart = (evt: TouchEvent) => {
startPoint.x = evt.targetTouches[0].pageX
startPoint.y = evt.targetTouches[0].pageY
}
const touchMove = (evt: TouchEvent) => {
const dis: number = evt.targetTouches[0].pageY - startPoint.y
if (dis > 0) {
if (Math.abs(dis) > MIN_DISTANCE) {
onMoveDown()
}
} else if (Math.abs(dis) > MIN_DISTANCE) {
onMoveUp()
}
}
onMounted(() => {
document.addEventListener('touchstart', touchStart, false)
document.addEventListener('touchmove', touchMove, false)
})
// 卸载事件
onBeforeUnmount(() => {
document.removeEventListener('touchstart', touchStart, false)
document.removeEventListener('touchmove', touchMove, false)
})
}
export default useMouseMove
使用方式:
// 手指滑动事件
import useMouseMove from '~/hooks/useMouseMove'
setup(){
const onMouseDown = () => {
}
const onMouseUp = () => {
}
useMouseMove(onMouseDown, onMouseUp)
}
Provide 和 Inject 使用
Provide 和 Inject 的使用和 Vuex 状态管理还是不太一样的。Vuex 管理的数据在页面的各个组件都可以使用,但是 Provide 和 Inject 有层级的概念。只有在父组件 Provide 数据,在子组件中才可以 Inject 获取到。如果两个组件没有父子或祖孙(即上下级)的关系的话,Provide 是失效的。
在最顶层组件使用 Provide 注册数据,可实现同 Vuex 类似的数据管理功能。cloud.tencent.com/developer/a… 较为清晰的介绍了 Provide 和 Inject (TypeScript)版的实现数据管理的方式。由于项目中用到的数据并不需要在最顶层去管理,所以对上述方法进行了一点点修改。如下:
// src/context/dates.ts
import { provide, inject, computed, Ref } from '@vue/composition-api';
import { DateItem } from '~/components/Calendar/Calendar.ts'
export type DatesContext = {
dates: Ref<DateItem[]>
datesList: Ref<DateItem[]>
setDates: (value: DateItem[]) => void
updateDate: (value: DateItem) => void
}
export const DateSymbol = 'DATE_SYMBOL'
// Provide 数据
export const useDateListProvide = (dates: Ref<DateItem[]>)=>{
// dates 所有日期
// 只显示当月日期
const datesList = computed(() => dates.value.filter(item => item.type === 'current'))
const setDates = (value: DateItem[]) => {
dates.value = value
}
const updateDate = (value: DateItem) => {
dates.value.every(item => {
if (item.date === value.date) {
item.id = value.id
item.tagName = value.tagName
item.selVisible = value.selVisible
item.popVisible = value.popVisible
return false
}
return true
})
}
provide<DatesContext>(DateSymbol, {
dates,
datesList,
setDates,
updateDate,
})
}
// Inject 获取
export const useDateListInject = ()=>{
const dateContext = inject<DatesContext>(DateSymbol);
if (!dateContext) {
throw new Error(`useDateListInject must be used after useDateListProvide`);
}
return dateContext;
}
先记录这么多吧,欢迎大家留言补充。