四、vue3的v-model、组件传值、异步组件、teleport组件

1.v-model

Vue3可以通过v-model指令实现对多个数据的双向绑定

<!-- 父级 -->
<template>
 <Five v-model:bg="bg" v-model:mb="mb" v-model:nn="nn" @click="appClick"></Five>
</template>
<script>
import { ref } from 'vue'
import Five from './components/Five.vue'
export default {
  name: 'App',
  components: {
    Five
  },
  setup() {
    //饼干
    let bg = ref(5)
    //面包
    let mb = ref(8)
    //牛奶
    let nn = ref(3)
    let appClick = (e)=>{
      alert(e)
    }
    return{
      bg,
      mb,
      nn,
      appClick
    }
  }
}
</script>
<!-- 子级 -->
<template>
  <div class="five">
    <h3>Five</h3>
    <div class="item">
      饼干:<button @click="bg--">-</button><input readonly type="text" :value="bg"><button @click="bg++">+</button>
    </div>
    <div class="item">
      面包:<button @click="mb--">-</button><input readonly type="text" :value="mb"><button @click="mb++">+</button>
    </div>
    <div class="item">
      牛奶:<button @click="nn--">-</button><input readonly type="text" :value="nn"><button @click="nn++">+</button>
    </div>
    <button @click="fiveClick">点击</button>
  </div>
</template>
<script>import { ref, watch } from "vue";
export default {
  // 接收属性
  props:['bg','mb','nn'],
  // 放在自定义事件名称跟原生事件名称同名
  // 这里统一注册自定义事件的名称,
  // 这样父组件只会触发子组件自定义的事件,不会触发原生事件
  emits:['click','update:bg','update:mb','update:nn'],
  setup(props,{emit}) {
    //获取父组件的传值
    let bg = ref(props.bg)
    let mb = ref(props.mb)
    let nn = ref(props.nn)
    watch(bg,(val)=>{
      // 注意:事件名称是update:属性名称
      emit('update:bg',val)
    })
    watch(mb,(val)=>{
      emit('update:mb',val)
    })
    watch(nn,(val)=>{
      emit('update:nn',val)
    })
    let fiveClick = ()=>{
      // 当我们的自定义事件,跟原生事件重名时,会执行两次
      emit('click','哈哈!我被点击了!')
    }
    return {
      bg,
      mb,
      nn,
      fiveClick
    }
  }
};

2.组件传值

1.父子组件传值

父级

<template>
  <div class="app">
    <div class="box">
      <div>汽车名称:{{carName}}</div>
      <div>汽车价格:{{carPrice}}</div>
      <div>汽车颜色:{{carColor}}</div>
      <div>汽车产地:{{carAddress}}</div>
      <!-- 通过props给子组件传值 -->
      <Three :car-name="carName" :car-price="carPrice" 
      :car-color="carColor" :car-address="carAddress"
      @change-name="changeName" @change-price="changePrice"
      @change-color="changeColor" @change-address="changeAddress"></Three>
    </div>
  </div>
</template>
<script>
import Three from './components/Three.vue'
import { ref } from 'vue'
export default {
  name: 'App',
  components: {
    Three
  },
  setup() {
    let carName = ref('奔驰')
    let carPrice = ref(20)
    let carColor = ref('红色')
    let carAddress = ref('德国')

    let changeName = (e)=>{
      carName.value = e
    }
    let changePrice = (e)=>{
      carPrice.value = e
    }
    let changeColor = (e)=>{
      carColor.value = e
    }
    let changeAddress = (e)=>{
      carAddress.value = e
    }
    return{
      carName,
      carPrice,
      carColor,
      carAddress,
      changeName,
      changePrice,
      changeColor,
      changeAddress
    }
  }
}
</script>

子级

<template>
  <div class="three">
    <h3>子组件</h3>
    <!-- 注意:props接过来的属性,在模板中可以直接修改 -->
    <div>汽车名称:{{ myCarName }}<button @click="setCarName">修改车名</button></div>
    <div>汽车价格:{{ myCarPrice }}<button @click="setCarPrice">修改价格</button></div>
    <div>汽车颜色:{{ myCarColor }}<button @click="setCarColor">修改颜色</button></div>
    <div>汽车产地:{{ myCarAddress }}<button @click="setCarAddress">修改地址</button></div>
  </div>
</template>
<script>
import { toRef } from "vue";
export default {
  // props接过来的属性在方法里面是只读的
  props: ["carName", "carPrice", "carColor"],
  // 使用vue3中的组合式API写法
  setup(props,context) {
    // props返回的是一个Proxy对象,里面保存的是props选项接的属性
    // context对象里面保存了三个重要属性:attrs,emit,slots
    // attrs 相当于 this.$arrts
    // emit 相当于 this.$emit
    // slots 是插槽信息
    let myCarName = toRef(props,'carName')   //ref(props.carName)
    let myCarPrice = toRef(props,'carPrice')
    let myCarColor = toRef(props,'carColor')
    let myCarAddress = toRef(context.attrs,'car-address')
    
    let setCarName = ()=>{
      myCarName = '宝马'
      context.emit('change-name',myCarName)
    }
    let setCarPrice = ()=>{
      myCarPrice = 30
      context.emit('change-price',myCarPrice)
    }
    let setCarColor = ()=>{
      myCarColor = '蓝色'
      context.emit('change-color',myCarColor)
    }
    let setCarAddress = ()=>{
      myCarAddress = '美国'
      context.emit('change-address',myCarAddress)
    }

    return{
      myCarName,
      myCarPrice,
      myCarColor,
      myCarAddress,
      setCarName,
      setCarPrice,
      setCarColor,
      setCarAddress
    }
  }
};
</script>

