面试官VUE系列总进度3/33:什么是双向绑定及原理?

本文为面试官VUE系列

总进度:3/33

一、什么是双向绑定

我们先从单向绑定切入

单向绑定十分容易,就是将数据源与界面关联起来,一旦通过脚本语言修改了数据源,界面就会相应地作出改变

关联性很强,就是单向绑定,用户改变了界面,模型中的信息也会随之改变,这就是双向绑定

举个栗子

用户在填写表单时,视图的状态会随之改变,如果在此刻能自动调整模型的状况,这就意味着模型和视图之间实现了相互关联

关系图如下

二、双向绑定的原理是什么

大家清楚 Vue 是一种支持数据双向关联的技术,这种双向关联包含三个核心要素

那个位于上方的层级化设计思路,可以用一个特定的概念来概括:MVVM

这个控制层面的关键作用在于实现“信息交互同步”。我们只要明白它的本质,就能深入探究数据同步的机制

理解ViewModel

它的主要职责就是:

当然,它还有两个主要部分组成

三、如何实现双向数据

我们仍然以Vue为例,首先来了解Vue中双向绑定的具体过程是怎样的

new Vue开始运行时先进行初始设置,接着对data部分实施响应式改造,此环节在Observe里完成

对模板进行编译操作,识别出其中动态绑定的数据项,接着从data集合中提取这些数据,并利用它们来初始化视图组件,以上步骤都在Compile阶段完成

另外设定一个修改方法以及监视者,当相关内容发生变动时,监视者将执行修改方法

某个键值在某个显示中或许会重复出现,因此每个键值都须配备一个管家Dep来照看多个监视者

数据在将来若发生改动,需先定位相关依赖,继而通知所有观察者去运行更新逻辑

流程图如下:

实现

先来一个构造函数:执行初始化,对data执行响应化处理

class Vue {
  constructor(options) {
    this.$options = options;
    this.$data = options.data;
    // 对data选项做响应式处理
    observe(this.$data);
    // 代理data到vm上
    proxy(this);
    // 执行编译
初始化编译器,参数为选项对象,当前实例作为上下文,执行构建过程,完成代码生成
  }
}

对data选项执行响应化具体操作

function observe(obj) {
当obj不是对象类型时,或者obj等于null,
    return;
  }
  new Observer(obj);
}
class Observer {
  constructor(value) {
    this.value = value;
    this.walk(value);
  }
  walk(obj) {
obj的键名逐个被遍历,用函数处理
设置响应式属性,针对对象,指定键名,以及键值,调用定义响应式函数
    });
  }
}

编译Compile

逐个检查元素节点的指令,实施分析,依照指令范例更改信息,同时关联对应的变更处理方法

class Compile {
  constructor(el, vm) {
    this.$vm = vm;
这个元素通过document查询,获取对应的dom结构,然后赋值给变量el
    if (this.$el) {
      this.compile(this.$el);
    }
  }
  compile(el) {
获取该元素的所有子节点,将它们存储在变量中,这个变量就是childNodes
将子元素逐一取出,对每个元素执行操作,使用Array.from方法转换节点列表,然后应用forEach函数进行遍历
当确定该对象属于元素类型时,确认其是否为具体节点,

vue数据绑定知识总结_Vue双向绑定实现过程_Vue双向绑定原理

输出元素名称,该名称为节点所含的标签名,节点为当前处理的那个节点。 倘若并非该节点属于插值,那么 输出编译后的文本内容,包含节点文本值,通过连接操作实现,针对插值表达式 {{}} 进行识别处理 } 倘若节点拥有子节点,并且子节点的数量超过零,那么就确认存在子元素 这个编译器会处理每一个子节点,并且继续向下探索所有后代元素 } }); } isElement(node) { return node.nodeType == 1; } isInterpolation(node) { 返回该节点的类型是文本节点,并且它的文本内容符合特定的模式,这个模式是以大括号包围的内容,例如模板字符串中的变量表达式,那么就返回真,否则返回假。 } }

依赖收集

数据中的特定键会在视图里被引用,这种情况叫做关联关系。同一个键可能会反复出现,每次出现都需要单独记录下来,并借助一个监视器来管理这些记录,这个操作叫做关联关系的聚合

多个监视者需要依赖一个管理者来统筹,当需要变更时由管理者集中传达

实现思路

定义响应式时,针对每个键,都会设立一个依赖实例,在初始化视图时,若读取某个键,比如name1,就会生成一个观察者实例watcher1,由于触发name1的获取函数,因此将watcher1加入到name1关联的依赖实例中,当name1发生变更,设置函数被调用时,能够通过相应的依赖实例,告知其管理的所有观察者实例进行更新

// 负责更新视图
class Watcher {
初始化函数接受三个参数,分别是视图模型实例,键名和更新器函数,通过这些参数完成构造过程,在内部进行一系列的设置和配置,确保组件能够正常运行
    this.vm = vm
    this.key = key
    this.updaterFn = updater
实例建立之际,需将此实例赋予Dep.target这个静态属性,作为当前对象
    Dep.target = this
    // 读一下key,触发get
    vm[key]
    // 置空
    Dep.target = null
  }
  // 未来执行dom更新函数,由dep调用的
  update() {
这个更新函数被调用,参数是这个实例的模型以及模型中指定属性的值
  }
}

声明Dep

class Dep {
  constructor() {
    this.deps = [];  // 依赖管理
  }
  addDep(dep) {
    this.deps.push(dep);
  }
  notify() { 
这个对象的所有依赖项依次被更新了。
  }
}

创建watcher时触发getter

class Watcher {
初始化函数接受三个参数,分别是视图模型实例,键名,以及更新函数,这三个参数依次为视图模型,键值,和更新方法
    Dep.target = this;
    this.vm[this.key];
    Dep.target = null;
  }
}

依赖收集,创建Dep实例

创建一个函数用来定义响应式属性,该函数接收三个参数,分别是目标对象,属性名和属性值,在函数体内对传入的对象进行操作,通过指定的键名来访问对应的值,并将这个值设置为可观测的状态
  this.observe(val);
  const dep = new Dep();
通过特定方法,在目标对象中,针对指定属性,设定新的定义,包含多个配置项,其中一项是可枚举性,另一项是不可删除性,还有一项是获取值时的处理函数,以及设置值时的处理函数,最终目的是实现对对象属性的严格管控,确保其不被随意修改或访问,增强代码的安全性和稳定性,同时也能更好地维护数据的完整性和一致性
    get() {
存在监视者实例时,将其添加到依赖列表中,监视者实例就是Dep.target
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      dep.notify(); // 通知dep执行更新方法
    },
  });
}

本文来自作者[admin]投稿,不代表芝麻开门立场,如若转载,请注明出处:https://aizmkm.com/zshi/202509-6141.html

(79)
admin的头像admin签约作者

文章推荐

发表回复

作者才能评论

评论列表(3条)

  • admin的头像
    admin 2025年09月05日

    我是芝麻开门的签约作者“admin”

  • admin
    admin 2025年09月05日

    本文概览:本文为面试官VUE系列总进度:3/33一、什么是双向绑定我们先从单向绑定切入单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时...

  • admin
    用户090508 2025年09月05日

    文章不错《面试官VUE系列总进度3/33:什么是双向绑定及原理?》内容很有帮助

联系我们

邮件:芝麻开门@gmail.com

工作时间:周一至周五,9:30-17:30,节假日休息

关注微信