# vue.sync

.sync 相当于对一个props进行双向绑定,也是一个语法糖

<!--语法糖.sync,vue3不支持这种语法了-->
<my-component :value.sync="msg" />
<!--编译后的写法,这种写法可以继续在vue3中使用,相当于v-model:value-->
<my-component 
  :value="msg" 
  @update:value="(val) => msg = val"
/>

# 案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>vue-03</title>
    <!-- 引入Vue -->
    <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
</head>

<body>

    <div class="container" style="margin-top: 12px;">
        <div id="demo" class="row">
            {{ say }}
            <br />
            <my-input :value.sync="say"></my-input>
        </div>
    </div>


    <script>
        new Vue({
            el: '#demo',
            data: {
                say: "123"
            },
            components: {
                "my-input": {
                    props: ['value'],
                    template: "<div><input v-bind:value='value' v-on:input='change1' />{{value}}</div>",
                    watch: {
                        value: function(newValue, oldValue) {
                            console.log('子组件value新旧值' + newValue + '/' + oldValue);
                        }

                    },
                    methods: {
                        change1: function(e) {
                            var v = e.target.value
                            this.$emit('update:value', v)
                        },

                    }
                }
            },
            watch: {
                say: function(n, o) {
                   console.log('父组件新旧值' + n + '-' + o)
                }
            },
        })
    </script>
</body>

props的.sync在vue3中被移除

替代方案:

<ChildComponent v-model:title="pageTitle" />

<!-- 是以下的简写: -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
+ v-model:show="valueChild" => show作为props传给子组件
+ 子组件调用update:show=>this.$emit('update:show', false); 通知父组件
+ 父组件直接valueChild = 传递过来的值;修改了show的值,子组件进行响应的变化
  • 父组件
<template>
	<div>
		 <Children1  v-model:show="valueChild" style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"/>
		  <button @click="changeValue">toggle</button>
	</div>
</template>

<script>
	import { ref } from 'vue'
	import Children1 from './com/Children1.vue'
	export default {
		data(){
		        return{
		            valueChild:true,
		        }
		    },
		    methods:{
		        changeValue(){
		            this.valueChild = !this.valueChild
		        }
		    },
		components:{
			Children1
		},
		
		   
	}
</script>
  • 子组件
<template>
	<div v-if="show">
		<p>默认初始值是{{show}},所以是显示的</p>
		<button @click.stop="closeDiv">关闭</button>
	 </div>
</template>

<script>
export default{
	props:['show'],
	  methods: {
		closeDiv() {
		  this.$emit('update:show', false); //触发 input 事件,并传入新值
		}
	  }	
}
</script>

# v-model

# v-model的修饰符

修饰符 lazy, number, trim

v-model其实只是语法糖,在input标签内写上v-model后,实际上在vue内会编译为:

<!--v-model写法-->
<input type="text" v-model="value">
<!--编译后的写法-->
<input type="text"
  :value="value"
  @input="value = $event.target.value"
>

问题来了,如果这个input是个组件(暂且叫input-component吧),想在父组件中使用input-component这个组件,我们能直接使用v-model进行双向绑定吗?

答案是:很遗憾,不能,因为v-model只是一个语法糖,他的原本写法上面已经提过,绑定value值,监听input事件,通过event拿到value并赋值给value。

那么要怎么做才能在组件上使用v-model呢? 很简单,当input-component触发input事件时,让他发布一个input事件,并带上$event.target.value,在父组件上,我们用v-model监听这个input事件,就能实现了

this.$emit('input',e.target.value)触发input事件怎么理解

<!--子组件-->
<input type="text"
  :value="value"
  @input="$emit('input', $event.target.value);"
>
<!--父组件-->
<input-component v-model="value">
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>vue-10</title>
    <!-- 引入Vue -->
    <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script>
</head>

<body>

    <div class="container" style="margin-top: 12px;">
        <div id="demo" class="row">
            {{ say }}
            <br />
            <my-input v-model="say"></my-input>
        </div>
    </div>


    <script>
        new Vue({
            el: '#demo',
            data: {
                say: "123"
            },
            components: {
                "my-input": {
                    props: ['value'],
                    template: "<div><input v-bind:value='value' v-on:input='change' />{{value}}</div>",
                    watch: {
                        value: function(newValue, oldValue) {
                             console.log('子组件value新旧值' + newValue + '/' + oldValue);
                        }

                    },
                    methods: {
                        change: function(e) {
                            this.$emit('input', e.target.value)
                        }
                    }
                }
            }
        })
    </script>
</body>

# v-model 自定义组件checkbox使用方法

  • 子组件T1, 定义一个model用来接受属性和调用的方法
<template>
	<div>
		<input type="checkbox" :checked='checked' @change="changeval">	
	</div>
	
</template>
<script>
	export default {
		props:{
			checked:Boolean
		},
		model:{
			prop:'checked',
			event:'change'
		},
		methods:{
			changeval(e){
				console.log(e.target.checked)
				this.$emit('change',e.target.checked)
			}
		}
	}
