v-model 的原理 和 .sync 修饰符

v-model 的原理

特点:双向绑定
​ 数据-->视图
​ 视图-->数据

常见的使用场景:

  1. 收集表单数据
  2. 用在组件上

原理:

v-model只是个语法糖,本质是:v-model=v-bind(:value属性) + v-on(@input事件)

<div id="app">
    <cdn></cdn>
</div>

<template id="temp">
    <div>
        <input type="text" v-model="text">
        {{text}}
    </div>
</template>
let app=new Vue({
    el:'#app',
    components:{
        cdn:{
            template:'#temp',
            data(){
                return{
                    text:'完全透明的包裹器'
                }
            },          
        }
    }

})

视图页面中实现了双向绑定

  • 以下代码的本质与上面一致(v-model的原理分析)
<div id="app">
    <cdn></cdn>
</div>

<template id="temp">
    <div>
        <!--        <input type="text" v-bind:value="text" v-on:input="handleInput">   -->
        <input type="text" v-bind:value="text" @input="(e)=>(this.text=e.target.value)">//ES6写法
        {{text}}
    </div>
</template>
let app=new Vue({
        el:'#app',
        components:{
            cdn:{
              //....省略
                // input 事件传过来了事件对象
                methods:{
                    handleInput(event){
                        this.text=event.target.value;
                    }
                }
            }
        }

    })

当在输入框中输入数据时,视图页面的 {{text}}也会实时更新


v-model 作用在组件上的原理

利用v-model实现父子组件的双向绑定通信

在组件上使用 v-model,使v-model绑定的变量发生改变时,能够影响组件的状态或行为,数据-->视图

当组件内部状态或行为发生改变时,能够影响到v-model绑定的变量,视图-->数据

官网的解释是:父组件将值通过v-model进行绑定,之后子组件通过propskeyvalue进行接收,通过事件input触发更改父组件。

组件与视图的绑定

组件与视图绑定,在v-model的原理中已经实现;

注意:

  • 官方注明使用props的属性进行传递数据
  • 但是props属性只能由父组件传递数据不能在模板里面修改props的数据,组件内props属性的数据只能用来展示不能修改
props:{
   		 num1:Number,
         num2:Number
},
template:
    `
        <div>
                    <h2>num1:{{num1}}</h2>
                    <input type="number" v-model="num1">
                    <br>
                    <h2>num2:{{num2}}</h2>
                 <input type="number" v-model="num2">
       </div>

以上代码的确实现了组件与视图的双向绑定,但是会报错

[Vue warn]:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "num2"

意思就是props里的属性不支持修改,当父组件重新渲染时,props属性也会被重新渲染;
应当使用一个data方法或者计算属性将属性的值重新赋给另外一个属性

  • 将绑定改到新的属性,而不是props属性,此时已经对dnum1进行了双向绑定,页面也可以同步更新
data() {
    return {
        dnum1:this.num1,
        dnum2:this.num2,
    }
},

template:` <input type="number" v-model="dnum1">`//模板中的v-model绑定改为 dnum1



//也可以使用自定义事件(效果是一致的):
 <!--        <input type="number" v-bind:value="text" v-on:input="handleInput">   -->
<input type="number" v-bind:value="dnum1" @input="(e)=>(this.dnum1=e.target.value)">//ES6写法

v-model绑定变量与组件的绑定

父子组件的双向绑定

  1. 父组件向子组件传值使用Props,子组件定义期望接收传值的名字、类型、默认值等等,父组件Props值的改变会自动同步到子组件。
  2. 子组件向父组件传值使用事件触发,即使用$emit注册事件,在父组件触发,这个事件可以携带值,而且一个事件的触发本身就是在传递信息。
  • 子组件中使用$emit触发事件发送数据

    components: {
        cdn: {
           //.....省略
            methods: {
                childHandleInput(event) {
                    this.outText = event.target.value;//改变赋予了`props属性的数据`的属性
                    this.$emit('handle-input', this.outText)//参数传递给handle-input事件
                }
            }
        },
    }
    
  • 父组件中接收数据并绑定到变量

<div id="app">
    <!--  这里事件不是input,不能使用 v-model  -->
    <cdn :value="handleText" @handle-input="handleInput">
    </cdn>
</div>

<script>

let app = new Vue({
        el: '#app',
        data: {
            handleText: ''
        },
        methods: {
            handleInput: function (value) {
                this.handleText = value;
            }
        },
    
</script>

完整代码:

<div id="app">
    <!--  这里事件不是input,不能使用 v-model  -->
    <cdn :value="handleText" @handle-input="handleInput">
    </cdn>

    <p>handleText-->{{handleText}}</p>
</div>

<template id="temp">
    <div>
        <input type="text" :value="outText" @input="childHandleInput">
        <p>outText-->{{outText}}</p>
    </div>
</template>
let app = new Vue({
    el: '#app',
    data: {
        handleText: ''
    },
    methods: {
        handleInput: function (value) {
            this.handleText = value;
        }
    },
    components: {
        cdn: {
            template: '#temp',
            props: {
                value: String,
            },
            data() {
                return {
                    outText: this.value
                }
            },
            methods: {
                childHandleInput(event) {
                    this.outText = event.target.value;
                    this.$emit('handle-input', this.outText)
                }
            }
        },
    }

})

.sync 修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源。

  • 官方推荐使用:update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)

发送update事件,携带数据发送给父组件

注意:titleprops属性

  • 然后父组件可以监听那个事件并根据需要更新一个本地的数据 property。例如:
<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

doc.title是父组件中的属性

这样写在父组件中就不需要再自定义处理事件函数了

  • 为了方便起见,我们为这种模式提供一个缩写,即 .sync 修饰符:
<text-document v-bind:title.sync="doc.title"></text-document>

完整代码:

<div id="app">
<!--    <cdn :value="handleText" @update:value="handleText=$event">-->
    <cdn v-bind:value.sync="handleText">
    </cdn>

    <p>handleText-->{{handleText}}</p>
</div>

<template id="temp">
    <div>
        <input type="text" :value="outText" @input="childHandleInput">
        <p>outText-->{{outText}}</p>
    </div>
</template>
let app = new Vue({
    el: '#app',
    data: {
        handleText: ''
    },
    //不需要自定义处理事件了
    methods: {
        /*handleInput: function (value) {
            this.handleText = value;
        }*/
    },
    components: {
        cdn: {
            template: '#temp',
            props: {
                value: String,
            },
            data() {
                return {
                    outText: this.value
                }
            },
            methods: {
                childHandleInput(event) {
                    this.outText = event.target.value;
                    this.$emit('update:value', this.outText)
                }
            }
        },
    }

})

注意:
.sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model

  • 当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用:
<text-document v-bind.sync="doc"></text-document>

这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。

posted @ 2021-11-22 20:26  夏末彡  阅读(393)  评论(0)    收藏  举报