有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。
1. Vue 3和Composition API的情况
Vue 3曾经公布了一年,它的次要新性能是:Composition API。从2021年秋季开始,举荐新我的项目应用Vue 3的 script setup
语法,所以心愿咱们能看到越来越多的生产级应用程序建设在Vue 3上。
这篇文章旨在展现一些乏味的办法来利用Composition API,以及如何围绕它来结构一个应用程序。
2. 可组合函数和代码重用
新的组合API开释了许多乏味的办法来重用跨组件的代码。温习一下:以前咱们依据组件选项API宰割组件逻辑:data、methods、created 等。
// 选项API格调data: () => ({ refA: 1, refB: 2, }),// 在这里,咱们常常看到500行的代码。computed: { computedA() { return this.refA + 10; }, computedB() { return this.refA + 10; },},
有了Composition API,咱们就不会受限于这种构造,能够依据性能而不是选项来拆散代码。
setup() { const refA = ref(1); const computedA = computed(() => refA.value + 10); /* 这里也可能是500行的代码。 然而,这些性能能够放弃在彼此左近! */ const computedB = computed(() => refA.value + 10); const refB = ref(2); return { refA, refB, computedA, computedB, }; },
Vue 3.2引入了<script setup>
语法,这只是setup()
函数的语法糖,使代码更加简洁。从当初开始,咱们将应用 script setup 语法,因为它是最新的语法。
<script setup>import { ref, computed } from 'vue'const refA = ref(1);const computedA = computed(() => refA.value + 10);const refB = ref(2);const computedB = computed(() => refA.value + 10);</script>
在我看来,这是一个比拟大想法。咱们能够把这些性能分成本人的文件,而不是用通过搁置 在script setup中的地位来放弃它们的拆散。上面是同样的逻辑,把文件宰割开来的做法。
// Component.vue<script setup>import useFeatureA from "./featureA";import useFeatureB from "./featureB";const { refA, computedA } = useFeatureA();const { refB, computedB } = useFeatureB();</script>// featureA.js import { ref, computed } from "vue";export default function () { const refA = ref(1); const computedA = computed(() => refA.value + 10); return { refA, computedA, };}// featureB.js import { ref, computed } from "vue";export default function () { const refB = ref(2); const computedB = computed(() => refB.value + 10); return { refB, computedB, };}
留神,featureA.js
和featureB.js
导出了Ref
和ComputedRef
类型,因而所有这些数据都是响应式的。
然而,这个特定的片段可能看起来有点矫枉过正。
- 设想一下,这个组件有500多行代码,而不是10行。通过将逻辑拆散
到use__.js
文件中,代码变得更加可读。 - 咱们能够在多个组件中自在地重复使用
.js
文件中的可组合函数 不再有无渲染组件与作用域槽的限度,也不再有混合函数的命名空间抵触。因为可组合函数间接应用了Vue的ref
和computed
,所以这段代码能够与你我的项目中的任何.vue
组件一起应用。
陷阱1:setup 中的生命周期钩子
如果生命周期钩子(onMounted
,onUpdated
等)能够在setup
外面应用,这也意味着咱们也能够在咱们的可组合函数外面应用它们。甚至能够这样写:
// Component.vue<script setup>import { useStore } from 'vuex';const store = useStore();store.dispatch('myAction');</script>// store/actions.jsimport { onMounted } from 'vue'// ...actions: { myAction() { onMounted(() => { console.log('its crazy, but this onMounted will be registered!') }) }}// ...
而且Vue甚至会在vuex外部注册生命周期钩子! (问题是:你应该)
有了这种灵活性,理解如何以及何时注册这些钩子就很重要了。请看上面的片段。哪些onUpdated
钩子将被注册?
<script setup lang="ts">import { ref, onUpdated } from "vue";// 这个钩子将被注册。咱们在 setup 中失常调用它onUpdated(() => { console.log('✅')});class Foo { constructor() { this.registerOnMounted(); } registerOnMounted() { //它也会注册! 它是在一个类办法中,但它是在 //在 setup 中同步执行 onUpdated(() => { console.log('✅') }); }}new Foo();// IIFE also works(function () { onUpdated(() => { state.value += "✅"; });})();const onClick = () => { /* 这不会被注册。这个钩子是在另一个函数外面。 Vue不可能在setup 初始化中达到这个办法。 最蹩脚的是,你甚至不会失去一个正告,除非这个 函数被执行! 所以要留神这一点。 */ onUpdated(() => { console.log('❌') });};// 异步IIFE也会不行 :((async function () { await Promise.resolve(); onUpdated(() => { state.value += "❌"; });})();</script>
论断:在申明生命周期办法时,应使其在setup
初始化时同步执行。否则,它们在哪里被申明以及在什么状况下被申明并不重要。
陷阱2:setup 中的异步函数
咱们常常须要在咱们的逻辑中应用async/await
。天真的做法是尝试这样做:
<script setup lang="ts">import { myAsyncFunction } from './myAsyncFunction.jsconst data = await myAsyncFunction();</script><template> Async data: {{ data }}</template>
然而,如果咱们尝试运行这段代码,组件基本不会被渲染。为什么?因为 Promise 不跟踪状态。咱们给 data 变量赋了一个 promise,然而Vue不会被动更新它的状态。侥幸的是,有一些变通办法:
解决方案1:应用.then
语法的ref
为了渲染该组件,咱们能够应用.then
语法。
<script setup>import { ref } from "vue";import { myAsyncFunction } from './myAsyncFunction.jsconst data = ref(null);myAsyncFunction().then((res) => data.value = fetchedData);</script><template> Async data: {{ data }}</template>
- 一开始时,创立一个等于null的响应式
ref
- 调用了异步函数script setup 的上下文是同步的,所以该组件会渲染
- 当
myAsyncFunction()
promise 被解决时,它的后果被赋值给响应性 data ref,后果被渲染
这种形式有本人优缺点:
- 长处是:能够应用
- 毛病:语法有点过期,当有多个
.then
和.catch
链时,会变得很蠢笨。
解决方案2:IIFE
如果咱们把这个逻辑包在一个异步IIFE外面,咱们就能够应用 async/await
的语法。
<script setup>import { ref } from "vue";import { myAsyncFunction } from './myAsyncFunction.js'const data = ref(null);(async function () { data.value = await myAsyncFunction()})();</script><template> Async data: {{ data }}</template>
这种形式也有本人优缺点:
- 长处:async/await语法
- 毛病:能够说看起来不那么洁净,依然须要一个额定的援用
解决方案3:Suspense (实验性的)
如果咱们在父组件中用<Suspense>
包装这个组件,咱们就能够自在在setup 中自在应用async/await
!
// Parent.vue<script setup lang="ts">import { Child } from './Child.vue</script><template> <Suspense> <Child /> </Suspense></template>// Child.vue<script setup lang="ts">import { myAsyncFunction } from './myAsyncFunction.jsconst data = await myAsyncFunction();</script><template> Async data: {{ data }}</template>
- 长处:到目前为止,最扼要和直观的语法
- 毛病:截至2021年12月,这依然是一个实验性的性能,它的语法可能会扭转。
<Suspense>
组件在子组件 setup 中有更多的可能性,而不仅仅是异步。应用它,咱们还能够指定加载和回退状态。我认为这是创立异步组件的前进方向。Nuxt 3曾经应用了这个个性,对我来说,一旦这个个性稳定下来,它可能是首选的形式
解决方案4:独自的第三方办法,为这些状况量身定做(见下节)。
长处。最灵便
毛病:对package.json的依赖
3. VueUse
VueUse库依附Composition API解锁的新性能,给出了各种辅助函数。就像咱们写的useFeatureA
和useFeatureB
一样,这个库能够让咱们导入预制的实用函数,以可组合的格调编写。上面是它的工作原理的一个片段。
<script setup lang="ts">import { useStorage, useDark} from "@vueuse/core";import { ref } from "vue";/* 一个实现localStorage的例子。 这个函数返回一个Ref,所以能够立刻用`.value`语法来编辑它。 用.value语法编辑,而不须要独自的getItem/setItem办法。*/const localStorageData = useStorage("foo", undefined);</script>
我无奈向你举荐这个库,在我看来,它是任何新的Vue 3我的项目的必备品。
- 这个库有可能为你节俭很多行代码和大量的工夫。
- 不影响包的大小
- 源代码很简略,容易了解。如果你发现该库的性能不够,你能够扩大该性能。这象征在抉择应用这个库时,不会有太大的危险。
上面是这个库如何解决后面提到的异步调用执行问题。
<script setup>import { useAsyncState } from "@vueuse/core";import { myAsyncFunction } from './myAsyncFunction.js';const { state, isReady } = useAsyncState( // the async function we want to execute myAsyncFunction, // Default state: "Loading...", // UseAsyncState options: { onError: (e) => { console.error("Error!", e); state.value = "fallback"; }, });</script><template> useAsyncState: {{ state }} Is the data ready: {{ isReady }}</template>
这种办法能够让你在setup
外面执行异步函数,并给你回退选项和加载状态。当初,这是我解决异步的首选办法。
4. 如果你的我的项目应用Typescript
新的defineProps
和defineEmits
语法
script setup 带来了一种在Vue组件中输出 props 和 emits 的更快形式。
<script setup lang="ts">import { PropType } from "vue";interface CustomPropType { bar: string; baz: number;}// defineProps的重载。// 1. 相似于选项API的语法defineProps({ foo: { type: Object as PropType<CustomPropType>, required: false, default: () => ({ bar: "", baz: 0, }), },});// 2. 通过一个泛型。留神,不须要PropType!defineProps<{ foo: CustomPropType }>();// 3.默认状态能够这样做。withDefaults( defineProps<{ foo: CustomPropType; }>(), { foo: () => ({ bar: "", baz: 0, }), });// // Emits也能够用defineEmits进行简略的类型化defineEmits<{ (foo: "foo"): string }>();</script>
就集体而言,我会抉择通用格调,因为它为咱们节俭了一个额定的导入,并且对null和 undefined 的类型更加明确,而不是Vue 2格调语法中的{ required: false }
。
留神,不须要手动导入 defineProps
和 defineEmits
。这是因为这些是Vue应用的非凡宏。这些在编译时被解决成 "失常 的选项API语法。咱们可能会在将来的Vue版本
中看到越来越多的宏的实现。
可组合函数的类型化
因为typescript要求默认输出模块的返回值,所以一开始我次要是用这种形式写TS组合物。
import { ref, Ref, SetupContext, watch } from "vue";export default function ({ emit,}: SetupContext<("change-component" | "close")[]>): // 上面的代码真的有必要吗?{ onCloseStructureDetails: () => void; showTimeSlots: Ref<boolean>; showStructureDetails: Ref<boolean>; onSelectSlot: (arg1: onSelectSlotArgs) => void; onBackButtonClick: () => void; showMobileStepsLayout: Ref<boolean>; authStepsComponent: Ref<string>; isMobile: Ref<boolean>; selectedTimeSlot: Ref<null | TimeSlot>; showQuestionarireLink: Ref<boolean>;} { const isMobile = useBreakpoints().smaller("md"); const store = useStore(); // and so on, and so on // ... }
这种形式,我认为这是个谬误。其实没有必要对函数返回进行类型化,因为在编写可组合的时候能够很容易地对它进行隐式类型化。它能够为咱们节俭大量的工夫和代码行。
import { ref, Ref, SetupContext, watch } from "vue";export default function ({ emit,}: SetupContext<("change-component" | "close")[]>) { const isMobile = useBreakpoints().smaller("md"); const store = useStore(); // The return can be typed implicitly in composables}
如果EsLint将此标记为谬误,将`
'@typescript-eslint/explicit-module-boundary-types': 'error'`
,放入EsLint配置(.eslintrc
)。
Volar extension
Volar是作为VsCode和WebStorm的Vue扩大来取代Vetur的。当初它被正式举荐给Vue 3应用。对我来说,它的次要特点是:typing props and emits out of the box。这很好用,特地是应用Typescript的话。
当初,我总是会抉择Vue 3我的项目中应用Volar。对于Vue 2, Volar依然实用,因为它须要更少的配置 。
5. 围绕组合API的利用架构
将逻辑从.vue组件文件中移出
以前,有一些例子,所有的逻辑都是在script setup 中实现的。还有一些例子是应用从.vue
文件导入的可组合函数的组件。
大代码设计问题是:咱们应该把所有的逻辑写在.vue
文件之外吗?有利有弊。
所有的逻辑都放在 setup中 | 移到专用的.js/.ts文件 |
---|---|
不须要写一个可组合的,不便间接批改 | 可扩大更强 |
重用代码时须要重构 | 不须要重构 |
更多模板 |
我是这样抉择的:
- 在小型/中型我的项目中应用混合办法。一般来说,把逻辑写在setup外面。当组件太大时,或者当很分明这些代码会被重复使用时,就把它放在独自的
js/ts
文件中 - 对于大型项目,只需将所有内容编写为可组合的。只应用setup来解决模板名称空间。
作者:Noveo 译者:小智 起源:noveogroup
原文:https://blog.noveogroup.com/2...
交换
有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。