vue自定义组件实现v-model(含vue3)
v-model原理
<input type="text" v-model="age">
<input type="text" v-bind="age" v-on:input="age = $event.target.value">
v-model的原理就是: v-bind 和 v-on的语法糖
vue2组件双向绑定
第一种: v-bind(★)
原理: 子组件通过监听父组件数据,子组件改变数据之后通知给父组件
错误写法: 不可以直接修改props的值
正确写法
父组件
// Users.vue
<template>
<div>
<Son :ageValue="age" @changeInput="changeInput"/>
<el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
</div>
</template>
<script>
export default {
data() {
return {
age: ''
}
},
methods: {
changeInput(val) {
this.age = val
}
}
}
</script>
子组件
// Son.vue
<template>
<div>
<input type="text" v-model="sonAge" @input="changeInput">
</div>
</template>
<script>
export default {
props: {
ageValue: {
typeof: String
}
},
data() {
return {
sonAge: ''
}
},
methods: {
changeInput() {
this.$emit('changeInput', this.sonAge)
}
},
/*
为什么要监听:
因为父组件传递过来属性, 可能有默认值,
子组件的input需要根据默认值回显,或者别的地方需要
*/
watch: {
ageValue: {
immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
handler(val) {
this.sonAge = val
}
}
}
}
</script>
第二种.sync修饰符(★★)
原理:
.sync:名字 是自己起的, 通过update:名字进行触发对象的事件
update:是vue为我们约定好的名称部分
父组件
// Users.vue
<template>
<div>
<Son :ageValue.sync="age" />
<el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
</div>
</template>
<script>
export default {
data() {
return {
age: ''
}
},
methods: {
}
}
</script>
子组件
// Son.vue
<template>
<div>
<input type="text" v-model="sonAge" @input="changeInput">
</div>
</template>
<script>
export default {
props: {
ageValue: {
typeof: String
}
},
data() {
return {
sonAge: ''
}
},
methods: {
changeInput() {
// this.$emit('changeInput', this.sonAge)
// 这样父组件内的值也同时被更改,省略了监听事件这一步
this.$emit('update:ageValue', this.sonAge)
}
},
watch: {
ageValue: {
immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
handler(val) {
this.sonAge = val
}
}
}
}
</script>
第三种 v-model(★★★)
原理: 通过 model新属性: 配置一个 props:接受的属性, 和一个事件名
// Users.vue
<template>
<div>
<Son v-model="age" />
<el-button @click="age = Math.floor(Math.random()*10)">添加</el-button>
</div>
</template>
<script>
export default {
data() {
return {
age: ''
}
}
}
</script>
子组件
// Son.vue
<template>
<div>
<input type="text" v-model="sonAge" @input="changeInput">
</div>
</template>
<script>
export default {
props: {
value: {
typeof: String
}
},
data() {
return {
sonAge: ''
}
},
// 超级牛
model: {
prop: 'value',
event: 'change'
},
methods: {
changeInput() {
this.$emit('change', this.sonAge)
}
},
watch: {
value: {
immediate: true, // 立即执行 :当刷新页面时会立即执行一次handler函数
handler(val) {
this.sonAge = val
}
}
}
}
</script>
vue3组件双向绑定
第一种(★★)
这里子组件input 用的是 :value 和 @input
父组件
// Users.vue
<template>
<div class="user-wrap">
<Son v-model="message" />
<h1>{{ message }}</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Son from './son.vue'
export default defineComponent({
name: 'user',
components: {
Son
},
setup() {
let message = ref('')
return {
message,
}
}
})
</script>
子组件
// Son.vue
<template>
<div>
<input type="text" :value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: ['modelValue'],
emits: ['update:modelValue'],
})
</script>
第二种: 通过computed计算属性(★★★)
这里用的是 v-model ,精简了 :vlaue 和@input
父组件
// Users.vue
<template>
<div class="user-wrap">
<!-- 两个方法等价 -->
<!-- <Son :modelValue="message" @update:modelValue="message = $event" /> -->
<Son v-model="message" />
<h1>{{ message }}</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Son from './son.vue'
export default defineComponent({
name: 'user',
components: {
Son
},
setup() {
let message = ref('')
return {
message,
}
}
})
</script>
子组件
// Son.vue
<template>
<div>
<!-- 两个方法等价 -->
<!-- <input type="text" :value="newValue" @input="newValue = $event.target.value" /> -->
<input type="text" v-model="newValue" />
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue'
export default defineComponent({
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
const newValue = computed({
// 子组件v-model绑定 计算属性, 一旦发生变化, 就会给父组件传递值
get: () => props.modelValue,
set: (nv) => {
emit('update:modelValue', nv)
}
})
return {
newValue
}
}
})
</script>
第三种: 组件绑定多个v-model
父组件
// Users.vue
<template>
<div class="user-wrap">
<!-- 这里绑定两个v-model -->
<Son v-model="message" v-model:title="title" />
<h1>message:{{ message }}</h1>
<h1>title:{{ title }}</h1>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Son from './son.vue'
export default defineComponent({
name: 'user',
components: {
Son
},
setup() {
let message = ref('')
let title = ref('')
return {
message,
title,
}
}
})
</script>
子组件
// Son.vue
<template>
<div>
<!-- 两个方法等价 -->
<!-- <input type="text" :value="newValue" @input="newValue = $event.target.value" /> -->
<input type="text" v-model="newValue" />
-
<input type="text" v-model="newTitle" />
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue'
export default defineComponent({
props: {
// v-model默认的名字
modelValue: {
type: String
},
title: {
//这里可以直接使用 v-model:title ,:号后面的名字
type: String
}
},
emits: ['update:modelValue', 'update:title'],
setup(props, { emit }) {
const newValue = computed({
get: () => props.modelValue,
set: (nv) => {
console.log(nv)
emit('update:modelValue', nv)
}
})
const newTitle = computed({
get: () => props.title,
set: (nv) => {
emit('update:title', nv)
}
})
return {
newValue,
newTitle
}
}
})
</script>
vue2 对比 vue3的 v-model区别
vue2在组件中这样设置:
父组件
<ChildComponent v-model = "title">
子组件
export default {
model: {
prop: 'title', // v-model绑定的属性名称
event: 'change' // v-model绑定的事件
},
props: {
value: String, // value跟v-model无关
title: { // title是跟v-model绑定的属性
type: String,
default: 'Default title'
}
},
methods: {
handle() {
// 这里的 change, 对应 event
this.$emit('change', 'xxx')
}
}
}
vue3在组件中这样设置
父组件
<!-- 两个方法等价 -->
<Son v-model="message" />
<!-- <Son :modelValue="message" @update:modelValue="message = $event" /> -->
子组件
export default defineComponent({
props: {
modelValue: {
type: String
}
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const newValue = computed({
get: () => props.modelValue,
set: (nv) => {
console.log(nv)
emit('update:modelValue', nv)
}
})
return {
newValue
}
}
})
总结:
vue2:
- v-model: 会把
value
用作 prop 且把input
用作 event; - 可以通过
.sync
修饰符 指定传递名字 - 支持model: 可以指定v-model的 value属性名 和 event事件名字
组件v-model原理:
<Son v-model="age" />
<Son :value="age" @change="age = $event" />
vue3:
- v-model: 不在绑定 value 而是
modelValue
, 接受方法也不再是 input 而是update:modelValue
- 组件支持多个 v-model, 并且可以指定名字 v-model:名字
组件v-model原理:
<Son v-model:value="age" />
<Son :modelValue="age" @update:modelValue="age = $event" />
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY