1、Vue3的状态和CompositionAPIVue3已经发布一年了,它的主要新特性是:CompositionAPI。从2021年秋季开始,建议新项目使用Vue3的脚本设置语法,因此希望我们能看到越来越多的生产级应用程序构建在Vue3之上。这篇文章旨在展示一些使用CompositionAPI的有趣方法,以及如何围绕它构建应用程序。2.可组合函数和代码重用新的组合API解锁了许多有趣的方式来跨组件重用代码。回顾:以往我们按照组件optionAPI来划分组件逻辑:data,methods,created等//OptionsAPIstyledata:()=>({refA:1,refB:2,}),//在这里,我们经常看到500行代码。computed:{computedA(){returnthis.refA+10;},computedB(){返回this.refA+10;},},有了CompositionAPI,我们就不再局限于这种结构,我们可以不用选项来分隔代码。setup(){constrefA=ref(1);constcomputedA=computed(()=>refA.value+10);/*这也可能是500行代码。但是,这些功能可以彼此靠近!*/constcomputedB=computed(()=>refA.value+10);常数refB=ref(2);返回{refA,refB,computedA,computedB,};},Vue3.2引入了在我看来,这是个好主意。我们可以将这些函数分离到它们自己的文件中,而不是通过将它们放在脚本设置中来将它们分开。下面是同样的逻辑,分文件的做法。//Component.vue//featureA.jsimport{ref,computed}from"vue";exportdefaultfunction(){constrefA=ref(1);constcomputedA=computed(()=>refA.value+10);return{refA,computedA,};}//featureB.jsimport{ref,computed}from"vue";exportdefaultfunction(){constrefB=ref(2);}constcomputedB=computed(()=>refB.value+10);return{refB,computedB,};}请注意,featureA.js和featureB.js导出Ref和ComputedRef类型,因此所有这些数据都是反应性的。然而,这个特定的片段可能看起来有点矫枉过正。想象一下这个组件有500多行代码而不是10行。通过将逻辑分离到use__.js文件中,代码变得更具可读性。我们可以在多个组件中自由重用.js文件中的可组合函数。对渲染组件和作用域插槽不再有限制,mixin函数也没有命名空间冲突。由于可组合函数直接使用Vue的ref和computed,因此此代码将适用于项目中的任何.vue组件。陷阱1:设置中的生命周期挂钩如果生命周期挂钩(onMounted、onUpdated等)可以在设置中使用,这也意味着我们也可以在可组合函数中使用它们。你甚至可以这样写://Component.vue//store/actions.jsimport{onMounted}from'vue'//...actions:{myAction(){onMounted(()=>{console.log('太疯狂了,但是这个onMounted会被注册!')})}}//。..Vue甚至会在vuex中注册生命周期钩子!有了这种灵活性,了解如何以及何时注册这些挂钩就很重要了。请参阅下面的代码段。哪些onUpdated钩子会被注册?结论:声明生命周期方法时,应该在setup初始化时同步执行,否则哪里声明哪里不声明不管它在哪里声明。陷阱2:设置中的异步函数我们经常需要在逻辑中使用async/await。天真地试试这个:Asyncdata:{{data}}但是,如果我们尝试运行这段代码,组件根本不会被渲染。为什么?因为Promises不要跟踪状态。我们为数据变量分配了一个承诺,但是Vue不会主动更新它的状态。幸运的是,有一些解决方法:解决方案1:使用.then语法的ref来渲染对于这个组件,我们可以使用.t母鸡语法。Asyncdata:{{data}}一开始创建一个等于null的reactiveref来调用异步函数脚本setup上下文是同步的,所以组件会在myAsyncFunction()时渲染promise被resolved,它的结果被赋值给响应数据ref,结果被渲染。这种方法各有优缺点:优点是:可以用。缺点:语法有点过时,当你有多个.then和.catch链时会变得笨拙。解决方案2:IIFE如果我们将此逻辑包装在异步IIFE中,我们可以使用async/await语法。异步数据:{{data}}这种方式也有自己的优缺点:优点:async/await语法。缺点:可以说看起来不那么干净,仍然需要额外的参考。解决方案3:Suspense(实验性)如果我们在父组件中用包裹这个组件,我们就可以在设置中自由使用async/await!//Parent.vue//孩子.vue异步数据:{{data}}优点:迄今为止最简洁直观的语法缺点:截至2021年12月,这仍是一项实验性功能,其语法可能会发生变化。组件在子组件设置中有更多的可能性,而不仅仅是异步。使用它,我们还可以指定加载和回退状态。我认为这是创建异步组件的前进方向。Nuxt3已经使用了这个特性,对我来说,一旦这个特性稳定下来,它可能是首选方式。解决方案4:针对这些情况量身定制的独立第三方方法(请参阅下一节)。优势。最灵活。缺点:依赖于package.json。3.VueUseVueUse库依赖于CompositionAPI解锁的新功能,提供各种辅助功能。就像我们编写useFeatureA和useFeatureB一样,这个库允许我们导入以可组合风格编写的预制实用函数。下面是它如何工作的一个片段。我极力推荐这个库,在我看来,它是任何新Vue3项目的必备工具。这个库有可能为您节省许多代码行和大量时间。不影响包装尺寸。源代码简单易懂。如果你发现这个库的功能还不够,你可以扩展功能。这意味着选择使用这个库并没有太大的风险。以下是该库如何解决上述异步调用执行问题。<脚本设置>从“@vueuse/core”导入{useAsyncState};从'./myAsyncFunction.js'导入{myAsyncFunction};const{state,isReady}=useAsyncState(//我们要执行的异步函数myAsyncFunction,//Defaultstate:"Loading...",//UseAsyncStateoptions:{onError:(e)=>{console.error("错误!",e);state.value="fallback";},});useAsyncState:{{state}}Isthedataready:{{isReady}}此方法允许您在设置中执行异步功能,并为您提供回退选项和加载状态。现在,这是我处理异步的首选方式。4.如果您的项目使用Typescript的新defineProps和defineEmits语法脚本设置带来了一种更快的方式来输入道具并在Vue组件中发出。就我个人而言,我会选择通用样式,因为它为我们节省了额外的导入,并且没有异常null和undefined的类型比Vue2-style语法中的{required:false}更明确。请注意,无需手动导入defineProps和defineEmits。这是因为这些是Vue使用的特殊宏。这些在编译时作为“正常选项API语法”处理。我们可能会在未来的Vue版本中看到越来越多的宏实现。ComposableFunctions的类型化由于TypeScript默认要求模块的返回值是类型化的,所以一开始我主要是这样写TS组合的。import{ref,Ref,SetupContext,watch}from"vue";exportdefaultfunction({emit,}:SetupContext<("change-component"|"close")[]>)://下面的代码确实有是有必要吗?{onCloseStructureDetails:()=>void;showTimeSlots:Ref;showStructureDetails:Ref;onSelectSlot:(arg1:onSelectSlotArgs)=>void;onBackButtonClick:()=>void;showMobileStepsLayout:Ref;:引用<字符串>;isMobile:Ref;selectedTimeSlot:Ref;showQuestionarireLink:Ref;}{constisMobile=useBreakpoints().smaller("md");conststore=useStore();//等等,等等//...}这种方式,我认为是错误的。实际上不需要键入函数return,因为在编写可组合项时可以轻松地隐式键入它。它可以为我们节省大量时间和代码行。从"vue"导入{ref,Ref,SetupContext,watch};导出默认函数({emit,}:SetupContext<("change-component"|"close")[]>){constisMobile=useBreakpoints().smaller("md");conststore=useStore();//返回值可以在可组合项中隐式输入}如果EsLint将此标记为错误,请将'@typescript-eslint/explicit-module-boundary-types':'error',放入EsLint配置(.eslintrc)。Volar扩展Volar是VsCode和WebStorm的Vue扩展,用于替代Vetur。现在正式推荐与Vue3一起使用。对我来说,它的主要功能是:开箱即用地输入props和emits。这很好用,尤其是在使用Typescript时。现在,我总是选择在Vue3项目中使用Volar。对于Vue2,Volar仍然可以工作,因为它需要更少的配置。5.围绕组合API的应用程序架构在将逻辑移出**.vue**组件文件之前,有一些示例在脚本设置中完成了所有逻辑。还有使用从.vue文件导入的可组合函数的组件示例。代码设计的大问题是:我们是否应该将所有逻辑都写在.vue文件之外?有利也有弊。所有逻辑都放在设置中,并移至专用的.js/.ts文件。无需编写可组合的,方便直接修改。可扩展且更强大。重用代码时,需要重构。不重构更多模板是我的选择。The:在中小型项目中使用混合方法。一般来说,把逻辑写在setup里。当一个组件太大,或者明确代码会被复用的时候,直接放在单独的js/ts文件里,对于大项目,就全部写成composable即可。仅使用设置来处理模板命名空间。