v-model

在原生上实现v-model效果

1
<input type="number" :value="msg" @input="(e) => { msg = parseInt(e.target.value) }" />

事件第一个参数是事件对象

Vue3.4以前 在组件上实现v-model效果

  • 自定义事件
    • 事件名为 update:value

App.vue

1
<Comp :value="msg" @update:value="val => msg = val"></Comp>

Comp.vue

1
<input :value="props.value" @input="emits('update:value', $event.target.value)"></input>

Vue3.4 defineModel

帮助我们封装组件

  • 可以同步
  • 一个组件可以同时使用多个v-model
  • 宏指令
    • 用于setup语法糖里
    • 编译时

App.vue

1
<Computer v-model:first-num="val1" v-model:second-num="val2"></Computer>

Computer.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
const firstNum = defineModel('firstNum')
const secondNum = defineModel('secondNum')
//如果父组件的v-model没有arg v-model="val" , 则 const val = defineModel()
</script>

<template>
<div>
<form>
<input type="number" v-model.number="firstNum"></input> +
<input type="number" v-model.number="secondNum"></input> =
{{ firstNum + secondNum }}
</form>
</div>
</template>

自定义指令手写v-mode

Question

  1. 为什么不能用 binding.value
    • 因为binding.value 是值而不是引用,不能实现 view—>model 层的流动
  2. 为什么要用 toRef(binding.instance, binding.arg)
    1. 首先不能 binding.instance[binding.arg] 它会被解包成值类型
    2. 所以创建一个ref, 它与 外部传进来的ref 保持同步
  3. 为什么要defineExpose({msg})
    • setup语法糖要手动暴露自身内部变量
    • defineExpose暴露出去的变量才会挂载到binding.instance 即这个组件实例上

已解决

  1. 双向绑定
  2. 同时使用多个v-oh-model

依然无法解决的问题

  1. 写法不同 必须要加args,但是value可以省略。 写成:v-oh-model:msg
  2. defineExpose <script setup> 下必须要手动暴露
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
<script setup lang='ts'>
import { ref, toRef, watchEffect } from "vue"
const VOhModel = {
mounted(el, binding) {
const _val = toRef(binding.instance, binding.arg)
// model ---> view
el.$onWatchEffect = watchEffect(() => {
el.value = _val.value
})
// view ---> model
el.$onInput = (e) => {
_val.value = el.value
}
el.addEventListener('input', el.$onInput)
},
unmounted(el, binding) {
el.removeEventListener('input', el.$onInput)
el.$onWatchEffect()
}
}

const msg = ref("Hello Vue.js")
defineExpose({ msg })

</script>

<template>
<input v-oh-model:msg="msg" type="text" />
<p>{{ msg }}</p>
<button @click="msg += 1"> + </button>
</template>

总结

根据这次体验,我感觉自动解包有时候真是副作用。
或许这就是Vue的设计模式,但是也确实限制了程序员的发挥。

如果这里能直接拿到msg变量的引用,哪还需要这么麻烦