#完整原文地址见简书www.jianshu.com/p/c8891bd3f… #更多完整Vue笔记目录敬请见《前端 Web 笔记 汇总目录(Updating)》
#本文内容提要
父子组件可通过事件 进行通信
携带参数的事件 发送和监听回调
使用 组件的
emits板块 整理组件事件使用 组件
emits板块的Object形式 校验外传的参数值结合
$emit、v-bind与v-model实现 父子组件通信(数据双向绑定)结合
$emit、v-bind与v-model实现 父子组件通信(多个字段的应用案例)自定义修饰符
- 实验
this.modelModifiers的作用下面在子组件的点击回调
handleClick()中,通过this.modelModifiers.[自定义修饰符名]实现自定义修饰符逻辑插槽【slot】【传组件示例】
注意,slot标签上是无法直接添加事件(修饰符)的,如有需要,可以在外层包裹一层标签,再加上事件
插槽【传 字符串示例】
插槽【传 自定义子组件 示例】
插槽作用域问题
插槽 UI默认值
插槽的灵活拆分与应用【具名插槽】
v-slot指令的简写普通的
v-for例子 进行 列表渲染
v-for结合v-bind、v-slot、<slot>做列表渲染使用
解构概念进行简写动态组件
- 常规的利用
双向绑定特性,通过点击事件切换UI的写法- 动态组件写法
异步组件
####父子组件可通过事件 进行通信 **前面的笔记 —— [《Vue3 | 组件的定义及复用性、局部组件、全局组件、组件间传值及其校验、单项数据流、Non-props属性》](https://www.jianshu.com/p/1bc868ff488f),单向数据流的概念, 即子组件无法修改来自父组件的数据字段,
如果确要修改,可以使用下面说的方式进行通信: 首先,在子组件的UI点击回调方法中,调用`this.$emit('【自定义事件名】')`, 向外发送一个`事件`;
接着各级父组件会收到这个事件, 则在父组件中 调用 子组件标签处, 以 `@【事件名】= "回调方法名"`的形式,`监听`该事件以及配置`回调方法`; `回调方法`中即可 对 子组件意图修改 的 父组件数据字段 进行修改;** >**注意, 触发事件的命名,用`驼峰命名法`(如下heHeDa); 监听事件的命名,用`横杆间隔法(如下he-he-da)`。**
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World! heheheheheheda</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="heheApp"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
methods: {
handleItemEvent() {
this.count += 1;
}
},
template: `
<div>
<counter :count="count" @he-he-da="handleItemEvent"/>
</div>`
});
app.component('counter', {
props: ['count'],
methods: {
handleItemClick() {
this.$emit('heHeDa');
}
},
template:`
<div @click="handleItemClick">{{count}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
</html>
运行,点击组件:
####携带参数的事件 发送和监听回调 **`this.$emit()`可以添加参数位, 父组件的监听回调中, 则可加形参位 用于接收参数(如`handleItemEvent(param)`中的 `param`);**
代码:
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
methods: {
handleItemEvent(param) {
this.count += param;
}
},
template: `
<div>
<counter :count="count" @add-count="handleItemEvent"/>
</div>`
});
app.component('counter', {
props: ['count'],
methods: {
handleItemClick() {
this.$emit('addCount', 8);
}
},
template:`
<div @click="handleItemClick">{{count}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
运行,点击效果:
子组件需 发送多个参数 亦可,只要在this.$emit()按需添加参数位,
父组件的监听回调中,添加对应的形参 去接收即可:
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
methods: {
handleItemEvent(param1, param2, param3) {
this.count = this.count + param1 + param2 + param3;
console.log(this.count);
}
},
template: `
<div>
<counter :count="count" @add-count="handleItemEvent"/>
</div>`
});
app.component('counter', {
props: ['count'],
methods: {
handleItemClick() {
this.$emit('addCount', 8, 2, 6);
}
},
template:`
<div @click="handleItemClick">{{count}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
效果:
**当然 父组件 接收 子组件参数 后的 计算逻辑, 可以在 子组件传参 的时候 计算完成 再传给`this.$emit()`! 父组件接收时,直接 受值即可(`handleItemEvent(count)`);** ``` ``` 效果同上一个例子;
####使用 组件的`emits`板块 整理组件事件 **实际开发场景中,我们一个组件自定义的触发事件可能会很多, 我们不可能一个一个去梳理核实, 这个时候就可以使用 组件的`emits`板块 来整理组件的事件;
可以把组件中 `自定义到的事件`都写在这里,`方便梳理`,提高`可读性`, 或者把 `想要定义的事件` 写在这里, 如此一来,如果`忘记`编写对应的自定义事件, Vue系统会在运行时 给予`警告`:** ``` ``` **如果`忘记`编写对应的自定义事件,Vue系统会在运行时 给予`警告`:** 
####使用 组件`emits`板块的 `Object`形式 校验外传的参数值 **可以根据需要,使用 组件`emits`板块的 `Object`形式 校验外传的参数值, 如下,子组件的`emits`板块, ‘key’值定义对应的事件名,‘value’值定义一个校验函数,
返回`true`表示同意数值外传, 返回`false`表示不同意,会给出警告;** ``` ``` 运行,点击效果:
####结合`$emit`、`v-bind`与`v-model` 实现 父子组件通信(数据双向绑定) >**v-model可以实现数据字段与DOM节点内容的双向绑定, 也可以实现数据字段与数据字段之间的双向绑定; 而`v-bind`只能是实现`单向数据流`;**
若不自定义承接的字段名,则需要用modelValue作为默认的承接字段名;
同时,$emit()的一参默认为update:modelValue,二参为绑定的数据;
如下代码,
子组件 的承接变量modelValue 同父组件的count字段 双向绑定,
(实际上就是v-model的特性 —— 将 子组件的内容即modelValue 同 父组件的数据字段双向绑定)
然后显示在子组件的DOM中({{modelValue}}):
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
template: `
<counter v-model="count"/>`
});
app.component('counter', {
props: ['modelValue'],
methods: {
handleItemClick() {
this.$emit('update:modelValue', this.modelValue + 16);
console.log(vm.$data.count);
}
},
template:`
<div @click="handleItemClick">{{modelValue}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
效果:
当然也可以自定义字段名,
这种方式需要给v-model字段接一个字段名,
同时将这个字段名替代子组件中所有modelValue的位置:
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
template: `
<counter v-model:testField="count"/>`
});
app.component('counter', {
props: ['testField'],
methods: {
handleItemClick() {
this.$emit('update:testField', this.testField + 16);
console.log(vm.$data.count);
}
},
template:`
<div @click="handleItemClick">{{testField}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
实现效果与上例相同;
####结合`$emit`、`v-bind`与`v-model` 实现 父子组件通信(多个字段的应用案例) **如下代码, 父组件的`count`与子组件承接的`testField`字段, 父组件的`count1`与子组件承接的`testField1`字段, 分别实现了双向绑定:** ``` ``` 效果:
####自定义修饰符 >**机制:在父组件调用处,在`v-model`后 使用`自定义修饰符`, >在`实现修饰符逻辑`的地方,如点击事件中, >通过`this.modelModifiers.[自定义修饰符名]`返回的`布尔值`, >判断用户是否使用了修饰符, >进而分别对使用与否做相应的处理; 另外`'modelModifiers'`板块中可以`指定默认值`(下代码指定为一个空对象`{}`);**
######实验this.modelModifiers的作用
首先下面是一个空的处理,'modelModifiers'板块中指定默认值(下代码指定为一个空对象{}),
mounted函数中打印 子组件modelModifiers属性的内容,
代码如下,
运行后,可以见打印了一个对象{captalize: true},
正是我们传入的自定义修饰符.captalize(这里未做处理)
【如果这里v-model不接修饰符,
console.log(this.modelModifiers);将打印一个空对象{}】:
<script>
const app = Vue.createApp({
data() {
return {
char: 'a'
}
},
template: `
<counter v-model.captalize="char"/>`
});
app.component('counter', {
props: {
'modelValue': String,
'modelModifiers': {
default: () => ({})
}
},
mounted() {
console.log(this.modelModifiers);
},
methods: {
handleClick() {
this.$emit('update:modelValue', this.modelValue + 'h');
console.log("vm.$data.count", vm.$data.char);
}
},
template:`
<div @click="handleClick">{{modelValue}}</div>
`
});
const vm = app.mount('#heheApp');
</script>
#####下面在子组件的点击回调`handleClick()`中,通过`this.modelModifiers.[自定义修饰符名]`实现自定义修饰符逻辑 **实现效果即 点击之后使得对应的字符串 全变大写;** ``` ``` **效果:**
####插槽【slot】【传组件示例】 >**使用关键 主要分两个部分: 自定义子组件: 在需要 被父组件`插入组件`的位置, 使用``标签对临时占位;
>父组件: 在调用`子组件标签对`时, 往`子组件标签对` 间 写上 要替换`子组件标签对`中``位置的组件
>【slot】的出现, 方便父子组件之间数据的传递, 方便DOM的传递;** ``` Hello World! heheheheheheda ``` 运行效果:
####注意,slot标签上是无法直接添加事件(修饰符)的,如有需要,可以在外层包裹一层标签,再加上事件 ``` ``` 运行,点击提交文本或按钮:
####插槽【传 字符串示例】 ``` ``` 
####插槽【传 自定义子组件 示例】 ``` ``` 运行:
####插槽作用域问题 >虽然,`父组件`中 往`子组件`标签间 插入的组件 会替换`子组件`的插槽位, 但是` 父组件`中 往`子组件`标签间 插入的组件, 其所使用的数据字段,仍然是`父组件`的,而非`子组件`;
**父组件的template中 调用的数据是 父组件中的 data; 子组件的template中 调用的数据是 子组件中的 data;** ``` ``` 
####插槽 UI默认值 >**可以在子组件的`插槽标签`间 编写`默认值`, 如果父组件没有使用 组件 注入`插槽`, 则对应位置 会显示`默认值`:** ``` ``` 效果:
####插槽的灵活拆分与应用【具名插槽】 - **使得插槽的 父组件注入部分 和 子组件占位部分,能够更加灵活的布局,
可以通过`v-slot:[插槽名]`来对一组插槽命名, 父组件定义之后 `插槽名`及其对应的`组件`之后,
子组件只需要在要占位的地方, 配合`name`属性 使用对应命名的标签, 即可将对应的父组件插槽组件占用过来;**
-
父组件 的插槽注入部分的组件, 需要用