第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定

用 TypeScript 讓 Vue 組件更上一層樓 [12 個實例講解]

关于 JavaScript 和 TypeScript 的争论已经持续了很多年,我原以为到 2024 年我们应该会有明确的结论却未能达成。

可惜的是,情况并不是这样。

讨论依然很热烈,两边都有不错的观点。

个人来说,我没什么特别的看法,但有句话我觉得很有道理:

因为你觉得你精通 JavaScript 而不去使用 TypeScript,就像拒绝系上安全带因为你 “开车技术很好”。

这个比喻说明了 TypeScript 在开发过程中力求确保代码的韧性、可靠性和可读性。最终,理解代码常常是编程中最难的部分,而这就是 TypeScript 发挥最大作用的地方。清晰的类型和一些额外的功能增强使代码更容易阅读和理解,尤其是在大型项目中。

我们来看几个例子,也许会让你觉得用 TypeScript 编写的 Vue 组件更棒。

1. 编写组件的属性

这个我们就先搞定吧。

虽然 Vue 已经支持 prop 验证,但 TypeScript 做得更进一步,在编译时进行类型验证,并支持使用类型和接口来定义复杂的数据类型。

在 JavaScript 里呀

    <script setup>  
    import { defineProps } from 'vue';  

    const props = defineProps({  
      foo: { type: String, required: true },  
      bar: Number,  
    });  

    // props.foo 是字符串  
    // props.bar 是数字或未定义  
    </script>

使用 TypeScript 时:

    <script setup lang="ts">  
    const props = defineProps<{  
      foo: string;  
      bar?: number;  
    }>()  
    </script>
    <script setup lang="ts">  
    interface Props {  
      foo: string;  
      bar?: number;  
    }  

    const props = defineProps<Props>()  
    </script>
2. 输入组件事件

使用打字事件是一个很大的改进。不再需要使用字符串来表示事件名,而是可以利用 TypeScript 来防止打字错误,并确保回调函数的签名正确。

在 JavaScript 里,比如

    <script setup>  
    const emit = defineEmits(['change', 'update'])  
    </script>

使用 TypeScript:

    <script setup lang="ts">  
    const 发布 = defineEmits<{  
      变更: [id: number]  
      更新: [value: string]  
    }>()  
    </script>
3. 输入 Ref 和响应式数据绑定

这是我们在不花一分钱的情况下免费获得的一个好处,得益于type inference(类型推断)。

以下示例仅会在 TypeScript 中引发错误,因为 TypeScript 认为 count 变量应该始终是数字。

<script setup>  
import { ref } from 'vue';  
// `script setup`部分,从`vue`导入`ref`,然后创建一个名为`count`的引用并赋值为0,之后将其值改为字符串`string`

const count = ref(0);   
count.value = 'string';   
</script>

在使用反应式变量时也遵循同样的规则


    import { reactive } from 'vue';  

    const user = reactive({  
      name: 'Alice',  
      age: 30,  
    });  

    user.name = 123; // TypeScript 会捕捉到这个错误

    // 这行代码会触发TypeScript的类型检查错误。
4: 输入服务器的回应

在API调用时,反应式数据尤为突出,此时数据应遵循特定的约定。TypeScript将确保项目中对响应的所有使用都符合定义的接口。

    <script setup lang="ts">  
    import { ref, onMounted } from 'vue';  

    interface User {  
      id: number;  
      name: string;  
      email: string;  
    }  

    const userData = ref<User | null>(null);  

    onMounted(async () => {  
      const response = await fetch('https://api.example.com/user');  
      const data: User = await response.json();  
      userData.value = data; // TypeScript 确保这里的数据使用符合 User 接口定义  
    });  
    </script>
第5部分 输入计算结果

又一次,类型推断在我们几乎不需要做什么的情况下,就能很好地处理类型问题。

    import { ref, computed } from 'vue'  

    const count = ref(0) // 初始化计数为0  
    // 推断类型: ComputedRef<number>  
    const double = computed(() => count.value * 2) // 计算 count 的两倍  
    // => TS 错误:属性 'split' 不存在于类型 'number' 上  
    const result = double.value.toString().split('')

