props与defineProps

在 Vue3 中,script 脚本存在两种情况。一种是 setup 函数,一种是 <script setup>。而针对这两种不同情况,Vue 也存在 props 和 defineProps 两种接收父组件传递数据的形式。

首先,默认已掌握 Vue2 的父子组件 props 传参,然后进行下面的过程。

setup () {} 函数模式

其实在第一章 拉开序幕的 setup 中,解释了 在 setup 函数中,props 的使用方式。

首先定义父组件,通过reactive 定义响应式数据且返回。然后在父组件中引入子组件 <Child>,将响应式数据 person 对象通过 props 方式传递到子组件中。

<template>
  <Child :person="person" />
</template>

<script>
import Child from "./components/Child.vue";
import { reactive } from "vue";

export default {
  name: "App",
  components: {
    Child,
  },
  setup() {
    let person = reactive({
      name: "al",
      age: 29,
    });

    return {person};
  },
};
</script>

然后定义子组件

<template>
  <div>
    {{ person }}
  </div>
</template>

<script>
export default {
  name: 'ChildComponent',
  setup(props) {
    console.log(props,'props');
  }
}
</script>

在 setup 函数中,接收两个参数,第一个就是 props。但是,此时如果直接打印该 props,发现值是一个空对象,且控制台报错。

这是因为,如果使用的是 setup 函数模式,需要像Vue2一样,先通过 props 属性接收,然后才能在 setup 函数中 接收到 props 

export default {
  name: 'ChildComponent',
  props: ['person'],
  setup(props) {
    console.log(props,'props');
  }
}

setup 函数 + ts

上面说的是最基础的 props 数据传递,在Vue3 中更好的支持了ts,以此来限制类型,提高代码质量。所以我们可以通过 ts 来规定参数类型。

// lang 需要配置,表示支持 ts 语法
<script lang="ts">
import Child from "./components/Child.vue";
import { reactive } from "vue";

// 通过 interface 定义接口类型
interface Person {
  name: string,
  age: number,
}

export default {
  name: "App",
  components: {
    Child,
  },
  setup() {
    // 直接调用 Person 接口,以此来控制类型
    let person:Person = reactive({
      name: "al",
      age: 29,
    });

    return {person};
  },
};
</script>

如果,reactive 定义的数据与 Person 接口存在区别,那么 TS 则会飘红报错,例如:我把对象数据的 name 属性故意写错成 ne属性,vs code 则给了警告

当然,我们可以写的更优雅一点,因为 reactive 本身是可以携带泛型的,所以可以写成

// 通过泛型来规范数据
let person = reactive<Person>({
  name: "al",
  age: 29,
});

然后再子组件中通过 props 接收时通过断言的形式指定属性的类型,通过defult指定默认值

props: {
  person: {
    type: Object as () => { name: string; age: number },
    default: () => ({
      name: "al",
      age: 29,
    }),
  },
},

到了这一步,就算是完成了 setup 函数 + TS 的编写。

 <script setup>

<script setup> 模式中是不存在 setup 函数的,当然也不能配置 props 属性。<script setup>里面的代码会被编译成组件中 setup() 函数的内容。

那么现在问题来了,我需要怎么接收父组件传递的 props,以及怎么使用该 props 数据呢?

父组件:数据不变,TS类型不变,改为  <script setup> 形式

<template>
  <Child :person="person" />
</template>

<script lang="ts" setup>
import Child from "./components/Child.vue";
import { reactive } from "vue";

interface Person {
  name: string;
  age: number;
}

let person = reactive<Person>({
  name: "al",
  age: 29,
});
</script>

子组件:Vue3 提供了 一个新的 API -- defineProps 用来接收父组件传递的props 数据。defineProps 是一个函数,接收一个数组参数,数组内部的元素则是父组件传递过来的数据,每个元素都是字符串形式

<template>
  <div>
    {{ person }}
  </div>
</template>

<script lang="ts" setup>
import { defineProps } from 'vue'

defineProps(['person'])

</script>

展示效果:数据正确展示

父组件传递多个数据

<template>
  <Child :person="person" new="newValue" old="oldValue"/>
</template>

子组件只需要在数组中新增元素即可

defineProps(['person','new','old'])

 展示效果:

Vue3 中的 defineProps() 函数返回一个 Proxy 对象,该对象内部包含的是 defineProps() 函数 中接收的 props 数据,可以针对于 props 中的数据进行某些代码逻辑判断。例如

<script lang="ts" setup>
import { defineProps } from 'vue'

let x = defineProps(['person','new','old'])

if (x.person.age > 1) {
  console.log(x);
}
</script>

展示效果:

到此为止,defineProps() 函数的基本使用就完成了,那如果此时我也想通过 TS 来规范数据类型又需要怎么做呢?

 <script setup> + TS

把上面的数据改一下,现在父组件传递的不是一个对象,而是一个数组。数组内部则是个人信息

<template>
  <Person :list="persons" />
</template>

<script lang="ts" setup name="App">
import Person from "./components/Child.vue";
import { reactive } from "vue";

let persons = reactive([
  { id: "e98219e12", name: "张三", age: 18 },
  { id: "e98219e13", name: "李四", age: 19 },
  { id: "e98219e14", name: "王五", age: 20 },
]);
</script>

现在需要对父组件中的数据进行 TS 类型限制。我们可以在当前文件中直接写,但是更建议是新开一个文件专门用来写 TS 类型。例如,在 src 目录下新建 types 文件夹,然后新建 index.ts 文件。

// 定义一个接口,限制每个Person对象的格式
export interface Person {
  id: string,
  name: string,
  age: number
}

// 定义一个自定义类型
// export type Persons = Array<Person>

// 简写的自定义类型
export type Persons = Person[]

父组件中根据 接口 和 自定义类型 来规范数据格式

<script lang="ts" setup name="App">
import Person from "./components/Person.vue";
import { reactive } from "vue";

// 引入类型时需要使用 type 声明,之前版本的ts是不需要的
import { type Persons } from "@/types";    

// 通过泛型规范 persons 数据类型
let persons = reactive<Persons>([
  { id: "e98219e12", name: "张三", age: 18 },
  { id: "e98219e13", name: "李四", age: 19 },
  { id: "e98219e14", name: "王五", age: 20 },
]);
</script>

 在子组件中 通过 defineProps 接收数据

const props = defineProps(['list'])

在子组件中 通过 defineProps 接收数据 + 限制类型:通过泛型限制数据类型

// 引入类型时需要使用 type 声明,之前版本的ts是不需要的
import { type Persons } from "@/types"; 

const props = defineProps<{list:Persons}>()

在子组件中 通过 defineProps 接收数据 + 限制类型 + 是否必须

// 引入类型时需要使用 type 声明,之前版本的ts是不需要的
import { type Persons } from "@/types"; 


// 通过 ? 表示该值不是必须值
const props = defineProps<{list?:Persons}>()

在子组件中 通过 defineProps 接收数据 + 限制类型 + 是否必须 + 指定默认值。此时需要引入新的 API --  withDefaults。该 API 也是一个函数,接收两个参数,第一个是 接收数据 + 限制类型 + 是否必须 的集合体,第二个就是指定的默认值。且默认值必须通过函数返回(类似于Vue2中的默认值)。

import {defineProps,withDefaults} from 'vue'
import {type Persons} from '@/types'

let props = withDefaults(
    defineProps<{list?:Persons}>(),
    { list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]}
)

总结

在Vue3 中接收父组件传递的 props 数据存在两种方式

  1. 通过配置式的props ,且使用数组形式 接收父组件传递的数据,然后在 setup 中通过 props 参数获取接收到的数据进行逻辑处理。不处理也可以直接使用。;例如:props : ['a','b','c']
  2. 通过 <script setup> 形式,使用 defineProps() 来接收父组件传递数据,defineProps() 函数接收一个字符串形式的数组,类似['a','b','c'],数组中每一项就是父组件传递的数据。写在 <script setup> 中的代码最终会被编译到 setup() 函数中。例如:defineProps(['list','a','b'])

通过 TS  限制参数类型 + 指定默认值 + 限制必要性 也存在两种方式

  1. 通过配置式的props,且使用对象形式 来接收父组件传递的数据,然后通过配置项来限制
    props: {
      person: {
        // 使用断言控制参数类型
        type: Object as () => { name: string; age: number },
    
        // 通过函数返回默认值,一般只有对象或数组才会如此,基本类型直接写值即可
        default: () => ({
          name: "al",
          age: 29,
        }),
    
        // 是否必须
        isRequied:false
      },
    },
  2. 通过 <script setup> 形式,使用 defineProps() 来接收父组件传递数据,通过泛型来限制数据类型和是否必须,通过 withDefaults() 设置默认值 

    let props = withDefaults(defineProps<{list?:Persons}>(),{
      list:() =>[{id:'asdasg01',name:'小猪佩奇',age:18}]
    })
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值