iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > html >vue.js中怎么实现动态数据绑定
  • 723
分享到

vue.js中怎么实现动态数据绑定

2024-04-02 19:04:59 723人浏览 独家记忆
摘要

这期内容当中小编将会给大家带来有关vue.js中怎么实现动态数据绑定,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。从new一个实例开始谈起网上的很多源码解读都是从 Obs

这期内容当中小编将会给大家带来有关vue.js中怎么实现动态数据绑定,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

从new一个实例开始谈起

网上的很多源码解读都是从 Observer 开始的,而我会从 new 一个MVVM实例开始,按照程序执行顺序去解释或许更容易理解。先来看一个简单的例子:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>test</title>
</head>
<body>
 <div class="test">
  <p>{{user.name}}</p>
  <p>{{user.age}}</p>
 </div>

 <script type="text/javascript" src="hue.js"></script>
 <script type="text/javascript">
  let vm = new Hue({
   el: '.test',
   data: {
    user: {
     name: 'Jack',
     age: '18'
    }
   }
  });
 </script>
</body>
</html>

接下来都将以其为例来分析。下面来看一个简略的 MVVM 的实现,在此将其命名为 hue。为了方便起见,为 data 属性设置了一个代理,通过 vm._data 来访问 data 的属性显得麻烦且冗余,通过代理,可以很好地解决这个问题,在注释中也有说明。添加完属性代理后,调用了一个 observe 函数,这一步做的就是 Observer 的属性劫持了,这一步具体怎么实现,暂时先不展开。先记住他为 data 的属性添加了 getter 和 setter。

function Hue(options) {
 this.$options = options || {};
 let data = this._data = this.$options.data,
  self = this;

 Object.keys(data).forEach(function(key) {
  self._proxyData(key);
 });

 observe(data);

 self.$compile = new Compile(self, options.el || document.body);
}

// 为 data 做了一个代理,
// 访问 vm.xxx 会触发 vm._data[xxx] 的getter,取得 vm._data[xxx] 的值,
// 为 vm.xxx 赋值则会触发 vm._data[xxx] 的setter
Hue.prototype._proxyData = function(key) {
 let self = this;
 Object.defineProperty(self, key, {
  configurable: false,
  enumerable: true,
  get: function proxyGetter() {
   return self._data[key];
  },
  set: function proxySetter(newVal) {
   self._data[key] = newVal;
  }
 });
};

再往下看,最后一步 new 了一个 Compile,下面我们就来讲讲 Compile。

Compile

new Compile(self, options.el || document.body) 这一行代码中,第一个参数是当前 Hue 实例,第二个参数是绑定的元素,在上面的示例中为class为 .test 的div。

关于 Compile,这里只实现最简单的 textContent 的绑定。而 Compile 的代码没什么难点,很轻易就能读懂,所做的就是解析 DOM,并添加 Watcher 订阅。关于 DOM 的解析,先将根节点 el 转换成文档碎片 fragment 进行解析编译操作,解析完成后,再将 fragment 添加回原来的真实 DOM 节点中。来看看这部分的代码:

function Compile(vm, el) {
 this.$vm = vm;
 this.$el = this.isElementnode(el)
  ? el
  : document.querySelector(el);

 if (this.$el) {
  this.$fragment = this.node2Fragment(this.$el);
  this.init();
  this.$el.appendChild(this.$fragment);
 }
}

Compile.prototype.node2Fragment = function(el) {
 let fragment = document.createDocumentFragment(),
  child;

 // 也许有同学不太理解这一步,不妨动手写个小例子观察一下他的行为
 while (child = el.firstChild) {
  fragment.appendChild(child);
 }

 return fragment;
};

Compile.prototype.init = function() {
 // 解析 fragment
 this.compileElement(this.$fragment);
};

以上面示例为例,此时若打印出 fragment,可观察到其包含两个p元素:

<p>{{user.name}}</p>
<p>{{user.age}}</p>

下一步就是解析 fragment,直接看代码及注释吧:

Compile.prototype.compileElement = function(el) {
 let childNodes = Array.from(el.childNodes),
  self = this;

 childNodes.forEach(function(node) {
  let text = node.textContent,
   reg = /\{\{(.*)\}\}/;

  // 若为 textNode 元素,且匹配 reg 正则
  // 在上例中会匹配 '{{user.name}}' 及 '{{user.age}}'
  if (self.isTextNode(node) && reg.test(text)) {
   // 解析 textContent,RegExp.$1 为匹配到的内容,在上例中为 'user.name' 及 'user.age'
   self.compileText(node, RegExp.$1);
  }

  // 递归
  if (node.childNodes && node.childNodes.length) {
   self.compileElement(node);
  }
 });
};

