我们这里继续使用蛋老师的例子

1
git checkout -f 06-start

v-on

v-on用于监听事件。在日常的开发中,常常会缩写成@,后面不需要加:

现在我们想要在网页上通过输入框和添加按钮新增一行我们不同口味的鱿鱼丝,这时候我们就用到了v-on,使用v-on在按钮处监听点击submit事件,然后再对事件进行处理。

1
2
3
4
<form @submit="处理方法">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." />
<button type="submit">添加</button>
</form>

那么这里处理方法我们应该如何添加呢,这时候就用到了在createApp中与computed等同级的属性methods

methods

methodscomputed都可以用来定义组件中的方法,但它们的使用场景和行为略有不同。

computed是一个计算属性,用于根据响应式依赖进行计算和缓存结果。计算属性通常用于对状态数据进行处理和转换,返回一个新的值,这个新值会被缓存,只有当依赖的响应式数据发生变化时才会重新计算。

methods定义的方法则是普通的函数,每次调用都会执行一次。在方法中可以进行复杂的逻辑操作,但是它们不会像计算属性那样被缓存,每次调用都会重新计算。

简单来说:methods是触发型的,computed是缓存好的。

现在我们需要添加一个method方法来处理点击添加后的相关处理:

1
2
3
4
5
6
7
8
9
10
methods:{
add() {
this.foods.push({
id: this.foods.length++,
name: '',
image: './images/原味鱿鱼丝.png',
purchased: false
})
}
},

然后我们会发现点击后,页面就自动刷新了,我们新增的零食也没了。这时候我们需要使用到prevent对事件响应后发生的自动刷新给禁用掉,在我们监听器处添加

1
<form @submit.prevent="add">

现在我们需要获取到输入框内的内容,我们就需要用到前面提及的v-model对数据进行双向绑定。

我们首先要在data()内新建一个newFood的空字符串,用于存储我们的临时数据,然后用v-model绑定上这个临时数据,最后完善我们的methods,使得加入数组的数据使用获取到的信息,使用完临时数据后,记得清除数据

以下是整个AppSections的完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import AppSectionsList from "./AppSectionsList.js";

export default {
components: { AppSectionsList },
template: /*html*/ `
<app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
<app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>

<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood"/>
<button type="submit">添加</button>
</form>
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
],
newFood: ''
}
},
methods:{
add() {
this.foods.push({
id: this.foods.length++,
name: this.newFood,
image: './images/原味鱿鱼丝.png',
purchased: false
})
this.newFood = ''
}
},
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.purchased),
afterBuy: this.foods.filter(item => item.purchased)
}
}
}
}

$emit

$emit是一个实例方法,用于触发当前实例上的自定义事件。使用 $emit 方法,可以在一个组件中触发一个自定义事件,从而通知其他组件监听该事件,并执行相应的逻辑。

$emit 方法接受两个参数,第一个参数是需要触发的事件名称,第二个参数是要传递给监听函数的参数。

根据我们上面的例子,我现在想将表单拆分成一个新的组件。由于foods为父组件的数据,如果将add方法放在新的组件里面,将无法对数据进行操纵,因此我们需要将add方法放在父组件,然后通过$emit向父组件传递我们得到的newFood,让父组件去执行操作数据的方法

因此,我们代码应该这么改

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import AppSectionsList from "./AppSectionsList.js";
import AppSectionsForm from "./AppSectionsForm";

export default {
components: {AppSectionsForm, AppSectionsList },
template: /*html*/ `
<app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
<app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>
<app-sections-form @add="add"></app-sections-form> <!--当接收到add事件,触发add方法-->
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
],
}
},
methods: {
add(newFood) { //接收子组件的newFood
this.foods.push({
id: this.foods.length + 1,
name: newFood, //使用传进来的newFood
image: '../images/鱿鱼丝.png',
purchased: false
});
}
},
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.purchased),
afterBuy: this.foods.filter(item => item.purchased)
}
}
}
}

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default {
template: /*html*/`
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
<button type="submit">添加</button>
</form>
`,
data() {
return {
newFood: ''
}
},
methods: {
add() {
this.$emit('add', this.newFood); //发出add事件,并向外传递newFood
this.newFood = ''
}
}
}

我们可以看出

  • props由父组件向子组件传递
  • $emit由子组件向父组件传递