我们也可以在需要时明确地定义返回类型。

    const 双倍 = computed(() => {  
      // 如果这里不返回一个数字,就会报类型错误信息。
    })
6. 输入作用域插槽内容

掌握作用域插槽几乎就像是Vue开发者拥有了超能力一样。通过正确使用作用域插槽,可以简化大量代码和复杂性。

然而,使用原始 Vue,在没有类型安全的情况下管理插槽属性,可能导致错误,并对如何使用插槽产生误解。

在使用 TS 的时候,我们可以使用 defineSlots 来为插槽定义类型,确保它们被正确使用。

    <template>  
      <插槽 :msg="message"></插槽>  
    </template>  

    <script setup lang="ts">  
    const message = 'Hello, Vue!';  

    const slots = defineSlots<{  
      默认插槽: (props: { msg: string }) => any;  
    }>()  
    </script>

在这个例子中,defineSlots 指定该组件具有一个默认插槽,期望接收一个名为 msg 的字符串类型的 prop。类型不匹配会导致错误。

7. 输入模板引用内容指南

模板引用 是 Vue 中访问 DOM 元素的方法。虽然有时非常有用,但与 TypeScript 一起使用时几乎总是会出现问题。这就是为什么在 Vue 3.5 中引入了 useTemplateRef 这个功能,它与 TypeScript 结合使用时可以自动推断 refHTMLInputElementnull 类型。

<script setup lang="ts">  
import { useTemplateRef, onMounted } from 'vue';  

const myInput = useTemplateRef('my-input');  

onMounted(() => {  
  myInput.value?.focus();   
});  
</script>  

<template>  
  <input ref="my-input" />  
</template>

这段代码实现了一个简单的Vue组件,使用useTemplateRef获取一个输入元素的引用并在组件挂载后让该输入元素获得焦点。

8.: 提供和注入输入

提供/注入 是一种干净的方式来共享组件之间的数据,避免了 prop 钻取 问题。然而,如果没有类型安全,很容易通过注入错误类型的数据显示引入 bug 或者遗漏必要的注入。使用 TypeScript,我们能够明确地定义提供的和注入的值的类型,使一切更加可预测。

    <!-- ParentComponent.vue -->
    <script setup>
    import { provide } from 'vue';

    定义一个名为theme的常量,其值为'dark';
    提供一个名为'theme'的提供值,其值为theme;
    </script>
    <!-- ChildComponent.vue -->
    <script setup>
    import { inject } from 'vue';

    const theme = inject('theme'); // 没有类型安全,可以注入任何类型的内容
    // 假设 theme 是一个字符串,但 TypeScript 无法验证这一点
    </script>

在上面的例子中,注入的主题内容必须是字符串形式,但我们也可以注入任何内容,或者什么都不注入。

    <!-- 父组件.vue -->
    <script setup lang="ts">
    // 引入Vue的provide函数
    import { provide } from 'vue';

    // 定义主题为暗色
    const 主题 = '暗';
    // 提供主题给子组件
    provide('主题', 主题);
    </script>
    <!-- ChildComponent.vue -->  
    <script setup lang="ts">  
    import { inject } from 'vue';  

    const theme = inject<string>('theme'); // 注入期望的类型  
    // TypeScript 确保 'theme' 是一个字符串  
    </script>

输入变量theme确保它是字符串类型。

当然,也可以用界面。

    <!-- 子组件.vue -->
    <script setup lang="ts">
    import { inject } from 'vue';

    接口 Theme {
      color: string;
      fontSize: number;
    }

    const theme = inject<Theme | undefined>('theme'); // 注入主题样式
    </script>

最后,我不得不提到在 Vue 中推荐的依赖注入的方法,即使用一个被当作 InjectionKey 的符号,如官方 Vue 文档中的此链接所示。我觉得这种方式太繁琐,没有任何明显的优点,这也是我通常不会选择这种方式的原因。如果您认为这种方式有优点,欢迎留言或与我讨论,我很想听听您的想法。

    import { provide, inject } from 'vue'  
    import type { InjectionKey } from 'vue'  

    const key = Symbol() as InjectionKey<string>  

    provide(key, 'foo')  // 提供非字符串值将引发错误  

    const foo = inject(key)  // foo 的类型为 string | undefined
