IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue3的双向绑定是如何实现的 -> 正文阅读

[JavaScript知识库]Vue3的双向绑定是如何实现的

Vue的双向绑定是指数据变化能引起界面的变化,界面数据的变化也能驱动数据的改变。

这个功能其实和单向数据流规范不一样,所以开始接触Vue的时候非常吸引我的一个功能。我们发现Element UI的表单也有大量使用v-model进行双向绑定。

双向绑定 其实 不是所有的元素/组件都支持的,目前Vue支持 inputselect, checkbox, radio 和组件 利用 v-model 指令进行 双向绑定。

我以前对 双向绑定 这个功能有很大的一个疑惑:就是双向绑定为什么不会造成更新死循环?即 界面变化 -> 数据变化 -> 界面变化 -> 数据变化 -> …

v-model对表单元素进行双向绑定

由于不同的表单元素使用的内部指令是不一样的,我们就用input作为例子进行分析,其他的表单元素的双向绑定原理非常类似。

这一节涉及到 指令事件处理 相关的知识点,如果不是太清楚的话,建议参阅我前面的两篇相关内容,否则有可能会有一些的疑惑。

案例分析

<input v-model="value" />
<div>{{ value }}</div>

setup() {
  let value = ref("");
  return {
    value
  };
}

简单几行代码就实现了input表单元素和数据value的双向绑定功能。

代码分析

我们来看看渲染函数
const _hoisted_1 = ["onUpdate:modelValue"]

_withDirectives(_createElementVNode("input", {
  "onUpdate:modelValue": $event => (value = $event)
}, null, 8 /* PROPS */, _hoisted_1), [
  [_vModelText, value]
])

我们分析withDirectives函数,看到input生成的VNode 使用了vModelText这个内部指令,且添加了一个名为onUpdate:modelValue的事件处理的pro 函数,onUpdate:modelValue函数用来修改value值;

vModelText 指令
export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    // 获取到 vnode.props!['onUpdate:modelValue'] 对应的函数
    el._assign = getModelAssigner(vnode)

    const castToNumber =
      number || (vnode.props && vnode.props.type === 'number')

    // 如果 有lazy修饰符 监听 input 的 change 事件,否则监听 input 的 input 事件
    addEventListener(el, lazy ? 'change' : 'input', e => {
      let domValue: string | number = el.value
      if (trim) {
        // 如果有trim修饰符,则将 input的value进行去空格
        domValue = domValue.trim()
      } else if (castToNumber) {                
        // 如果有number修饰符,或者 input 类型是 number类型,则把 input的value变成number类型
        domValue = toNumber(domValue)
      }
      // 然后进行参数的回调实现 界面 到 数据的更改
      el._assign(domValue)
    })
  },
  beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
    // 更新 'onUpdate:modelValue' 函数,因为有可能不会更新数据,所以
    el._assign = getModelAssigner(vnode)
    // 如果 input的值没变,不进行任何操作
    if (document.activeElement === el) {
      if (lazy) {
        return
      }
      if (trim && el.value.trim() === value) {
        return
      }
      if ((number || el.type === 'number') && toNumber(el.value) === value) {
        return
      }
    }

    const newValue = value == null ? '' : value
    // 更新值
    if (el.value !== newValue) {
      el.value = newValue
    }
  }
}
  1. created钩子函数中,如果有lazy修饰符,input表单监听change事件,否则监听input事件;
  2. beforeUpdate钩子函数中,要重新获取onUpdate:modelValue函数,因为重新渲染函数可能更改了这个函数,并且重新给input赋值;
  3. input中输入新的内容后,如果有trim修饰符就进行去空格,如果有有number修饰符或者 input类型是number类型需要转换成number,然后通过onUpdate:modelValue对应的函数修改value 值。

总结:

  1. 数据->DOM: 响应式数据value变化触发组件更新,input的内容将发现变化;
  2. DOM->数据: vModelText指令实现了对inputvalue变化的监听,根据vModelText指令的修饰符处理完inputvalue值,然后通过onUpdate:modelValue对应的函数$event => (value = $event),重新完成响应式数据value的修改。响应式数据的修改会触发组件更新。

一些思考

为什么不会出现更新循环呢?

input输入数据 -> 数据处理 -> 调用onUpdate:modelValue对应的$event => (inputValue = $event)方法 -> 响应式数据变化触发组件更新 -> input设置新值input.value = newValue 更新至此终止

双向绑定

为什么更新input的新值放在vModelText指令的beforeUpdate中执行?

指令的更新有两个方法:beforeUpdateupdated
beforeUpdate中执行有两个优势:

  1. 在更新DOM前更新input的新值,如果只是修改了input值,就省去了patchProp的部分操作,提高了patch性能;
  2. 指令的beforeUpdate是DOM更新前同步执行的,而updated钩子函数是在DOM更新后异步执行的,如果业务复杂同步任务太多的情况下可能会出现更新延迟或者卡顿的现象。

v-model对组件进行双向绑定

<Son v-model="modelVlue" />

其实等同于:

<Son
  :modelValue="modelVlue"
  @update:modelValue="modelVlue = $event"
></Son>

v-model对组件进行双向绑定 本质上就是一个 语法糖,通过pro给子组件传递数据,子组件通过v-on 进行事件绑定可以进行数据的修改。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 22:59:45  更:2022-01-29 23:01:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:20:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码