1.props

vue2中组件间数据共享大概有这几种方式:

props$emit、v-model、 .sync、 ref、$bus$attrs$listeners$parent$children$root、provide、inject、vuex

在vue3中数据共享的方式要分成几种情况,由于在组合式api中vue2语法也可以使用,但是官方不推荐这种混合的语法,所以我们只讨论在组合式api中进行组件间数据共享.大致分成两种情况,普通的setup写法和setup的语法糖形式写法

用 props 传数据给子组件

1.1普通的setup写法

one.vue组件:

<template>
  <div>
    <h3>父组件</h3>
    <!-- 第一步在子组件的UI标签上,绑定自义定属性,自定义属性的值就是要传递给子组件的值 -->
    <two :coffe="coffe" :title1="title1" :tea="tea"></two>

  </div>
</template>
<script>
import {ref,reactive,toRaw} from "vue"
import two from "./two"
export default {
  components:{
    two
  },
  setup(props) {
    const coffe=ref("厚乳拿铁!");
    const title1=ref("我是父组件数据");
    const tea=toRaw(reactive(["乌龙茶","西湖龙井"])) ; 

    return {
      coffe,
      title1,
      tea
    }
  }
}
</script>

two.vue组件:

<template>
  <div>
    <h3>子组件</h3>
    <p>接收父组件传递过来的值:{{coffe}}---{{tea}}---{{title1}}</p>
  </div>
</template>
<script>
export default {
  //第二步:在子组件中使用props接收父组件传值过来的
  //如果在子组件中不接收父组件传递过来的数据,setup(props)中的props参数是没有值的
  //props的写法有两种:对象和数组
  /** 
    props:{
       coffe:{
         type:String,//传递的数据类型
         required:true, //是否必须要传递
         default:'coffe' //默认值
       },
       tea:{
         type:Array,
         required:true,
         default:'tea'
       },
       title1:{
         type:String
       }

     },
     */
    //props数组的简写形式
    props:["coffe","tea","title1"],
    setup(props) {
      console.log(props);
    } 
}
</script>

1.2在setup语法糖写法

父组件Home.vue组件:

<template>
  <div class="home">
    <h3>home.vue组件</h3>

    <child :isshow="isshow" :money="money"></child>
  </div>
</template>

<script setup>
import {ref,reactive} from "vue";
  import child from "./child"
  const isshow=ref(false);
  const money=ref("我这有一些钱拿去花吧!")


</script>

子组件child.vue组件

<template>
  <div>
    <h3>child.vue组件</h3>
    <p>接收父组件传递过来的数据(setup语法糖):----{{money}}</p>
    <p><button @click="toggle">控制盒子的显示和隐藏</button> </p>
    <div class="child-box" v-if="isshow"></div>
  </div>
</template>
<script setup>
//defineProps:在语法糖中不需要引入 直接使用
const props=defineProps({
  isshow:Boolean,
  money:{
    type:String,
    default:''
  }
})
console.log(props) //Proxy {isshow: false, money: '我这有一些钱拿去花吧!'}

const toggle=()=>{
  //想要使用传递过来的数据:props.属性   
    console.log(props.isshow); //false
    //点击按钮让child-box盒子显示
    //需要修改父组件传递过来的isshow属性,这时会报一个警告:reactivity.esm-bundler.js?89dc:521 Set operation on key "isshow" failed: target is readonly. 
    //props.isshow=true;
}
</script>
<style lang="scss" scoped>
.child-box{
  width: 200px;
  height: 200px;
  background: purple;
}

</style>

1.3修改父组件传递过来的属性

<script setup>
//defineProps:在语法糖中不需要引入 直接使用
const props=defineProps({
  isshow:Boolean,
  money:{
    type:String,
    default:''
  }
})

const toggle=()=>{
  //想要使用传递过来的数据:props.属性   
    console.log(props.isshow); //false
    //点击按钮让child-box盒子显示
    //需要修改父组件传递过来的isshow属性,这时会报一个警告:reactivity.esm-bundler.js?89dc:521 Set operation on key "isshow" failed:         //target is readonly. 
    props.isshow=true;
}
</script>

由于vue是单向数据流,父组件传递给子组件的属性,子组件只能使用不能修改,在vue2中可以使用.sync修饰符.但是在vue3中v-bind的.sync修饰符和组件的model选项被删除了.在vue3中我们可以直接使用v-model语法糖进项绑定,绑定属性发生了变化,属性名是 modelValue, 事件名是:update:modelValue.

在one.vue中:

<template lang="">
  <div>
    <h3>父组件</h3>
    <p>count的值为:{{count}}</p>
    <!-- 如果想要获取原生事件对象:
    加入绑定事件函数fn fn(e) {e就是事件对象}
    如果绑定的是js表达式,提供的是一个默认的变量$event 

    如果想要获取自定义事件
    绑定函数fn fn(data){ data触发自定义事件的参数}
    如果绑定的是js表达式 此时 $event代表触发自定义事件的传参 -->
    <two :modelValue="count"  @update:modelValue="count=$event" ></two>

  </div>
