当前位置: 首页 > 科技观察

基于在Vue3和TypeScript项目中大量实践的思考

时间:2023-03-15 22:28:40 科技观察

概述Vue3出来已经有一段时间了。在团队中,我也进行了大量的业务实践,也有了一些自己的想法。总的来说,Vue3无论是在底层原理上,还是在业务的实际发展上,都取得了长足的进步。使用proxy来替代之前的Object.defineProperty的API,性能更好,同时也解决了Vue在处理对象和数组方面的缺陷;在diff算法中,采用了静态标记的方式,大大提高了Vue.js的执行效率。在使用层面,我们从optionsApi变成了compositionApi,在实际业务中逐渐摒弃了data、methods、computed原本孤立的写法。CompositonApi,它比较专注,它关注相关业务的聚合。同时,在compositionApi中,为了防止业务逻辑过于繁重,提供了关注点分离的方式,大大提高了我们代码的可读性。全面支持TypeScript,类型验证成为Vue3日后开发大型项目的质量保证。同时,这也顺应了趋势——前端的未来是TypeScript!1.CompositonApicompositionApi的本质体现在代码上,就是一个setup函数。在此设置函数中,返回的数据将在组件的模板中使用。return的对象,在一定程度上代表了之前vue2中的data属性。import{defineComponent,ref}from'vue';exportdefaultdefineComponent({name:'Gift',setup(){constcounter=ref(0);return{counter}}})这个时候,对于大部分初学者来说,可能的存在的疑问是,我能不能定义optionsApi的写法,比如data,computed,watch,methods等等。这里需要说明的是,Vue3完全兼容Vue2的optionsApi的写法,但是概念上更推荐setup的方式来写我们的组件。原因如下:Vue3的存在本身就是为了解决Vue2的问题。Vue2的问题是聚合的缺失会导致代码越来越臃肿!setup方法可以聚合数据、方法逻辑、依赖关系等,更易于维护。也就是说,我们以后尽量不要写单独的data,computed,watch,methods等等。不是Vue3不支持,而是违反了Vue3的理念。components属性,即一个组件的子组件,在这个配置上Vue2和3区别不大。如何使用Vue2,Vue3还是一样。1.ref和reactive有什么区别?在功能上,ref和reactive都可以??实现响应式数据!在语法层面上,两者是不同的。ref定义的响应式数据需要通过[data].value的方式进行改变;reactive中定义的data需要通过[data].[prpoerty]的方式进行修改。constTitle:Ref=ref('活动名称');constactData=reactive({list:[],total:0,curentPage:1,pageSize:10});actTitle.value='活动名称2';actData.total=100;但在应用层面,还是有区别的。一般来说:对于单一的常见类型的数据,我们使用ref来定义响应。在表单场景中,描述表单key:value对象的场景使用了reactive;在某些场景下,某个模块的一组数据通常使用反应式方法来定义数据。那么,对象是否必须使用响应式定义?其实也不是,是可以做到的,根据自己的业务场景,具体问题具体分析!ref强调的是一个数据值的变化,reactive强调的是定义对象的某个属性的变化。2.Periodic函数Periodic函数,在Vue3中,单独使用,如下:import{defineComponent,ref,onMounted}from'vue';exportdefaultdefineComponent({name:'Gift',setup(){constcounter=ref(0);onMounted(()=>{//处理业务,一般是请求数据})return{counter}}})3.Vue2中使用store。其实可以直接通过this.$store获取,但是在Vue3中,其实没有这个概念,用法如下:import{useStore}from"vuex";import{defineComponent,ref,computed}from'vue';exportdefaultdefineComponent({name:'Gift',setup(){constcounter=ref(0);conststore=useStore();conststoreData=computed(()=>store);//配合computed得到商店的价值。return{counter,storeData}}})4.router的使用在Vue2中是通过this.$router的方式进行路由的函数式编程,但是在Vue3中是这样使用的:import{useStore}from"vuex";import{useRouter}from"vue-router";import{defineComponent,ref,computed}from'vue';exportdefaultdefineComponent({name:'Gift',setup(){constcounter=ref(0);constrouter=useRouter();constonClick=()=>{router.push({name:"AddGift"});}return{counter,onClick}}})2.关注点分离关注点分离应该分为两层:第一层它意思是Vue3本身的设置把相关的数据和处理逻辑放在一起。这是一种关注点的聚合,方便我们看业务代码。第二层的意思就是当setup变大的时候,我们可以在setup里面抽取一个相关的业务来实现第二层的关注点分离。import{useStore}from"vuex";import{useRouter}from"vue-router";import{defineComponent,ref,computed}from'vue';importuseMerchantListfrom'./merchant.js';exportdefaultdefineComponent({name:'Gift',setup(){constcounter=ref(0);constrouter=useRouter();constonClick=()=>{router.push({name:"AddGift"});}//在这个例子中,我们将获取商家列表相关业务分离。也就是下面的merchant.tsconst{merchantList}=useMerchantList();return{counter,onClick,merchantList}}})merchant.tsimport{getMerchantlist}from"@/api/rights/gift";import{ref,onMounted}from"vue";exportdefaultfunctionuseMerchantList():Record{constmerchantList=ref([]);constfetchMerchantList=async()=>{letres=awaitgetMerchantlist({});merchantList.value=res?.data?.child;};onMounted(fetchMerchantList);return{merchantList};}第三,TypeScript支持这部分内容,准确的说是TS的内容,但是和Vue3项目的开发密切相关,所以如果我们真的要用Vue3,我们还是要了解TS的使用。不过这部分我不会介绍TS的基本语法,主要是业务场景下如何组织TS。使用TS进行业务开发,一个核心思想是先关注数据结构,然后根据数据结构开发页面。以前的前端开发模式是先写页面,再专注于数据。比如写一个礼物列表页面,我们可能需要定义这样的接口。总而言之,我们需要注意的是:页面数据的接口,接口返回的数据类型,接口的入参类型等等。//礼品的创建、编辑、列表中的每一项都是该数据类型。interfaceIGiftItem{id:string|number;name:string;desc:string;[key:string]:any;}//全局对应类型定义//一般来说,我们不确认接口返回的类型是什么(可能为null,可能是对象,也可能是数组),所以使用泛型定义接口interfaceIRes{code:number;msg:string;data:T}//接口返回数据类型定义interfaceIGiftInfo{list:Array;pageNum:number;pageSize:number;total:number;}在一个普通的接口请求中,我们一般用TS来定义一个数据请求,数据请求的req类型,res类型的数据请求。exportconstgetGiftlist=(params:Record):Promise>=>{returnHttp.get("/apis/gift/list",params);};