自定义事件
事件名
推荐你始终使用 kebab-case 的事件名。
<div id="app">
<blog-post @my-welcome="sayHi()"></blog-post>
</div>
<script>
Vue.component('blog-post', {
props: ['post'],
template: `<button @click="$emit('my-welcome')">click me</button>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {}
},
methods: {
sayHi: function () {
console.log('hi')
}
}
})
</script>
自定义组件的v-model
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
attribute 用于不同的目的。model
选项可以用来避免这样的冲突。
<div id="app">
<check-input v-model="checkbox"></check-input>
<check-input v-model="checkbox"></check-input>
<p>checkbox : {{checkbox}}</p>
</div>
<script>
Vue.component('check-input', {
// 组件默认model
// model: {
// prop: 'value',
// event: 'input'
// },
model: {
prop: 'checked',
event: 'change'
},
props: ['checked', 'value'],
template: `
<div>
<input
type='checkbox'
:checked="checked"
@change="$emit('change',$event.target.checked)"
/>
<input
type='text'
:value='value'
@input="$emit('input',$event.target.value)"
/>
</div>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
checkbox: ''
}
}
})
</script>
上述代码中:
-
template
中的value
和checked
,分别绑定到了props
的value
和checked
,用于父组件向子组件传递数据,在此例所要展示的功能并无关系。 -
上面提到过,一个组件上的
v-model
默认会利用名为value
的prop
和名为input
的事件,而在前面的表单输入绑定中提到过因此,如果我们的想要
v-model
的子组件中除了text
和textarea
之外的其他input
标签,就需要对子组件的默认model
进行改变// 组件默认model // model: { // prop: 'value', // event: 'input' // }, // 修改成所要的model model: { prop: 'checked', event: 'change' },
此时,再对子组件整体进行
v-model
绑定,就可以绑定到所需的prop
和event
-
对于
v-model
绑定的事件,子组件中也需要通过v-on
绑定,并通过$emit('eventName',$event.target.propName)
进行回调,而你所要传递的数据,则通过$event.target.propName
进行传递。
将原生事件绑定到组件
在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on
的 .native
修饰符:
<base-input v-on:focus.native="onFocus"></base-input>
Vue.component('base-input', {
template: `
<input type='text'
v-on="inputListeners"
/>`
})
如果上述 <base-input>
组件做了如下重构,所以根元素实际上是一个 <label>
元素:
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
这时,父级的 .native
监听器将静默失败。它不会产生任何报错,但是 onFocus
处理函数不会如你预期地被调用。
为了解决这个问题,Vue 提供了一个
$listeners
property,它是一个对象,里面包含了作用在这个组件上的所有监听器。
<div id="app">
<base-input
@focus="onFocus()"
@focus2="onFocus2()"
v-model="value1"
:value="value2"
@input2="val=>{value2=val}"
></base-input>
<br />
<span>value1 : {{value1}}</span><br />
<span>focus1 : {{focus1}}</span><br />
<span>value2 : {{value2}}</span><br />
<span>focus2 : {{focus2}}</span>
</div>
<script>
Vue.component('base-input', {
model: {
prop: 'value',
event: 'input1'
},
computed: {
inputListeners: function () {
let vm = this
return Object.assign({}, this.$listeners, {
input: function (event) {
vm.$emit('input1', event.target.value)
},
focus: function (event) {
vm.$emit('focus')
}
})
},
inputListeners2: function () {
let vm = this
return Object.assign({}, this.$listeners, {
input: function (event) {
vm.$emit('input2', event.target.value)
},
focus: function (event) {
vm.$emit('focus2')
}
})
}
},
template: `
<label>
<span>input1</span>
<input type='text'
v-on="inputListeners"
/><br />
<span>input2</span>
<input type='text'
v-on="inputListeners2"
/>
</label>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
value1: '',
value2: '',
focus1: '',
focus2: ''
}
},
methods: {
onFocus: function () {
return (this.focus1 = 'focusing1')
},
onFocus2: function () {
return (this.focus2 = 'focusing2')
}
}
})
</script>
-
-
上述代码中,
v-model="value1"
可以被:value="value1" @input1="value=>{value2=value}"
代替,即v-model的另一种写法。 -
base-input
组件中,model
property重写了组件的model传参,默认组件model为prop: 'value', event: 'input'
,将event修改为下面input1的监听器中改写的input1
。 -
计算属性中的inputListeners,通过
this.$listeners
从父级添加所有的监听器,之后添加或覆写了一些自定义的监听器行为,然后通过Object.assign()
将所有的对象合并为一个对象进行返回,从而就返回了所需要的监听器。
.sync
修饰符
对一个 prop 进行“双向绑定”
<div id="app">
<!-- 方法一 -->
<base-input
:title="doc.title"
@update:title="doc.title=$event"
:name="doc.name"
@update:name="doc.name=$event"
></base-input>
<hr />
<!-- 方法二:方法一的简化 -->
<base-input :title.sync="doc.title" :name.sync="doc.name"></base-input>
<hr />
<!-- 方法三:用一个对象设置多个prop时使用 -->
<base-input v-bind.sync="doc"></base-input>
<p>doc.title : {{doc.title}}</p>
<p>doc.name : {{doc.name}}</p>
</div>
<script>
Vue.component('base-input', {
props: ['title', 'name'],
data: function () {
return {
newUpdate: {
title: 'Vue',
name: 'Evan You'
}
}
},
template: `
<label>
<p>{{title}}</p>
<p>{{name}}</p>
<button @click="$emit('update:title', newUpdate.title)">change title</button>
<br />
<button @click="$emit('update:name', newUpdate.name)">change name</button>
</label>`
})
let vm = new Vue({
el: '#app',
data: function () {
return {
doc: {
title: 'docTitle',
name: 'docName'
}
}
}
})
</script>