WHAT?
使用 v-model , 允许你在视图(模板)和数据之间建立一种动态的关联,使得当数据发生变化时,视图会自动更新,反之,当视图更新时,数据也会产生相应的变化。
HOW TO USE?
对原生元素 Element 使用 v-model,
<input v-model="searchText" />
这段代码等价于:
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
:value, 将响应式数据绑定到元素上;
@input, 监听input的输入事件,当输入时,调用函数改变响应式数据的值;
v-model 已经帮我们自动设置了input的输入回调函数,我们不需要自己写。
这样,就实现了双向数据绑定。
那如何在组件上使用 v-model 呢 ?
如下列代码所示:
<CustomInput v-model="searchText" />
它又会被展开为什么形式呢?
先考虑其所要实现的功能,
在子组件中有一个 input 元素,通过 :value 绑定了 searchText,当 input 元素
触发 input 事件时,需要通知父组件,从而在父组件中改变searText的值。
所以需要完成两件事,
- 父组件通过 props 将 searchText 传给子组件
- 子组件通过 emit 将 event 信号发给父组件,父组件监听到信号并处理
所以组件上的 v-model, 展开后为:
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
注意,propsName 默认为 modelValue, eventName 默认为 update:modelValue。
现在,我们还需要在子组件中定义 emit 并 调用,如下列代码所示:
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
这里也可以直接使用 v-model, 借助 computed 实现,小技巧 get!
在准备默认修改 value 的值时,拦截 set 操作,转而调用 emit
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
<template>
<input v-model="value" />
</template>
HOW IT WORKS?
由视图更新,引起响应式数据改变,借助事件监听回调实现;
但是响应式数据驱动视图更新,这又是为啥呢?
简单来说,响应式数据会把组件渲染的逻辑作为依赖收集起来,当然这个逻辑包含初始化和更新组件,
再响应式数据发生变化后,会触发依赖,从而更新组件。
那又是如何收集依赖,收集什么依赖呢?
涉及到 vue 的 响应式原理 和 组件更新原理,
这里不得不提 vue 组件渲染的流程:
无论是组件的初始化,还是组件更新,最后都会调用一个函数叫做 componentUpdateFn,
在这个函数中,通过对组件实例的 isMounted 的值来判断是初始化还是更新组件:
组件在初始化的时候,调用componentUpdateFn,大致是会调用 render 函数生成一个虚拟节点 VNode,再调用 patch 函数渲染出真实的元素。
在这个过程中会用到响应式数据,当获取响应式数据时,执行 get 操作,收集依赖(fn);
在响应式数据变更时,执行 set 操作,会触发依赖,重新调用 render 函数,生成新的虚拟节点 VNode,再调用 patch 函数实现视图更新。