Compile.prototype.compileText = function(node, exp) {
 // this.$vm 即为 Hue 实例,exp 为正则匹配到的内容,即 'user.name' 或 'user.age'
 compileUtil.text(node, this.$vm, exp);
};

let compileUtil = {
 text: function(node, vm, exp) {
  this.bind(node, vm, exp, 'text');
 },

 bind: function(node, vm, exp, dir) {
  // 获取更新视图的回调函数
  let updaterFn = updater[dir + 'Updater'];

  // 先调用一次 updaterFn,更新视图
  updaterFn && updaterFn(node, this._getVMVal(vm, exp));

  // 添加 Watcher 订阅
  new Watcher(vm, exp, function(value, oldValue) {
   updaterFn && updaterFn(node, value, oldValue);
  });
 },

 // 根据 exp,获得其值,在上例中即 'vm.user.name' 或 'vm.user.age'
 _getVMVal: function(vm, exp) {
  let val = vm;
  exp = exp.trim().split('.');
  exp.forEach(function(k) {
   val = val[k];
  });
  return val;
 }
};

let updater = {
 // Watcher 订阅的回调函数
 // 在此即更新 node.textContent,即 update view
 textUpdater: function(node, value) {
  node.textContent = typeof value === 'undefined'
   ? ''
   : value;
 }
};

正如代码中所看到的,Compile 在解析到 {{xxx}} 后便添加了 xxx 属性的订阅,即 new Watcher(vm, exp, callback)。理解了这一步后,接下来就需要了解怎么实现相关属性的订阅了。先从 Observer 开始谈起。

Observer

从最简单的情况来考虑,即不考虑数组元素的变化。暂时先不考虑 Dep 与 Observer 的联系。先看看 Observer 构造函数:

function Observer(data) {
 this.data = data;
 this.walk(data);
}

Observer.prototype.walk = function(data) {
 const keys = Object.keys(data);
 // 遍历 data 的所有属性
 for (let i = 0; i < keys.length; i++) {
  // 调用 defineReactive 添加 getter 和 setter
  defineReactive(data, keys[i], data[keys[i]]);
 }
};

接下来通过 Object.defineProperty 方法给所有属性添加 getter 和 setter,就达到了我们的目的。属性有可能也是对象,因此需要对属性值进行递归调用。

function defineReactive(obj, key, val) {
 // 对属性值递归,对应属性值为对象的情况
 let childObj = observe(val);

 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function() {
   // 直接返回属性值
   return val;
  },
  set: function(newVal) {
   if (newVal === val) {
    return;
   }
   // 值发生变化时修改闭包中的 val,
   // 保证在触发 getter 时返回正确的值
   val = newVal;

   // 对新赋的值进行递归,防止赋的值为对象的情况
   childObj = observe(newVal);
  }
 });
}

最后补充上 observe 函数,也即 Hue 构造函数中调用的 observe 函数:

function observe(val) {
 // 若 val 是对象且非数组,则 new 一个 Observer 实例,val 作为参数
 // 简单点说:是对象就继续。
 if (!Array.isArray(val) && typeof val === "object") {
  return new Observer(val);
 }
}

这样一来就对 data 的所有子孙属性(不知有没有这种说法。。)都进行了“劫持”。显然到目前为止,这并没什么用,或者说如果只做到这里,那么和什么都不做没差别。于是 Dep 上场了。我认为理解 Dep 与 Observer 和 Watcher 之间的联系是最重要的,先来谈谈 Dep 在 Observer 里做了什么。

Observer & Dep

在每一次 defineReactive 函数被调用之后,都会在闭包中新建一个 Dep 实例,即 let dep = new Dep()。Dep 提供了一些方法,先来说说 notify 这个方法,它做了什么事?就是在属性值发生变化的时候通知 Dep,那么我们的代码可以增加如下:

function defineReactive(obj, key, val) {
 let childObj = observe(val);
 const dep = new Dep();

 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function() {
   return val;
  },
  set: function(newVal) {
   if (newVal === val) {
    return;
   }

   val = newVal;
   childObj = observe(newVal);

   // 发生变动
   dep.notify();
  }
 });
}

如果仅考虑 Observer 与 Dep 的联系,即有变动时通知 Dep,那么这里就算完了,然而在 Vue.js 的源码中,我们还可以看到一段增加在 getter 中的代码:

// ...
get: function() {
 if (Dep.target) {
  dep.depend();
 }
 return val;
}
// ...

这个 depend 方法呢,它又做了啥?答案是为闭包中的 Dep 实例添加了一个 Watcher 的订阅,而 Dep.target 又是啥?他其实是一个 Watcher 实例,???一脸懵逼,先记住就好,先看一部份的 Dep 源码:

// 标识符,在 Watcher 中有用到,先不用管
let uid = 0;

