Vue3组合是api父子组件传值

这个是用来记录父子组件传值的一种方式,我这里用的是vite+ts+element plus

在Vue3中props方式,父组件传值给子组件

后面有完整代码,这里是简单的描述过程

父组件引用子组件,在子组件中定义一个属性(这个属性是子组件中defineProps定义的),属性值是自己定义的参数,也就是你要传给子组件的参数值

<template>
    <div>
        <!-- 这里两种形式都可以,子组件引用,通过props传值, 这里的:sonValue(:son-value 这个是驼峰命名在标签的一种转换形式而已,跟使用:sonValue效果是一样的) 是子组件定义defineProps中定义的sonValue, 具体看子组件代码-->
      <Son :son-value="value"></Son> <!-- 这个是引用的子组件 -->
      <!-- <Son :sonValue="value"></Son> -->
    </div>
</template>

子组件的script中通过defineProps定义属性,用来接收父组件传过来的值,注意在vue3中,不写import { defineProps } from ‘vue’;这一句也是可以的,因为.vue文件中内置了这个,这是一个宏。所以不一定要引用的,可以不写import { defineProps } from ‘vue’;直接用defineProps()

<script setup lang="ts">
import { defineProps } from 'vue';
// props传值,非ts写法,ts写法后面后面的完整代码有,具体看后面的代码
defineProps({
  // 这个就是父组件在使用子组件时的属性值,也就是父组件的::son-value或者:sonValue
  sonValue: String // 注意这里的String的S是大写的,不是String,也可以用小写的string,看个人需求
})
</script>

完整代码如下

父组件parent.vue

<template>
  <div>
    <h1>这个是父组件</h1>
    <el-input v-model="value" placeholder="请输入内容,父组件输入框"></el-input>
    <hr>
    <el-row>
      <!-- 这里两种形式都可以,子组件引用,通过props传值 -->
      <Son :son-value="value"></Son>
      <!-- <Son :sonValue="value"></Son> -->
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'
const value = ref('')
</script>

<style scoped>
</style>

子组件Son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      这是父组件传进来的值 {{ sonValue }}
    </el-row>
  </div>
</template>

<script setup lang="ts">
import { defineProps } from 'vue';
// props传值,非ts写法,ts写法后面后面的完整代码有,具体看后面的代码
defineProps({
  sonValue: String // 注意这里的String的S是大写的,不是String
})
</script>

<style scoped>
</style>

结果就是,在父组件的输入框输入数据,子组件接受到数据并展示

在这里插入图片描述

通过上面的方式就可以将父组件的内容传给子组件了,但是这里就有一个问题。Vue中如果在子组件中修改props的值,会发出警告的。并不是很支持直接修改props传的值,所以我们这里可以通过一个变量接受props传入的值,然后在template中展示。而且这里会有一个现象就是,在template标签中,传入的props的值是可以跟父组件实时改变的。但是在script setup lange="ts"中的数据这个不是实时改变的,总结一句话就是,如果是直接使用props的值,可以实时改变,如图过是想在js或者ts赋值给变量后使用,就不会实时改变。具体如下。

父组件Parent.vue

<template>
  <div>
    <h1>这个是父组件</h1>
    <el-input v-model="value" placeholder="请输入内容,父组件输入框"></el-input>
    <hr>
    <el-row>
      <!-- 这里两种形式都可以,子组件引用,通过props传值,也可以传一个user对象 -->
      <Son :son-value="value" :user="user"></Son>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'
const value = ref('这是父组件的初始value值')
const user = ref({
  username: '张三',
  age: 23
})
</script>

<style scoped>
</style>

子组件Son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      这是父组件传进来的值 {{ sonValue }}
    </el-row>
    <hr>
    <el-row>
      这是子组件中的Script中的值scriptValue1: {{ scriptValue1 }} <br>不会实时改变的
    </el-row>
    <hr>
    <el-row>
     user对象数据:用户名:{{ user.username }}, 年龄:{{ user.age }}
    </el-row>
  </div>
</template>

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

//  下面这里是ts的写法
const props = defineProps<{
  sonValue: String, // 注意这里的String的S是大写的,不是String
  user: {
    username: String,
    age: Number
  }
}>()

const scriptValue1 = props.sonValue

// js的写法,非ts的写法
// const props = defineProps(
//   {
//     sonValue: String, // 注意这里的String的S是大写的,不是String
//     user: {
//       username: String,
//       age: Number
//     }
//   }
// )

</script>

<style scoped></style>

