自定义组件v-model的实质性理解
用了几个月vue一直很纠结自定义组件的v-model实现,最近开始学习react时,react中受控组件与状态提升的理念与v-model不谋而合。
转载请注明地址:
在vue与react中其实都存在单向数据流的概念,只不过vue中通过各种语法糖被弱化了,比如react与vue中的props都是单向传输数据的。在react中如果想实现类似于v-model的功能,需要这样实现:
父组件:
class calculator extends react.component { constructor(props) { super(props); this.handlecelsiuschange = this.handlecelsiuschange.bind(this); this.handlefahrenheitchange = this.handlefahrenheitchange.bind(this); this.state = {temperature: '', scale: 'c'}; } handlecelsiuschange(temperature) { this.setstate({scale: 'c', temperature}); } handlefahrenheitchange(temperature) { this.setstate({scale: 'f', temperature}); } render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = scale === 'f' ? tryconvert(temperature, tocelsius) : temperature; const fahrenheit = scale === 'c' ? tryconvert(temperature, tofahrenheit) : temperature; return ( <div> <temperatureinput scale="c" temperature={celsius} ontemperaturechange={this.handlecelsiuschange} /> <temperatureinput scale="f" temperature={fahrenheit} ontemperaturechange={this.handlefahrenheitchange} /> <boilingverdict celsius={parsefloat(celsius)} /> </div> ); } }
子组件:
class temperatureinput extends react.component { constructor(props) { super(props); this.handlechange = this.handlechange.bind(this); } handlechange(e) { this.props.ontemperaturechange(e.target.value); } render() { const temperature = this.props.temperature; const scale = this.props.scale; return ( <fieldset> <legend>enter temperature in {scalenames[scale]}:</legend> <input value={temperature} onchange={this.handlechange} /> </fieldset> ); } }
这是一个输入水温从而监控水是否沸腾的一个小组件。子组件是一个温度输入的input控件,父组件是由两个温度输入(华氏与摄氏)与一个现实水是否沸腾的指示器组成。父组件存在一个state作为唯一数据源用于存放温度等值于状态,温度通过子组件的prop传入子组件内部通过input的value属性显示在基础输入框中,当在基础输入框中触发输入时间时,onchange事件触发由prop传入的ontempreture事件并附带变化后的值,再由父组件的handlecelsiuschange/handlefahrenheitchange事件处理方法将基础输入框传来的值写入state中,再由state通过prop将温度传入子组件完成一次数据的更新。这其中其实已经完成了对vue中基础组件v-model的理解与自定义组件v-model的理解。
转载请注明地址:
在vue官方文档中,对原生组件v-model的解释是这样的:
<input v-bind:value="searchtext" v-on:input="searchtext = $event.target.value" >
v-model其实是上面写法的语法糖。其实就是将this.searchtext的值通过名为value的prop传入input组件内,而后当input事件触发时将事件带来的input的新值写入this.searchtext中,然后根据this.searchtext中值的变化通过value的prop传入input控件完成input控件上值的变化,如果去掉v-on...后,这个控件将变为一个只读控件。
对于自定义组件,文档中有这样的解释:
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
特性用于不同的目的。model
选项可以用来避免这样的冲突:
vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
现在在这个组件上使用 v-model 的时候:
<base-checkbox v-model="lovingvue"></base-checkbox>
这里的 lovingvue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingvue 的属性将会被更新。
其实就是将原来v-model默认使用的名为value的prop与名为input的event自定义一个名字使用,在上面自定义组件中存在
props: { checked: boolean }
说明checked本质上还是一个prop,然后在子组件的model属性中将自定义的prop与event注册,而触发model中event时也就是通过触发子组件的事件在父组件中修改绑定自定义prop的变量的值的过程,这样这个过程就很明显了:
1.父组件创建一个名为tmp变量绑定名为checked的prop的值(已被修饰为v-model)并根据父组件中tmp值的变化将变化后的值传入子组件中,引起子组件checkbox状态变化;
2.子组件中checkbox被勾选,触发checkbox的change事件,通过this.$emit方法触发子组件的change事件并将change事件产生的新值传入;
3.因为在model属性中已将v-model语法糖中event注册为change(换成其他名字也都可以),v-model会自动将子组件传来的值传入tmp变量中;
4.vue监听到tmp值的变化,执行第一步,更新子组件中checkbox的状态;
其实上面的子组件可以换个写法更容易理解:
vue.component('base-checkbox', { model: { prop: 'checked', event: 'test' }, props: { checked: boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('test', $event.target.checked)" > ` })
父组件中调用时可以这样写:
<base-checkbox :checked="something" @test="something='子组件中的$event.target.value'"></base-checkbox>
这样对v-model的理解也就一目了然了。
转载请注明地址: