精华内容
下载资源
问答
  • 进阶属性预览options.data发生的事情:会被Vue监听,每次data的读写都会被vue监控会被Vue的实例监听(vm)代理vue会在data变化时更新UIcomputed属性需求一:展示用户信息,如果有名字,展示名字;没有名字,展示邮箱;...

    进阶属性预览

    5c754f8deb0945870b06172ace085f75.png

    options.data发生的事情:

    1. 会被Vue监听,每次data的读写都会被vue监控
    2. 会被Vue的实例监听(vm)代理
    3. vue会在data变化时更新UI

    computed属性

    需求一:展示用户信息,如果有名字,展示名字;没有名字,展示邮箱;没有邮箱,展示手机号。

    代码:

    new Vue({
      data() {
        return {
          user:{
            email:"xiequantai@qq.com",
            nickname:"啊谢",
            phone:"13812345678"
          }
        }
      },
      computed: {
        displayName(){
          const user=this.user
          return user.nickname || user.email || user.phone
        }
      },
      template:`
        <div>
           {{displayName}}
           <div>
             {{displayName}}
           </div>
        </div>
      `
    }).$mount('#app')

    displayName是属性名,可以将display改成对象的形式,用getter、setter(写成函数)读取、设置displayName的值:

    new Vue({
      data() {
        return {
          user:{
            email:"xiequantai@qq.com",
            nickname:"啊谢",
            phone:"13812345678"
          }
        }
      },
      computed: {
        displayName:{
          get(){
            const user=this.user
          return user.nickname || user.email || user.phone
          },
          set(value){
              //用户传入一个value,将nick的值改成value
            this.user.nickname=value
          }
        }
      },
      template:`
        <div>
           {{displayName}}
           <div>
             {{displayName}}
             <button @click="add">set</button>
           </div>
        </div>
      `,
      methods: {
          //点击时,将计算属性改成"圆圆"
        add(){
          this.displayName="圆圆"
        }
      },
    }).$mount('#app')

    第二个需求:点击"男"按钮,展示男的;点击“女”,展示女的;点击“全部”,展示全部列表。首先,写出3个按钮,然后将用户的姓名、性别都展示出来。

    //封装一个写入姓名、性别的函数
    let id=0
    const createUser=(name,gender)=>{
      id+=1
      return{
        id:id,
        name:name,
        gender:gender
      }
    }
    
    new Vue({
      data() {
        return {
          users:[
            createUser("方方","男"),
            createUser("圆圆","女"),
            createUser("lisa","女"),
            createUser("啊谢","男")
          ]
        }
      },
      template:`
      <div>
        <div>
          <button>全部</button>
          <button>男</button>
          <button>女</button>
        </div>
        <ul>
          //v-for语法,u是给users里面的每一项取个名字。
          <li v-for="(u,index) in users":key="index">{{u.name}} - {{u.gender}}</li>
        </ul>
      </div>
      `,
    }).$mount("#app")

    下一步是让按钮有效:使用computed。

    //封装一个函数:导出用户的姓名、性别
    let id = 0;
    const createUser = (name, gender) => {
      id += 1;
      return { id: id, name: name, gender: gender };
    };
    new Vue({
      data() {
        return {
          users: [
            createUser("方方", "男"),
            createUser("圆圆", "女"),
            createUser("小新", "男"),
            createUser("小葵", "女")
          ],
          //提前声明一下,也可以写成gender:undefined。否则,后面有用set,会很麻烦
          gender: ""
        };
      },
      //计算属性
      computed: {
        displayUsers() {
          const hash = {
            male: "男",
            female: "女"
          };
          //users和gender从vm对象上拿到,函数返回值就可以不加this了(在displayUsers)
          const { users, gender } = this;
          if (gender === "") {
            return users;
          } else if (typeof gender === "string") {
            return users.filter(u => u.gender === hash[gender]);
          } else {
            throw new Error("gender 的值是意外的值");
          }
        }
      },
      methods: {
        setGender(string) {
          this.gender = string;
        }
      },
    
      template: `
        <div>
          <div>
          <button @click="setGender('') ">全部</button>
          <button @click="setGender('male')">男</button>
          <button @click="setGender('female')">女</button></div>
          <ul>
            <li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
          </ul>
          
        </div>
      `
    }).$mount("#app");

    @click的用法

    第一种—— v-on:click="fn"

    第二种——@click="fn"

    第三种——@click="fn(参数)"

    第二步:不用computed怎么筛选

    //封装一个函数:导出用户的姓名、性别
    let id = 0;
    const createUser = (name, gender) => {
      id += 1;
      return { id: id, name: name, gender: gender };
    };
    
    new Vue({
      data() {
        return {
          users: [
            createUser("方方", "男"),
            createUser("圆圆", "女"),
            createUser("小新", "男"),
            createUser("小葵", "女")
          ],
          //用来复制users里面数据的
          displayUsers: []
        };
      },
      //完成对users里面的复制
      created() {
        this.displayUsers = this.users;
      },
      methods: {
        showMale() {
          this.displayUsers = this.users.filter(u => u.gender === "男");
        },
        showFemale() {
          this.displayUsers = this.users.filter(u => u.gender === "女");
        },
        showAll() {
          this.displayUsers = this.users;
        }
      },
    
      template: `
        <div>
          <div>
          <button @click="showAll">全部</button>
          <button @click="showMale">男</button>
          <button @click="showFemale">女</button></div>
          <ul>
            <li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
          </ul>
          
        </div>
      `
    }).$mount("#app");

    计算属性是有缓存的,属性如果没有改变,数据不会重新渲染。

    watch属性

    当数据变化时,就会执行一个函数。

    例子一:撤销

    new Vue({
      data:{
        n:0,
        history:[],
        //开始的撤销模式是关闭的
        inUndoMode:false
      },
      watch:{
          //观察n的变化
        n(newValue,oldValue){
          if(!this.inUndoMode){
            //如果撤销模式关闭的,
            this.history.push({from:oldValue,to:newValue})
          }
        }
      },
      template:`
         <div>
          {{n}}
          <hr/>
          <button @click="add1">+1</button>
          <button @click="add2">+2</button>
          <button @click="minus1">-1</button>
          <button @click="minus2">-2</button>
          <hr/>
          <button @click="undo">撤销</button>
          <hr/>
          {{history}}
         </div>
      `,
      methods: {
        add1(){
          this.n+=1
        },
        add2(){
          this.n+=2
        },
        minus1(){
          this.n-=1
        },
        minus2(){
          this.n-=2
        },
        undo(){
          //pop从数组中拿到最后一项,然后拿到最后一项的from值
          const last=this.history.pop()
          console.log("----")
          console.log(last)
          console.log("----")
          const old=last.from
          console.log("-----")
          console.log(old)
    
          //打开撤销状态
          this.inUndoMode=true
          //watch是异步的
          this.n=old
          //一会关闭撤销模式,相当于setTimeout(,0)
          this.$nextTick(()=>{
            this.inUndoMode=false
          },0)
        }
      },
    }).$mount("#app")

    设置撤销模式的原因是,让history不记录撤销的数组。watch是异步的,关闭撤销模式需要再次设置一个异步,this.$nextTick().

    例子二:模拟computed

    new Vue({
      data: {
        user: {
          email: "fangfang@qq.com",
          nickname: "方方",
          phone: "13812312312"
        },
        displayName:undefined
      },
      watch: {
          //观察user.email的变化
        "user.email": {
          handler:"changed",
          immediate: true // 第一次渲染是也触发 watch
        },
        "user.nickname": {
          handler: "changed",
          immediate: true // 第一次渲染是也触发 watch
        },
        "user.phone": {
          handler: "changed",
          immediate: true // 第一次渲染是也触发 watch
        }
      },
      template: `
        <div>
           {{displayName}}
           <!--点击时,触发user.nickname函数,使nickname为空-->
           <button @click="user.nickname=null">remove nickname</button>
        </div>
      `,
      methods: {
        changed() {
          console.log(arguments);
          const user = this.user;
          //由于vue默认undefined或者null不展示,所以展示邮箱
          this.displayName =  user.nickname ||user.email || user.phone;
        }
      }
    }).$mount("#app");

    小结

    • 我们给"user.email"绑定了一个handler方法,我们写的 watch 方法其实默认写的就是这个handler,Vue会去处理这个逻辑,最终编译出来其实就是这个handler,handler后面可以接变量,也可以接函数。

    而immediate:true代表如果在 wacth 里声明了"user.email"之后,就会立即先去执行里面的handler方法。如果没有immediate或者immediate:false,第一次变化不会执行。

    • deep选项,deep:true,只监听一个对象就可以,不需要监听里面的所有属性。如果deep:flase,就只看对象表层的地址,如果对象里面的属性发生了变化,对象的地址不发生变化。
    • watch选项里面不能写箭头函数,因为箭头函数里面不能用this,里面的this指代window,而vue里面的每一个this都应该指代vm。
    • watch里面的各种写法:

    第一种:key:function(newValue,oldValue){ }

    第二种:key(newValue,oldValue){ }

    第三种:key变化时,分别执行f1和f2,key:[ f1,f2 ]

    第四种:

    key:'methodsName',然后在methods里面写相关的方法

    第五种:

    key:{
       handle:fn,
       deep:true,
       immediate:true,
       'object.a':function(){}
       }

    第六种:第五种写到外面的形式

    vm.$watch(
        'n',//监听的东西
        function(){},
        {deep:true,
         immediate:true,
        }
    )

    computed和watch的区别

    computed就是计算属性的意思,watch就是监听的意思。computed如果依赖属性不变,computed的值就不会重新计算,即根据依赖会产生缓存。watch的作用是,监听一个属性,如果发生变化了,就执行函数。watch有两个属性,第一个是immediate,如果是true,第一次渲染就会执行computed里面的函数;第二个是deep,如果是true,表示监听一个对象的同时,也要监听对象里面属性的变化。

    展开全文
  • 在vue中,监听数据变化常用 watch / computed ,在微信小程序里怎么用呢?通过一通学习,找到了一个插件,然后自己操作了一波。 1、引入插件 npm install --save miniprogram-computed 2、使用插件 如果你...

    目录

    1、引入插件

    2、使用插件

     


    在vue中,监听数据变化常用 watch / computed ,在微信小程序里怎么用呢?通过一通学习,找到了一个插件,然后自己操作了一波。

     

    1、引入插件

    npm install --save miniprogram-computed

    2、使用插件

    如果你已经有一个这样的页面:

    Page({
       data: {
         a: 111,
         b: 222,
       },
       onLoad: function() {},
       aaa(){},
       bbb(){}
    })

    可以先把它改成:

    Component({
       data: {
         a: 111,
         b: 222,
       },
       methods:{
         onLoad: function() {},
         aaa(){},
         bbb(){}  
       } 
    })
    

    然后就可以用了:

    const computedBehavior = require('miniprogram-computed')
    
    Component({
       behaviors: [computedBehavior],
       data: {
         a: 111,
         b: 222,
       },
       computed: {
         sum(data) {
           return data.a + data.b
         },
       },
       methods: {
         onLoad: function() {},
         aaa(){},
         bbb(){}
      },
    })

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了。 angularjs使用者想要知道data-binding是如何工作的...

    这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的,

    如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了。

     

    angularjs使用者想要知道data-binding是如何工作的,就会遇到很多的关的术语

    比如$wacth,$apply,$digest,dirty-checking(脏值检测)...等等,这些又是做什么的呢?

    在这篇文章里我会解决所有的疑问,通过结合这些术语在一起来学习。

    但是我会尽量用简单的方式来说明。

     

    现在开始

    The browser events-loop and the Angular.js extension

    我们的浏览器会检测等待事件发生,比如用户的一些行为,假如你点击了一个button或者在input写东西,

    事件的回调就会在内置的JavaScript跑起来,然后你就能够做一些DOM操作了。

    所以当回调发生的时候,浏览器中的DOM会发生一些变化。

     

    而Angularjs扩展了这个事件轮询,创建了一个叫angular content的东西(记住它,非常重要的一个概念),

    为了解释这个context是什么以及它是怎么工作的,我们需要先了解一下其他的一些概念。

    The $watch list

    每当你在ui上绑定了东西,就会添加了一个$wacth到$watch list中

    你可以把$watch想象成为一个能够察觉model的变化的检测器,

    比如你的html代码是

    User: <input type="text" ng-model="user" />
    Password: <input type="password" ng-model="pass" />

    在这里,我们将$scope.user绑定到了第一个input上,把$scope.pass绑定到了第二个input上,

    这样就意味着,我们添加了两个$wacth到了$watch list中了。

     

    再比如

    app.controller('MainCtrl', function($scope) {
      $scope.foo = "Foo";
      $scope.world = "World";
    });
    //页面htnl
    Hello, {{ World }}

    这个例子中,虽然我们在控制器中定义了foo和world,但是只有一个绑定到了页面上,

    所以这个例子中,我们只创建了一个$watch。

     

    我们再来看看下面这种情况:

    复制代码
    app.controller('MainCtrl', function($scope) {
      $scope.people = [...];
    });
    
    //HTML代码
    <ul>
      <li ng-repeat="person in people">
          {{person.name}} - {{person.age}}
      </li>
    </ul>
    复制代码

    那这个例子有多少个$watch被创建呢?

    我们在这里假设peple数组中的每个元素都拥有name和age这2个属性。

    由于我们实用ng-repeat遍历$scope.people,

    如果有10个人,则我们一共创建了10*2+1 = 22

    注意:其中的1,为ng-repeat所创建的。

     

    所以要注意的是,当我们在ui上使用(绑定)用directives 指令的时候,就创建了一个$watch,

    对了,那它是什么时候创建的呢?

    当我们的模板加载完成(亦叫做linking phase),compiler就会找到所有的指令,并创建对应的$watch。

    $digest loop

    还记得我们在上面讨论的event-loop吗?

    当浏览器发送一个事件,我们就能通过angular context管理这个事件,此时$digest就会被激活

    这个事件轮询loop由两个小loop组成,

    一个是处理$evalAsync队列

    一个是处理$watch list,这就是本文的主题了。

    那处理的过程是怎样的?$digest会轮询我们有的$watch list,大概就想下面这样

    ---》Hey,$watch,你的value值是多少啊?

    ---》我的value值是9。

    ---》好的,有变化吗?

    ---》没有。

    (什么都没有变化,就会跳到下个继续询问)

    ---》来,你的value值是多少啊?

    ---》是foo。

    ---》有变化吗?

    ---》有啊,本来是bar的

    (很好,那么现在我们有一个DOM要更新下了)

    流程就会这样继续下去,知道$watch list中所有的$watch都被询问...

     

    现在讲下脏值检测(dirty-checking),现在所有的$watch都被轮询过了,

    会再次询问是否所有的$watch都已经更新了?

    如果其中有一个改变了,就会重新轮询,直到所有的$wacth没有改变。这样做事为了确保所有的model都是“干净的”

    注意:如果轮询loop超过十次,就会抛出异常,来退出无限的轮询。

     

    当$degest loop完成,DOM就会发生改变

    看下面的例子

    复制代码
    app.controller('MainCtrl', function() {
      $scope.name = "Foo";
    
      $scope.changeFoo = function() {
          $scope.name = "Bar";
      }
    });
    复制代码
    {{ name }}
    <button ng-click="changeFoo()">Change the name</button>

    这里我们只有一个$watch,,因为ng-click不会创建任何的$watch(函数function是不会发生改变的)

    1·当我们点击按钮的时候

    2·浏览器发送事件,进入angular context(后面再解释这个context)

    3·然后执行$degest loop会轮询所有的$watch是否发生改变

    4·新的一轮loop报告说没有新的改变了

    5·然后浏览器就会拿回控制权,然后更新DOM,这里是更新新的值,$scope.name

    这里最重要的一点(也是难点)是所有的事件会进入angular context然后执行$digest loop

    这意味着每当我们在input键入一个字符,轮询loop会检查页面上所有的$watch。

    Entering the angular context with $apply

    那是怎么进入angular context的呢?答案是$apply

    当一个事件发生时吗,你如果调用$apply,就会进入angular context。

    如果你没有调用,就只会在外部(这里指的是区别于angular context内部)执行

    那么你就可能会有疑问了,

    上面的那个例子我没有调用$apply,它为什么会进入angular context呢?答案是Angular帮我们做了。

    所有当你调用ng-click时候,这个事件就会被包含进了$apply调用了。

    如果你在一个页面上有input,并且标签上写着ng-model="foo",

    然后输入“f”,事件就像这样$apply("foo = 'f';")被调用,换句话说,就是被$apply包含着调用了。

    When angular doesn’t use $apply for us

    这是很多Angular的新手共同的疑问,我在页面上上使用了jQuery,为什么JQuery没有更新我的绑定呢?

    因为jQuery没有调用$apply,所有的事件并没有进入angular context中,所以$digest loop没有执行

     

    下面让我们看看一个有趣的例子:

    假如我们的代码中有下面的指令directive 和控制器controller

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    app.directive('clickable'function() {
     
    return {
      restrict: "E",
      scope: {
        foo: '=',
        bar: '='
      },
      template: '<ul style="<li>{{foo}}</li><li>{{bar}}</li></ul>',
      link: function(scope, element, attrs) {
        element.bind('click'function() {
          scope.foo++;
          scope.bar++;
        });
      }
    }
     
    });
     
    app.controller('MainCtrl'function($scope) {
      $scope.foo = 0;
      $scope.bar = 0;
    });

    例子中我们绑定了foo和bar到了ul上的li中,我们想每当我们点击的时候,foo和bar都会增加一

    那实际情况中我们点击之后会发生什么呢?我们能不能看到foo和bar按照我们预期的变化呢?

    答案是不会,因为上面的click是没有被包含在$apply中调用的。

    这样是否意味着我们不能这样控制我们想要的变化呢?实际上不是的,我们是可以的

    实际上$scope上的foo和bar都是变化了的,只是它没有执行$digest loop,所以也就没有意识到$watch的变化

    这样也意味着,如果我在之后调用$apply,这样所有的$watch都会知道变化,然后就会更新相应的DOM

    复制代码
    <!DOCTYPE html>
    <html ng-app="app">
    <head>
    <script src="http:cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
    <meta charset=utf-8 />
    <title>Directive example</title>
    </head>
    <body ng-controller="MainCtrl">
      <clickable foo="foo" bar="bar"></clickable>
      <hr />
      
      {{ hello }} <button ng-click="setHello()">Change hello</button>
    </body>
    </html>
    复制代码
    复制代码
    app = angular.module('app', []);
    
    app.controller('MainCtrl', function($scope) {
      $scope.foo = 0;
      $scope.bar = 0;
      
      $scope.hello = "Hello";
      
      $scope.setHello = function() {
        $scope.hello = "World";
      };
    });
    
    app.directive('clickable', function() {
    
      return {
        restrict: "E",
        scope: {
          foo: '=',
          bar: '='
        },
        template: '<ul style="<li>{{foo}}</li><li>{{bar}}</li></ul>',
        link: function(scope, element, attrs) {
          element.bind('click', function() {
            scope.foo++;
            scope.bar++;
          });
        }
      }
    
    });
    复制代码

    如果你点击上面蓝色区域,你是不会看到任何变化的,但是之后再点击button的话,

    你就会突然看到0的数字变成一个数字,这个数字就是刚刚你点击了蓝色区域多少下的数字

    就像刚刚我上面说的那样,指令里的click并没有触发$digest loop,但是当你点击button按钮的时候

    按钮button上的ng-click就会调用$apply,然后就会执行$digest loop,

    所有的$watch就会检查有没有改变(其中包含了foo和bar这两个$watch)

     

    这时,你可能会想,这样的结果不是你想要的,你想要你点击那个指令块的时候马上就能看到变化。

    其实这很简单,只需要调用的时候包含$apply就可以了

    复制代码
    element.bind('click', function() {
      scope.foo++;
      scope.bar++;
    
      scope.$apply();
    });
    复制代码

    $apply是$scope(或者是指令link function上的scope)上的一个函数function,所以调用它会强制执行$digest loop

    注意,如果已经有一个轮询loop了,在这种情况下调用它将抛出一个异常,这是一个迹象表明,我们不需要再调用$apply了。

    其实上面的用法更好的是这样使用

    复制代码
    element.bind('click', function() {
      scope.$apply(function() {
          scope.foo++;
          scope.bar++;
      });
    })
    复制代码

    这两种用法有什么区别呢?

    区别是第一种用法中,我们更新新的值是在angular context外部,所以当有错误的时候,Angular是不会知道的。

    显然在上面的小例子中它不会产生多大影响,但是想像下当我们在复杂项目使用多种库,然后出错的时候,Angular是不会知道自己的错误

     

    所以如果你想在在项目中使用 jQuery plugin,你要确保你有调用$apply来执行$degest loop来更新DOM的变化。

    Conclusion

    最后,我希望你看完就明白了 Angular中data-binding的工作原理,我猜你看完的第一印象会觉得dirty-checking是很慢的

    实际上它是很快的,实际上只有页面上达到2000-3000 个$watch的时候,它才会出现性能上的延迟,但是我觉得你应该用不到那么多个。

     

    注:本文基本取自于$watch How the $apply Runs a $digest

    原文地址http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 

    转载于:https://www.cnblogs.com/answercard/p/5081433.html

    展开全文
  • 这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了。 angularjs使用者想要知道data-binding是如何工作...

    这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的,

    如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了。

    angularjs使用者想要知道data-binding是如何工作的,就会遇到很多的关的术语

    比如$wacth,$apply,$digest,dirty-checking(脏值检测)...等等,这些又是做什么的呢?

    在这篇文章里我会解决所有的疑问,通过结合这些术语在一起来学习。

    但是我会尽量用简单的方式来说明。

    现在开始

    The browser events-loop and the Angular.js extension

    我们的浏览器会检测等待事件发生,比如用户的一些行为,假如你点击了一个button或者在input写东西,

    事件的回调就会在内置的JavaScript跑起来,然后你就能够做一些DOM操作了。

    所以当回调发生的时候,浏览器中的DOM会发生一些变化。

    而Angularjs扩展了这个事件轮询,创建了一个叫angular content的东西(记住它,非常重要的一个概念),

    为了解释这个context是什么以及它是怎么工作的,我们需要先了解一下其他的一些概念。

    The $watch list

    每当你在ui上绑定了东西,就会添加了一个$wacth到$watch list中

    你可以把$watch想象成为一个能够察觉model的变化的检测器,

    比如你的html代码是

    User: <input type="text" ng-model="user" />
    Password: <input type="password" ng-model="pass" />

    在这里,我们将$scope.user绑定到了第一个input上,把$scope.pass绑定到了第二个input上,

    这样就意味着,我们添加了两个$wacth到了$watch list中了。

    再比如

    app.controller('MainCtrl', function($scope) {
      $scope.foo = "Foo";
      $scope.world = "World";
    });
    //页面htnl
    Hello, {{ World }}

    这个例子中,虽然我们在控制器中定义了foo和world,但是只有一个绑定到了页面上,

    所以这个例子中,我们只创建了一个$watch。

    我们再来看看下面这种情况:

    app.controller('MainCtrl', function($scope) {
      $scope.people = [...];
    });
    
    //HTML代码
    <ul>
      <li ng-repeat="person in people">
          {{person.name}} - {{person.age}}
      </li>
    </ul>

    那这个例子有多少个$watch被创建呢?

    我们在这里假设peple数组中的每个元素都拥有name和age这2个属性。

    由于我们实用ng-repeat遍历$scope.people,

    如果有10个人,则我们一共创建了10*2+1 = 22

    注意:其中的1,为ng-repeat所创建的。

    所以要注意的是,当我们在ui上使用(绑定)用directives 指令的时候,就创建了一个$watch,

    对了,那它是什么时候创建的呢?

    当我们的模板加载完成(亦叫做linking phase),compiler就会找到所有的指令,并创建对应的$watch。

    $digest loop

    还记得我们在上面讨论的event-loop吗?

    当浏览器发送一个事件,我们就能通过angular context管理这个事件,此时$digest就会被激活

    这个事件轮询loop由两个小loop组成,

    一个是处理$evalAsync队列

    一个是处理$watch list,这就是本文的主题了。

    那处理的过程是怎样的?$digest会轮询我们有的$watch list,大概就想下面这样

    ---》Hey,$watch,你的value值是多少啊?

    ---》我的value值是9。

    ---》好的,有变化吗?

    ---》没有。

    (什么都没有变化,就会跳到下个继续询问)

    ---》来,你的value值是多少啊?

    ---》是foo。

    ---》有变化吗?

    ---》有啊,本来是bar的

    (很好,那么现在我们有一个DOM要更新下了)

    流程就会这样继续下去,知道$watch list中所有的$watch都被询问...

    现在讲下脏值检测(dirty-checking),现在所有的$watch都被轮询过了,

    会再次询问是否所有的$watch都已经更新了?

    如果其中有一个改变了,就会重新轮询,直到所有的$wacth没有改变。这样做事为了确保所有的model都是“干净的”

    注意:如果轮询loop超过十次,就会抛出异常,来退出无限的轮询。

    当$degest loop完成,DOM就会发生改变

    看下面的例子

    app.controller('MainCtrl', function() {
      $scope.name = "Foo";
    
      $scope.changeFoo = function() {
          $scope.name = "Bar";
      }
    });
    {{ name }}
    <button ng-click="changeFoo()">Change the name</button>

    这里我们只有一个$watch,,因为ng-click不会创建任何的$watch(函数function是不会发生改变的)

    1·当我们点击按钮的时候

    2·浏览器发送事件,进入angular context(后面再解释这个context)

    3·然后执行$degest loop会轮询所有的$watch是否发生改变

    4·新的一轮loop报告说没有新的改变了

    5·然后浏览器就会拿回控制权,然后更新DOM,这里是更新新的值,$scope.name

    这里最重要的一点(也是难点)是所有的事件会进入angular context然后执行$digest loop

    这意味着每当我们在input键入一个字符,轮询loop会检查页面上所有的$watch。

    Entering the angular context with $apply

    那是怎么进入angular context的呢?答案是$apply

    当一个事件发生时吗,你如果调用$apply,就会进入angular context。

    如果你没有调用,就只会在外部(这里指的是区别于angular context内部)执行

    那么你就可能会有疑问了,

    上面的那个例子我没有调用$apply,它为什么会进入angular context呢?答案是Angular帮我们做了。

    所有当你调用ng-click时候,这个事件就会被包含进了$apply调用了。

    如果你在一个页面上有input,并且标签上写着ng-model="foo",

    然后输入“f”,事件就像这样$apply("foo = 'f';")被调用,换句话说,就是被$apply包含着调用了。

    When angular doesn’t use $apply for us

    这是很多Angular的新手共同的疑问,我在页面上上使用了jQuery,为什么JQuery没有更新我的绑定呢?

    因为jQuery没有调用$apply,所有的事件并没有进入angular context中,所以$digest loop没有执行

    下面让我们看看一个有趣的例子:

    假如我们的代码中有下面的指令directive 和控制器controller

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

    例子中我们绑定了foo和bar到了ul上的li中,我们想每当我们点击的时候,foo和bar都会增加一

    那实际情况中我们点击之后会发生什么呢?我们能不能看到foo和bar按照我们预期的变化呢?

    答案是不会,因为上面的click是没有被包含在$apply中调用的。

    这样是否意味着我们不能这样控制我们想要的变化呢?实际上不是的,我们是可以的

    实际上$scope上的foo和bar都是变化了的,只是它没有执行$digest loop,所以也就没有意识到$watch的变化

    这样也意味着,如果我在之后调用$apply,这样所有的$watch都会知道变化,然后就会更新相应的DOM

    <!DOCTYPE html>
    <html ng-app="app">
    <head>
    <script src="http:cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script>
    <meta charset=utf-8 />
    <title>Directive example</title>
    </head>
    <body ng-controller="MainCtrl">
      <clickable foo="foo" bar="bar"></clickable>
      <hr />
      
      {{ hello }} <button ng-click="setHello()">Change hello</button>
    </body>
    </html>
    app = angular.module('app', []);
    
    app.controller('MainCtrl', function($scope) {
      $scope.foo = 0;
      $scope.bar = 0;
      
      $scope.hello = "Hello";
      
      $scope.setHello = function() {
        $scope.hello = "World";
      };
    });
    
    app.directive('clickable', function() {
    
      return {
        restrict: "E",
        scope: {
          foo: '=',
          bar: '='
        },
        template: '<ul style="background-color: lightblue"><li>{{foo}}</li><li>{{bar}}</li></ul>',
        link: function(scope, element, attrs) {
          element.bind('click', function() {
            scope.foo++;
            scope.bar++;
          });
        }
      }
    
    });

    如果你点击上面蓝色区域,你是不会看到任何变化的,但是之后再点击button的话,

    你就会突然看到0的数字变成一个数字,这个数字就是刚刚你点击了蓝色区域多少下的数字

    就像刚刚我上面说的那样,指令里的click并没有触发$digest loop,但是当你点击button按钮的时候

    按钮button上的ng-click就会调用$apply,然后就会执行$digest loop,

    所有的$watch就会检查有没有改变(其中包含了foo和bar这两个$watch)

    这时,你可能会想,这样的结果不是你想要的,你想要你点击那个指令块的时候马上就能看到变化。

    其实这很简单,只需要调用的时候包含$apply就可以了

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

    $apply是$scope(或者是指令link function上的scope)上的一个函数function,所以调用它会强制执行$digest loop

    注意,如果已经有一个轮询loop了,在这种情况下调用它将抛出一个异常,这是一个迹象表明,我们不需要再调用$apply了。

    其实上面的用法更好的是这样使用

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

    这两种用法有什么区别呢?

    区别是第一种用法中,我们更新新的值是在angular context外部,所以当有错误的时候,Angular是不会知道的。

    显然在上面的小例子中它不会产生多大影响,但是想像下当我们在复杂项目使用多种库,然后出错的时候,Angular是不会知道自己的错误

    所以如果你想在在项目中使用 jQuery plugin,你要确保你有调用$apply来执行$degest loop来更新DOM的变化。

    Conclusion

    最后,我希望你看完就明白了 Angular中data-binding的工作原理,我猜你看完的第一印象会觉得dirty-checking是很慢的

    实际上它是很快的,实际上只有页面上达到2000-3000 个 $watch的时候,它才会出现性能上的延迟,但是我觉得你应该用不到那么多个。

    展开全文
  • watch for changes to the source code and automatically create a new build on change start a web server at localhost:1234. Go to http://localhost:1234/examples/ to test the examples. Deploy to a ...
  • Provided that the requested raw dataset is located in the correct folder (nnUNet_raw_data_base/nnUNet_raw_data/TaskXXX_MYTASK, also see here), you can run this step with the following command: nnUNet_...
  • 如何利用Vue.js库中的v-html指令...HtmlPanel.vue文件 export default{ // 使用时请使用 :url.sync=""传值 props: { url: { required: true } }, data () { return { loading: false, html: '' } }, watch: { url...
  • 总结:1. 计算属性computed在使用时,一定要注意,函数里面的变量都会被监听,只要里面的某一个值变动,便会将整个函数执行一遍。 而 watch 只是监听某一个值,若是监听的值里面也有很多变量... 而 watch 监听 data...
  • 然后在一个组件渲染时,该组件对应的watcher会将使用到的data中的哪些数据,记录为依赖dep。然后当data 的某部分发生了改变时,会看哪些watcher记录了改变的那部分为依赖,然后会通知这些watch...
  • 可以访问data, compued,watch,methods上的方法和数据。使用场景:初始化完成时事件可以写在这里,少量的异步请求也可以在这里调用(请求不宜过多,避免白屏时间太长)beforeMounted:dom元素还不能得到,但vue挂载....
  • 例如以下实例,我们通过使用 \(watch 属性来实现数据的监听,\)watch 必须添加在 Vue 实例之外才能实现正确的响应。实例中通过点击按钮计数器会加 1。setTimeout 设置 10 秒后计算器的值加上 20 。计数器: {{ ...
  • datajson.data; //console.log(this.data) }, methods: { //选择省 sheng1(val) { //locations是v-for里面的也是datas里面的值 let obj = {}; let code = val.substring(val.length - 6, val....
  • {name:'Watch',textStyle:{color: '#ffff00'}}, {name: 'Warning',textStyle:{color:'#ff7f00'}}, {name:'Abnormal value',textStyle:{color:'#cd0000'}}, {name:'Baseline',...
  • 1.父组件向子组件传值 在子组件上绑定一个v-bind:绑定的数据名,或者是使用冒号后面跟数据名,例如 v-bind:a="a" :a="a", ...可以使用watch,vue中的监听函数,监听到a中数据的变化 2.子组件向父组件传值 ...
  • model是什么 怎么使用 Vue中的标签怎么绑定事件vue-loader是什么 使用它的用途nextTick是做什么Vue组件中data为什么是函数(脚手架中)对keep-alive的了解Vue中key的作用在Vue中使用插件的步骤watch与computed差异...
  • Vue面试相关问题整理

    2020-05-16 01:22:26
    Vue面试相关问题整理前言什么是vue生命周期Vue生命周期总共有几个阶段Vue的双向数据绑定原理是什么vue组件间通信六种方式对于MVVM的理解...怎么使用?哪种功能场景使用它?vuex中的变量是响应式的,页面刷新后数据无法
  • 我们在上一篇了解了发布-订阅模式的实现,这...传入了props,data,watch,methods等属性。在实例化的过程中,Vue提供的构造函数就使用我们传入的options去完成数据的依赖管理,初始化的过程只有一次,但是数据的依赖管...
  • 该网站使用生成,该使用网站生成器从一组源文件和模板创建静态网站。 该存储库保存源材料(包括_data中的各种数据文件以及HTML或MD文件)以及用于表示内容的模板(在_layouts和_includes中)。 但是,请注意,这些...
  • Vue在指定页面隐藏某个组件

    千次阅读 2019-07-21 23:27:34
    本人小白,经常会遇到很多问题,所以记录下来供大家参考 ...2. 在data中设置一个变量来作为开关,并在watch中监听路由 靠path获取地址,再判断是否是需要隐藏的页面 可以使用if-else判断也可以像我这样...
  • 使用vue的时候,data,computed,watch是一些经常用到的概念,那么他们是怎么实现的呢,让我们从一个小demo开始分析一下它的流程。 demo演示代码片段 html代码 &lt;!DOCTYPE html&gt; &lt;html&gt; ...
  • 先来看一下整体的需求分析 ...beforeRouteUpdate ,computed, watch使用,提高代码的质量 element-ui的分组组件提供的四个事件 1.组件封装 把需要的属性提取出来 <template> <div class
  • 调试过UE3代码的同学都知道 UE3中有大量自定义结构体如FString、TArray、TMap等,这些数据...这里使用VS12特有的.natvis文件用来显示自定义的调试信息,VS10以前的autoexp文件怎么修改google一下一大堆 就不多写了...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

watchdata怎么使用