Vue 3 迁移策略笔记—— 第26节:在组件上使用 v-model 的变化

前言

本笔记主要基于官方文档《迁移策略—— v-model》汇总而来。如有理解出入,请以官方文档为主。建议您以官方文档为主,本文为辅。这样您可以“以自己为主”审视的阅读,从而不被我的观点带偏。

知识储备:

在阅读本文前,您最好熟知一下知识:

概述

  • v-modelpropevent重命名:
    • prop: value -> modelValue;
    • event: input -> update:modelValue;
  • v-bind.syncmodel选项被移除
  • 同一组件实现添加多v-model
  • v-model 可添加自定义修饰符

v-model 的前世今生

​ 在 Vue 2.0 版本,组件上自定义v-model,Vue 要求开发者必须使用prop: value来接收父组件传递过来的双向绑定值。如果,开发者还需要对其他 prop 进行双向绑定,则需要使用 v-bind.sync 来实现。因此,这个版本的组件自定义 v-modelvalue 强制绑定了。从而导致了”如何处理原生标签和自定义标签的问题“。

​ 到了 Vue 2.2 版本,Vue 引入了 model 的组件选项(component option),允许开发者自由指定 v-modelpropevent。从而解决了**"v-modelvalue强制绑定"的问题。但是,这次改进还存在一个问题,即”一个组件上只能自定义一个v-model,其他的双向绑定还是只能通过v-bind.sync来实现“**。

Vue 2.x 组件自定义v-model

在 Vue 2.x 中,在组件上使用 v-model 将相当于传递一个 prop 和一个 input 事件:

<ChildComponent v-model="pageTitle" />

<!-- 等价于: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

如果我们想要修改 propevent,可以在子组件中的 model 选项中设置:

// ChildComponent.vue

export default {
  model: {
    prop: 'title',	// 声明 prop 中用来接收 v-model 的绑定值
    event: 'change'	// 声明修改 v-model 绑定值的事件,注意事件需要在子组件主动触发
  },
  props: {
    // 这里的 value 另作他用
    value: String,
    // 用 title 来替代默认的 value,来接收 v-model 的绑定值
    title: {
      type: String,
      default: 'Default title'
    }
  }
}

在父组件中调用:

<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

一个完整的例子:

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)"
    >
  `
})
<base-checkbox v-model="lovingVue"></base-checkbox>

摘自——《自定义组件的 v-model

Vue 2.x 的v-bind.sync

前面提到过,因为组件中只能定义一个 v-model,如果我们需要对多个值进行双向绑定就需要借助.sync修饰符来实现。

.syncv-model非常相似,唯一的变化就是触发事件变成了update:myPropName。例如前面有 prop:title 的例子ChildComponent,可以修改成:

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

使用.sync的简写:

<ChildComponent :title.sync="pageTitle" />

一个完整的例子:

// 父组件
<template>
  <Children :message.sync="message"/>
</template> 
<script> import children from "./children.vue";
export default {  
  components: {children},  
  data() {    
    return {      
      message: "parent"   
     };
  },  
  watch: {    
    // 监听message变化    
    message(n, o) {
      console.log(n, o);    
     }  
  } 
}; 
</script>

// 子组件
<template>  
  <h1>{{ message }}</h1> 
</template>
<script>
export default {  
  props: {
    message: String    
  },  
  mounted() {  
    setTimeout(_ => {      
      this.$emit("update:message", "children");      
     }, 1500);  
   }
  }; 
</script>

摘自——《 vue中v-model与.sync修饰符的使用差异

上面的例子还可以看到.sync并不需要添加model选项。

v-modelv-bind.sync的关系

现在看来,我们很明显的发现,两者个作用是一致的——都是用来在组件间实现双向绑定。我们可以说,v-bind.syncv-model 的替代品,是延续;也可以说,v-modelv-bind.sync 的一个"子集"。

两者区别的详细讲解可参考:《 vue中v-model与.sync修饰符的使用差异

既然两者这么相似,为什么不合并呢?

很显然,Vue 也注意到这个问题了,Vue 3.x 就将两者合并了。

Vue 3.x 的 组件自定义 V-model

在 Vue 3.x , v-model 变化如下:

  • prop: value -> modelValue;
  • event: input -> update:modelValue;

换而言之,Vue 3.x 使用了一个特定的prop ——modelValue,从而避免了对value的占用。而绑定值修改的触发事件采用了v-bind.syncupdate:modelValue形式。

<ChildComponent v-model="pageTitle" />

<!-- 等价于: -->
<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>

此外,Vue 3.x 的v-model 移除了model选项。取而代之的是,v-bind.sync.PropName 传参形式。如下:

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

<!-- 等价于: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

在这里插入图片描述

需要注意的是,Vue 3.x 新增 emits 选项。所以,update:title 是需要在其中声明的。如下:

app.component('ChildComponent', {
  props: {
    title: String
  },
  emits: ['update:title'],
})

正是这样的改变,从而实现在组件上的可以多v-model

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

<!-- 等价于: -->
<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>
新增v-model自定义修饰词

在 Vue 3.x, 我们除了可以给v-model添加 Vue 提供的修饰词外(如:.trim),还可以自定义修饰词:

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

关于事件自定义修饰符更多知识,可查看:《Custom Events

参考资料

本系列目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值