首页 > 其他分享 >从vue2到vue3,自定义组件的v-model实现原理

从vue2到vue3,自定义组件的v-model实现原理

时间:2023-07-25 09:56:11浏览次数:48  
标签:vue 自定义 value vue2 vue3 组件 input model

前言

相信使用vue开发的同学应该都体会过v-model的便利,它可以非常方便地进行双向数据绑定,只要重新输入内容,视图就会立刻发生改变。本文将着重介绍如何在自定义组件当中使用v-model,以及在vue2和vue3中使用方式上的差异。

概述

v-model是一个语法糖,它在组件使用时相当于如下简写:

// vue2 原生组件
<input v-model="val" />
// 等价于
<input :value="val" @input="val = $event.target.value" />

要让组件的v-model生效,需要接收一个value属性,并在有新的value时触发input事件。以上面代码为例,绑定value属性到名为val的响应式对象,然后在input触发的时候绑定一个函数,每次input的值改变就会更新val,从而实现数据更新。

vue2实现方式

同理,自定义组件要如何支持v-model?先说说vue2的实现思路:

// vue2 自定义组件
<my-component v-model="val" />
// 等价于
<my-component :value="val" @input="val = arguments[0]" />

在MyComponent这个组件上面创建一个v-model,它的实际执行就是value的属性,之后触发input的事件,value接收的值就是事件回调函数的第一个参数。所以在自定义组件中实现事件绑定,我们需要使用$emit去触发input事件。

// MyComponent.vue
<template>
  <input type="text" :value="value" @input="updateInput" />
</template>

<script>
export default {
  props: {
    value: String,
  },
  methods: {
    updateInput(e) {
      this.$emit("input", e.target.value);
    },
  },
};
</script>

以上是常规组件的实现方法,那么面对一些不同寻常的组件又该如何应对呢?

用vue2的方式实现数据绑定的解决方案会出现这么一个问题:vue2的普通组件会默认使用value的属性名和input的事件。但是当在如checked这种单选框、复选框等类型的输入控件可能会将value属性用于不同的目的,不能用来指代当前的状态。如下在使用的属性是checked而非value来表示是否选中,改变的值使用的事件是change而非input,针对这种不走寻常路的组件,vue2的解决方案是添加一个model字段,里面有两个属性,prop表示想要绑定的属性,event表示触发事件的名称。

// BaseCheckbox.vue
<template>
  <input type="checkbox" :checked="checked" @change="updateInput">
</template>

<script>
export default {
  props: {
    checked: Boolean
  },
  model: {
    prop: 'checked',
    event: 'change'
  },
  methods: {
    updateInput(e) {
      this.$emit('change', e.target.checked)
    }
  }
}
</script>
// App.vue
<template>
  <div id="app">
    <my-component v-model="val"></my-component>
    <base-checkbox v-model="checked"></base-checkbox>
    <h1>{{ val }}</h1>
    <h1>{{ checked }}</h1>
  </div>
</template>

<script>
import MyComponent from "./components/MyComponent";
import BaseCheckbox from "./components/BaseCheckbox";

export default {
  name: "App",
  components: {
    MyComponent,
    BaseCheckbox,
  },
  data() {
    return {
      val: '',
      checked: false
    }
  }
};
</script>

vue3实现方式

那么vue2的实现方式是否已经完美的呢?显然不是,它还有以下明显的缺点:

  • 繁琐:需要新建model属性
  • 只能支持一个v-model:组件中可能会出现需要使用多个v-model双向绑定的场景
  • 理解困难:在不同的应用场景(如input输入框和check复选框)需要使用不同的方式

针对这些问题,vue3也给出了新的解决方案:

// vue3
<my-component v-model="foo" />
h(Comp, {
	modelValue: foo,
	'onUpdate:modelValue': value => (foo = value)
})

直接移除了组件上的model属性,不再使用value和input这两个非常容易混淆的属性和事件,换成了属性名称modelValue和更加详细的事件名称onUpdate:modelValue,换言之,要在vue3的自定义组件中使用v-model,首先需要有modelValue属性,然后需要在更新的时候触发onUpdate:modelValue事件。以下是vue3改造后的代码:

// App.vue
<template>
  <div id="app">
    <my-component v-model="inputVal"></my-component>
    <base-checkbox v-model="checkVal"></base-checkbox>
    <h1>{{ inputVal }}</h1>
    <h1>{{ checkVal }}</h1>
  </div>
</template>

<script>
import { defineComponent, ref } from "vue";
import MyComponent from "./components/MyComponent";
import BaseCheckbox from "./components/BaseCheckbox";
export default defineComponent({
  components: {
    MyComponent,
    BaseCheckbox,
  },
  setup() {
    const inputVal = ref("test");
    const checkVal = ref(false);
    return {
      inputVal,
      checkVal,
    };
  },
});
</script>
// MyComponent.vue
<template>
  <input type="text" :value="inputRef.val" @input="updateInput" />
</template>