在这里插入图片描述

要想Script中的值也实时改变,我们可以通过watch监听的props的方式进行实时改变script中的值

父组件parent.vue

<template>
  <div>
    <h1>这个是父组件</h1>
    <el-input v-model="value" placeholder="请输入内容,父组件输入框"></el-input>
    <hr>
    <el-row>
      <!-- 这里两种形式都可以,子组件引用,通过props传值,也可以传一个user对象 -->
      <Son :son-value="value" :user="user"></Son>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'
const value = ref('这是父组件的初始value值')
const user = ref({
  username: '张三',
  age: 23
})
</script>

<style scoped>
</style>

子组件Son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      这是父组件传进来的值 {{ sonValue }}
    </el-row>
    <hr>
    <el-row>
      这是子组件中的Script中的值scriptValue1: {{ scriptValue1 }} <br><b>通过监听之后这时就会实时改变</b>
    </el-row>
    <hr>
    <el-row>
     user对象数据:用户名:{{ user.username }}, 年龄:{{ user.age }}
    </el-row>
  </div>
</template>

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

// 面这里是ts的写法
const props = defineProps<{
  sonValue: string, // 这里的String是小写的s
  user: {
    username?: String, // 有?说明非必须的,这里也可以是string
    // default: '----', 这个可以为username设置默认值
    age: Number
  }
}>()

const scriptValue1 = ref('')

scriptValue1.value = props.sonValue

/**
 * 监听props,当props变化时改变scriptValue1的值
 * 我们也可以通过监听做一些事情,如封装echarts组件组件通过props传值的时候,或许可以
 * 使用到类似这样的监听,当父组件传入的数据发生变化时,根据父组件传入的数据重新渲染值的到新的图表,后面会写一篇文章的
 */
watch(props, (newValue) => {
  console.log(newValue) // console.log(newValue)可以知道这里是vue3中reactive类型的数据
  console.log(`这是变化的新值:`, newValue.sonValue)
  // 统监听props的值变化,动态修改scriptValue1的值
  scriptValue1.value = newValue.sonValue
})


// const result = ref('0')
// watch(result, (newValue) => {
//   console.log(newValue)
// })

// js的写法,非ts的写法
// const props = defineProps(
//   {
//     sonValue: String, // 注意这里的String的S是大写的,不是String
//     user: {
//       username: String,
//       age: Number
//     }
//   }
// )

</script>

<style scoped>
</style>

在这里插入图片描述

通过defineEmits子组件传值给父组件

在子组件中声明定义一个常量,用户实现方法传参,方法名可以自定义

const emit = defineEmits(['sonChange', 'sonChangParams'])

这个需要在子组件中定义一个方法去触发emit这个方法,具体如下代码所示

emit("sonChangParams", value) // 其中value是参数

在父组件中

<template>
    <div>
         <!-- 只调用一个事件 -->
      <!-- <Son  @son-change="sonChange"></Son> -->
      <!-- <Son  @sonChange="sonChange"></Son> -->
      <!-- 自定义事件中,可以值传一个时间,也可以传两个参数,前提是名称要跟子组件定义的const emit = defineEmits(['sonChange', 'sonChangParams'])参数个数
        和名称一样,这里的可以用@son-change 、 @son-chang-params 或者  @sonChange 、sonChangParams都可以 -->
      <!-- 调用两个事件 -->
      <Son @son-change="sonChange" @son-chang-params="sonChangeParams"></Son>
    </div>
</template>

完整代码如下:
父组件parent.vue

<template>
  <div>
    <h1>这个是父组件</h1> 
    <hr>
    <el-row>
      子组件修改调用父组件:
    </el-row>
    <el-row>
      {{ parentValue }}
    </el-row>
    <hr>
    <el-row>
      <!-- 只调用一个事件 -->
      <!-- <Son  @son-change="sonChange"></Son> -->
      <!-- <Son @sonChange="sonChange"></Son> -->
      <!-- 自定义事件中,可以值传一个时间,也可以传两个参数,前提是名称要跟子组件定义的const emit = defineEmits(['sonChange', 'sonChangParams'])参数个数
        和名称一样,这里的可以用@son-change 、 @son-chang-params 或者  @sonChange 、sonChangParams都可以 -->
      <!-- 调用两个事件 -->
      <Son @son-change="sonChange" @son-chang-params="sonChangeParams"></Son>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'

const parentValue = ref('')

const getList = () => {
  parentValue.value = '子组件按钮调用父组件的函数getList()'
}

