浅谈AngularJs 双向绑定原理(数据绑定机制)


Posted in Javascript onDecember 07, 2017

那么什么是双向绑定,下面简单进行讲解。

首先我们要理解数据绑定。我们看到的网站页面中,是由数据和设计两部分组合而成。将设计转换成浏览器能理解的语言,便是html和css主要做的工作。而将数据显示在页面上,并且有一定的交互效果(比如点击等用户操作及对应的页面反应)则是js主要完成的工作。很多时候我们不可能每次更新数据便刷新页面(get请求),而是通过向后端请求相关数据,并通过无刷新加载的方式进行更新页面(post请求)。那么数据进行更新后,页面上相应的位置也能自动做出对应的修改,便是数据绑定。

在以前的开发模式中,这一步一般通过jq操作DOM结构,从而进行更新页面。但这样带来的是大量的代码和大量的操作。如果能在开始的时候,便已经确定好从后端获取的数据到页面上需要进行的操作,当数据发生改变,页面的相关内容也自动发生变化,这样便能极大地方便前端工程师的开发。在新的框架中(angualr,react,vue等),通过对数据的监视,发现变化便根据已经写好的规则进行修改页面,便实现了数据绑定。可以看出,数据绑定是M(model,数据)通过VM(model-view,数据与页面之间的变换规则)向V(view)的一个修改。

而双向绑定则是增加了一条反向的路。在用户操作页面(比如在Input中输入值)的时候,数据能及时发生变化,并且根据数据的变化,页面的另一处也做出对应的修改。有一个常见的例子就是淘宝中的购物车,在商品数量发生变化的时候,商品价格也能及时变化。这样便实现了V——M——VM——V的一个双向绑定。

AngularJs 为 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。每次绑定一个东西到 view(html) 上时 AngularJs 就会往 $watch 队列里插入一条 $watch,用来检测它监视的 model 里是否有变化的东西。当浏览器接收到可以被 angular context 处理的事件时,$digest 循环就会触发。$digest 会遍历所有的 $watch。从而更新DOM。

$watch

这有点类似于我们的观察者模式,在当前作用域$scope下,我们创建一个监控器$watchers和一个监听器$watch,$watchers 负责管理所有的 $watch,当我们每次绑定到UI上的时候就<font color=red>自动</font>创建一个$watch,并把它放到 $watchers。

controller.js

app.controller('MainCtrl', function($scope) {
 $scope.Hello = "Hello";
 $scope.world = "World";
});

index.html

<div>{{Hello}}</div>

这里,即便我们在$scope上添加了两个变量,<font color=red>但是只有一个绑定在了UI上,因此在这里只生成了一个$watch</font>

$digest

当浏览器接收到可以被angular context处理的事件时,$digest循环就会触发。$digest将会遍历我们的$watch,如果$watch没有变化,这个循环检测就将停止,如果有至少一个更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。这就是脏检查(Dirty Checking)机制

controller.js

app.controller('MainCtrl', function() {
 $scope.name = "Foo";

 $scope.changeFoo = function() {
  $scope.name = "Bar";
 }
});

index.js

<div>{{ name }}</div>
<button ng-click="changeFoo()">Change the name</button>
  1. 当我们按下按钮
  2. 浏览器接收到一个事件,进入angular context。
  3. $digest循环开始执行,查询每个$watch是否变化。
  4. 由于监视$scope.name的$watch报告了变化,它会强制再执行一次$digest循环。
  5. 新的$digest循环没有检测到变化。
  6. 更新与$scope.name新值相应部分的DOM。

$apply

$apply 我们可以直接理解为刷新UI。<font color=red>如果当事件触发时,你调用$apply,它会进入angular context,如果没有调用就不会进入,之后的$digest检测机制就不会触发</font>

app.directive('clickable', function() {
 return {
  restrict: "E",
  scope: {
  foo: '='
  },
  template: '<ul style="background-color: lightblue"><li>{{foo}}</li></ul>',
  link: function(scope, element, attrs) {
  element.bind('click', function() {
   scope.foo++;
   console.log(scope.foo);
  });
  }
 }
});

当我们调用clickable指令的时候,我们可以看到foo的值增加了,但是界面上显示的内容并没有改变。$digest脏检测机制没有触发,检测foo的$watch就没有执行。

$apply()方法的两种形式

1) 无参

$scope.$apply();
element.bind('click', function() {
 scope.foo++;
 //if error
 scope.$apply();
});

当我们使用这种形式的时候,如果在scope.$apply之前程序发生异常,那scope.$apply没有执行,界面就不会更新

2) 有参

$scope.$apply(function(){
 ...
})
element.bind('click', function() {
 scope.$apply(function() {
  scope.foo++;
 });
})

如果用这种形式,即使后面的发生异常,数据还是会更新。

在 AngularJS 中使用 $watch

常用的使用方式:

$scope.name = 'Hello';
$scope.$watch('name', function(newValue, oldValue) {
 if (newValue === oldValue) { return; } 
 $scope.updated++;
});