<script>
import { defineComponent, reactive } from "vue";
export default defineComponent({
  props: {
    modelValue: String,
  },
  setup(props, context) {
    const inputRef = reactive({
      val: props.modelValue || "",
    });
    const updateInput = (e) => {
      const targetVal = e.target.value;
      inputRef.val = targetVal;
      context.emit("update:modelValue", targetVal);
    };
    return {
      inputRef,
      updateInput,
    };
  },
});
</script>
// BaseCheckbox.vue
<template>
  <input type="checkbox" :checked="checkedRef.val" @change="updateCheck" />
</template>

<script>
import { defineComponent, reactive } from "vue";
export default defineComponent({
  props: {
    checkedValue: Boolean,
  },
  setup(props, context) {
    const checkedRef = reactive({
      val: props.checkedValue,
    });
    const updateCheck = (e) => {
      const targetVal = e.target.checked;
      checkedRef.val = targetVal;
      context.emit("update:modelValue", targetVal);
    };
    return {
      checkedRef,
      updateCheck,
    };
  },
});
</script>

结语

本文讲述了v-model的原理、以及在vue2和vue3中的实现方式,代码的验证可以在codesandbox上来去进行,可以直接选择vue2或者vue3的运行环境,无需本地配置。

标签:vue,自定义,value,vue2,vue3,组件,input,model
From: https://www.cnblogs.com/askknow/p/17578993.html

相关文章

  • 自定义树莓派开机启动画面-plymouth版本
    apt-get install-yplymouth-themes#查看当前已安装的主题$plymouth-set-default-theme--listfutureprototype #默认主题,貌似和details一样details #纯文本启动日志输出joy #Debian的背景图+四个小白点循环点亮动画lines #Debian的背景图+线条绘制循环动画moon......
  • 自定义gradle插件并且发布到本地仓库
    转载请标明出处,维权必究:http://77blogs.com/?p=189一、在工程上新建一个Module。二、删除其余不必要的文件,最终目录结构如下:注意:由于我用的是kotlin语言,所以用的是java目录,java语言也是用java目录,如果用的是groovy语言,那么就用groovy目录。src/main/resources/META-INF/gra......
  • Tool-CMake-添加自定义宏定义
    Tool-CMake-添加自定义宏定义cmake,makefile中定义的宏变量,其实和C/C++中的#define是一致的,可以传入到C/C++中。控制程序的编译比如:cmake中有宏定义:add_definitions(-Dhello="hellocmake")......
  • VSCODE自定义代码风格
    VSCode启用代码风格修正1.打开Settings2.设置Clang_format搜索clang_format相关配置设定clang_format基础风格基础风格中LLVM与Google都是缩进为2,所以还需要自定义风格。设定clang_format自定义风格文件的路径指定自定义分割文件的路径(XXX.clang-format)。自定义风格......
  • 【jQuery】smartMenu右键自定义上下文菜单插件(似web QQ)
    DEMO: http://sources.ikeepstudying.com/menu-mail-qq/ 一、这是什么样的一个插件我们都知道,默认状态下,我们右键web页面,会出现一个上下文菜单,例如下图:【jQuery】smartMenu右键自定义上下文菜单插件(似webQQ)但是,浏览器默认的右键选项有时候并不是我们所需要的,......
  • 前端系列16集-vue3范型,vue-i18n-next,watch,watchEffect
    中台落地手记——业务服务化与数据资产化vue-i18n-nextvue3中使用i18n需要安装的是 [vue-i18nv9] 的版本npminstallvue-i18n@9创建src\lang\index.ts,使用 createI18n 创建i18n实例://src\lang\index.tsimport{createI18n}from'vue-i18n'import{LANG_......
  • 加入自定义块对fashion_mnist数据集进行softmax分类
    在之前,我们实现了使用torch自带的层对fashion_mnist数据集进行分类。这次,我们加入一个自己实现的block,实现一个四层的多层感知机进行softmax分类,作为对“自定义块”的代码实现的一个练习。我们设计的多层感知机是这样的:输入维度为784,在展平层过后,第一层为全连接层,输入输出维度分......
  • vue3.0 外部配置文件一 (导入json文件方式)
    vue3.0外部配置文件,重点是打包后也可以修改配置参数 注:js文件中必须是标准的json格式一、在public中创建static文件夹,在static文件夹中创建config.json  文件 config.json (必须是标准的json格式){"webSocketUrl":"ws://192.168.1.120:5011/chat/","......
  • Python list里面定义自定义类型
    PythonList中定义自定义类型在Python中,List(列表)是一种非常常见且强大的数据结构。它允许我们以有序的方式存储和访问多个元素。在List中,我们可以存储各种类型的数据,包括整数、浮点数、字符串等。但是,Python的灵活性还允许我们在List中存储自定义的数据类型,从而提供更高的灵活性和......
  • uboot添加自定义命令 CMD
    原文:https://blog.csdn.net/weixin_41252596/article/details/128317180有些用户玩uboot比较花,除了引导系统还要做一堆驱动,有些驱动除了按流程执行还要留出命令行接口用于调试。比如我现在的设备外接了个fpga,fpga和cpu的接口已经做好了,但是为了调试要新增个命令,在命令行下手动与f......