国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

angular的ViewModel設(shè)計(jì)

int64 / 2910人閱讀

摘要:換言之,的對(duì)應(yīng)的,此外它還有。它們共同構(gòu)成的監(jiān)控系統(tǒng)。和是相輔相成的。兩者一起,構(gòu)成了作用域的核心功能數(shù)據(jù)變化的響應(yīng)。迭代的最大值稱為。框架設(shè)計(jì)第三版,敬請(qǐng)期待

angular的ViewModel有一個(gè)專門的官方術(shù)語(yǔ)叫$scope, 它只是一個(gè)普通構(gòu)造器(Scope)的實(shí)例。換言之,它是一個(gè)普通的JS對(duì)象。為了實(shí)現(xiàn)MVVM框架通常宣傳的那種“改變數(shù)據(jù)即改變視圖”的魔幻效果,它得裝備上更多更強(qiáng)大的外掛。

名:
姓:

姓名: {{firstName + " " + lastName}}

app.controller會(huì)產(chǎn)生一個(gè)$scope對(duì)象, 這個(gè)$scope是傳進(jìn)去的。相當(dāng)于:

var $scope = new Scope();
$scope.firstName = "Jane";
$scope.lastName = "Smith";

相對(duì)于avalon將所有vm扁平化地放到avalon.vmodels中,angular則傾向?qū)?b>$scope對(duì)象以樹(shù)的形式組織起來(lái)。

function Scope() {
this.$id = nextUid();
      this.$$phase = this.$parent = this.$$watchers =
                     this.$$nextSibling = this.$$prevSibling =
                     this.$$childHead = this.$$childTail = null;
      this.$root = this;
      this.$$destroyed = false;
      this.$$listeners = {};
      this.$$listenerCount = {};
      this.$$watchersCount = 0;
      this.$$isolateBindings = null;
}

其中$parent$$nextSibling, $$prevSibling, $$childHead, $$childTail,$root是指向其他$scope對(duì)象。 $$watchers是綁定對(duì)象的訂閱數(shù)組,$$watchersCount是其長(zhǎng)度, $$listeners 是放手動(dòng)觸發(fā)的函數(shù),$$listenerCount是其長(zhǎng)度。

由于angular是一個(gè)普通的JS對(duì)象,當(dāng)屬性發(fā)生變化時(shí),它本身不可能像avalon那么靈敏地跑去$fire。 于是它實(shí)現(xiàn)了一套復(fù)雜的$fire方法,但它不叫$fire, 叫做$digest

換言之,avalon的$watch對(duì)應(yīng)angular的$watch,此外它還有$watchGroup, $watchCollection。avalon的$fire方法對(duì)應(yīng)angular的$digest, 為了安全,它外面還有$applyAsync$apply, $evalAsync等幾個(gè)殼函數(shù)。它們共同構(gòu)成angular的監(jiān)控系統(tǒng)。$watch$digest是相輔相成的。兩者一起,構(gòu)成了angular作用域的核心功能:數(shù)據(jù)變化的響應(yīng)。

先看$watch方法, 傳參比avalon復(fù)雜多,但結(jié)果都是返回一個(gè)移除監(jiān)聽(tīng)的函數(shù):

Scope.prototype.$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
  //將表達(dá)式轉(zhuǎn)換為求值函數(shù)
    var get = $parse(watchExp); 

    if (get.$$watchDelegate) {
      return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
    }
    
    var scope = this,
    //所有綁定對(duì)象都放在一個(gè)數(shù)組中,因此存在性能問(wèn)題
        array = scope.$$watchers,
    //構(gòu)建綁定對(duì)象
        watcher = {
          fn: listener,//刷新函數(shù)
          last: initWatchVal,//舊值
          get: get,//求值函數(shù)
          exp: prettyPrintExpression || watchExp,//表達(dá)式
          eq: !!objectEquality// 比較方法
        };

    lastDirtyWatch = null;

    if (!isFunction(listener)) {
      watcher.fn = noop;
    }

    if (!array) {
      array = scope.$$watchers = [];
    }
    array.unshift(watcher);
    incrementWatchersCount(this, 1);

    return function deregisterWatch() {//移除綁定對(duì)象
      if (arrayRemove(array, watcher) >= 0) {
        incrementWatchersCount(scope, -1);
      }
      lastDirtyWatch = null;
    };
},

而$digest則復(fù)雜多了,我們先實(shí)現(xiàn)它的一個(gè)簡(jiǎn)化版,遍歷其所有綁定對(duì)象,執(zhí)行其刷新函數(shù)。

Scope.prototype.$digest = function() {
  var list = this.$$watchers || []
  list.forEach(function(watch) {
    var newValue = watch.get()
    var oldValue = watch.last;
    if (newValue !== oldValue) {
      watch.fn(newValue, oldValue, self);
    }
    watch.last = newValue;
  })
}