</template>
<script>
import {ref,reactive,toRaw} from "vue"
import two from "./two"
export default {
  components:{
    two
  },
  setup(props) {
     const count=ref(100)
    return {
      count
    }
  }
}
</script>

two.vue组件:

Vue3setup函数上提供了两个参数,一个props,一个是context下面的emit方法,分别来处理输入和输出。

<template lang="">
  <div>
    <h3>子组件</h3>
    <p><button @click="changModelValue">点击修改count</button> </p>
  </div>
</template>
<script>
export default {
    props:["modelValue"],
    setup(props,{emit}) {
    const changModelValue=()=>{
        //改变父组件传递过来的数据
        emit("update:modelValue",200)
    }

      return {
        changModelValue
      }
    } 
}
</script>

可以简写为v-model

 <!-- :modelValue="count"   @update:modelValue="count=$event"可以简写为v-model="count" -->

   <two :coffe="coffe" :title1="title1" :tea="tea" v-model:count="count"  ></two>

const changModelValue=()=>{
        emit("update:count",200)
  }

setup语法糖的写法:

//定义抛出事件的名字 
//defineEmits适用于 Vue3.2版本,不需要引入
const emit=defineEmits(["update:count"])

 const changModelValue=()=>{
        emit("update:count",200)
    }

2.$emit

子组件向父组件传值可以使用$emit

vue3setup函数上提供了两个参数,一个props,一个是context下面的emit`方法,分别来处理输入和输出。

2.1setup写法

子组件two.vue

<template>
  <div>
    <h3>子组件</h3>
    <p>子组件向父组件传值</p>
    <p><button @click="myclick">点击向父组件传递数据</button> </p>
  </div>
</template>

<script>
import {ref,reactive} from "vue"
export default {
    setup(props,{emit}) {
    const movie={
        name:'这个杀手不太冷静',
        price:56
      }

    const myclick=()=>{
        emit("send",movie)
    }
      return {
        myclick
      }
    } 
}
</script>

父组件one.vue:

<template>
  <div>
   <two @send="resiveMovie"  ></two>
    <p>接收从子组件传递过来的值:{{resiveData.film}}</p>
  </div>
</template>

<script>
import {ref,reactive,toRaw} from "vue"
import two from "./two"
export default {
  components:{
    two
  },
  setup(props) {
    const resiveData=reactive({})
     const resiveMovie=(film)=>{
       console.log(film)
       resiveData.film=film;
     }
    return {
      coffe,
      resiveMovie,
       resiveData
    }
  }
</script>

2.2setup语法糖写法

child.vue子组件:

<template>
  <div>
   <p>
      <input type="text" v-model="str"  @input="changStr" />
    </p>
  </div>
</template>

<script setup>
import {ref} from "vue"
const emit=defineEmits(["sendStr"])
const str=ref("")
const changStr=()=>{
     emit("sendStr",str)
}
</script>

home.vue父组件:

<template>
  <div>
   <p>接收子组件传递过来的数据:{{message}}</p>
    <child  @sendStr="reaiveStr"  ></child>
  </div>
</template>

<script setup>
import {ref} from "vue"
const message=ref("")
const reaiveStr=(msg)=>{
     message.value=msg
}
</script>

3.ref

子组件child.vue

//向外部暴露属性或者方法适用于Vue3.2版本, 不需要引入
    defineExpose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })

父组件home.vue

<template>
     <child  :money="money"   v-model:isshow="isshow" @sendStr="reaiveStr"  ref="childCom"   ></child>
   <p><button @click="handlerClick">点击获取子组件</button> </p>
</template>
<script setup>
   const childCom=ref(null);
   const handlerClick = () => {
   console.log(childCom.value)
        console.log(childCom.value.childName) // 获取子组件对外暴露的属性
        childCom.value.someMethod() // 调用子组件对外暴露的方法
    }
</script>

4.attrs

attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合

4.1setup中获取

父组件:

<template>
  <two :coffe="coffe" :title1="title1" :tea="tea" class="demo" ></two>
</template>

子组件:

<script>
    props:["coffe"],
    setup(props,{emit,attrs}) {
      console.log("attrs",attrs) //{title1: '我是父组件数据', tea: Array(2), class: 'demo'}
    }
</script>

4.2setup语法糖获取

父组件:

<template>
  <child  :money="money" ></child>
</template>

子组件:

<script setup>
   import {ref,useAttrs} from "vue"
//接收attrs
 const attrs = useAttrs();
 console.log("attrs",attrs) //{money: '我这有一些钱拿去花吧!}
</script>

5. provide / inject

provide / inject 为依赖注入

provide:可以让我们指定想要提供给后代组件的数据或

inject:在任何后代组件中接收想要添加在这个组件上的数据,不管组件嵌套多深都可以直接拿来用

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("people",{name:"张三",age:18});
    provide("id","1005678");
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
   const people=inject("people"); //{ "name": "张三", "age": 18 }
const id=inject("id"); //id:1005678
</script>
文档更新时间: 2023-01-02 14:02   作者:董老师