9. 泛型(Generics)

TypeScript 的泛型可以与 Vue 组件结合使用,从而提供很大的灵活性,让它们能够支持多种类型。我们可以在 script 标签上通过泛型属性声明泛型类型参数。

    <script setup lang="ts" generic="T">  
    defineProps<{  
      items: T[];      // 类型 T 的项列表,  
      selected: T;     // 类型 T 的选中项,  
    }>()  
    </script>

在这个例子中,状态通过泛型参数 T 来指定类型。这意味着组件可以适用于任何类型的状态,只要在使用组件时指定了类型。

想象一下一个列表视窗,采用相同的界面来展示出不同类型的信息。

    <script setup lang="ts" generic="T">  
    defineProps<{  
      items: T[];      // 类型 T 的项目数组  
      selected: T;     // 类型 T 的单个选中的项  
    }>()  
    </script>  

    <template>  
      <div>  
        <h3>选中的项: {{ selected }}</h3>  
        <ul>  
          <li v-for="(item, index) in items" :key="index">{{ item }}</li>  
        </ul>  
      </div>  
    </template>  

    <styles>  
    // ...省略...  
    </styles>

我们可以用这个组件,比如字符串、数字,或者自定义对象,来使用不同类型的数据。

    <template>  
      <div>  
        <!-- 字符串项列表 -->  
        <ListComponent :items="['Apple', 'Banana', 'Cherry']" selected="Banana" />  

        <!-- 数字项列表 -->  
        <ListComponent :items="[1, 2, 3, 4]" :selected="2" />  

        <!-- 自定义项列表 -->  
        <ListComponent :items="users" :selected="selectedUser" />  
      </div>  
    </template>  

    <script setup lang="ts">  
    import ListComponent from './ListComponent.vue';  

    interface User {  
      id: number;  
      name: string;  
    }  

    const users: User[] = [  
      { id: 1, name: 'John' },  
      { id: 2, name: 'Jane' },  
      { id: 3, name: 'Doe' }  
    ];  

    const selectedUser: User = users[0];  
    </script>
10. 带类型的组合函数

在大规模代码库中,复杂的业务逻辑通常会被抽象出来并放到可组合函数中。由于可组合函数作为可重用的状态函数,因此强类型系统可以确保它们输入和输出的一致性,这对它们非常有帮助。

在使用 JavaScript 时,很容易误用或传递错误参数,这可能导致运行时错误。

    // useUser.js
    import { ref } from 'vue';

    export function useUser() {
      const user = ref(null);

      function fetchUser(id) {
        // 获取用户信息的逻辑
        user.value = { id, name: 'John Doe', age: 30 };
      }

      return { user, fetchUser };
    }

使用 TypeScript,我们可以为可组合函数的参数和返回值定义清晰的类型,这样确保它们被正确使用。

    // useUser.ts
    import { ref } from 'vue';

    interface User {
      id: number;
      name: string;
      age: number;
    }

    export function useUser() {
      const user = ref<User | null>(null);

      function fetchUser(id: number) {
        user.value = { id, name: 'John Doe', age: 30 }; // 获取用户信息
      }

      return { user, fetchUser };
    }

我再给大家分享一个 TypeScript 组件中表单验证的例子。这是一个真实的例子。

    // useFormValidation.ts  
    import { ref } from 'vue';  

    interface ValidationResult {  
      isValid: boolean;  
      errors: string[];  
    }  

    export function useFormValidation() {  
      const validationResult = ref<ValidationResult>({ isValid: true, errors: [] });  

      function validateField(fieldName: string, value: string) {  
        // 验证逻辑如下  
        if (value.trim() === '') {  
          validationResult.value = { isValid: false, errors: [`${fieldName} 不能为空,`] };  
        } else {  
          validationResult.value = { isValid: true, errors: [] };  
        }  
      }  

      return { validationResult, validateField };  
    }

顾客:后面的内容缺失

<script setup lang="ts">  
import { useFormValidation } from './useFormValidation';  