到目前為止,它的邏輯與 avalon的一樣,但要明白一點(diǎn),avalon的監(jiān)控是智能的,如果更新A屬性,導(dǎo)致了B屬性也發(fā)生變化,那么avalon也連忙更新B涉及的視圖。而angular的$$watcher 里面都是一個(gè)個(gè)普通對(duì)象,假如里面有A,B兩個(gè)對(duì)象。先執(zhí)行A,A值沒(méi)有變化,再執(zhí)行B,B變化了,但B在變化時(shí)的同時(shí),也修改了A值。但這時(shí),循環(huán)已經(jīng)完畢。B涉及的視圖變動(dòng) ,A沒(méi)有變動(dòng),這就不合理了。因此,我們需要在某個(gè)綁定對(duì)象發(fā)生了一次改動(dòng)后,再重新檢測(cè)這個(gè)數(shù)組。

我們把現(xiàn)在的$digest函數(shù)改名為$$digestOnce,它把所有的監(jiān)聽(tīng)器運(yùn)行一次,返回一個(gè)布爾值,表示是否還有變更了:

Scope.prototype.$$digestOnce = function() {
  var self  = this;
  var dirty;
  _.forEach(this.$$watchers, function(watch) {
    var newValue = watch.get();
    var oldValue = watch.last;
    if (newValue !== oldValue) {
      watch.fn(newValue, oldValue, self);
      dirty = true;
    }
    watch.last = newValue;
  });
  return dirty;
};

然后,我們重新定義$digest,它作為一個(gè)“外層循環(huán)”來(lái)運(yùn)行,當(dāng)有變更發(fā)生的時(shí)候,調(diào)用$$digestOnce

Scope.prototype.$digest = function() {
  var dirty;
  do {
    dirty = this.$$digestOnce();
  } while (dirty);
};

$digest現(xiàn)在至少運(yùn)行每個(gè)監(jiān)聽(tīng)器一次了。如果第一次運(yùn)行完,有監(jiān)控值發(fā)生變更了,標(biāo)記為dirty,所有監(jiān)聽(tīng)器再運(yùn)行第二次。這會(huì)一直運(yùn)行,直到所有監(jiān)控的值都不再變化,整個(gè)局面穩(wěn)定下來(lái)了。

但這里面有一個(gè)風(fēng)險(xiǎn),比如A的求值函數(shù)里會(huì)修改B, B的求值函數(shù)又修改A,那么大家都無(wú)法穩(wěn)定下來(lái),不斷死循環(huán)。因此我們得把digest的運(yùn)行控制在一個(gè)可接受的迭代數(shù)量?jī)?nèi)。如果這么多次之后,作用域還在變更,就勇敢放手,宣布它永遠(yuǎn)不會(huì)穩(wěn)定。在這個(gè)點(diǎn)上,我們會(huì)拋出一個(gè)異常,因?yàn)椴还茏饔糜虻臓顟B(tài)變成怎樣,它都不太可能是用戶想要的結(jié)果。

迭代的最大值稱為TTL(short for Time To Live)。這個(gè)值默認(rèn)是10,可能有點(diǎn)小(我們剛運(yùn)行了這個(gè)digest 成千上萬(wàn)次),但是記住這是一個(gè)性能敏感的地方,因?yàn)閐igest經(jīng)常被執(zhí)行,而且每個(gè)digest運(yùn)行了所有的監(jiān)聽(tīng)器。

Scope.prototype.$digest = function() {
  var ttl = 10;
  var dirty;
  do {
    dirty = this.$$digestOnce();
    if (dirty && !(ttl--)) {
      throw "10 digest iterations reached";
    }
  } while (dirty);
};

但這只是模擬了angular的$digest的冰山一角,可見(jiàn)沒(méi)有訪問(wèn)器屬性這高階魔法,想實(shí)現(xiàn)MVVM是非常麻煩與復(fù)雜,并且用戶使用起來(lái)也別扭。

有關(guān)$digest的源碼與解決可見(jiàn)這里

https://github.com/angular/an...

http://www.cnblogs.com/xuezhi...

我們?cè)倏?digest 是怎么與angular的ng-model 關(guān)聯(lián)在一起。

ng-model指令有一個(gè)$post方法,它在里面進(jìn)行綁定事件,如果用戶提供了updateOn這個(gè)選項(xiàng),選項(xiàng)是一些事件名,那么它就為元素綁定對(duì)應(yīng)的事件,否則就綁定blur方法

post: function ngModelPostLink(scope, element, attr, ctrls) {
  var modelCtrl = ctrls[0];
  if (modelCtrl.$options.getOption("updateOn")) {
    element.on(modelCtrl.$options.getOption("updateOn"), function(ev) {
      modelCtrl.$$debounceViewValueCommit(ev && ev.type);
    });
  }
  function setTouched() {
    modelCtrl.$setTouched();
  }
  element.on("blur", function() {
    if (modelCtrl.$touched) return;

    if ($rootScope.$$phase) {
      scope.$evalAsync(setTouched);
    } else {
      scope.$apply(setTouched);
    }
  });
}

我們先看blur的回調(diào),里面$evalAsync$apply方法,它們里面就會(huì)調(diào)用$digest,進(jìn)行臟檢測(cè)。