2.隔代组件传值

父级

setup() {
    //定义数据
    let name = ref('张三')
    let age = ref(20)
    //汽车数据
    let car = reactive({
      name:'大众',
      color:'黑色'
    })
    //修改汽车的名称
    let updateCarName = (val)=>{
      car.name = val
    }
    //修改汽车的价格
    let updateCarColor = (val)=>{
      car.color = val
    }
    // 定义依赖数据
    // provide方法的第一个参数是key,第二个参数是value
    provide('name',name)
    provide('age',age)
    // 将两个修汽车信息的方法,作为依赖数据传出去
    provide('updateCarName',updateCarName)
    provide('updateCarColor',updateCarColor)
    return{
      name,
      age,
      car,
    }
  }

子级

<template>
  <div class="two">
    <h3>Two</h3>
    <div>姓名:{{ myName }}</div>
    <div>年龄:{{ myAge }}</div>
    <button @click="updateName">修改姓名</button>
    <button @click="updateAge">修改年龄</button>
    <button @click="updateCarName('比亚迪')">修改汽车名称</button>
    <button @click="updateCarColor('黄色')">修改汽车颜色</button>
  </div>
</template>
<script>
import { inject } from "vue";
export default {
  setup() {
    // 通过inject注入父级的依赖数据集
    let myName = inject("name");
    let myAge = inject("age");
    let updateName = () => {
      myName.value = "李四";
    };
    let updateAge = () => {
      myAge.value = "王五";
    };

    // 通过inject注入两个方法
    let updateCarName = inject("updateCarName");
    let updateCarColor = inject("updateCarColor");

    return {
      myName,
      myAge,
      updateName,
      updateAge,
      updateCarName,
      updateCarColor,
    };
  },
};
</script>

3.兄弟组件传值

1.下载mitt :npm i mitt

2.main.js中导入mitt

import { createApp } from 'vue'
import App from './App.vue'

//导入插件mitt(第三方中央事件中线库)
import mitt from "mitt"

//创建一个Vue实例
let app = createApp(App)

//挂载事务总线为全局属性
// config.globalProperties 就相当于返回Vue的原型对象
app.config.globalProperties.$bus = new mitt()

//将Vue实例挂载到#app容器中
app.mount('#app')

3. 监听事件

<script>
// getCurrentInstance方法,用于在setup函数中获取当前组件实例
import {ref,getCurrentInstance} from 'vue'
export default {
  // vue3推崇的写法
  setup() {
    // 获取当前组件实例 不等同于this
    let $this = getCurrentInstance()
    // $this.appContext 获取当前项目中的Vue实例
    let dogName = ref('琪琪')
    let dogSex = ref('女生')
    
    //监听事件
    $this.appContext.config.globalProperties.$bus.on('update-dog-name',(e)=>{
      dogName.value = e
    })
    $this.appContext.config.globalProperties.$bus.on('update-dog-sex',(e)=>{
      dogSex.value = e
    })
    return {
      dogName,
      dogSex
    }
  }
}
</script>

4.触发事件 

<template>
  <div class="four">
    <h3>Four</h3>
    <button @click="updateDogName">修改狗狗昵称</button>
    <button @click="updateDogSex">修改狗狗性别</button>
  </div>
</template>
<script>
import {getCurrentInstance} from 'vue'
export default {
  setup() {
    //获取当前组件实例
    let $this = getCurrentInstance()
    let updateDogName = ()=>{
      $this.appContext.config.globalProperties.$bus.emit('update-dog-name','天天')
    }
    let updateDogSex = ()=>{
      $this.appContext.config.globalProperties.$bus.emit('update-dog-sex','男生')
    }
    return {
      updateDogName,
      updateDogSex
    }
  }
};
</script>

3.异步组件

<template>
  <div class="home">
    <h1>首页</h1>
    <One></One>
    <!-- suspense内置组件,用于加载异步组件,添加loading效果 -->
    <suspense>
      <!-- default插槽里面放置异步组件 -->
      <template #default>
        <Two></Two>
      </template>
      <!-- fallback插槽里面制作loading效果 -->
      <template #fallback>
        Loading...
      </template>
    </suspense>
    <!-- 异步组件,可以不用suspense,
    但是如果异步组件内部的setup函数返回的是一个Promise对象,
    此时的异步组件就必须要使用suspense -->
    <suspense>
      <template #default>
        <Three></Three>
      </template>
      <template #fallback>
        Loading...
      </template>
    </suspense>
    <!-- <Three></Three> -->
  </div>
</template>

<script>
// defineAsyncComponent方法,用于异步加载组件
import {defineAsyncComponent} from 'vue'
import One from '../components/One.vue'
// import Two from './components/Two.vue'
// 异步导入Two组件和Three
let Two = defineAsyncComponent(()=>import('../components/Two.vue'))
// 注意:如果组件中setup方法返回的是Promise对象,那么组件必须要采用异步的方式加载
// import Three from './components/Three.vue'
let Three = defineAsyncComponent(()=>import('../components/Three.vue'))
export default {
  name: 'App',
  components: {
    One,
    Two,
    Three
  }
}
</script>

4.teleport组件

    <button @click="show=true">显示</button>
    <!-- teleport组件:官方起的名称:瞬移。通过to属性确定里面的元素移动到哪 -->
    <teleport to="body">
      <div v-show="show" class="box">
        <button @click="show=false">关闭</button>
        <div>{{dog}}</div>
      </div>
    </teleport>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值