vue3的setup语法如何自定义v-model为公用hooks

目录
  • 前言
  • 基础
    • 基本的v-model
    • 多个v-model绑定
    • v-model修饰符
  • 进阶
    • 问题背景
    • 方式一:通过watch中转
    • 方式二:computed的get和set
  • 终极:封装v-model的hooks
    • 提取公用逻辑
    • 继续抽离封装

前言

  • 基础篇:简单介绍vue3setup语法如何自定义v-model
  • 进阶篇:如何提取v-model语法作为一个公用hooks

基础

基础篇可绕过,只是对于官网给出的教程,进行了总结概括并给出demo

基本的v-model

子组件中满足两个点,即可完成自定义双向绑定:

  • props中定义一个值xxx
  • emit中定义一个update:xxx事件

下面我们来写一个最基本的v-model组件:

  • props中定义一个modelValue值,并绑定到inputvalue属性上;
  • emit中定义一个update:modelValue事件

需要注意的是,当modelValue作为props传入,update:modelValue事件将被自动注册到emit事件中

<template>
  <input
    type="text"
    @input="emit('update:modelValue', $event.target.value)"
    :value="props.modelValue"
  />
</template>

<script setup>
const emit = defineEmits();
const props = defineProps({
  modelValue: String,
});
</script>

父组件中,引入modelComp子组件,并绑定test值到v-model上,test便完成了一次双向绑定。

<template>
  <modelComp v-model="test"></modelComp>
</template>

<script setup>
import { ref, watch } from "vue";
import modelComp from "./components/model/modelComp.vue";
const test = ref("");
</script>

这便是一个最基本的自定义v-model组件;

多个v-model绑定

当我们需要多个双向绑定时,如下:

<modelComp
  v-model="test"
  v-model:test1="test1"
  v-model:test2="test2"
></modelComp>

<script setup>
import { ref, watch } from "vue";
import modelComp from "./components/model/modelComp.vue";
const test = ref("");
const test1 = ref("");
const test2 = ref("");
</script>

子组件中,同样按着两个点来定义:

  • props中定义两个值,test1test2
  • emits中定义两个事件,update:test1update:test2
<template>
  <input
    type="text"
    @input="emit('update:modelValue', $event.target.value)"
    :value="props.modelValue"
  />
  <input
    type="text"
    @input="emit('update:test1', $event.target.value)"
    :value="props.test1"
  />
  <input
    type="text"
    @input="emit('update:test2', $event.target.value)"
    :value="props.test2"
  />
</template>

<script setup>
const emit = defineEmits(["update:modelValue","update:test1", "update:test2"]);
const props = defineProps({
  modelValue: String,
  test1: String,
  test2: String,
});
</script>

v-model修饰符

vue提供了一些v-model修饰符,我们可以在v-model中使用他们:

<modelComp
  v-model.trim="test"
  v-model:test1.lazy="test1"
  v-model:test2.trim.lazy="test2"
></modelComp>

在一些场景下,我们需要自己定义修饰符,来满足我们的需求,举个栗子:

<modelComp
  v-model.a="test"
  v-model:test1.b.c="test1"
></modelComp>

默认v-model中我们绑定了a修饰符,v-model:test1中则绑定bc两个修饰符;

对于修饰符,我们需要满足以下条件:

  • 对于默认v-model来说,需要props中定义两个值

    • modelValue
    • modelModifiers,接受修饰符key
  • 对于自定义v-model:xxx来说,props中:
    • xxx
    • xxxModeifiers,接受修饰符key

由此,上代码:

<template>
  <input type="text" @input="vModelInput" :value="props.modelValue" />
  <input type="text" @input="vModelTest1" :value="props.test1" />
</template>

<script setup>
const emit = defineEmits(["update:modelValue", "update:test1"]);
const props = defineProps({
  modelValue: String,
  //接受v-model的修饰符
  modelModifiers: {
    default: () => ({}),
  },
  test1: String,
  //接受v-model:test1的修饰符
  test1Modifiers: {
    default: () => ({}),
  }
});

