首先,组件上一样可以绑定事件。但注意:
事件是绑定在组件自己的template上,不是调用组件的地方Vue.component('todo-item', {
template: '<a @click="greet" >点我</a>',
methods: {
greet: function () {
之前我们学过的事件知识,完全适用于子组件。props的传值是单向的,所以子组件无法影响父组件,除非使用事件:
greet: function () {
this.$emit("welcome"); //welcome是一个自定义的事件名
}
<todo-item @welcome="sayHello"></todo-item>
//父组件实例的methods中
sayHello: function () {
alert('hello');
}
$emit()可以带一个额外的参数
this.$emit("welcome", {
sname: '阿泰'
});
父组件使用$event获取该参数值
<todo-item @welcome="sayHello($event)"></todo-item>
也可以省略掉这个参数,一样可以通过方法处理函数中的event拿到:(复习事件event)
sayHello: function (event) {
alert('hello,' + event.sname);
}
注意子组件的事件名,建议总是使用 kebab-case 命名法,因为如果使用 camelCase 或 PascalCase的话,在父组件中都会被转化成kebab-case,造成不一致(演示)
演示:
template: '<input type="text" name="username" v-model="username" />',
data() {
return {
username: "atai"
}
},
vm.$children[0].username = "大飞哥"
但是,如果我们要将组件的值$emit到父组件,或者更进一步,vue data就是由父组件提供,就需要明白表单绑定的本质是事件(复习):
这样的v-model声明:
<input type="text" name="username" v-model="username" />就等价于:
<input @input="username=$event.target.value" :value="username"></input>我们利用这一点:
<yz-enroll v-model="username"></yz-enroll>其实就类似于:
<yz-enroll @input="username=$event" :value="username"></yz-enroll>暂时看不懂没关系,后面在回过来来看,^_^
props: ["value"],
template: `<input type="text" name="username"
@input="$emit('input', $event.target.value)" :value="value" />`
即:
理解:之前绑定(默认)使用的是input事件和value属性。
但其实这两个值都是可以自定义的,在子组件中额外引入一个model:
model: {
prop: 'selected', //属性值:之前默认value
event: 'custom' //事件名:之前默认input
},
然后,相应的更改props和template
props: ["selected"], //之前是value
template: `<input type="text" name="username"
@input="$emit('custom', $event.target.value)" //之前是input
:value="selected" />` //之前是value
vue的文档不知道在说些啥,ʅ(‾◡◝)ʃ
PS:尽信书不如无书,飞哥的视频/讲义也一样,实践是检验真理的唯一标准。
就算是选择性表单(radio/checkbox),一样的呀,既可以使用默认的input和value,也可以使用自定义的model:
data: {
lodging: true
}
<yz-enroll v-model="lodging"></yz-enroll>唯一需要注意的是:$emit()的时候,向上传递的不是$event.target.value,而是$event.target.checked
v-on:change="$emit('custom', $event.target.checked)"
如果我们想要:
就需要使用slot:
template: `<p><slot></slot></p>`
<yz-enroll>hello,源栈</yz-enroll>
模板中当然也可以有其他内容,比如:
template: `<p><slot></slot>,^_^</p>`
template: `<p><slot>hello,源栈</slot></p>`
这样,
<yz-enroll>大飞哥你好!</yz-enroll>
即当组件的template中要使用多个slot:
template: `<p><slot name="before"></slot><slot name="after"></slot></p>`
<yz-enroll>
<template v-slot:before>hello</template>
<template v-slot:after>源栈</template>
</yz-enroll>
命名slot可以和未命名(默认)slot混用,一个不带 name 的 <slot> 出口会带有隐含的名字“default”,比如:
template: `<p>
<slot name="before"></slot>
<slot></slot>
<slot name="after"></slot>
</p>`
调用时,可以:
<yz-enroll>
<template v-slot:before>hello</template>
,
<template v-slot:after>源栈</template>
</yz-enroll>
也可以:
<yz-enroll>
<template v-slot:before>hello</template>
<template v-slot:default>,</template>
<template v-slot:after>源栈</template>
</yz-enroll>
<yz-enroll url="/code"> <!-url是传给子组件的->
{{username}} <!-OK,username是vue data中的->
{{url}} <!-undefined->
</yz-enroll>
为了讲解的清晰简洁,前面我们都使用的是文本,其实HTML标签一样是可以的:
<yz-enroll>
<span> {{username}}</span>
</yz-enroll>
template: `<a :href='url'>
<small> <slot></slot></small>
</a>`
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。
可在调用组件时动态的切换不同的组件。
比如,首先准备两个组件:learn和yz-enroll
var learn = {
template: `<p>开始学习</p>`
};
Vue.component("yz-enroll", {
template: `<input type="checkbox" />`
})
learn作为局部组件需要注册一下:
components: {
learn
},
然后,在vue data中声明一个变量show:
data: {
show: "learn", //值为组件名
}
最后,就可以通过vue data中的show动态调用子组件了:
<div :is="show"></div>演示:更改show的值
动态component是每次都新生成的。
演示:勾选上这个checkbox,再切换template,再切换回checkbox,你会发现:勾选的状态没了……
如果想要保持住以前的勾选状态,就要使用keep-alive缓存这个组件:
<keep-alive>
<component :is="show"></component>
</keep-alive>
如果组件需要通过ajax获取数据,怎么办?
一种办法,和我们之前axios中讲的一样,在钩子函数(比如mounted)中修改vue data:
template: `<label>
<input type="checkbox" />{{sname}} <!-template中使用vue data-> </label>`,
data() {
return {
sname: 'atai'
}
},
mounted: function () {
axios.get("/Student/23").then(response => {
this.sname = "feige"; //data()方法不影响像对象一样使用this.sname
})
}
但是,vue为我们提供了一种更简单的方法,异步组件:
Vue.component("yz-enroll", function (resolve) { //回调函数
axios.get("/Student/23").then(response => {
resolve({ //必须resolve template: `<label>
<input type="checkbox" /> ${response.data.sname}
</label>` }
)
})
})
Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
组件也可以被循环渲染出来,先上vue data:
data: {
todos: [
{
id: 1,
title: '写讲义',
},
{
id: 2,
title: '作业点评',
},
{
id: 3,
title: '招生宣传'
}
],
}
再弄一个简单的组件:
Vue.component('todo-item', {
template: `<li>{{ title }}</li>`
})
父组件使用props向子组件传值:
props: ['title']
<ul>
<todo-item v-for="(todo, index) in todos" :key="todo.id" :title="todo.title"></todo-item>
</ul>
注意:组件列表必须要有key!
早期版本会使用这种写法避免ul和todo-item的冲突:
<li is="todo-item" v-for="(todo, index) in todos" ……
添加一个新的子组件,关键是key值怎么办?如果是真实开发环境,添加的todo数据要首先传往后台,在后台会生成相应的id再返回给前台!纯前端演示时我们可以id++示意
<input v-model="newTodoText" id="new-todo"> <button v-on:submit.prevent="addNewTodo">添加</button>
methods: {
addNewTodo: function () {
this.todos.push({
id: ++this.nextTodoId,
title: this.newTodoText
})
this.newTodoText = ''
}
}
删除子组件,template中添加一个button,这个button的点击事件能向父组件传递:
template: `<li>{{ title }}<button v-on:click="$emit('remove')">X</button></li>`,
注意:子组件没有将自己的key/id传递给父组件,而是由父组件获取该值然后处理:
v-on:remove="todos.splice(index, 1)"
将以下页面部分封装成组件
多快好省!前端后端,线上线下,名师精讲
更多了解 加: