父传子

一、父标签的属性是传值的容器

  • html属性可以通过父标签的属性传递给子组件;
  • 字符可以通过父标签的自定义属性传递给子组件;
  • 变量、DOM对象、函数可以通过v-bind绑定父标签的自定义属性传递给子组件。
  • v-on指令

二、透传-属性式传值

默认,父标签的所有属性(html属性、自定义属性、绑定属性、事件)都会透传给子组件的标签上,转换为子组件的标签属性。

a、自动接收

默认,子组件的标签会自动接收透传的所有属性与事件,接收后子标签与父标签的属性、事件一一对应(事件不会显示在标签上)。

b、手动接收

可以在子组件标签上用指令手动接收父标签上的属性与事件。

  • 接收全部属性与事件:v-bind="$attrs" (注意attrs要带“$”符号),接收后子标签与父标签的属性、事件一一对应。
  • 选择接收属性:v-if="$attrs.属性名"(子标签可以自由选择指令接收父属性值(不再继承父标签的属性名),所有指令都可用来接收,包括文本插值。)
  • 选择接收事件与自定义:@keyup="$attrs.on事件名"(注意事件名前要加on,事件、自定义事件名首写字母要大写。)

选择接收的强大功能
子标签可以自由选择指令接收父属性值,使传参能够自由的在子组件中使用。
如下:子标签的id属性值引用父标签的自定义属性zi;v-if指令值引用父标签的自定义属性two赋;v-html指令和文本插值引用父标签的自定义属性one;@click引用父标签的自定义属性func,父事件onClick。
父组件

<template>
  <button ref="wow" >草</button><br>
  <HelloWor  :one="wow.outerHTML"  :two="wo" :func="w" @click="w" zi="我是字符"/>
</template>
<script setup>
import HelloWor from './Hello.vue'
import {ref} from 'vue'
const wow=ref({})
const wo=ref(false)
function w(){console.log('我是函数')}
</script>

子组件

<template>
   <button id="$attrs.zi" @click="$attrs.func">我是子组件</button>
   <div v-html="$attrs.one" v-if="!$attrs.two"  @click="$attrs.onClick"></div>
   {{$attrs.onClick}}
</template>
<script></script>

c、禁止自动接收inheritAttrs

自动接收副作用

  • 指定警告
    当子组件<template>下有多个顶级标签且都没有手动接收父参时,系统不知哪个顶级标签自动接收,会跳出指定接收标签警告。
  • 非需要作用
    当子组件<template>下只有一个顶级标签时,子标签会全部继承父标签的属性与事件,如果子标签不需要这些属性与事件,会引发不必要的后果。
    如下,因<div>自动接收了父的click事件,点击按钮时会触发两次事件。

    <template>
    <div>
      <p>我是子组件2</p>
      <button @click="$attrs.onClick">点击我触发自定义事件click</button>
    </div>
    </template>

    因此,当多个顶级标签且都没有手动接收父参或只有一个顶级标签要避免父属性或事件对顶级标签污染时,要禁用自动接收!!

禁止自动接收
可通过设置 inheritAttrs: false禁止自动接收透传。
在vue3.3版以上,inheritAttrs配置项可以书写在defineOptions函数里,如:defineOptions({inheritAttrs: false})
在vue3.3版以下,需另开一个<script>以默认暴露的形式书写,如下:

  <script>
  export default { inheritAttrs: false }
  </script>

d、透传特性

  • 透传与原生共同作用、冲突覆盖
    接收与本标签的原有属性、指令共同作用于本标签,当接收与本标签原有属性、指令有冲突时,接收体会覆盖本标签的原有属性、指令。
  • 自动与指定共同作用、冲突覆盖
    自动接收可以和指定接收共存,当有冲突时,自动接收​属性会覆盖指定接收属性,。
  • 透传DOM对象
    DOM对象属性巨多,子标签引用书写要几百行,因此引用时只会显示[object HTMLButtonElement]这样一个说明,只有DOM对象下的属性才能正常引用。
  • 响应性
    引用的响应性由父参响应性决定。
    父参是响应性变量,引用会响应更新,普通变量改变时,只能通过手动刷新引用来更新。