const vModelInput = (e) => {
  let value = e.target.value
  console.log(props.modelModifiers);
  //{a:true}
  if(props.modelModifiers.a){
      //处理value值
  }
  emit("update:modelValue", value);
};

const vModelTest1 = (e) => {
  let value = e.target.value
  console.log(props.test1Modifiers);
  //{b:true,c:true}
  if(props.modelModifiers.b){
      //处理value值
  }
  if(props.modelModifiers.c){
      //处理value值
  }
  emit("update:test1", value);
};
</script>

进阶

问题背景

基础篇中已经讲解了如何封装一个自定义v-model的组件,可是在实际开发中,子组件中使用@input:value来绑定我们的值,会比较麻烦,有没有更简单的办法呢?

我们通常想要对需要双向绑定的子组件,直接进行v-model绑定:

<!-- 子组件 -->
<input type="text" v-model="xxx" />

问题来了,在子组件中接受到父组件的传值时,xxx我们应该绑定谁?直接绑定props.modelValue么?

<!-- 子组件 -->
<input type="text" v-model="props.modelValue"/>

我们会得到一个错误:

️reactivity.esm-bundler.js:512 Set operation on key "modelValue" failed: target is readonly.

因为props是一个readonly的值(isReadonly(props) === true),所以我们不能直接这么使用

所以,我们是需要一个中间值来绑定v-model

方式一:通过watch中转

借助内部变量绑定v-model,使用watch监听它,并同步数据props.xxx

<!-- 子组件 -->
<template>
  <input type="text" v-model="proxy" />
</template>

<script setup>
import { ref, watch } from "vue";
const emit = defineEmits();
const props = defineProps({
  modelValue: String,
});

const proxy = ref(props.modelValue);

watch(
  () => proxy.value,
  (v) => emit("update:modelValue",v)
);
</script>

因为有时候我们双向绑定的可能是一个对象或者数组,因此我们可以使用watch里的deep选项来深度监听并同步proxy;

watch(
  () => proxy.value,
  (v) => emit("update:modelValue",v),
  {deep:true}
);

当然,props.modelValue可能存在默认值传入,所以我们也可以加上immediate选项,使得组件在创建时,就直接给proxy赋上默认值;

方式二:computed的get和set

我们也可以借助computed提供的getset来进行数据同步

const proxy = computed({
  get() {
    return props.modelValue;
  },
  set(v) {
    emit("update:modelValue", v);
  },
});

终极:封装v-model的hooks

我们先来提取watch这种方式,将其封装为一个hooks

<!-- 子组件 -->
<template>
  <input type="text" v-model="proxy" />
</template>

<script setup>
import { ref, watch, computed } from "vue";
const emit = defineEmits();
const props = defineProps({
  modelValue: String,
});

const proxy = ref(props.modelValue);

watch(
  () => proxy.value,
  (v) => emit("update:modelValue", v)
);
</script>

在子组件中,我们用v-modelinput上绑定了一个内部值proxy,并以props.modelValue的值初始化proxy变量(ref(props.modelValue));

watch中,我们监听input上的绑定值proxy,在input进行输入其值变化时,向外分发emit('update:modelValue',v)事件,将改变的值动态传到外部组件上

提取公用逻辑

// useVmodel1.js
import { ref, watch } from "vue";
export function useVmodel(props, emit) {
  const proxy = ref(props.modelValue);
  watch(
    () => proxy.value,
    (v) => emit("update:modelValue", v)
  );
  return proxy;
}

一个最简单的hooks便被封装好了;

<template>
  <input type="text" v-model="proxy" />
</template>

<script setup>
import { ref, watch, computed } from "vue";
import { useVmodel } from "./hooks/useVmodel1";
const emit = defineEmits();
const props = defineProps({
  modelValue: String,
});
const proxy = useVmodel(props, emit);
</script>