$evalAsync: function(expr, locals) {
  if (!$rootScope.$$phase && !asyncQueue.length) {
    $browser.defer(function() {
      if (asyncQueue.length) {
        $rootScope.$digest();
      }
    });
  }

 //...略
},
$apply: function(expr) {
  try {
    beginPhase("$apply");
    //...略
  } finally {
    try {
      $rootScope.$digest();
    } catch (e) {
      $exceptionHandler(e);
      throw e;
    }
  }
},

再看$$debounceViewValueCommit方法,里面也有一個(gè)$apply方法。換言之,殊途同歸,全部匯在$digest里面處理。

但如果許多地方同時(shí)發(fā)生改變,會(huì)不會(huì)將它搞死呢?不會(huì),我們留意一下$digest的源碼最上方有一句 beginPhase("$digest"),臨結(jié)束時(shí)也有一句clearPhase()$apply 里面也是 beginPhase("$apply")clearPhase(),它們標(biāo)識(shí)這個(gè)$scope對(duì)象進(jìn)行臟檢測(cè),直接拋錯(cuò)。

function beginPhase(phase) {
     if ($rootScope.$$phase) {
       throw $rootScopeMinErr("inprog", "{0} already in progress", $rootScope.$$phase);
     }

     $rootScope.$$phase = phase;
}

 function clearPhase() {
   $rootScope.$$phase = null;
 }

$apply會(huì)將錯(cuò)誤catch住,不讓它影響程序繼續(xù)運(yùn)行。這就是官方推我們使用$apply驅(qū)動(dòng)程序運(yùn)行,而不直接用$digest的緣故。

通過(guò)上面的分析,avalon與angular的設(shè)計(jì)重點(diǎn)是不同的,avalon是忙于發(fā)掘語(yǔ)言特征,通過(guò)訪問(wèn)器中的setter與getter將其那個(gè)簡(jiǎn)單的觀察者模式放進(jìn)去。angular則忙于構(gòu)建其復(fù)雜無(wú)比的觀察者模式(本節(jié)沒(méi)有展現(xiàn)其全貌,它除了$$watchers隊(duì)列,還有asyncQueue隊(duì)列,postDigestQueue隊(duì)列,applyAsyncQueue隊(duì)列), 并且為了diff新舊值的不同,發(fā)展出一套名叫臟檢測(cè)的機(jī)制。

from 《javascript框架設(shè)計(jì)》第三版,敬請(qǐng)期待

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87933.html

相關(guān)文章

  • 【教學(xué)向】150行代碼教你實(shí)現(xiàn)一個(gè)低配版MVVM庫(kù)(2)- 代碼篇

    摘要:也放出地址,上面有完整工程以及在線演示地址相關(guān)閱讀教學(xué)向行代碼教你實(shí)現(xiàn)一個(gè)低配版的庫(kù)原理篇教學(xué)向行代碼教你實(shí)現(xiàn)一個(gè)低配版的庫(kù)代碼篇教學(xué)向再加行代碼教你實(shí)現(xiàn)一個(gè)低配版的庫(kù)設(shè)計(jì)篇教學(xué)向再加行代碼教你實(shí)現(xiàn)一個(gè)低配版的庫(kù)原理篇 書接上一篇: 150行代碼教你實(shí)現(xiàn)一個(gè)低配版的MVVM庫(kù)(1)- 原理篇 寫在前面 為了便于分模塊,和閱讀,我使用了Typescript來(lái)進(jìn)行coding,總行數(shù)是正好...

    loonggg 評(píng)論0 收藏0
  • MVC MVP MVVM

    摘要:,的事件回調(diào)函數(shù)中調(diào)用的操作方法。以為例調(diào)用關(guān)系模式實(shí)際就是將中的改名為,調(diào)用過(guò)程基本一致,最大的改良是間的雙向綁定。和間,有一個(gè)對(duì)象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開(kāi)發(fā)的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實(shí)的MV...

    wushuiyong 評(píng)論0 收藏0
  • MVC MVP MVVM

    摘要:,的事件回調(diào)函數(shù)中調(diào)用的操作方法。以為例調(diào)用關(guān)系模式實(shí)際就是將中的改名為,調(diào)用過(guò)程基本一致,最大的改良是間的雙向綁定。和間,有一個(gè)對(duì)象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開(kāi)發(fā)的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實(shí)的MV...

    Tangpj 評(píng)論0 收藏0
  • vue簡(jiǎn)單入門(一)vue是什么,為什么我們要學(xué)vue?

    摘要:是什么為什么我們要使用說(shuō)到了,我們就不得不先聊一下是什么以及為什么我們要使用,他能給我們的開(kāi)發(fā)帶來(lái)什么樣的便利呢首先,我們來(lái)看一下的自我介紹讀音,類似于是一套用于構(gòu)建用戶界面的漸進(jìn)式框架。 作為一個(gè)剛?cè)胄胁痪玫牟锁B(niǎo)不知從什么時(shí)候開(kāi)始就有了寫一個(gè)自己的專欄的想法,剛好今天沒(méi)事就給自己挖一個(gè)坑,分享一下我對(duì)vue的見(jiàn)解和一些領(lǐng)悟,整個(gè)專欄應(yīng)該會(huì)包括vue,vue-cli,vue-route...

    Lucky_Boy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<