Evan's blog Evan's blog
首页
关于
  • 分类
  • 标签
  • 归档
  • H5&CSS3
  • JS
  • TS
  • Node
  • Webpack
  • Vue2
  • Vue3
  • 微信小程序
  • Andorid
  • Flutter
推荐
GitHub (opens new window)

conanan

真相只有一个
首页
关于
  • 分类
  • 标签
  • 归档
  • H5&CSS3
  • JS
  • TS
  • Node
  • Webpack
  • Vue2
  • Vue3
  • 微信小程序
  • Andorid
  • Flutter
推荐
GitHub (opens new window)
  • 基础

  • 组件

    • 父子组件通讯
    • 非父子组件通讯
    • 插槽
    • 动态组件
    • 异步组件
    • 生命周期
    • 组件的v-model
      • 推荐 🔥
      • 使用 🔥
      • 绑定多个值 🔥
      • 注意—绑定对象 🔥
        • 方式一
        • 方式二
  • 动画

  • Composition Api

  • 高级语法

  • Vue源码

  • VueCLI&Vite

  • VueRouter

  • Vuex

  • 项目

  • Vue3.x
  • 组件
xugaoyi
2022-01-31
目录

组件的v-model

# 组件的v-model 🔥

# 推荐 🔥

  • 只推荐绑定单个值!
  • 绑定对象推荐自己写!

# 使用 🔥

前面我们在input中可以使用v-model来完成双向绑定,这个时候往往会非常方便,因为v-model默认帮助我们完成了两件事:

  • v-bind:value的数据绑定
  • @input的事件监听
<input v-model="message">
<input :value="message" @input="message = $event.target.value">
1
2

如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢? 也是可以的,vue也支持在组件上使用v-model。类似Vue2的.sync

当我们在组件上使用的时候,等价于如下的操作:

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

    <h2>{{ message }}</h2>
</template>

<script>
    import HyInput from './HyInput.vue'

    export default {
        components: {
            HyInput,
        },
        data() {
            return {
                message: 'Hello World',
            }
        },
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

所以 hy-input 组件中也应该提供 modelValue prop 和 update:model-value emit

<template>
  <div>
    <!-- 1.默认绑定和事件处理。事件还得button触发,不优雅 -->
    <!-- <button @click="btnClick">hyinput按钮</button>
    <h2>HyInput的message: {{modelValue}}</h2> -->

    <!-- 2.通过input,也不是很优雅 -->
    <!-- <input :value="modelValue" @input="btnClick"> -->

    <!-- 3.绑定到props中是不对的,单向数据流,不应该修改props中的值 -->
    <!-- <input v-model="modelValue"> -->

    <!-- 4.利用计算属性!!!推荐!!!-->
    <input v-model="value">

  </div>
</template>

<script>
  export default {
    props: {
      modelValue: String
    },
    emits: ["update:modelValue"],
    computed: {
      value: {
        set(value) {
          this.$emit("update:modelValue", value);
        },
        get() {
          return this.modelValue;
        }
      }
    },
    methods: {
      btnClick(event) {
        this.$emit("update:modelValue", event.target.value);
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 绑定多个值 🔥

<template>
	<!-- 绑定两个v-model,更多的一样! -->
    <hy-input v-model="message" v-model:title="title"></hy-input>

    <h2>{{ message }}</h2>
    <h2>{{ title }}</h2>
</template>

<script>
    import HyInput from './HyInput.vue'

    export default {
        components: {
            HyInput,
        },
        data() {
            return {
                message: 'Hello World',
                title: '哈哈哈',
            }
        },
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
  <div>
    <input v-model="value">
    <input v-model="titleData">
  </div>
</template>

<script>
  export default {
    props: {
      modelValue: String,
      title: String 
    },
    emits: ["update:modelValue", "update:title"],
    computed: {
      value: {
        set(value) {
          this.$emit("update:modelValue", value);
        },
        get() {
          return this.modelValue;
        }
      },
      titleData: {
        set(title) {
          this.$emit("update:title", title);
        },
        get() {
          return this.title;
        }
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 注意—绑定对象 🔥

上述只能绑定1个值,不能绑定对象!否则对象属性改变不能触发set方法。详细见项目!

改成下面绑定对象写法还是有问题,解决方式:

# 方式一

直接修改父组件formData,但是浅拷贝里对象的修改不会触发

可以在父组件中不直接修改props的formData,而是修改formData中的属性!

// 父组件修改属性 formData.value.xxx = xxx

// 子组件
const formData = ref({...props.modelValue})
watch(
    formData,
    (newValue) => {
        console.log(newValue)
        emit('update:modelValue', newValue)
    },
    {
        deep: true
    }
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

不能使用computed来替代ref,配合watch会产生无限递归!

# 方式二

不适用v-model双向绑定语法糖,而是自定义!

子组件

<template>
	<el-input
          :model-value="modelValue[`${item.field}`]"
          @update:modelValue="handleValueChange($event, item.field)"
          />
</template>

<script lang="ts" setup >
    const props = defineProps({
        modelValue: {
            type: Object,
            required: true,
        }
    })
    
    const emit = defineEmits(['update:modelValue'])
    
    const handleValueChange = (value: any, field: string) => {
      emit('update:modelValue', { ...props.modelValue, [field]: value })
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

父组件直接修改 formData.value 都可以!

编辑 (opens new window)
上次更新: 2022/04/30, 01:54:50
生命周期
Vue 动画

← 生命周期 Vue 动画→

最近更新
01
重点
04-12
02
搭建项目
04-04
03
TS补充
03-30
更多文章>
Theme by Vdoing | Copyright © 2019-2022 conanan | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式