继续抽离封装

考虑到以下几个点,继续进行抽离封装:

  • emit可以不传,更简洁的调用方式
  • 多个v-model:test1这种情况的事件,emit("update:xxxx")中的xxxx事件名需要提取

我们可以通过vue3提供的getCurrentInstance方法,获取当前的组件实例,而modelValue可覆盖,则抽取成变量:

//useVmodel2.js
import { ref, watch, getCurrentInstance } from "vue";
export function useVmodel(props, key = "modelValue", emit) {
  const vm = getCurrentInstance();
  const _emit = emit || vm?.emit;
  const event = `update:${key}`;
  const proxy = ref(props[key]);
  watch(
    () => proxy.value,
    (v) => _emit(event, v)
  );
  return proxy;
}

好了,现在我们可以更简单的调用我们的hooks了:

<!-- 子组件 childModel -->
<template>
  <input type="text" v-model="modelValue" />
  <input type="text" v-model="test" />
</template>

<script setup>
import { useVmodel } from "./hooks/useVmodel2";
const emit = defineEmits();
const props = defineProps({
  modelValue: String,
  test: String,
});
const modelValue = useVmodel(props);
const test = useVmodel(props, "test");
</script>

<!-- 父组件 -->
<template>
  <Model v-model="modelValue" v-model:test="test" />
</template> 

<script setup>
import { ref, watch } from "vue";
import Model from "./childModel.vue";

const modelValue = ref("");
const test = ref("");
</script>

到此这篇关于vue3的setup语法如何自定义v-model为公用hooks的文章就介绍到这了,更多相关vue自定义v-model内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue3组件中v-model的使用以及深入讲解

    目录 v-model input中使用双向绑定数据 组件中的v-model 其他写法 总结 v-model input中使用双向绑定数据 v-model在vue中我们经常用它与input输入框的输入值进行绑定,简单的实现原理大家也应该都知道 通过v-bind绑定value值 及结合@input输入事件动态改变绑定的value值来实现双向绑定,如下vue3实现代码: <template> <input type="text" :value="tryText&q

  • vue3的setup语法如何自定义v-model为公用hooks

    目录 前言 基础 基本的v-model 多个v-model绑定 v-model修饰符 进阶 问题背景 方式一:通过watch中转 方式二:computed的get和set 终极:封装v-model的hooks 提取公用逻辑 继续抽离封装 前言 基础篇:简单介绍vue3的setup语法如何自定义v-model: 进阶篇:如何提取v-model语法作为一个公用hooks: 基础 基础篇可绕过,只是对于官网给出的教程,进行了总结概括并给出demo 基本的v-model 子组件中满足两个点,即可完成自定

  • Vue3.2 setup语法糖及Hook函数基本使用

    目录 引言 setup(语法糖) 1.基本使用 2.自动注册 3.组件通信 defineEmit ----> [子组件向父组件事件传递] 代码示列: defineExpose ----> [组件暴露出自己的属性] 代码示列: hook函数 介绍: 示列 : 引言 在2021 年 8 月 5 日,Vue发布了3.2版本的新写法,其中最主要的亮点就在于setup的语法糖,学过Vue3.0的小伙伴都清楚,当我们在使用Vue3的语法就构建组件的时候,总是需要把外面定义的方法变量必须要return出去,

  • vue3 使用setup语法糖实现分类管理功能

    目录 setup语法糖简介 setup语法糖中新增的api 模块简介 分类模块路由 分类列表组件 获取分类列表数据 分类表单组件 setup语法糖简介 直接在 script 标签中添加 setup 属性就可以直接使用 setup 语法糖了. 使用 setup 语法糖后,不用写 setup 函数,组件只需要引入不需要注册,属性和方法也不需要再返回,可以直接在 template 模板中使用. setup语法糖中新增的api defineProps:子组件接收父组件中传来的 props defineE

  • vue3中setup语法糖下通用的分页插件实例详解

    目录 vue3中setup语法糖下父子组件之间的通信 准备工作 父传子: 子传父: 先给大家介绍下vue3中setup语法糖下通用的分页插件,内容如下所示: 效果 自定义分页插件:PagePlugin.vue <script setup lang="ts"> // total :用来传递数据总条数 // pageSize :每页展示几条数据 // currentPage :当前默认页码 // change-page :页码改变时触发的事件,参数为当前页码 const pro

  • Vue3编程流畅技巧使用setup语法糖拒绝写return

    目录 Vue3.2 setup语法糖 1.<script setup>在<template/>使用 2.<script setup>引入组件将自动注册 3.组件通信: defineProps 代替props defineEmit 代替emit 4.<script setup>需主动向父组件暴露子组件属性 :defineExpose 5.语法糖其他功能 6.在setup访问路由 Vue3.2 setup语法糖 Vue3.2 setup语法糖 [ 单文件组件的语

  • 如何在vue3中同时使用tsx与setup语法糖

    目录 前言 Tsx与setup语法糖的优势 遇到的问题 最后 前言 想这样做的原因是在vue3里使用tsx开发时,props的声明显得异常麻烦,不禁想到defineProps的便利,但同时在vue3里tsx文件里tsx语法的书写必须在setup函数或者render函数里,无法正常的使用defineProps等一系列的宏.为了能够更加便利,使用了下文的方法. Tsx与setup语法糖的优势 目前,在vue3中使用tsx已经越来越流行且便捷,对于webpack与vite构建的项目,都有很好的插件支持

  • 强烈推荐!Vue3.2中的setup语法糖

    目录 前文 1.什么是setup语法糖 2.使用setup组件自动注册 3.使用setup后新增API 3.1 defineProps 3.2 defineEmits 3.3 defineExpose vue3项目如何开启setup语法糖 总结: 前文 作为一个前端程序员,说起 Vue 3肯定不会陌生,作为时下最火的前端框架之一,很多人将它作为入门框架. 但是尽管 Vue 3很久之前就已经开始投入使用,也不免会有人抱怨 Vue 3的知识点太多太杂,更新太快.这不,最近 Vue 3又定稿了一项新技

  • vue3更新的setup语法糖实例详解

    目录 前言 语法糖用法: 语法糖带来的体验 一.组件自动注册 二.属性及方法无需return 三.自动将文件名定义为组件的name属性 1.defineProps 2.defineEmits 3.defineExpose 总结 前言 vue3最近更新了一个setup语法糖,这两天才看到,使用起来雀食很甜,特发个帖子记录下 语法糖用法: // 将 `setup` attribute 添加到 `<script>` 代码块上 // 里面的代码会被编译成组件 `setup()` 函数的内容 // 就是

  • vue3:setup语法糖使用教程

    目录 1.setup语法糖简介 2.setup语法糖中新增的api 2.1defineProps 2.2defineEmits 2.3defineExpose 补充:与普通的script一起使用 总结 1.setup语法糖简介 直接在script标签中添加setup属性就可以直接使用setup语法糖了. 使用setup语法糖后,不用写setup函数:组件只需要引入不需要注册:属性和方法也不需要再返回,可以直接在template模板中使用. <template> <my-component

  • vue3基于script setup语法$refs的使用

    目录 一.vue2语法 二.vue3使用 1. 组件设置ref值 2. 组件实例获取 3. 子组件内设置对外公开的变量 一.vue2语法 vue2语法在组件上设置ref属性后,在代码里可以通过 this.$refs.ref值 访问到对应的子组件. 一个设置ref值的组件: <base-input ref="usernameInput"></base-input> 在js代码中可以通过如下代码访问到这个组件: this.$refs.usernameInput 可以

随机推荐