Vue2、3组件通信、双向绑定、插槽slot、内置指令

目录

Vue3

父子传值:props(attrs)/emit

父传子

props

父child :属性名="变量"

子props=defineProps({属性名:type...})

attrs父作用域(除 class、 style 、 props )属性集合

父child :属性名="变量",属性名="常量"

子 attrs = useAttrs()

子传父emits+@=v-on:

父child @事件名"="parentClick",parentClick(msg)

子emit = defineEmits(["事件名",...]),emit("key",msg)

双向绑定v-model(一个元素可多个v-model,所以取消了.sync)

父child  v-model:属性名="变量"

子emit = defineEmits(["事件名",...]),emit("事件名",值)

父调子事件/属性:expose / ref

父child ref="childComp",childComp.value.属性/方法

子defineExpose({属性: "msg",方法(){  }})

祖传孙provide / inject依赖注入【不推荐】

耦合性增加->依赖关系复杂化->重用性和可维护性低

父provide("key",msg)

子inject("key")

跨组件事件触发/监听器mitt:on/off,emit

代替vue2的eventbus

轻量级:仅有200字节

支持全部事件的监听和批量移除

跨框架:不依赖 Vue 实例,React / Vue/jQuery

原理:map 保存函数

直接在组件内导入使用:分散式更方便管理和排查问题

Vue2

父子传值:props(attrs)/emit

父传子

props

父child :属性名="父组件实例变量"

A.数组:props:['属性名']

B.对象:props:{属性名:{type:,default:}}

attrs父作用域(除 class、 style 、 props )属性集合

父child :属性名="变量",属性名="常量"

子 this.$attrs

子传父emits+@=v-on:

父child @事件名"="parentClick",parentClick(msg)

子this.$emit("事件名",msg)

双向绑定值

.sync(支持多个,update:属性名)

父child :属性名.sync="变量"

子 this.$emit("update:属性名", 属性值 ); 

v-model​​​​​​​一个元素一个v-model,可自定义prop+event名

父子传属性/方法

父传子

this.$parent

this.$root : App.vue

this.$listeners:父组件.native 除外的监听事件集合

调用:this.$listeners.事件名

子传父

this.$children[0]

ref

父child ref="childComp",childComp.所有的属性/方法

祖传孙provide / inject依赖注入:推荐传递常量/方法

父provide(){return obj }

子inject[“name”,“msg”],this.msg

跨组件事件通信EventBus中央事件总线

定义

使用:emit、on、off

slot:父传子组件,子传父数据

单个/默认/匿名插槽slot

v-slot="name",在2.6后可缩写为#"name"

父v-slot="子slot中的变量"

子slot :属性名="变量"

具名插槽slot name="":多个插槽,需要区分

作用域/带数据的插槽

内置指令:数据/事件关联DOM

v-bind(简写:):绑定属性到 Vue 实例中的数据

v-on (简写@): 监听DOM事件、触发Vue实例中的方法

v-model:modelValue=v-modelv-model=v-bind(:)+v-on(@)

Vue3

Vue3 从实例中完全删除了 $on$off 和 $once 方法

父子传值:props(attrs)/emit

父传子

props
父child :属性名="变量"
子props=defineProps({属性名:type...})
// Parent.vue 传送
<child :msg2="msg2"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("这是传给子组件的信息2")
    // 或者复杂类型
    const msg2 = reactive(["这是传级子组件的信息2"])
</script>

// Child.vue 接收
<script setup>
    // 不需要引入 直接使用
    // import { defineProps } from "vue"
    const props = defineProps({
        // 写法一
        msg2: String
        // 写法二
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // { msg2:"这是传级子组件的信息2" }
</script>
attrs父作用域(除 class、 style 、 props )属性集合
父child :属性名="变量",属性名="常量"
子 attrs = useAttrs()
// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue 接收
<script setup>
// 3.2版本不需要引入,直接用
    import { useAttrs } from "vue"
    
// 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" }
    const props = defineProps({
        msg1: String
    })

    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>

子传父emits+@=v-on:

父child @事件名"="parentClick",parentClick(msg)
子emit = defineEmits(["事件名",...]),emit("key",msg)
//父组件
<template>
  <my-son  @childClick="childClick" />
</template>

<script lang="ts" setup>
import MySon from "./MySon.vue";

let childClick = (e: any):void => {
  console.log('from son:',e);  
};
</script>


//子组件
<template>
  <span @click="sonToFather">信息:{{ props.foo }}</span>
</template>

<script lang="ts" setup>
const emit = defineEmits(["childClick"]);     // 声明触发事件 childClick

const sonToFather = () =>{
    emit('childClick' , props.foo)
}
</script>

双向绑定v-model(一个元素可多个v-model,所以取消了.sync

父child  v-model:属性名="变量"
子emit = defineEmits(["事件名",...]),emit("事件名",值)
//child.vue
<template>
  <span @click="changeInfo">我叫{{ modelValue }},今年{{ age }}岁</span>
</template>

<script setup>
  // import { defineEmits, defineProps } from 'vue'
  // defineEmits和defineProps在<script setup>中自动可用,无需导入
  // 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】

  defineProps({
    modelValue: String,
    age: Number
  })

  const emit = defineEmits(['update:modelValue', 'update:age'])
  const changeInfo = () => {
    // 触发父组件值更新
    emit('update:modelValue', 'Tom')
    emit('update:age', 30)
  }
</script>
//parent.vue
<template>
  // v-model:modelValue简写为v-model
  // 可绑定多个v-model
  <child
    v-model="state.name"
    v-model:age="state.age"
  />
</template>

<script setup>
  import { reactive } from 'vue'
  // 引入子组件
  import child from './child.vue'

  const state = reactive({
    name: 'Jerry',
    age: 20
  })
</script>


父调子事件/属性:expose / ref

父child ref="childComp",childComp.value.属性/方法

子defineExpose({属性: "msg",方法(){  }})

// Child.vue
<script setup>
    // 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const ctx = useContext()
    // 对外暴露属性方法等都可以
    ctx.expose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
    
    // 方法二 适用于Vue3.2版本, 不需要引入
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
</script>

// Parent.vue  注意 ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // 获取子组件对外暴露的属性
        comp.value.someMethod() // 调用子组件对外暴露的方法
    }
</script>

祖传孙provide / inject依赖注入【不推荐】

耦合性增加->依赖关系复杂化->重用性和可维护性低

耦合性:关联度

父provide("key",msg)

子inject("key")

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "沐华")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // 沐华
</script>

跨组件事件触发/监听器mitt:on/off,emit

代替vue2的eventbus

轻量级:仅有200字节

支持全部事件的监听和批量移除

跨框架:不依赖 Vue 实例,React / Vue/jQuery

原理:map 保存函数

export default function mitt(all) {
	all = all || new Map();
//命名为type的事件
	return {
		all,
        //监听type事件,绑定handler
		on(type, handler) {
			const handlers = all.get(type);
			const added = handlers && handlers.push(handler);
			if (!added) {
				all.set(type, [handler]);
			}
		},
       //移除type事件,解绑handler
		off(type, handler) {
			const handlers = all.get(type);
			if (handlers) {
				handlers.splice(handlers.indexOf(handler) >>> 0, 1);
			}
		},
       //触发type事件,传递evt事件参数给handler(遍历执行与其绑定的所有handler)
		emit(type, evt) {
			((all.get(type) || [])).slice().map((handler) => { handler(evt); });
			((all.get('*') || [])).slice().map((handler) => { handler(type, evt); });
		},
        clear(){
            // 清空所有事件处理程序
            all.clear();
        },
	};
}

直接在组件内导入使用:分散式更方便管理和排查问题

npm i mitt -save

import mitt from 'mitt'

const emitter = mitt()

// listen to an event
emitter.on('foo', e => console.log('foo', e) )

// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )

// fire an event
emitter.emit('foo', { a: 'b' })

// clearing all events
emitter.all.clear()

// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

Vue3.x 推荐使用 mitt.js - 掘金

Vue2

父子传值:props(attrs)/emit

父传子

props
父child :属性名="父组件实例变量"
A.数组:props:['属性名']
B.对象:props:{属性名:{type:,default:}}
// Child.vue 接收
export default {
  // 写法一 用数组接收
  props:['msg'],
  // 写法二 用对象接收,可以限定接收的数据类型、设置默认值、验证等
  props:{
      msg:{
          type:String,
          default:'这是默认数据'
      }
  },
  mounted(){
      console.log(this.msg)
  },
}
attrs父作用域(除 class、 style 、 props )属性集合
父child :属性名="变量",属性名="常量"
子 this.$attrs

子传父emits+@=v-on:

父child @事件名"="parentClick",parentClick(msg)
子this.$emit("事件名",msg)
// Child.vue 派发
export default {
  data(){
      return { msg: "这是发给父组件的信息" }
  },
  methods: {
      handleClick(){
          this.$emit("sendMsg",this.msg)
      }
  },
}
// Parent.vue 响应
<template>
    <child v-on:sendMsg="getChildMsg"></child>
    // 或 简写
    <child @sendMsg="getChildMsg"></child>
</template>

export default {
    methods:{
        getChildMsg(msg){
            console.log(msg) // 这是父组件接收到的消息
        }
    }
}

双向绑定值

.sync(支持多个,update:属性名

 .sync 修饰符是单向数据流的典型范式。 『数据向下,事件向上』

单向数据流8字真诀

父child :属性名.sync="变量"
子 this.$emit("update:属性名", 属性值 ); 
//父组件
<template>
    <TestCom :num.sync="data"></TestCom>
</template>
<script>
export default({
  components: {
    TestCom,
  },
  data() {
    return {
      data:2
    }  
  },
});
</script>


//子组件
<template>
  <div>
    <button @click="cahngeNum">按钮</button>
    {{ num }}
  </div>
</template>

<script>
export default({
  props: {
    num: {
      default: "",
      type: String,
    },
  },
  methods: {
    cahngeNum() {
       // 事件名称的格式 'update:' + prop名称 
      this.$emit("update:num", 999); 
    },
  },
});
</script>
v-model​​​​​​​一个元素一个v-model,可自定义prop+event名

除了传递基本的值之外,还可以传递其他类型,比如对象或数组。

主要用于表单元素(如输入框、复选框、单选框等input, textarea,select,radio,checkbox等用户输互动入事件)

语法糖,简单来说就是『便捷写法』。

<input v-model="searchText">
语法糖 等价于:
<input
  :value="searchText"
  @input="searchText = $event.target.value"
>

当用户在输入框中输入文本时,userMessage 的值会实时更新,

并且当 userMessage 的值改变时,输入框中的值也会自动更新。

v-model 在内部相当于使用 :value @input 来实现数据的绑定监听

// Parent.vue
<template>
    <child v-model="value"></child>
</template>
<script>
export default {
    data(){
        return {
            value:1
        }
    }
}

// Child.vue
<template>
    <input :value="value" @input="handlerChange">
</template>
export default {
    props:["value"],
    methods:{
        handlerChange(e){
            this.$emit("input", e.target.value)
          
        }
    }
}
</script>

父子传属性/方法

父传子

this.$parent
this.$root : App.vue
this.$listeners:父组件.native 除外的监听事件集合
调用:this.$listeners.事件名

将父组件传递的事件监听器绑定到子组件上,以实现一种“透明传递”的效果。这种方式使得子组件不需要显式声明和监听来自父组件的事件

父组件 自定义的所有事件,都被保存在子组件的vm.$listeners属性里,和$attrs一样可以层层传递

son组件可以响应father组件的2个自定义事件speak()、write()

grandson组件可以响应father组件的2个自定义事件speak()、write(),并且还可以响应son组件的1个自定义事件sonCry()

子传父

this.$children[0]
ref
父child ref="childComp",childComp.所有的属性/方法
// Child.vue
export default {
    data(){
        return {
            name:"沐华"
        }
    },
    methods:{
        someMethod(msg){
            console.log(msg)
        }
    }
}

// Parent.vue
<template>
    <child ref="child"></child>
</template>
<script>
export default {
    mounted(){
        const child = this.$refs.child
        console.log(child.name) // 沐华
        child.someMethod("调用了子组件的方法")
    }
}
</script>
祖传孙provide / inject依赖注入:推荐传递常量/方法

要注意的是 provide 和 inject 传递的数据不是响应式的,除非传入的就是一个可监听的对象

父provide(){return obj }

obj={name:,msg:}

子inject[“name”,“msg”],this.msg

跨组件事件通信EventBus中央事件总线

定义

// 方法一:独立的事件总线文件
// 抽离成一个单独的 js 文件 Bus.js导出 ,然后在需要的地方导入
// 保持了代码的模块化和清晰性。事件总线和组件逻辑分离,使得维护和理解代码变得更容易。
// Bus.js
import Vue from "vue"
export default new Vue()

// 方法二 全局挂载到Vue原型
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()

// 方法三 注入到 Vue 根对象上,作为Vue实例的数据属性
// 组件通过this.$root.Bus访问
// main.js
import Vue from "vue"
new Vue({
    el:"#app",
    data:{
        Bus: new Vue()
    }
})

使用:emit、on、off

// 在需要向外部发送自定义事件的组件内
<template>
    <button @click="handlerClick">按钮</button>
</template>
import Bus from "./Bus.js"
export default{
    methods:{
        handlerClick(){
            // 自定义事件名 sendMsg
            Bus.$emit("sendMsg", "这是要向外部发送的数据")
        }
    }
}

// 在需要接收外部事件的组件内
import Bus from "./Bus.js"
export default{
    mounted(){
        // 监听事件的触发
        Bus.$on("sendMsg", data => {
            console.log("这是接收到的数据:", data)
        })
    },
    beforeDestroy(){
        // 取消监听
        Bus.$off("sendMsg")
    }
}

slot:父传子组件,子传父数据

单个/默认/匿名插槽slot

v-slot="name",在2.6后可缩写为#"name"

父v-slot="子slot中的变量"

子slot :属性名="变量"

// Child.vue
<template>
    <div>
        <slot :user="user"></slot>
    </div>
</template>
export default{
    data(){
        return {
            user:{ name:"沐华" }
        }
    }
}

// Parent.vue
<template>
    <div>
        <child v-slot="slotProps">
            {{ slotProps.user.name }}
        </child>
    </div>
</template>

具名插槽slot name="":多个插槽,需要区分

<template>
  // 匿名插槽
  <slot/>
  // 具名插槽
  <slot name='title'/>
</template>

<script setup>
  import { useSlots, reactive } from 'vue'
  const slots = useSlots()
  // 匿名插槽使用情况
  const defaultSlot = reactive(slots.default && slots.default().length)
  console.log(defaultSlot) // slot内的标签
  // 具名插槽使用情况
  const titleSlot = reactive(slots.title && slots.title().length)
  console.log(titleSlot) // 1
  const state = reactive({
    name: '张三',
    age: '25岁'
  })
  
</script>

<template>
  <child>
    // 匿名插槽
    <span>我是默认插槽</span>
    // 具名插槽
    <template #title>
      <h1>我是具名插槽</h1>
    </template>
  </child> 
</template>

作用域/带数据的插槽

<template>
  <slot name="footer" :scope="state" />
</template>

<script setup>
  import { reactive } from 'vue'

  const state = reactive({
    name: '张三',
    age: '25岁'
  })
  
</script>

 //  { scope } 表示从插槽的内容中解构出一个名为 scope 的变量
    <template #footer="{ scope }">
      <footer>作用域插槽——姓名:{{ scope.name }},年龄{{ scope.age }}</footer>
    </template>

内置指令:数据/事件关联DOM

数据事件处理程序DOM 元素关联,以实现动态数据渲染、事件处理、条件渲染、循环渲染等功能。

v-bind(简写:):绑定属性Vue 实例中的数据

它允许你在模板中动态设置元素属性的值

<div v-bind:id="id"></div>
<div :id="id"></div>

v-on (简写@): 监听DOM事件、触发Vue实例中的方法

<button @click="doSomething">Click me</button>

vue父子组件之间双向数据绑定的(vue2/vue3)_vue3父子组件双向绑定_前端一枚的博客-CSDN博客

Vue3的8种和Vue2的12种组件通信,值得收藏 - 掘金

v-model:modelValue=v-modelv-model=v-bind(:)+v-on(@)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值