function Dep() {
 this.id = uid++;
 this.subs = [];
}

Dep.prototype.depend = function() {
 // 这一步相当于做了这么一件事:this.subs.push(Dep.target)
 // 即添加了 Watcher 订阅,aDDDep 是 Watcher 的方法
 Dep.target.addDep(this);
};

// 通知更新
Dep.prototype.notify = function() {
 // this.subs 的每一项都为一个 Watcher 实例
 this.subs.forEach(function(sub) {
  // update 为 Watcher 的一个方法,更新视图
  // 没错,实际上这个方法最终会调用到 Compile 中的 updaterFn,
  // 也即 new Watcher(vm, exp, callback) 中的 callback
  sub.update();
 });
};

// 在 Watcher 中调用
Dep.prototype.addSub = function(sub) {
 this.subs.push(sub);
};

// 初始时引用为空
Dep.target = null;

也许看到这还是一脸懵逼,没关系,接着往下。大概有同学会疑惑,为什么要把添加 Watcher 订阅放在 getter 中,接下来我们来说说这 Watcher 和 Dep 的故事。

Watcher & Dep

先让我们回顾一下 Compile 做的事,解析 fragment,然后给相应属性添加订阅:new Watcher(vm, exp, cb)。new 了这个 Watcher 之后,Watcher 怎么办呢,就有了下面这样的对话:

Watcher:hey Dep,我需要订阅 exp 属性的变动。

Dep:这我可做不到,你得去找 exp 属性中的 dep,他能做到这件事。

Watcher:可是他在闭包中啊,我无法和他联系。

Dep:你拿到了整个 Hue 实例 vm,又知道属性 exp,你可以触发他的 getter 啊,你在 getter 里动些手脚不就行了。

Watcher:有道理,可是我得让 dep 知道是我订阅的啊,不然他通知不到我。

Dep:这个简单,我帮你,你每次触发 getter 前,把你的引用告诉 Dep.target 就行了。记得办完事后给 Dep.target 置空。

于是就有了上面 getter 中的代码:

// ...
get: function() {
 // 是否是 Watcher 触发的
 if (Dep.target) {
  // 是就添加进来
  dep.depend();
 }
 return val;
}
// ...

现在再回头看看 Dep 部分的代码,是不是好理解些了。如此一来, Watcher 需要做的事情就简单明了了:

function Watcher(vm, exp, cb) {
 this.$vm = vm;
 this.cb = cb;
 this.exp = exp;
 this.depIds = new Set();

 // 返回一个用于获取相应属性值的函数
 this.getter = parseGetter(exp.trim());

 // 调用 get 方法,触发 getter
 this.value = this.get();
}

Watcher.prototype.get = function() {
 const vm = this.$vm;
 // 将 Dep.target 指向当前 Watcher 实例
 Dep.target = this;
 // 触发 getter
 let value = this.getter.call(vm, vm);
 // Dep.target 置空
 Dep.target = null;
 return value;
};

Watcher.prototype.addDep = function(dep) {
 const id = dep.id;
 if (!this.depIds.has(id)) {
  // 添加订阅,相当于 dep.subs.push(this)
  dep.addSub(this);
  this.depIds.add(id);
 }
};

function parseGetter(exp) {
 if (/[^\w.$]/.test(exp)) {
  return;
 }

 let exps = exp.split(".");

 return function(obj) {
  for (let i = 0; i < exps.length; i++) {
   if (!obj)
    return;
   obj = obj[exps[i]];
  }
  return obj;
 };
}

最后还差一部分,即 Dep 通知变化后,Watcher 的处理,具体的函数调用流程是这样的:dep.notify() -> sub.update(),直接上代码:

Watcher.prototype.update = function() {
 this.run();
};

Watcher.prototype.run = function() {
 let value = this.get();
 let oldVal = this.value;

 if (value !== oldVal) {
  this.value = value;
  // 调用回调函数更新视图
  this.cb.call(this.$vm, value, oldVal);
 }
};

上述就是小编为大家分享的vue.js中怎么实现动态数据绑定了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网html频道。

--结束END--

本文标题: vue.js中怎么实现动态数据绑定

