首先,组件上一样可以绑定事件。但注意:
事件是绑定在组件自己的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)"
将以下页面部分封装成组件
多快好省!前端后端,线上线下,名师精讲
更多了解 加: