Vue2、3组件通信、双向绑定、插槽slot、内置指令、事件修饰符
除了传递基本的值之外,还可以传递其他类型,比如对象或数组。语法糖:value默认情况下,v-model会使用子组件的value属性作为 prop 名,使用input事件来触发更新。适应不同的数据源,与其他组件集成不会产生命名冲突。比起value更具有语义,使其他开发者更容易理解组件的预期用法和行为。props: {// 使用自定义的 prop 名 customProp},// 通过 model 选
目录
子props=defineProps({属性名:type...})
attrs父作用域(除 class、 style 、 props )属性集合
父child @事件名"="parentClick",parentClick(msg)
子emit = defineEmits(["事件名",...]),emit("key",msg)
双向绑定v-model(一个元素可多个v-model,所以取消了.sync)
子emit = defineEmits(["事件名",...]),emit("事件名",值)
父child ref="childComp",childComp.value.属性/方法
子defineExpose({属性: "msg",方法(){ }})
跨框架:不依赖 Vue 实例,React / Vue/jQuery
B.对象:props:{属性名:{type:,default:}}
attrs父作用域(除 class、 style 、 props )属性集合
父child @事件名"="parentClick",parentClick(msg)
子 this.$emit("update:属性名", 属性值 );
v-model一个元素一个v-model,可自定义prop+event名
this.$listeners:父组件.native 除外的监听事件集合
父child ref="childComp",childComp.所有的属性/方法
祖传孙provide / inject依赖注入:推荐传递常量/方法
子inject[“name”,“msg”],this.msg
v-slot="name",在2.6后可缩写为#"name"
v-on (简写@): 监听DOM事件、触发Vue实例中的方法
v-model:modelValue=v-modelv-model=v-bind(:)+v-on(@)
.stop=event.stopPropagation():阻止事件向父元素传播
.prevent = event.preventDefault():阻止事件的默认行为
.capture = addEventListener ('click', handleClick, true)捕获阶段监听
.once = addEventListener ('click', handleClick,{once: true}):事件处理只触发一次,之后不再生效
.self = event.target ==event.currentTarget 只在事件目标是当前元素时处理事件
.passive =element.addEventListener('scroll', handleScroll, { passive: true });优化滚动事件的性能
Vuex
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
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
修饰符是单向数据流的典型范式。 『数据向下,事件向上』
父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(@)
事件修饰符和JS原生方法
.stop=event.stopPropagation():阻止事件向父元素传播
<div @click="handleDivClick">
<button @click.stop="handleButtonClick">Click Me</button>
</div>
.prevent
= event.preventDefault()
:阻止事件的默认行为
.prevent
阻止了表单的默认提交行为,因此页面不会刷新
.capture
= addEventListener
('click', handleClick, true)捕获阶段监听
希望最外层的组件能够最先处理点击事件
<div @click.capture="handleCaptureClick">
<button @click="handleButtonClick">Click Me</button>
</div>
.once
= addEventListener
('click', handleClick,{once: true}):事件处理只触发一次,之后不再生效
弹出提示
.self
= event.target
==event.currentTarget
只在事件目标是当前元素时处理事件
防止在子元素上点击时触发事件处理程序
点击 div
的空白区域会触发 handleDivClick
方法,但点击按钮不会触发该方法:
<template>
<div @click.self="handleDivClick">
<button>Click Me</button>
</div>
</template>
<script>
export default {
methods: {
handleDivClick() {
console.log('Div clicked');
}
}
}
</script>
.passive
=element.addEventListener('scroll', handleScroll, { passive: true });优化滚动事件的性能
告诉浏览器事件处理程序不会调用 event.preventDefault()
来提高性能,不会等待事件处理程序执行完毕再进行滚动渲染。
更多推荐
所有评论(0)