本文链接: https://www.lsjlt.com/news/74658.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue.js中怎么实现动态数据绑定
    这期内容当中小编将会给大家带来有关vue.js中怎么实现动态数据绑定,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。从new一个实例开始谈起网上的很多源码解读都是从 Obs...
    99+
    2024-04-02
  • vue.js中怎么实现数据绑定操作
    这篇文章给大家介绍vue.js中怎么实现数据绑定操作,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。数据绑定响应式的数据绑定系统。建立绑定之后,DOM将和数据保持同步,无须手动维护DOM...
    99+
    2024-04-02
  • WXML中的动态数据怎么绑定
    这篇文章主要介绍“WXML中的动态数据怎么绑定”,在日常操作中,相信很多人在WXML中的动态数据怎么绑定问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”WXML中的动态数据怎么绑定”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-26
  • C#如何实现dataGridView动态绑定数据
    目录dataGridView动态绑定数据1.动态绑定DataTable数据2.动态添加数据datagridView数据插入dataGridView动态绑定数据 1.动态绑定DataT...
    99+
    2024-04-02
  • BootStrap中如何实现selectpicker后台动态绑定数据
    这篇文章主要介绍BootStrap中如何实现selectpicker后台动态绑定数据,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!html部分代码<select cl...
    99+
    2024-04-02
  • 使用Vue.js实现数据的双向绑定
    目录如何用Vue.js实现数据的双向绑定?1. 理解双向绑定2. 使用v-model指令3. 使用自定义组件实现双向绑定4. 数据劫持5. 模板引擎6.Object.definePr...
    99+
    2023-05-16
    Vue.js数据的双向绑定 Vue.js双向绑定
  • EasyUI使用DataGrid实现动态列数据绑定
    最近,在对公司的一个老项目进行优化调整。有个使用的三方插件报表页面,一旦查询时间过长就会自动异常并使浏览器崩溃,由于这个插件只有个前人遗留的dll文件,实在看不懂里面的代码无从下手,...
    99+
    2024-04-02
  • vue.js中怎么实现双向绑定操作
    这期内容当中小编将会给大家带来有关vue.js中怎么实现双向绑定操作,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。首先在页面引入vue.js以及其他需要用到的或者可能要用...
    99+
    2024-04-02
  • Vue.js中的绑定样式实现
    目录绑定class样式1、字符串写法2、数组写法3、对象写法style 绑定样式scoped绑定class样式 1、字符串写法 <!DOCTYPE html> <...
    99+
    2024-04-02
  • VB.NET中怎么实现数据绑定
    本篇文章给大家分享的是有关VB.NET中怎么实现数据绑定,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。TextBox组件通过下列语句就可以把已经得到的数据集"myDa...
    99+
    2023-06-17
  • Flex中怎么实现数据绑定
    Flex中怎么实现数据绑定,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。首先来看一下这个例子最终完成的Demo演示:下面来看看是如何实现这套机制的,首先我们来创建一个可绑定...
    99+
    2023-06-17
  • vue中怎么动态绑定class
    本篇文章为大家展示了vue中怎么动态绑定class,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。对象方法最简单的绑定(这里的active加不加单引号都可以,以下也一...
    99+
    2024-04-02
  • layui数据绑定怎么实现
    在layui中,可以通过使用模板引擎来实现数据绑定。以下是一个实现数据绑定的示例代码:1. 在HTML中定义一个模板:```html...
    99+
    2023-09-15
    layui
  • vue动态怎么绑定
    这篇文章主要介绍了vue动态怎么绑定的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue动态怎么绑定文章都会有所收获,下面我们一起来看看吧。理解数据驱动视图的概念Vue.js是一种数据驱动视图的框架,这意味着我...
    99+
    2023-07-06
  • Flex怎么实现数据绑定
    小编给大家分享一下Flex怎么实现数据绑定,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Flex数据绑定的概念从字面上看,“绑定”表示将若干个物体捆绑在一起。使用...
    99+
    2023-06-17
  • 怎么在Vue.js中嵌套Grid表格并绑定数据
    这篇文章主要讲解了“怎么在Vue.js中嵌套Grid表格并绑定数据”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么在Vue.js中嵌套Grid表格并绑定数据”吧!在Vue.js中嵌套Gri...
    99+
    2023-07-05
  • 怎么在Java项目中实现一个动态与静态绑定
    本篇文章为大家展示了怎么在Java项目中实现一个动态与静态绑定,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Java的动态绑定所谓的动态绑定就是指程执行期间(而不是在编译期间)判断所引用对象的实际类...
    99+
    2023-05-31
    java 动态绑定 静态绑定
  • vue.js如何使用defineProperty实现数据的双向绑定
    这篇文章主要介绍了vue.js如何使用defineProperty实现数据的双向绑定,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。vue.j...
    99+
    2024-04-02
  • vue.js怎么实现动态面包屑
    本篇内容介绍了“vue.js怎么实现动态面包屑”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!需求描述:点击左侧的导航,跳转到不同的页面,并且...
    99+
    2023-06-30
  • Vue中怎么实现数据双向绑定
    这篇文章主要介绍了Vue中怎么实现数据双向绑定的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue中怎么实现数据双向绑定文章都会有所收获,下面我们一起来看看吧。在我们使用vue的时候,当数据发生了改变,界面也会...
    99+
    2023-07-04
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作