ts定义数组类型_Vue3+TS,写一个逼格满满的项目

本文手把手教你使用Vue3和TypeScript创建项目,涵盖版本升级、路由定义、数据结构声明、状态管理和异步数据获取。重点讲解了如何在TS中定义数组类型,以及在Vuex中管理状态,提升开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

382b0efc536435c28f2adb30e2e41497.gif戳蓝字"Web前端严选"关注我们哦

Vue3和TS的概念学了那么多,有没有心动手痒想实践一下呢?

本次千字长文将手把手介绍如何使用Vue3+TS写一个基础项目,有了主要框架概念之后,后续对于应用的开发能更加地得心应手。

1.版本升级

大家之前在做Vue开发时想必都是2.x版本,在这个项目之前要先检查Vue处于哪个版本,本次示例需要4.5.3版本及以上:

vue --version
//@vue/cli 4.5.3

版本升级之后即可创建项目,使用的命令还同之前一样:

vue create project-name

下面展示的是创建示例项目过程中各个步骤的选择内容:

Vue CLI v4.5.8
? Please pick a preset: (Use arrow keys)
  Default ([Vue 2] babel, eslint)  //默认,vue2版本
  Default (Vue 3 Preview) ([Vue 3] babel, eslint) //默认,vue3版本
> Manually select features    //手动选择配置
? Check the features needed for your project: (Press  to select,  to toggle all,  to invert selection)
>(*) Choose Vue version  //选择vue版本
 (*) Babel
 (*) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
 (*) Vuex
 (*) CSS Pre-processors
 ( ) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing    
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
  2.x
> 3.x (Preview)  
? Use class-style component syntax? (y/N)  N
//是否使用类样式组件
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) N
//不需要babel去配合TS
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y 
//使用路由的历史模式
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
> Sass/SCSS (with dart-sass)
  Sass/SCSS (with node-sass)
  Less
  Stylus 
//选择CSS预处理器
? Where do you prefer placing config for Babel, ESLint, etc.?
> In dedicated config files   生成独立的配置文件
  In package.json 
? Save this as a preset for future projects? (y/N) N 
//保存这个配置以备将来使用

项目创建完成之后,为了配合TS以及异步请求的使用,还需要自己增加几个文件,相应的目录结构展示如下:

─public
├─server
└─src
    ├─api
    │  └─index.ts
    │  └─home.ts
    ├─assets
    ├─components
    ├─router
    │  └─index.ts
    ├─store
    │  └─modules
    │     └─home.ts
    │  └─action-types.ts
    │  └─index.ts
    ├─typings
    │  └─home.ts
    │  └─index.ts
    └─views
        ├─cart
        ├─home
        │  └─index.vue
        │  └─homeHeader.vue
        │  └─homeSwiper.vue
        └─mine

在src目录下有个shims-vue.d.ts文件,它是一个垫片文件,用于声明**.vue文件**是这样的一个组件:

declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent
  export default component
}

1.1引入Vant

import Vant from 'vant'
import 'vant/lib/index.css'

createApp(App).use(store).use(router).use(Vant).mount('#app')

3.x版本开始使用函数式编程,因此可以使用链式调用。

1.2引入axios

对请求进行简单封装,axios/index.ts:

import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'

axios.defaults.baseURL = 'http://localhost:3001/'

axios.interceptors.request.use((config:AxiosRequestConfig) => {
    return config;
})

axios.interceptors.response.use((response:AxiosResponse) => {
    if(response.data.err == 1){
        return Promise.reject(response.data.data);
    }
    return response.data.data;
},err => {
    return Promise.reject(err);
})

export default axios

2.定义路由

router/index.ts中:

const routes: Array = [];

规定了数组元素类型是RouteRecordRaw,它可以在定义路由时进行友善地提示。

其他路由的处理,与之前没有太大差异:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array = [
  {path: '/',name: 'Home',component: () => import('../views/home/index.vue')
  },
  {path: '/cart',name: 'Cart',component: () => import('../views/cart/index.vue')
  },{path: '/mine',name: 'Mine',component: () => import('../views/mine/index.vue')
  }
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),
  routes
})export default router

定义完路由后,就可以在App.vue中配置底部导航:

首页购物车我的

3.定义数据结构

基本路由定义好后,下面开始写数据结构;一般的开发思路是先定义好所需的数据结构之后,使用时才会更方便,思路也会清晰。

3.1.声明类别

在typings/home.ts中添加如下:

CATEGORY_TYPES有5个值:全部,鞋子,袜子,衬衫,裤子

export enum CATEGORY_TYPES {
ALL,
SHOES,
SOCKES,
SHIRT,
PANTS
}

枚举类型的值默认从0开始,自动升序;也可手动指定第一个枚举类型的值,而后在此基础上自动加一。

然后声明home文件的接口IHomeState,规定包含当前分类属性,并且必须是CATEGORY_TYPES类型:

export interface IHomeState {
currentCategory: CATEGORY_TYPES
}

在store/moudles/home.ts中声明home的状态:

const state:IHomeState = {
    currentCategory: CATEGORY_TYPES.ALL,
};
//action-types.ts中添加状态名称
export const SET_CATEGORY = 'SET_CATEGORY'
const home:Module = {namespaced: true,
    state,mutations: {
        [Types.SET_CATEGORY](state,payload:CATEGORY_TYPES){
            state.currentCategory = payload;
        }
    },actions: {}
}export default home;

这里定义的home属于Vuex中的Module类型,需要传递两个泛型S和R,分别是当前模块的状态和根状态。当前状态即IHomeState,根状态则需要在index.ts中声明全局类型接口:

export interface IGlobalState{
  home: IHomeState,
      //这里后续增加其他状态模块
}

后续将在这里管理所有状态,既用于使用时的代码提示,也用于全局状态管理。

3.2状态操作

状态添加完毕后,到home/index.vue中进行状态的操作:



为了获得传递给 setup() 参数的类型推断,需要使用 defineComponent。

这里定义了一个useCategory方法,专门用来处理切换状态。方法中使用computed获取currentCategory值;如果不用computed,那么这里就是一个死值,只是取到值放在这里;只有使用计算属性才能保证状态变了,计算的新值也变了,并响应到视图中去。

computed具有缓存属性,只有当依赖的值发生变化时,才会重新计算一次wathcer.value

setCurrentCategory作为子组件的发射事件,用于调用状态管理修改currentCategory,这里再回到homeHeader组件中:


//....


setup方法此时接收了两个参数:props和context,其中props对象是响应式的,注意不要结构props对象,这样会令它失去响应性;context是一个上下文对象,类似2.x中的this属性,并选择性地暴露了一些property。

首先仍是props接收父组件的属性传值,但是这里需要注意的是在type声明时,使用as断言PropType为CATEGORY_TYPES类型;

其次,使用reactive将数据处理为响应式对象,使用toRefs将一个响应式对象转换成普通对象,在 setup 中返回的 ref 在模板中会自动解开,不需要写 .value

emits用于注册数组发射事件的方法名,在真正要调用的时候可以使用代码提示的便捷形式提交;

modelValue目前属于一个内部编译的绑定属性方式,这里用于value的值绑定,具体编译可看参考文档[2]。

到这一步,就可以实现父子组件通信,并且通过选择当前种类,改变页面状态值。

3.3异步数据获取

接下来趁热打铁,实现动态获取轮播数据:

首页注册homeSwiper组件,这里不再表述,直接看组件内部逻辑



这里不建议将async写到setup处,但是为了演示先放在这里。

useStore直接使用vuex暴露出来的方法,相较于之前都绑定在this上更加方便。

同上一个组件,这里也利用computed获取要轮播的图片数据,当数据为空时则发起异步dispatch。

首先定义数据类型typings/home.ts:

export interface ISlider{
url:string
}
export interface IHomeState {
currentCategory: CATEGORY_TYPES,
sliders: ISlider[]
}

增加状态操作名称action-types.ts:

export const SET_SLIDER_LIST = 'SET_SLIDER_LIST'

在原基础上增加slider类型,定义home状态中的sliders为ISlider类型数组。

然后到modules/home.ts中增加状态操作:

const state:IHomeState = {
    currentCategory: CATEGORY_TYPES.ALL,
    sliders: []
}
const home:Module = {namespaced: true,
    state,mutations: {
        [Types.SET_CATEGORY](state,payload:CATEGORY_TYPES){
            state.currentCategory = payload;
        },
        [Types.SET_SLIDER_LIST](state,payload:ISlider[]){
            state.sliders = payload;
        }
    },actions: {async [Types.SET_SLIDER_LIST]({commit}){let sliders = await getSliders();
            commit(Types.SET_SLIDER_LIST,sliders)
        }
    }
}

在action中异步获取sliders数据,然后提交到Types.SET_SLIDER_LIST中请求接口获取数据,然后通过提交数据状态,使依赖的数据发生改变,通过computed获取的值将重新获取,这时轮播图将显示在页面中。

3.4 小节

在以上过程中,都没有带入import的内容代码,如果你正确使用了TS这时就能发现,通过代码提示的功能,import都能自动导入所需内容,这也是将来TS的一大特点。不仅具备类型校验,更能提升开发过程体验。

4.组件插槽

loading...

这里展示的是在Vue3中提供了一种组件异步渲染插槽的使用方式:在Suspense组件中,当获取数据后默认执行default部分的内容,为获取数据时执行fallback部分的内容,在异步处理上更加简单。

但是,细心的你将会发现控制台输出了这么一段话:

is an experimental feature and its API will likely change.

这是一个实验性语法,将来的API是否能继续保留也未可知,这也为Vue3的发布增加了更多的期待。

5.参考文档

1.Vue 组合式 API https://composition-api.vuejs.org/zh/api.html

2.Vue 3 Template Exploer https://vue-next-template-explorer.netlify.app/#%7B%22src%22%3A%22%3Cdiv%3EHello%20World!%3C%2Fdiv%3E%22%2C%22options%22%3A%7B%22mode%22%3A%22module%22%2C%22prefixIdentifiers%22%3Afalse%2C%22optimizeImports%22%3Afalse%2C%22hoistStatic%22%3Afalse%2C%22cacheHandlers%22%3Afalse%2C%22scopeId%22%3Anull%2C%22ssrCssVars%22%3A%22%7B%20color%20%7D%22%2C%22bindingMetadata%22%3A%7B%22TestComponent%22%3A%22setup%22%2C%22foo%22%3A%22setup%22%2C%22bar%22%3A%22props%22%7D%7D%7D

❤️ 感谢大家

如果你觉得这篇内容对你挺有有帮助的话:

  1. 看到这里了就点个在看支持一下吧,你的[在看]是我创作的动力;

  2. 关注公众号【Web前端严选】,进交流群,定期为你推送好文。

af0946a91bb7cdd7cecc78c581eae177.png

f77d57143decef6ff77c91b6586849c3.png

点个在看,大家都看 608f43dab4417c9225a322efc515d39b.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值