// 子组件按钮事件
const sonChange = () => {
  // 子组件按钮时间
  getList()
}

/**
 * 获取子组件传给父组件的参数
 * @param val 这个是子组件传的参数,这里调用的是带参数的子组件触发方法
 */
const sonChangeParams = (val: any) => {
  // 子组件修改父组件的parentValue值
  parentValue.value = val

  // 返回子组件传给父组件的参数
  return val;
}

</script>

<style scoped>
</style>

子组件son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      <el-button type="primary" @click="usedParentMethods">子组件通过点击事件调用父组件的方法(无参)</el-button>
    </el-row>
    <el-row>
      <el-button type="success" @click="usedParentMethodsParams">子组件通过点击事件调用父组件的方法(通过携带参数将子组件的某些数据传给父组件)</el-button>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import { ref, defineEmits, reactive } from 'vue';
const childrenValue = ref('这个是子组件传给父组件的参数的值,这是子组件传给父组件的参数值')
// const user = reactive({
//   userName: '子组件张三'
// })
// 名字自定义,如这里有两个sonChange、sonChangParams
const emit = defineEmits(['sonChange', 'sonChangParams'])

// 调用父组件的函数
const usedParentMethods = () => {
  // 这里的名字要跟const emit = defineEmits(['sonChange', 'sonChangParams'])定义的一样
  emit("sonChange") // 无参传值
}

// 传入参数
const usedParentMethodsParams = () => {
  // emit既可以传复杂数据,也可以传简单数据
  // emit("sonChangParams", user)
  // 这里的名字要跟const emit = defineEmits(['sonChange', 'sonChangParams'])定义的一样
  emit("sonChangParams", childrenValue.value)
}
</script>

<style scoped>
</style>

其结果如下所示

在这里插入图片描述

在这里插入图片描述

通过ref的方式给子组件传值给父组件

另外一种方式就是通过ref的方式子组件给父组件传值,这种方法在子组件中导入使用defineExpose方法,把你想要传的参数或者方法(函数)抛出去

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

const childrenValue = ref('这是子组件的childrenValue值')

const childrenValue2 = ref('这是子组件的childrenValue值')

const sum = ref(0)

const play1 = () => {
  sum.value = 3 + 7
  console.log("调用了子组件的play()函数")
}

const play2 = () => {
  return '这个是子组件的play2()函数'
}

defineExpose({
  // 只有暴露出去的参数和函数在ref中才可以访问到,如这里childrenValue2没有暴露出去,所以
  // 不能在父组件中使用ref访问到childrenValue2的值
  childrenValue,
  play1, // 这个是函数,但是不加括号(),否则报错,在父组件中使用是需要加括号,具体看父组件的代码
  play2, // 这个是函数,但是不加括号(),否则报错,在父组件中使用是需要加括号,具体看父组件的代码
})

</script>

在父组件中引用的子组件上接收ref,名字自定义

<tepmlate>
    <div>
       <Son ref="childrenRef"></Son>
    </div>
</tepmlate>

通过ref就可以直接获取到子组件的值了

// 这里ref中什么都不写
const childrenRef = ref()
  console.log(childrenRef.value.childrenValue)
  childrenRef.value.play1()
  const play2ReturnValue = childrenRef.value.play2()
  console.log(play2ReturnValue)

完整代码如下:

父组件parent.vue

<template>
  <div>
    <h1>这个是父组件</h1>
    <el-input v-model="value" placeholder="请输入内容,父组件输入框"></el-input>
    <el-button type="primary" @click="getChildrenRef">运行或者获取子组件的参数和函数</el-button>
    <hr>
    <el-row>
      <Son ref="childrenRef"></Son>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'
const value = ref('这是父组件的初始value值')

// 这里ref中什么都不写
const childrenRef = ref()

// 这几个不能直接这样写,应该放到一个函数中,直接在这里写会报错的,代码都运行不起来
// console.log('子组件的childrenValue值')
// console.log(childrenRef.value.childrenValue)
// console.log('子组件的play方法')
// childrenRef.value.paly()
// childrenRef.value.play2()

const getChildrenRef = () => {
  // 这里的childrenValue和play1()、play2()要跟子组件中defineExpose暴露的一一对应,
  // 如果是函数,记得加括号(),在子组件的defineExpose中不需要加括号(),具体看子组件的代码
  // console.log('子组件的childrenValue值=====》')
  console.log(childrenRef.value.childrenValue)
  // console.log('子组件的play1()方法')
  childrenRef.value.play1()
  // console.log('调用了play2()的方法')
  const play2ReturnValue = childrenRef.value.play2()
  console.log(play2ReturnValue)
}

