Vue3组合式API+TypeScript写法入门

文章目录

  • 前言
  • 1.reactive
  • 2.ref
  • 3.props
  • 4.computed
  • 5.emit
  • 6.watch
  • 总结


前言

参考Vue3官网.
本篇以组合式API为例, 但不包含setup语法糖式写法.

原本打算结合class-component, Vue3不推荐就不用了: OverView|Vue Class Component.
而且是不再推荐基于类的组件写法, 推荐单文件组件, 组合式 API和setup语法糖.

在这里插入图片描述

而且这个库也好久没更新了: vue-class-component npm

在这里插入图片描述


1.reactive

可以选择使用多个reactive来保存状态.
但reactive只接受对象类型, 分开写势必要面临非对象类型数据的响应式处理问题.

你尽可以把它塞到一个对象里, 就像这样:

type asd = number;
const a = reactive<{ a: asd }>({ a: 1 });
console.log(a.a);

个人推荐用ref处理, 接受非对象类型:

type Count = number;
const count = ref<Count>(0);

不管怎么写都得给reactive下的每个属性规定类型, 这些类型之间可以互相引用
分开写的话就不需要组合一个约束整个reactive的接口, 约束每个值就可以了:

setup() {
  interface Book {
    title: string;
  }
  interface Info {
    id: number;
    book: Book
  }

  const book = reactive<Book>({ title: "Vue" }); // 在赋值时指定类型
  const info: Info = reactive({ id: 2, book: book }); // 在创建变量时指定类型
 
  const addToDo = function (todo: Info): void { // 函数要注明参数和返回值类型
  };
}

合起来写的情况下需要给reactive定义接口:

setup() {
  type Finished = number;
  type Still = number;
  interface Info {
    id: number;
    title: string;
    isCompleted: boolean;
  }

  interface react {
    finished: Finished;
    still: Still;
    todos: Info[];
  }

  const state = reactive<react>({
    finished: 0,
    still: 3,
    todos: [
      { id: 1, title: "task0", isCompleted: false },
      { id: 2, title: "task1", isCompleted: true }
    ],
  });
}

也可以选择不给reactive定义接口, 那就得挨个定义reactive内属性的类型, 我不知道这样写是否规范:

setup() {
  type Finished = number;
  type Still = number;
  interface Info {
    id: number;
    title: string;
    isCompleted: boolean;
  }

  const state = reactive({
    finished: <Finished> 0,
    still: <Still> 3,
    todos: <Info[]> [
      { id: 1, title: "task0", isCompleted: false },
      { id: 2, title: "task1", isCompleted: true }
    ],
  });
}

2.ref

对于非对象类型数据, 依然推荐使用ref处理:

export default defineComponent({
 setup() {
   let still = ref<Still>(3);
   let finished = ref<Finished>(0);
   let todos = ref<TaskInfo[]>([
     { id: 0, title: "task0", isCompleted: false },
     { id: 1, title: "task1", isCompleted: true },
   ]);
  }
});

可以选择引入Vue自带的Ref类型, 这并不会影响其他地方对该值的接收, 比如const a: Ref<string> = ref('3'); a.value依然可以作为string类型的参数.

import type { Ref } from 'vue';

export default defineComponent({
  setup() {
    let still: Ref<Still> = ref(3);
    let finished: Ref<Finished> = ref(0);
    let todos: Ref<TaskInfo[]> = ref([
      { id: 0, title: "task0", isCompleted: false },
      { id: 1, title: "task1", isCompleted: true },
    ]);
 }
});

3.props

很遗憾在不使用setup语法糖的情况下Composition API的props并不能接受一个接口, 如下方式不被允许:

interface Book {
  page: number;
  id: number
}
interface propsInterface { 
  book: Book,
  a: Function
}

export default defineComponent({
  props: <propsInterface> { // 此处标红, <propsInterface>不能加在此处
    book: {
      type: Object,
      require: true
    }
    a: {
      type: Function,
      require: true
    }
  },
  setup(props) {
    // props.xxx;
  }
})

所以就不要接口了, 用文档推荐的方法, 简单类型的type直接写, 复杂类型在类型后面加as PropType<???>来做进一步的描述:

interface Title {
  a: number;
  s: string;
}

export default defineComponent({
  name: "Item",
  props: {
    id: {
      type: Number,
      require: true,
    },
    title: {
      type: Object as PropType<Title>,
      require: true,
    },
    isCompleted: {
      type: Boolean,
      require: true,
    },
    deleteToDo: {
      type: Function as PropType<(id: number) => void>, // 进一步描述函数
      require: true,
    },
    finish: {
      type: Function as PropType<(id: number, complete: boolean) => void>, // 进一步描述对象
      require: true,
    }
  }
})

4.computed

参考文档用例:

setup(props) {
  const getId = computed<number>(() => { // 计算属性
    return props.id;
  });
  
  const isCompleted = computed<boolean>({ // 可写计算属性
    get(): boolean {
      return props.isCompleted;
    },
    set(): void {
      props.finish!(props.id!, props.isCompleted);
    },
  });
  
  return {
    getId,
    isCompleted,
  };
}

