本文为面试官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函数进行遍历
当确定该对象属于元素类型时,确认其是否为具体节点,

输出元素名称,该名称为节点所含的标签名,节点为当前处理的那个节点。
倘若并非该节点属于插值,那么
输出编译后的文本内容,包含节点文本值,通过连接操作实现,针对插值表达式 {{}} 进行识别处理
}
倘若节点拥有子节点,并且子节点的数量超过零,那么就确认存在子元素
这个编译器会处理每一个子节点,并且继续向下探索所有后代元素
}
});
}
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
评论列表(3条)
我是芝麻开门的签约作者“admin”
本文概览:本文为面试官VUE系列总进度:3/33一、什么是双向绑定我们先从单向绑定切入单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时...
文章不错《面试官VUE系列总进度3/33:什么是双向绑定及原理?》内容很有帮助