</script>

<style scoped>
</style>

子组件son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      {{ childrenValue }}
    </el-row>
    <el-row>
      {{ childrenValue2 }}
    </el-row>
    <el-row>
      3 + 7 = {{ sum }}
    </el-row>
  </div>
</template>

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

const childrenValue = ref('这是子组件的childrenValue值')

const childrenValue2 = ref('这是子组件的childrenValue值')

const sum = ref(0)

const play1 = () => {
  sum.value = 3 + 7
  console.log("调用了子组件的play()函数")
}

const play2 = () => {
  return '这个是子组件的play2()函数'
}

defineExpose({
  // 只有暴露出去的参数和函数在ref中才可以访问到,如这里childrenValue2没有暴露出去,所以
  // 不能在父组件中使用ref访问到childrenValue2的值
  childrenValue,
  play1, // 这个是函数,但是不加括号(),否则报错,在父组件中使用是需要加括号,具体看父组件的代码
  play2, // 这个是函数,但是不加括号(),否则报错,在父组件中使用是需要加括号,具体看父组件的代码
})

</script>

<style scoped>
</style>

具体结果如下所示,浏览器中可以f12可以看到看到这个结果

在这里插入图片描述

在这里插入图片描述

既然我们在父组件中能达到子组件的值了,这里我们就可以尝试在父组件中修改子组件的值了,还不需要使用监听就可以修改子组件的值了,前提是子组件暴露出来的值或者函数对应的值是响应式的,如果不是响应式的值,改了也没用。调用子组件的函数,

父组件parent.vue

<template>
  <div>
    <h1>这个是父组件</h1>
    <el-input v-model="parentValue" placeholder="请输入内容,父组件输入框" @input="changChildValue"></el-input>
    <el-row>
      调用子组件的求和函数得到的结果为:12 + 13 = {{ sum }}
    </el-row>
    <hr>
    <el-row>
      <Son ref="childrenRef"></Son>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import Son from '../components/Son.vue';
import { ref } from 'vue'
const parentValue = ref('这是父组件的初始value值')

// 这里ref中什么都不写
const childrenRef = ref()
const sum = ref(0)
// element plus中el-input封装的change事件
const changChildValue = () => {
  // console.log(childrenRef.value.childrenValue) 获取子组件暴露的childrenValue值,具体看子组件代码
  childrenRef.value.childrenValue = parentValue.value
  sum.value = childrenRef.value.sum(12, 13) // 函数记得添加括号(),还要看函数需不需要传参
}

</script>

<style scoped>
</style>

子组件son.vue

<template>
  <div style="width: 500px; height: 400px; background-color: pink;">
    <h1>这是子组件</h1>
    <hr>
    <el-row>
      这是暴露给父组件的值: {{ childrenValue }}
    </el-row>
    <el-row>
      父组件调用求和函数之后的值:{{ sumValue }}
    </el-row>
    <el-row>
      3 + 7 = {{ sumValue }}
    </el-row>
  </div>
</template>

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

const childrenValue = ref('这是子组件的childrenValue值')

const sumValue = ref(0)

const sum = (value1: number, value2: number) => {
  sumValue.value = value1 + value2
  return value1 + value2
}


defineExpose({
  // 只有暴露出去的参数和函数在ref中才可以访问到,如这里sumValue没有暴露出去,所以
  // 不能在父组件中使用ref访问到sumValue的值
  childrenValue,
  sum, // 这个是函数,但是不加括号(),否则报错,在父组件中使用是需要加括号,具体看父组件的代码
})

</script>

<style scoped>
</style>

运行结果如下所示

在这里插入图片描述

总结,在子组件给父组件传值的过程中,如果子组件有点击事件,然后传值给父组件,使用defineEmits方式比较方便。如果是子组件中没有任何触发一个方法去触发emit这个方法时,使用ref的方式比较方便,可以拿到子组件传给父组件的值,还可以很方便的调用子组件的方法。使用ref父组件还可以很方便修改子组件的响应式的值,这个就相当于props的监听那里一样,可以在父组件修改script中的值。如果结合echarts的话,还可以调用对应的子组件方法设置echarts的配置项,从而修改echarts的图表配置。这个后面会写一篇使用vue封装echarts的学习文章的。

Logo

前往低代码交流专区

更多推荐