5.emit

type.ts:

export type Answer = string;

子组件:

<template>
  <div class="todo-header">
    <input
      type="text"
      @keyup.enter="pressEnter"
      @keyup.esc="pressESC"
    />
  </div>
</template>
emits: ["enter", "esc"],
setup() {
  const pressEnter = function () {
     const hq: Answer = "press Enter";
     emit("enter", hq);
  };

  const pressESC = function () {
    const nq: Answer = "press ESC";
    emit("esc", nq);
  };

  return {
    pressEnter,
    pressESC,
  };
}

父组件:

<template>
  <div>
    <Header @esc="pressESC" @enter="pressEnter" />
  </div>
</template>
setup() {
  const pressEnter = function (answer: Answer): void {
    console.log(answer);
  };

  const pressESC = function (answer: Answer): void {
    console.log(answer);
  };

  return {
    pressEnter,
    pressESC,
  };
}

6.watch

<template>
  <div>
    <p>Counter: {{ counter }}</p>
    <button @click="incrementCounter">Increment</button>
  </div>
</template>
setup() {
  interface Counter = number;
  const counter = ref<Counter>(0);

  const incrementCounter = (): void => {
    counter.value++;
  };

  // 使用 watch 函数观察 counter 的变化
  watch(counter, (newValue, oldValue) => {
    console.log(`counter changed from ${oldValue} to ${newValue}`);
  }, { deep: true });

  return {
    counter,
    incrementCounter,
  };
}

总结

如果有漏掉的部分, 以后会补充.

<think>好的,我现在需要解决用户的问题:在Vue3中使用组合式API实现父组件向子组件传递数据的示例。用户已经提供了一些引用内容,我需要结合这些引用和我的知识来回答。 首先,用户提到了引用[1]、[2]、[3]、[4],这些都是关于Vue组合式API的父子通信相关内容。我需要仔细查看这些引用来确保回答的准确性。 引用[1]指出父组件通过绑定属性传递数据,子组件用props接收。引用[3]给出了子组件使用defineProps的具体示例,说明在<script setup>中使用编译器宏defineProps来接收数据,不需要导入,会被编译掉。这应该是关键步骤。 接下来,我需要构建一个完整的示例,包括父组件和子组件的代码。根据引用[3],子组件部分需要定义props,并在模板中显示。父组件部分需要引入子组件,绑定属性传递数据。 然后,考虑是否需要提到setup函数的参数,但引用[2]提到setup接收props和context,不过在<script setup>语法中可能不需要显式处理props参数,因为defineProps已经处理了。 另外,引用[4]提到依赖注入,但用户的问题是关于父子通信,所以主要用props,可能不需要涉及provide/inject,除非用户需要更深入的信息,但问题明确要求父传子,所以应该集中在props上。 需要注意组合式API的语法,特别是<script setup>的简洁写法。确保示例中的父组件正确传递数据,子组件正确接收并显示。 可能还需要解释defineProps的作用,以及为什么不需要导入,因为它是一个编译器宏。同时,提醒用户props是只读的,不能在子组件中直接修改,如果需要修改应该通过事件触发父组件修改。 最后,生成相关问题,比如props的验证、响应式处理、事件通信等,帮助用户进一步学习。</think>在Vue3组合式API中,父组件通过`props`向子组件传递数据是最常用的方式。以下是具体实现步骤和示例代码: ### 一、父组件传递数据 ```vue <!-- ParentComponent.vue --> <template> <div> <ChildComponent :message="parentMessage" :count="parentCount" /> </div> </template> <script setup> import ChildComponent from &#39;./ChildComponent.vue&#39; import { ref } from &#39;vue&#39; const parentMessage = ref(&#39;来自父组件的消息&#39;) const parentCount = ref(100) </script> ``` ### 二、子组件接收数据 ```vue <!-- ChildComponent.vue --> <template> <div class="child"> <h3>接收的父组件数据:</h3> <p>消息内容:{{ message }}</p> <p>数字计数:{{ count }}</p> </div> </template> <script setup> // 使用编译器宏定义props(不需要import) const props = defineProps({ message: { type: String, required: true }, count: { type: Number, default: 0 } }) // 在JS中访问props数据需要带props前缀 console.log(&#39;接收的消息:&#39;, props.message) </script> ``` ### 三、关键特性说明 1. **响应式传递**:父组件使用`ref()`创建的响应式数据,子组件接收时会自动保持响应性[^3] 2. **类型验证**:在`defineProps`中可以指定`type`、`required`、`default`等验证规则 3. **只读属性**:子组件接收的props是只读的,如需修改应通过`emit`事件通知父组件[^2] 4. **编译器优化**:`defineProps`是编译器宏,不会出现在最终产物中 ### 四、TS类型支持(可选) ```typescript // 子组件支持TypeScript的类型标注 interface Props { message: string count?: number } const props = defineProps<Props>() ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值