传入到$watch()中的第二个参数是一个回调函数,该函数在name的值发生变化的时候会被调用。

如果要监听的是一个对象,那还需要第三个参数:

$scope.data.name = 'Hello';
$scope.$watch('data', function(newValue, oldValue) {
 if (newValue === oldValue) { return; } 
 $scope.updated++;
}, true);

表示比较的是对象的值而不是引用,如果不加第三个参数true,在 data.name 变化时,不会触发相应操作,因为引用的是同一引用。

总结

1) 只有在$scope变量绑定到页面上,才会创建 $watch

2) $apply决定事件是否可以进入angular context

3) $digest 循环检查model时最少两次,最多10次(多于10次抛出异常,防止无限检查)

4) AngularJs自带的指令已经实现了$apply,所以不需要我们额外的编写

5) 在自定义指令时,建议使用带function参数的$apply

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js实现翻页后保持checkbox选中状态的实现方法
Nov 03 Javascript
Javascript 命名空间模式
Nov 01 Javascript
js将控件隐藏及display属性的使用介绍
Dec 30 Javascript
js在指定位置增加节点函数insertBefore()用法实例
Jan 12 Javascript
jQuery的基本概念与高级编程
May 14 Javascript
详解用vue.js和laravel实现微信授权登陆
Jun 23 Javascript
详解react-native WebView 返回处理(非回调方法可解决)
Feb 27 Javascript
小程序实现多选框功能
Oct 30 Javascript
Vue自定义render统一项目组弹框功能
Jun 07 Javascript
从零开始在vue-cli4配置自适应vw布局的实现
Jun 08 Javascript
vue实现给div绑定keyup的enter事件
Jul 31 Javascript
vue3使用vue-count-to组件的实现
Dec 25 Vue.js
面包屑导航详解
Dec 07 #Javascript
谈谈JS中的!!
Dec 07 #Javascript
JS动态添加元素及绑定事件造成程序重复执行解决
Dec 07 #Javascript
Angular2.0/4.0 使用Echarts图表的示例代码
Dec 07 #Javascript
jquery学习笔记之无new构建详解
Dec 07 #jQuery
利用Node.js检测端口是否被占用的方法
Dec 07 #Javascript
禁止弹窗中蒙层底部页面跟随滚动的几种方法
Dec 07 #Javascript
You might like
PHP+Mysql实现多关键字与多字段生成SQL语句的函数
2014/11/05 PHP
PDO::getAvailableDrivers讲解
2019/01/28 PHP
javascript 获取页面的高度及滚动条的位置的代码
2010/05/06 Javascript
javascript之Partial Application学习
2013/01/10 Javascript
js判断当前浏览器类型,判断IE浏览器方法
2014/06/02 Javascript
jQuery中bind()方法用法实例
2015/01/19 Javascript
JS实现自动切换文字的导航效果代码
2015/08/27 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
Angular.js项目中使用gulp实现自动化构建以及压缩打包详解
2017/07/19 Javascript
深入理解React中何时使用箭头函数
2017/08/23 Javascript
微信小程序url与token设置详解
2017/09/26 Javascript
vue的diff算法知识点总结
2018/03/29 Javascript
使用Vue如何写一个双向数据绑定(面试常见)
2018/04/20 Javascript
React Native基础入门之调试React Native应用的一小步
2018/07/02 Javascript
小程序如何使用分包加载的实现方法
2019/05/22 Javascript
[01:24]DOTA2上海特锦赛OG战队抵达 专车接机入驻总统套房
2016/02/23 DOTA
[01:42:49]DOTA2-DPC中国联赛 正赛 iG vs PSG.LGD BO3 第一场 2月26日
2021/03/11 DOTA
Python基于checksum计算文件是否相同的方法
2015/07/09 Python
简单讲解Python中的闭包
2015/08/11 Python
python 网络爬虫初级实现代码
2016/02/27 Python
轻松掌握python设计模式之策略模式
2016/11/18 Python
Python列表list内建函数用法实例分析【insert、remove、index、pop等】
2017/07/24 Python
python 用pandas实现数据透视表功能
2020/12/21 Python
python获取天气接口给指定微信好友发天气预报
2020/12/28 Python
css3实现图片遮罩效果鼠标hover以后出现文字
2013/11/05 HTML / CSS
浅谈css3中calc在less编译时被计算的解决办法
2017/12/04 HTML / CSS
html5中去掉input type date默认样式的方法
2018/09/06 HTML / CSS
Michael Kors加拿大官网:购买设计师手袋、手表、鞋子、服装等
2019/03/16 全球购物
演讲稿怎么写才完美
2014/01/02 职场文书
优秀大专毕业生求职信
2014/08/04 职场文书
关于读书的演讲稿800字
2014/08/27 职场文书
学校联谊协议书
2014/09/16 职场文书
《我爱祖国》演讲稿1000字
2014/09/26 职场文书
淘宝好评语句大全
2014/12/31 职场文书
会议主持词结束语
2015/07/03 职场文书
大学生创业计划书常用模板
2019/08/07 职场文书