在 vue
开发开发中, v-model
是一个非常常用的属性.
常规模式下,我们给定当前组件的某个 data 属性值,并设置 v-model
到对应的 表单元素
的属性上.
这样就建立了 input 表单元素和 data 数据的双向绑定了.
<div id="app">
<input type="text" v-model="userName" />
</div>
<script>
new Vue({
el: '#app',
data: {
userName: ''
}
})
</script>
这个时候:
- 我们在 input 文本框里修改 value 值,会影响 userName 这个 data 属性.
- 我们在代码逻辑中,修改 this.userName = 'xxxx' 也会影响到 input 的 value 值.
原理也很简单.
- data里的userName,被 Object.defineProperty() 定义后,具备了 set/get 的监听器.
- input 元素本身就有 input 事件,能监听自己的 value 值修改.
- 在对应的自己修改的时机里,修改对方的值即可.
也就是说,当我们使用 v-model 指令时,这个指令实质上帮我们做了两件事情:
- 将对应的 data 属性 userName 绑定到 Dom.value 上.由于 data.userName 有 set,当值修改的时候,会同步更新 DOM.value
- 被绑定的 DOM 元素会发布 @input 事件,当前组件根根据这个 input 来获取最新的 DOM.value 并同步被绑定的 data.userName
v-model 到底是怎么做的?
现在不使用 v-model
指令,而是利用上述得出的规律,手动的完成 v-model
的特性.
HTML 部分
<div id='app'>
<!-- v-model 等价于下面这段HTML -->
<input type="text" v-model="userName" />
<!-- v-model做的事情. -->
<input type="text" :value="userName" @input="hanlderInputClick" />
</div>
JavaScript 部分
var app = new Vue({
el: '#app',
data: {
userName: '李四'
},
methods: {
// 我们在使用 v-model 时 会我们隐式添加这个事件响应方法.
hanlderInputClick (e) {
this.userName = e.target.value
console.log(this.userName)
}
},
})
- 首先在 input 元素中,我们使用
:value='userName'
建立了 data -> dom 之间的单向数据流动关系.这样在 data 属性发生改变时(监听到 set),就可以同步的修改 input 的 value 值了. - 其次,我们在让 input 元素发布 input 事件,并让组件提供事件响应方法. 建立了 dom -> data 直接单向数据流动的关系. 这样,input 元素才发生 value 的 change 时,能同步的修改 data 里的值. dom -> data
所以, v-model 指令,就帮我们做了两件事情.
- 利用 :value="data" 建立 data -> dom 的流动关系.
- 利用 @inutput 建立 dom -> data 的流动关系.
于是数据的双向流动关系就打通了.
所以 v-model="userName" 等价于 :value="userName" & @input="hanlderInputChange".(@input 事件监听以及 handlerInputChange 是 vue 在后帮我们自动提供的)
将数据绑定到 dom 上,并提供 dom 的@input 事件.
关于组件间的 v-model
根据上述内容,我们已经知道了 v-model
无非就是做了两件事情.
- 属性绑定到对应的 input
- 对应的 input 像外发布 @input 事件.
第一点什么好说的. 属性绑定任何元素可组件都可以非常方便的执行.
关键在于第二点,像外发布 @input 事件.
为什么 v-model
不能绑定在 DIV
,p
,<img />
.... 上 ?
常规情况下,这些元素都这是用于容器,或者展示静态文字和图片的 HTML 元素.
它基本不具备用户交互(这里的交互指的是编辑和修改)的功能(没有数据修改)
同时,它们也不具备类似 @input 事件. (即使有数据修改,也不能发布出来)
数据只能是单项的,从 data -> div,p, img.
所以我们现在知道了 v-model 一个非常重要的前提:
被绑定的元素必须包含可以变化自身数据 & 以及能将这种变化传递出去的能力.
那在组件上使用 v-model
是想表达什么意思呢?
组件是什么?
组件就是一个 js 对象.
里面包含一个数据,方法,以及最重要的 template 模板(render).
我们肉眼能看到的组件,就是 template 里的 HTML 模板外加一个数据展示.
那如果,我们在组件上使用 v-model ,对于这个组件来说,意味着什么呢?
查看下面这段代码
<div id='root'>
<child v-model="parentData"></child>
</div>
- 首先 parentData 是 顶层 root 组件的一个 data 属性.
- 接着在 child 组件标签上设置了 v-model='parentData'
在视觉上,就好像我们给一个 input 元素设置了 v-model 一样.
但是问题是:
- 在 input 元素,它有一个位置可以显示这个 parentData, 一般是 value 值.
- 第二个,input 元素有 @input 事件,可以将自身 value 的改变,同步到 parentData 上.
在组件上写 v-model 是个什么鬼??
首先,不管是什么鬼.
我们在组件上写 v-model
并传递了数据 parentData
.
主要的目的之一,肯定是希望在将 root
组件的 parentData
传递到 child
组件内部的.
那 child
组件内部如何去接受这个 parentData
这个值呢?
官方文档介绍说:
在组件上使用 v-model 会默认的设置到组件的 props 中名为 value 的属性值上.
根据上述说明,在组件上使用 v-model 可以等价于.
<div id='root'>
<!--<child v-model='parentData'></child>-->
<!--等价于-->
<child :value='parentData'></child>
</div>
<child :value='parentData'></child>???
这不就是父组件向子组件传递数据的 props 语法么?
Vue.component('child',{
props:['value']
template: `<h1>{{value}}</h1>`
})
这样,在组件上使用 v-model 将数据从 root -> child 就已经打通了.
<div id='app'>
<p>一般v-model在常规的情况下,我们是用作于input表单元素的</p>
<input type="text" v-model="userName" />
<!-- v-model到底做了什么事情? -->
<!-- <input type="text" :value="userName" @input="hanlderInputClick">
<button @click="hanlderClick">getUserName</button> -->
<p>当一个v-model作用与一个组件的时候,到底代表的是什么意思?</p>
<comp-one v-model="userName"></comp-one>
</div>
let CompOne = {
props:['value'], //v-model 等价于 :value='parentData' 所以这里写的是 value
data () {
return {
msg: '我是compone组件'
}
},
template: `<div @click="hanlderClick">
<label>{{msg}}</label>
<input type='text' :value="value" />
</div>`
}
到目前为止,v-model 的数据从父组件传递到了子组件,且可以在子组件内部通过 props 在任意元素上去显示了.
但是数据流动只是单向的,从父组件传递到了子组件.
怎么才能在子组件中的数据修改之后,能够会传到父组件呢?
对于不可编辑修改以及没有往外发布变动实际的 span 来说,肯定是没戏了.
那如果我们利用组件内的那个 input 元素的事件呢?
let CompOne = {
props:['value'], //v-model 等价于 :value='parentData' 所以这里写的是 value
data () {
return {
msg: '我是compone组件'
}
},
template: `<div>
<label>{{msg}}</label> <br />
一个可以发布 input 事件的元素:<input type='text' :value="value" @input="hanlderClick" /> <br/>
一个普的 span 标签,没有 input 事件: <span>{{value}}</span>
</div>`,
methods: {
// 让组件内的 input 元素,往外发布 input 事件.
hanlderClick (e) {
this.$emit('input',e.target.value)
}
},
}
查看结果:
结论
- 在组件上使用 v-model ,会默认将这个值以 :value 的形式传递到组件的 props 上.
- 我们在组件的内部,仍然需要自己拿到这个 props.value 在使用.
- 一般使用到 input 元素上,可以在组件内部发布一个当前表单元素的 input 事件到外部.
- 外部组件会默认的帮我们注册 input 事件的响应函数,并同步更新 v-model 传递过去的数据.
未完待续......