e、在<script setup>里引用

  • 参数只读对象除外
    为避免污染父数据,父传子的参数只可读(只能引用不能操作)。
    但如果参数是对象或数组,可以操作对象的子属性或数组的子项。
    DOM对象型父参,子组件可以操作DOM对象的属性。
    应避免操作对象的子属性或数组的子项,这样会污染父数据,有可能造成未知的后果。
  • 需要引入useAttrs
    <script setup>里引用透传需要从vue引入useAttrs函数,useAttrs函数会把所有参数打包成一个对象,各参数为对象的属性。如下:

    <script setup>
    import { useAttrs } from 'vue'
    const attrs = useAttrs()
    </script>
  • 生命周期
    父标签渲染完成子组件才开始运行,父标签内的属性在子组件初始化时(子组件DOM渲染前)就传递给了子组件。因此父参在子组件初始化时即可使用。
  • dom对象
    父标签如要放置DOM对象的引用,DOM对象标识变量需定义为ref响应性变量(使引用响应变量的更新)且变量要赋空对象(避免‘无法读取未定义属性’警告)
    父标签渲染完首次传给子组件的DOM对象标识变量是个空对象,变量获得DOM对象后,子组件会响应更新。
    因此DOM对象型的父参在子组件更新前才可正常引用。需放在onBeforeUpdate生命函数之上的函数体内。
  • 未注册透传生效
    透传引用并不会总是生效,当透传属性、事件被defineprops 或 defineemits 注册时,引用此透传属性、事件不会生效。

三、defineProps-声明式传值

属性透传可以直接引用,defineProps传值需用defineProps函数声明后才可用。

(一)defineProps函数​书写规范

普通书写:defineProps(['属性名','属性名'])

  • 所要传进来的属性名放在[]中括号里
  • 属性名要加引。

较验书写:defineProps({属性名:{较验项},属性名:{较验项}})

  • 所要传进来的属性放在{}大括号里
  • 属性​值为较验项,较验项需放在{}大括号里。

事件名的书写: 注意事件名前要加on,事件、自定义事件名首写字母要大写。

(二)较验项

对传入的属性进行各种校验。如果传入的值校验不合格,会在后台抛出警告来提醒开发者。
共有4个较验项,分别为:

  • type 数据类型
  • default 默认值
  • required 必须传值
  • validator 自定义验证函数
1、type: 数据类型较验项

用于较验传入参数的数据类型,当传入的参数类型与type较验项类型不符时,跳出警告

  • type较验项类型可以是下列这些原生类型
    String、Number、Boolean、Array、Object、Date、Function、Symbol、Error。
    注意类型的首字母要大写。
  • 多种类型
    当允许属性值​为多种类型时,可以将类型放在数组内,如:type: [String, Number]
  • 所有类型
    当type值为null和undefined时,允许属性值​为所有类型,即会跳过类型检查。
  • 空值类型
    当null和undefined与其它类型组成数组时,允许传入空值,即传入一个没有类型的空值。
  • 自定义构造类型
    除了原生类型,也可以较验自定义的类或构造函数。defineProps() 宏中的参数不可以访问 <script setup> 中定义的其他变量、函数(包括构造函数),因此构造函数放在子上不能被引用,需单独成js文件,通过import引入父子组件才可在较验中使用。
  • 较验项简写
    当只有type较验项时,较验项可以省略书写,直接在属性名后书写类型。如defineProps({属性名:String}),实例如下:
    父组件

    <template>
    <button ref="wow" v-show="false">草</button><br>
    <HelloWor  :propsG='person' :one="wow"  :two="wo"  :three="w" />
    <HelloWor  :propsG='person' one="wow"  :two="woo"  three="w" />
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import {Person}  from './kkk.js'
    import {ref} from 'vue'
    const person=new Person()
    const wow=ref({})
    const wo=ref(false)
    const woo=ref()
    function w(){wo.value=!wo.value, console.log('我是函数')}
    </script>

    子组件

    <template>
     <button >我是子组件{{propsG.name}}</button>
    </template>
    <script setup>
    import {Person}  from './kkk.js'
     defineProps({
        propsG:Person,       //自定义类型较验
        one:[String, Object],//多种类型都可以
        three:null,           //不较验类型
        two:[Boolean, null] })//空类型也可以
    </script>

    构造函数js文件

    function Person(){
      this.name = '传参是构造类型';}
    export {Person}
2、default: 默认值设置项

当父标签无参、空值参时,vue会自动赋与默认值 undefined,通过default可以手动设置无参、空值参时赋与的默认值。
default有两种书写方式,分为属性直赋式、函数返回式。

  • 属性直赋式
    缺省值以default属性值的形式书写,如:default:{message: 'hello'}default:100
  • 函数返回式
    缺省值以default函数的返回值形式书写,如:default(rawProps){return {message:'hello'}}
    函数返回式能在函数体内设置条件返回,能使默认值更有针对性。
    参数: vue会给default函数传递一个参数,这个参数是父标签上所有传参再加Prototype集合组成的一个对象。
    当type的类型为Function​时,缺省值不是返回值,而是整个default函数。此时如果父传过来的函数用于子事件,default函数会传递一个事件对象参数。
  • 类型一致性
    default设置的默认值类型要与type类型一致,否则后台会警告“传入值非定义类型”。
  • Boolean 类型默认值
    父标签无参不是自动赋与默认值 undefined而是false,空值参时不是自动赋与默认值 undefined而是true。
    当type是多种类型时(type数组中含有Boolean),依照Boolean无参赋false空值参赋true。当type类型组中有String类型时,Boolean在前时依照无参赋false空值参赋true原则,String在前时赋undefined。
3、required: 无参报警设置项

当父标签无参数传递,是否报警设置,当值为true时,无参传递后台会发出警告(空值参不会后台发出警告)。

4、validator: 自定义验证函数

当validator函数返回值为true时,不发出报警,​无返回值或返回值为false时会发出报警。
参数: vue会给validator函数传递两个参数,第1个参数是要较验的传参,第2个参数是父标签上所有传参组成的一个对象。实例如下:
父组件

<template>
  <HelloWor  :one="wow"  :tt="w"/>
</template>
<script setup>
import HelloWor from './Hello.vue'
import {ref} from 'vue'
const wow=ref(1)
function w(){wow.value++}
</script>

子组件

<template>
  <button @click="tt">我是子组件{{one}}</button>
</template>
<script setup>
 defineProps({
   one:{validator(value, props) {if(value<10){return true}else{return false}}},
   tt:null})
</script>

(三)引用父参

1、生命周期

父标签渲染完成子组件才开始运行,父标签内的属性在子组件初始化时(子组件DOM渲染前)就传递给了子组件。因此父参在子组件初始化时即可使用。
DOM对象标识变量需定义为ref响应性变量(<template>下引用会响应变量的更新)且变量要赋空对象(避免‘无法读取未定义属性’警告),在<script setup>下引用需放在onBeforeUpdate生命函数之上的函数体内。

2、参数只读对象除外

为避免污染父数据,父传子的参数只可读(只能引用不能操作)。
但如果参数是对象或数组,可以操作对象的子属性或数组的子项。
DOM对象型父参,子组件可以操作DOM对象的属性。
应避免操作对象的子属性或数组的子项,这样会污染父数据,有可能造成未知的后果。

3、响应性

引用的响应性由父参响应性决定。
父参是响应性变量,引用会响应更新,普通变量改变时,只能通过手动刷新引用来更新。

4、模板部、逻辑部引用书写规范不同

模板部
defineProps函数声明后的属性可以在<template>下直接引用。
逻辑部
defineProps函数声明后需赋值给变量,以这个变量的属性的形式引用。如下:
父组件

<template>
  <button ref="woo"></button>
  <HelloWor  :one="wow" :two="woo" :tt="w"/>
</template>
<script setup>
import HelloWor from './Hello.vue'
import {ref} from 'vue'
const wow=ref(1)
const woo=ref({})
function w(){wow.value++}
</script>

子组件

<template>
  <button>我是子组件{{two.outerHTML+one}}</button>
</template>
<script setup>
import {onBeforeUpdate} from 'vue'
 const uiu=defineProps({
   one:null,
   two:null  })
 onBeforeUpdate(()=>{console.log(uiu.two.outerHTML)})
</script>

四、透传与defineProps的区别

1、defineProps有较验项

defineProps有较验项来对传值进行较验与控制,透传不能对传值进行较验。

2、defineProps具有排它性

被defineprops注册了的传值,透传就不能再引用此传值。

五、修改父参

除了对象或数组外的父参只读(不能操作),以免父参被污染。
如果要对除了对象或数组外的父参修改,可以通过调用函数来实现。
把修改代码放在父组件的函数里,把函数传递给子组件调用,就能实现修改父参的目地。


百分之一百零八
15 声望3 粉丝