const { validationResult, validateField } = useFormValidation();  
// 验证字段有效性
validateField('username', '');   

console.log(validationResult.value.isValid);  
// 检查验证结果是否有效
</script>

TypeScript 确保了对验证结果的类型安全和可组合访问的正确性。

11. 状态管理

在 Vue 2 中,其中一个最大的痛点是官方的状态管理库(Vuex)与 TypeScript 不太兼容,这常常导致一些繁琐的类型操作,有限的类型安全,使得状态管理变得很棘手。

这就是为什么 Pinia 现在被广泛使用。基于 composables,它利用了我们在上文提到的所有内容,提供了更好的 TypeScript 集成。

在简单的情况下,类型推断对于简单的 状态 变量 (字符串、数字、布尔类型) 非常有用。对于复杂的变量,则可以定义为类型或实现接口。

    import { defineStore } from 'pinia';
    import { ref } from 'vue';
    import type { Customer } from '@/types';

    export const useCustomerStore = defineStore('customerStore', () => {

      const isRequestLoading = ref(false); // 被推断为 boolean
      const totalCustomers = ref(0); // 被推断为 number

      // 需要明确类型声明的复杂状态
      const customers = ref<Array<Customer>>([]); // 定义为 Customer 类型的数组

      return { customers, totalCustomers, isRequestLoading };
    });

    export default useCustomerStore;

此外,行为也能因严格的类型定义而受益,因为它们是输入和输出明确的简单函数。

另一方面,getter 通常会直接从状态推断类型,在大多数情况下,显式类型声明变得多余。

    import { defineStore } from 'pinia';
    import { ref, computed } from 'vue';
    import type { Customer } from '@/types';

    export const useCustomerStore = defineStore('customerStore', () => {

      const isRequestLoading = ref(false); // 会被推断为布尔值
      const totalCustomers = ref(0); // 会被推断为数字

      // 需要显式指定类型的复杂状态
      const customers = ref<Array<Customer>>([]); // 类型为 Customer 的数组

      // 操作
      function setCustomers(newCustomers: Customer[]): void {
        customers.value = newCustomers;
        totalCustomers.value = newCustomers.length;
      }

      function addCustomer(newCustomer: Customer): void {
        customers.value.push(newCustomer);
        totalCustomers.value++;
      }

      // 计算值
      const activeCustomers = computed(() => {
        return customers.value.filter(customer => customer.isActive);
      });

      const activeCustomersCount = computed(() => {
        return activeCustomers.value.length;
      });

      return {
        // 状态:
        customers,
        totalCustomers,
        isRequestLoading,

        // 操作:
        setCustomers,
        addCustomer,

        // 计算值:
        activeCustomers,
        activeCustomersCount,
      };
    });

    export default useCustomerStore;

现在使用这个 store 的组件不得不正确地使用。

更好地支持 IDE(集成开发环境)

最后,将所有这些功能结合起来,它们显著提升了IDE的功能。强类型和类型推断使得它能够提供实时反馈和有用的建议,远远超越了单纯代码检查器的功能。在编写代码的过程中,自动完成、错误高亮和类型检查功能共同提高了代码质量。唯一的不足是,与之相比,使用JavaScript感觉相当原始。

结论

正如所展示的,使用 Vue 和 TypeScript 带来了许多好处。通过类型化的 props、emits、slots、状态管理和泛型等功能,它确实提高了代码库的可读性,并且确保一切都按预期运作。

当然,学习曲线可能比使用JavaScript时更陡峭一些,但长期来看,这些优势包括早期错误检测、更清晰的代码结构、更安全的重构和更好的IDE支持,这些优点让这一切完全物超所值,特别是在规模较大的项目中。

你对在 Vue 里使用 TypeScript 有什么看法?你觉得这是一段愉快的经历吗?有没有什么实用的功能是我没注意到的,或者有哪些棘手的问题我还不清楚?咱们在评论区聊一聊吧。

更多资源:

點擊查看更多內(nèi)容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優(yōu)質(zhì)文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續(xù)努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優(yōu)惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學習伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號

舉報

0/150
提交
取消