开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
vue 中有两个“特别的”概念:响应式和双向数据绑定。
其实响应式原理是一种单向行为:它是数据到 DOM (也就是view视图)的映射;而真正的双向绑定,除了数据变化会引起 DOM 的变化之外,还应该在操作 DOM 改变后反过来影响数据的变化!
vue 中提供了(内置的) v-model指令实现双向绑定。
v-model和双向绑定的简单实现
首先,v-model并不是可作用到任意标签,它只能在一些特定的表单标签如 input、select、textarea以及自定义组件中使用。
通常你会了解到 v-model其实只是一个语法糖,它实际是依靠v-bind:绑定响应式数据 & 触发 input 绑定事件并传递数据。
这么说也可以:
<input v-model="value">
<!--可以认为等价于-->
<input
v-bind:value="value"
v-on:input="value= $event.target.value"
>
我们用自定义组件和上面代码来实现一个类似v-model的数据绑定:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="root"></div>
<script type="text/javascript">
const component = {
template: `
<div>
<input type="text" @input="handleInput">
</div>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
let vm = new Vue({
conponents: {
CompA: component
},
el: '#root',
template: `
<div>
<comp-a></comp-a>
</div>
`
})
</script>
这样一个初始化的 demo 就搭建好了:
- 我们定义了一个组件
component,实例化了一个 Vue 对象。v-model绑定的值,是从外层的 Vue 实例中传进去的。 - 首先我们要在组件
component里面定义一个props; - 然后就可以在 Vue 实例的
template模板里面去加上这个 value ,同时绑定input事件; - 同样,组件
component里面的input也得绑定 value :
我们将上面代码中script部分完善一下:
const component = {
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value">
</div>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
let vm = new Vue({
components: {
CompA: component
},
el: '#root',
template: `
<div>
<div>{{value}}</div>
<comp-a :value="value" @input="_handleInput"></comp-a>
</div>
`,
data () {
return {
value: 'mxcnb'
}
},
methods:{
_handleInput(e){
this.value=e
}
}
})
既然是双向绑定,我们不妨试着改变一下 value 的值:
<button @click="handleInput">改变</button>
handleInput(){
this.value='1231'
},
嗯,确实改变了。
我们大概了解了:vue双向数据绑定的原理是通过 prop 向组件传递数据(对自定义组件来说就是:在数据渲染时使用 prop 渲染数据,将 prop 绑定到子组件自身的数据上);并监听自定义事件接受组件反传的数据并更新(对自定义组件来说就是:修改数据时更新自身数据来替代 prop ,监听子组件自身数据的改变,触发事件通知父组件更改绑定到prop的数据)。
这里监听的事件对原生input组件来说就是内置的
onUpdate:modelValue函数;对自定义组件来说就是自定义事件;
通过 prop 传递的数据就是
v-bind绑定的data;
反传的数据就是用户输入后改变了的value;
为了进一步体验“监听子组件数据”的过程,我们完全可以将上面 components 部分修改如下:
const component = {
props: ['value'],
template: `
<div>
<input type="text" v-model="_value">
</div>
`,
computed:{
_value:{
get(){
return this.value
},
set(value){
this.$emit('input', value)
}
}
},
}