戳蓝字"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
❤️ 感谢大家
如果你觉得这篇内容对你挺有有帮助的话:
看到这里了就点个在看支持一下吧,你的[在看]是我创作的动力;
关注公众号【Web前端严选】,进交流群,定期为你推送好文。
点个在看,大家都看