</script>
<style>
	
</style>
  • 父组件
<template>
  <div class="home">
	<T1 v-model='checked'/>
	{{checked}}
  </div>
</template>

<script>
import T1 from '@/components/T1.vue'

export default {
  name: 'Home',
  components: {
	T1
  },
  data(){
	  return {
		  checked:false
	  }
  }
}
</script>

# vue3 v-model的改变

<!-- input上使用v-model -->
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">

<!-- 组件上使用v-model -->
<hy-input v-model="message"></hy-input> 
<!-- 等价于 -->
<hy-input :modelValue="message" @update:model-value="message = $event"></hy-input>

依然希望在组件内部按照双向绑定的做法去完成,应该如何操作呢?可以使用计算属性的setter和getter来完成。

<template>
  <div class="validate-input-container pb-3">
    <input
      v-model="inputRef.val"
      v-bind="$attrs"
    >
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, PropType, computed } from 'vue'

export default defineComponent({
  props: {
    modelValue: String,
  },
  setup(props, context) {
    const inputRef = reactive({
      val: computed({
        get: () => props.modelValue || '',
        set: val => {
          context.emit('update:modelValue', val)
        }
      }),
    })
    return {
      inputRef
    }
  }
})
</script>

v-model 可以使用在双向绑定的场景,此时,v-model的用法有限制

  1. 传递给子组件props名字固定为modelValue,更新值得事件需要$emit发送'update:modelValue'
  2. 如果不想使用modelValue,需要改成v-model:xx='ss',props=>xx ,发送事件为update:xx
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 18</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  const app = Vue.createApp({
    data() {
      return { count: 1 }
    },
    template: `
      <counter v-model="count" />
    `
  });

  app.component('counter', {
    props: ['modelValue'],
    methods: {
      handleClick() {
        this.$emit('update:modelValue', this.modelValue + 3);
      }
    },
    template: `
      <div @click="handleClick">{{modelValue}}</div>
    `
  });

  const vm = app.mount('#root');
</script>
</html>

自定义插件如果需要监听多个数据的变动,可以同时绑定多个v-model,需要自主命名来区分. v-model:app1 ,v-model:app2,同时还可以添加自定义修饰符

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>lesson 19</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="root"></div>
</body>
<script>
  const app = Vue.createApp({
    data() {
      return {
        count: 'a',
      }
    },
    template: `
      <counter v-model.uppercase="count" />
    `
  });

  app.component('counter', {
    props: {
      'modelValue': String,
      'modelModifiers': {
        default: ()=> ({})
      }
	  //modelModifiers固定写法
    },
    methods: {
      handleClick() {
        let newValue = this.modelValue + 'b';
        if(this.modelModifiers.uppercase) {
          newValue = newValue.toUpperCase();
        }
        this.$emit('update:modelValue', newValue);
      },
    },
    template: `
      <div @click="handleClick">{{modelValue}}</div>
    `
  });

  const vm = app.mount('#root');
</script>
</html>

# vue3 v-model自定义修饰符

当我们学习表单输入绑定时,我们看到 v-model 有内置修饰符——.trim、.number 和 .lazy。但是,在某些情况下,你可能还需要添加自己的自定义修饰符。

让我们创建一个示例自定义修饰符 capitalize,它将 v-model 绑定提供的字符串的第一个字母大写。

添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。在下面的示例中,我们创建了一个组件,其中包含默认为空对象的 modelModifiers prop。

请注意,当组件的 created 生命周期钩子触发时,modelModifiers prop 会包含 capitalize,且其值为 true——因为 capitalize 被设置在了写为 v-model.capitalize="myText" 的 v-model 绑定上。

<my-component v-model.capitalize="myText"></my-component>
app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({xxx:false||true})//
	  //比如在父组件v-model如果没有定义这个自定义修饰符,可以在这里使用默认值。根据实际情况去设置
	  // v-model.capitalize =>v-model 那就取这里的默认值了
    }
  },
  emits: ['update:modelValue'],
  template: `
    <input type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true }
  }
})

现在我们已经设置了 prop,我们可以检查 modelModifiers 对象键并编写一个处理器来更改发出的值。在下面的代码中,每当 <input/> 元素触发 input 事件时,我们都将字符串大写。

<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>
const app = Vue.createApp({
  data() {
    return {
      myText: ''
    }
  }
})

app.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    :value="modelValue"
    @input="emitValue">`
})

app.mount('#app')

对于带参数的 v-model 绑定,生成的 prop 名称将为 arg + "Modifiers":

<my-component v-model:description.capitalize="myText"></my-component>
app.component('my-component', {
  props: ['description', 'descriptionModifiers'],
  emits: ['update:description'],
  template: `
    <input type="text"
      :value="description"
      @input="$emit('update:description', $event.target.value)">
  `,
  created() {
    console.log(this.descriptionModifiers) // { capitalize: true }
  }
})

# vue2和vue3的v-model的总结

  • 2.x 中 v-model 语法糖底层使用的是 :value 和 emit(‘input’), 绑定属性值是 value
  • 3.0 中可以绑定一个自定义值,支持统一组件绑定多个 v-model,v-model:firstName=“firstName”, :value=“firstName” 和 @input="$emit(‘update:firstName’, $event.target.value)" 添加自定义修饰符 v-model.capitalize
  • app.vue
<template>
  <div id="app">
    <h1>Vue3中v-model的变化</h1>
    <input type="text" v-model="name"/>
    <p>{{ name }}</p>
    <!-- Vue2的写法 -->
    <!-- v-model实际上就是:value和@input的语法糖 -->
    <!-- 双向绑定多个属性的时候可以使用.sync关键字 -->
    <CustomInput v-model="age" :name.sync="name"/>
    <!-- Vue3的写法 -->
    <CustomInput v-model:age="age" v-model:name="name"/>
  </div>
</template>

<script>
import CustomInput from "../components/CustomInput.vue";
export default {
  name: "App",
  components: {
  	CustomInput
  },
	data() {
    return {
    	name: "你好",
      age: 20,
    }
  },
}
</script>
  • CustomInput.vue
<template>
  <div class="custom-input">
    <h1>自定义的input</h1>
    <!-- Vue2的写法 -->
    <input type="text" :value="value" @input="onInput" />
    <input type="text" :value="name" @input="onNameInput" />
    <!-- Vue3的写法 -->
    <input type="text" :value="age" @input="onInput" />
    <input type="text" :value="name" @input="onNameInput" />
  </div>
</template>

<script>
import CustomInput from "../components/CustomInput.vue";
export default {
  // Vue2的写法
  props: ["value", "name"],
  // Vue3的写法,直接接收绑定的参数
  props: ["age", "name"],
  // Vue3双向绑定单个属性时,可以使用modelValue来接收参数并更新,对应的触发事件为update:modelValue
  props: ["modelValue"],
  methods: {
  	onInput(e) {
      // Vue2的写法
      // 触发的事件只能是input
      // e.target.value是字符串需要转换成数字
    	this.$emit("input", parseInt(e.target.value));
      // Vue3的写法
      this.$emit("update:age", e.target.value);
    },
    onNameInput(e) {
      // 只能用update
    	this.$emit("update:name", e.target.value);
    },
  },
}
</script>

# 2022年4月27日整理(vue2)

  1. v-model是语法糖,可以用在自定义组件中,会被解析为value和@input事件
  2. 如果子组件中完全没用到input,select,textarea,checkbox也是可以用v-model,如例myinput.vue
  3. 默认子组件接受value,触发事件input,但是可以通过子组件中的model修改props中的名字和事件触发的名字,如myinput2.vue
  4. (如果不是用model,那么参数仍然是value,触发条件仍然是input事件)
  5. 注意checkbox修改的值是e.target.checked,不是e.target.value
<template>
	<div>
		<input type="text" v-model='t1'/>
		<myinput type="text" v-model='t2'/>
		t2:{{t2}}
		<br>
		<myinput1 v-model='t3' />
		t3:{{t3}}
		<br>
		<myinput2 v-model='t4'/>
		t4:{{t4}}
	</div>
</template>

<script>
	import myinput from './myinput.vue'
	import myinput1 from './myinput1.vue'
	import myinput2 from './myinput2.vue'
	export default{
		data(){
			return {
				t1:10,
				t2:20,
				t3:30,
				t4:true
			}
		},
		components:{
			myinput,
			myinput1,
			myinput2
		},
		methods:{
			
		}
	}
</script>
  • myinput.vue
<template>
	<div>
		<p>my input</p>
		{{value}}
		<button @click='add'>+</button>
	</div>
</template>
<script>
	export default {
		props:{
			value:Number,
		},
		methods:{
			add(){
				this.$emit('input',this.value+5)
			}
		}
	}
</script>
  • myinput1.vue
<template>
	<div>
		<select name="" :value='value' @change='changefun'>
			<option :value="item" v-for='item in arr' :key='item' >{{item}}</option>
		</select>
	</div>
</template>
<script>
	export default{
		props:['value'],
		data(){
			return{
				arr:[5,10,15,20,25,30,35,40]
			}
		},
		methods:{
			changefun(e){
				console.log(e.target.value)
				this.$emit('input',e.target.value)
			}
		}
	}
</script>
  • myinput2.vue
<template>
	<input type="checkbox" :checked="checked" @change='fun'/>
</template>

<script>
	export default{
		props:['checked'],
		model:{
			prop:'checked',
			event:'change'
		},
		methods:{
			fun(e){
				this.$emit('change',e.target.checked)
			}
		}
	}
</script>

v-model

vue2和vue3的关于v-model的区别 (opens new window)

最后更新: 8/20/2022, 6:14:06 PM