angular知識體系

1.類庫( 提供類方法 ) 和框架

類庫提供一系列的函數和方法的合集,能夠加快你寫代碼的速度。但是主導邏輯的還是自己的代碼。常用的類庫 eg: jquery

框架 特殊的已經實現了 web 的應用。只需要按照其邏輯填充你的業務邏輯就能得到完整的應用

angular 的特點

提供端對端的解決方案

構建一個 CRUD(add retrieve update delete) 應用的全部內容:`數據綁定,表單驗證,路由,深度鏈接,組件重用,依賴注入`

測試方案: `單元測試, 端對端測試,模擬和自動化測試`

具有各種種子應用作為模板和起點

特點

angular 主要考慮構建 CRUD 應用,并不是所有的應用都適合使用 angular 來構建

例如游戲,圖形編輯界面就不適合使用 angular

angular 的標榜概念

angular 認為聲明式的代碼比命令式的代碼更加符合 構建 (視圖 + 軟件)邏輯的代碼

聲明式的語言 :提前將所有的操作內置,使用時只需要按照規定聲明該操作,語言或者機器本身可以進行構建應用

聲明式的語言介紹:HTML 就是聲明式的結構,比如需要某個元素居中,不需要告訴瀏覽器具體的行為(需要找到元素的中間位置,將元素放在那里),只需要添加一個 align='center' 的屬性給新元素的可以了。這就是聲明式的語言

聲明式的語言也有不好的地方,就是所有可以使用的操作已經提前內置,所以他不能識別新的語法結構,比如你想讓元素居左 1/3 處就很難處理

將 DOM 操作和應用邏輯解耦

將測試和開發同等看待

大幅度減少應用中需要使用的各種 回調的邏輯,擺脫大量的回調邏輯

解放DOM 操作,

對頁面的UI操作可控,例如大量的DOM事件

angular 已經有了許多搭建好的基礎服務框架

angular 的初始化信息

angular 會在 DOMContentLoaded 事件觸發時執行, 通過 ng-app 指令 尋找你的應用的根作用域

1. 首先載入和指令相關的模塊

2. 穿件應用的 注入器(injector)

3. 將 ng-app 作為根節點編譯 DOM 。

也可以使用 angular.bootstrap( 節點 ) 來手動裝載節點

2. angular 的指令

指令的定義:由一個新的屬性,元素名稱,css類名等帶來DOM 樣式或者行為的改變。

指令( angular 的行為擴展 ):HTML 編譯器,能夠識別新的 HTML 語法,可以將行為動作關聯到HTML或者其屬性上面,設置可以創造自定義行為的元素,可復用。

注意指令是在最開始的時候被載入頁面的

指令本質上就是一個代用功能的函數 ** return 一個函數 **,類比于 react 的自定義組件

** angular API 有幾個大的的分類 **

ng.function ( 功能函數,類比于jquery 的方法函數 )

** ng.directive( angular 的重大模塊,eg: ng-model 等 ) **

** ng.provider ( 依賴注入功能 )**

.......

3. angular 的 編譯器( compiler )

編譯器通過遍歷 DOM 來查找和關聯屬性, 其分為 編譯 和 鏈接 兩個階段

編譯:遍歷所有的 DOM 收集指令,生成一個 鏈接函數集合

鏈接:將指令和作用域綁定,生成一個動態的視圖。

作用域模型的改變會反映到視圖上,視圖的操作會反映到底作用域模型( 中間通過鏈接函數得以實現 )

4. angular 的視圖 ( 動態的 )

視圖.png

5. angular 核心

啟動程序+執行期+作用域+控制器 ( 應用的行為 )+模型 ( 應用的數據 )+視圖+指令+過濾器+注入器+模塊+命名空間

angular 執行流程.png

1. 啟動程序

concepts-startup.png

** 啟動階段主要工作是建立指令關聯關系和渲染DOM **

瀏覽器解析HTML,然后將其解析成為 DOM

瀏覽器載入 angularJS

angular 等待 DOMContentLoaded event 事件觸發

angular 找到 ng-app 指令,作為應用程序的邊界或者根作用域

使用 ng-app 中的模塊來逐個配置注入器( $injector )

注入器 ( $injector ) 是用于創建 “編譯服務($compile service)” 和 “根作用域( $rootScope )”。

編譯服務的作用: 首先將 DOM 和 根作用域進行鏈接

編譯服務將指令( ng-model ... ng-init...等 ) 和 作用域的變量進行一一關聯。

通過變量替換,將構件好的視圖展現在頁面上

注意上面 編譯服務的作用:兩個階段:編譯階段和鏈接階段

** 注意點: **

ng-app 作為根應用指令,首先將注入器配置在根模塊上面。( 這一步與 DOM 無關 )

$injector 創建了 $compile 和 $rootScope

$compile 將得到的所有的根 DOM 和 $rootScope 進行關聯

2. 執行時期 ( 主要是事件回調,響應操作等觸發執行 )

concepts-runtime.png

** 執行時期主要工作內容是 事件要被正確的執行和渲染 **

關于執行時期的重點概念

只有在angular 的執行的上下文環境中才能享受到angular 提供的各種數據綁定,異常處理,資源管理和服務等等。eg: 使用 angular 的 $setTimeOut 完成延時后可以自動更新頁面視圖

可以使用 $apply() 來從普通的JavaScript 進入 angularJs的上下文環境。只有在使用自定義的事件或者使用第三方類庫時,才需要執行 $apply。

執行時期的流程:

通過調用 scope.$apply( fn )? 進入angular 的上下文環境。fn 為需要在上下文中執行的函數

angular 執行 fn, 此函數改變應用的狀態

angular 進入 $digest 循環,$digest 由兩個小循環組成($evalAsync 隊列和$watch列表,如上圖), 該循環一直迭代,直到模型穩定.

一個大循環由兩個小循環構成。

模型穩定的標志是:$evalAsync 隊列為空,$watch 列表中再無任何改變。

$evalAsync 通常用于管理視圖渲染前需要在當前框架外面執行的操作

$watch是個表達式的集合,若檢測到有更改,$watch 函數就會調用,將新的值更新到 DOM 中

一旦 angular 的 $digest 結束循環,整個執行就會離開 angular 和 JavaScript 的上下文環境,

最后一步,瀏覽器更新界面視圖重新渲染。

3. 作用域

mvc.png

將模型整理好傳遞給視圖,將瀏覽器的動作和事件傳遞給控制器

1. 作為中介存在 ( 鏈接數據模型和數據視圖 )

2. 作用域擁有層級結構,此層級結構和 DOM 的層級結構相互對應

3. 每一個作用域都有獨立的上下文環境

作用域的特點:

1. 作用域提供 API ( $watch 來觀察模型的變化 )

2. 作用域提供 API ( $apply ) 將任何模型的改變從 angular 領域 通過系統映射到視圖上

3. 作用域通過共享模型成員的方法嵌套到應用組件上面,一個作用域從父作用域繼承屬性

4. 作用域提供表達式執行的上下文環境

控制器和視圖都持有對作用域的引用,但是控制器和視圖之間沒有任何關系。

作用域的事件傳遞:

作用域的事件傳遞和 DOM 的事件傳遞類似,事件可以廣播給子作用域,也可以傳遞給父作用域。

作用域的聲明周期

1. 創建: 根作用域在應用被 $injector 啟動的時候被創建,在模板鏈接階段,有些執行會自動創建新的作用域 ( eg:ng-repeat )

2. 觀察者注冊:模板鏈接階段,指令會在作用域上注冊觀察者,觀察者用于將 DOM 的改變傳遞給 DOM

3. 模型改版: 只有在 scope.$apply() 中變化的數據才能被準確反映到模型上

angular 本身的 API 會自動應用 apply,eg: $http $timeout 不需要額外的 $apply

4. 變化的觀測:在 $apply 的最后,angular 會在根作用域執行一個 $digest 循環,將所有的變化傳遞給子作用域,只要在 $digest 循環中的所有表達式和函數都會被檢測,用于觀察模型的變化。

模型的變化檢測機制就是 angular 的 臟檢查機制

4. 控制器

控制器用于構造視圖的控制代碼,主要作用就是構造數據模型。

控制器的特點 (? 控制器應該和視圖做到分離 )

1. 控制器是由 JavaScript 書寫,控制器不應該包含任何 HTML 代碼。

2. 視圖使用 HTML 書寫的,視圖不應該包含任何 JavaScript 代碼。

3. 控制器和視圖沒有直接的關系。所以可以使用一個控制器對應多個視圖。

控制器的三個作用( 書寫,分清 c 和 v 的區別 )

1. 在應用中設置模型的初始狀態,

2. 將整理好的模型和函數交給作用域( scope )

3. 監聽模型的變化并響應事件或者動作( 事件響應函數 )

5. 模型

模型就是和模板結合生成視圖

模板就是單純的 HTML 代碼,

模型的特點:

模型必須使用作用域來引用

模型可以是任何 JavaScript 類型的數據

mvc.png

說明:

控制器和 視圖沒有直接的關系

mvc 的核心是由作用域承擔起來的

一個控制器可以對應多個模型

6. angular 的 watch, apply, digest

$watch

$watch 隊列是在 UI 上使用了一個指令時,就自動生成一條 $watch,所有的 $watch 組合成為一個 $watch 列表,

$watch 列表是在編譯的時候就生成完畢

  • {{person.name}} - {{person.age}}

    對于上述 ng-repeat? 若有 10 個 people 會生成 10 * 2 +1 個 $watch

    *** $watch 參數詳解 ***

    使用 $watch 函數可以進行 自定義的 操作監聽,更改 視圖

    $scope.$watch('name', function(newValue, oldValue) {

    if (newValue === oldValue) { return; } // AKA first run

    $scope.updated++;

    });

    $watch 的 第二個參數是一個 函數,用于 監聽 前面的變量是否更改。

    $scope.$watch('user', function(newValue, oldValue) {

    if (newValue === oldValue) { return; }

    $scope.updated++;

    }, true);

    $watch 的 第三個參數是 boolear 類型的值,

    ** 作用:**

    ** newValue 和 oldValue 默認是比較 新舊值的引用,若 user 是一個對象,則無法判斷對象內部值的改變,需要使用第三個參數來進行判斷對象內部深層次的值是否改變。**

    $digest

    $digest 循環過程 會 包含兩個小循環,$evalAsync 和 $watch 隊列循環。

    $digest 會涉及到臟檢查機制,反復詢問 $watch 隊列是否有數據改變

    {{ name }}

    Change the name

    // 這里有 一個 $watch( name 會生成 $watch , 而 ng-click 不會生成 $watch, 因為函數 是不會變的。

    我們按下按鈕

    瀏覽器接收到一個事件,進入angular context(后面會解釋為什么)。

    $digest循環開始執行,查詢每個$watch是否變化。

    由于監視$scope.name的$watch報告了變化,它會強制再執行一次$digest循環。

    新的$digest循環沒有檢測到變化。

    瀏覽器拿回控制權,更新與$scope.name新值相應部分的DOM

    $apply 用于進入 angular 的 上下文環境, 只是一個angular 提供的一個 api 沒有 循環等

    $apply 會自動觸發 $digest 循環

    angular 自己的事件動作會自動觸發 $apply

    非 angular 環境需要手動觸發 $apply

    有時自己添加了額外的 操作或動作,需要手動 $apply() 執行 $digest 循環( 典型:訪問服務器以后的回調操作 )

    element.bind('click', function() {

    scope.foo++;

    scope.bar++;

    scope.$apply();? ? ? // 手動觸發,進行一次 $digest 循環

    });

    // 第二種

    element.bind('click', function() {? ? //

    scope.$apply(function() {? ? ? 使用 $apply 函數進行 $digest 循環

    scope.foo++;

    scope.bar++;

    });

    })

    angular 執行時期 再解釋 ( 主要是 $apply $digest $watch 這三個方法的流程 )

    concepts-runtime.png

    1. 頁面觸發 DOM 事件,回調等。

    2. 應用程序自動調用 $apply()? 方法進入 angular 上下文,觸發 $digest 循環開始,

    3. $digest 循環 遍歷 $watch 列表集合,臟檢查 數據模型的改變,循環過程

    4. 檢查完畢,更新 數據模型,( 更改變量或者其他動作操作 )

    5. 作用域根據數據模型渲染 UI視圖。

    6. 繼續監聽。

    7. angular 的 通訊

    angular 模塊之間的通訊方式

    1. 作用域傳遞( 父子模塊通訊 )$parent $child

    2. 作用域數據傳遞 + $watch ( 類似于指令的數據傳遞的 = )

    3. 事件廣播 ( $emit $on $boardcast )

    使用 $rootscope

    將 $rootscope 作為依賴注入項,在子組件中使用 $rootscope

    使用場景: 對于一處改變,需要多處通知更改的變量,頻繁的調用可以使用 $rootscope(對于登錄使用的用戶名稱,在一個地方使用,需要多處顯示,并且更改后需要多處更改)

    myAppModule.controller('myCtrl', function($scope, $rootScope) {

    $scope.change = function() {

    $scope.test = new Date();

    };

    $scope.getOrig = function() {

    return $rootScope.test;

    };

    })

    作用域繼承 的 模塊通訊

    作用域繼承是在子模塊中可以直接使用父模塊方法/變量的 通訊方式

    ** 只適合數據從 父模塊傳遞到子模塊 **

    ** 在指令的數據傳遞中 通常使用這種模式進行通訊 **

    Do

    特點:

    只適合數據從 父模塊傳遞到子模塊

    子模塊的同名方法會覆蓋父模塊的 方法/變量

    不能進行同級組件之間的數據傳遞。除非顯示更改 $rootscope

    層級較多時 維護比較麻煩

    作用域通訊 + $watch

    作用域的數據只能從 父作用域傳遞到子作用域,** 使用 $watch() 可以監控子作用于數據的改變,類似于 指令數據傳遞的 = ( 等于號 ) **

    .controller("Parent", function($scope){

    $scope.VM = {a: "a", b: "b"};

    $scope.$watch("VM.a", function(newVal, oldVal){

    // code

    });

    })

    // 需要用到 $parent 等

    .controller('Child', function($scope){

    $scope.$parent.$watch('$scope.VM.a', function() { ..... })

    })

    消息機制

    scope 提供了冒泡和隧道機制,$on, $emit, $boardcast

    $boardcast 將事件廣播給所有的子組件,

    $on 用于注冊事件函數,

    $emit 用于事件向上冒泡傳遞

    優缺點: 相比于 $emit,$boardcast 需要向所有的子組件廣播組件的改變,會消耗更多的資源

    ** 所以 在應用的通訊數據達到很大體量 **

    $rootscope $scope + watch 都可以成為設計的基本手法

    使用 Service 進行通訊

    因為 angular 中所有的 Service 都是單例的,使用 Service 能夠比 時間隧道機制在邏輯上更加清晰。

    簡單抽取基本的數據

    var myApp = angular.module('myApp', []);

    myApp.factory('Data', function() {

    return { message: "I'm data from a service" }

    })

    function FirstCtrl($scope, Data) {

    $scope.data = Data;

    }

    function SecondCtrl($scope, Data) {

    $scope.data = Data;

    }

    進階版一: 使用 $watch 來監測數據變化

    angular.module('Store', [])

    //? 提供基本數據模型初始數據的 Service

    .factory('Products', function() {

    return {

    query: function() {

    return [{ name: 'Lightsaber', price: 1299.99}, { name: 'Jet Pack', price: 9999.99}, { name: 'Speeder', price: 24999.99}];

    }

    };

    })

    //? 提供數據模型的 Service

    .factory('Order', function() {

    var add = function(item, qty) {

    item.qty = qty;

    this.items.push(item);

    };

    var remove = function(item) {

    if (this.items.indexOf(item) > -1) {

    this.items.splice(this.items.indexOf(item), 1);

    }

    };

    var total = function() {

    return this.items.reduce(function(memo, item) {

    return memo + (item.qty * item.price);

    }, 0);

    };

    return {? ? ? ? // 返回完整的數據模型

    items: [],

    addToOrder: add,

    removeFromOrder: remove,

    totalPrice: total

    };

    }).controller('OrderCtrl', function(Products, Order) {

    this.products = Products.query();

    this.items = Order.items;

    this.addToOrder = function(item) {

    Order.addToOrder(item, 1);

    };

    this.removeFromOrder = function(item) {

    Order.removeFromOrder(item);

    };

    this.totalPrice = function() {

    return Order.total();

    };

    }).controller('CartCtrl', function($scope, Order) {

    $scope.items = Order.items;

    $scope.$watchCollection('items', function() {

    $scope.totalPrice = Order.totalPrice().toFixed(2);

    }.bind(this));

    });

    //? 整個頁面只有這里需要根據數據模型的變化而 改變視圖 UI

    Total Price: {{totalPrice}}

    • {{product.name}} - {{product.price}}

      Add

      Remove

      angularjs的$watch、$watchGroup、$watchCollection 使用方式 區別

      angular 幾種數據通訊機制

      var myModule = angular.module('myModule', []);

      myModule.factory('mySharedService', function($rootScope) {

      var sharedService = {};

      sharedService.message = '';

      sharedService.prepForBroadcast = function(msg) {

      this.message = msg;

      this.broadcastItem();? ? ? // 執行 廣播

      };

      sharedService.broadcastItem = function() {

      $rootScope.$broadcast('handleBroadcast');

      };

      return sharedService;

      });

      function ControllerZero($scope, sharedService) {

      $scope.handleClick = function(msg) {

      sharedService.prepForBroadcast(msg);? ? ? ? // 調用包含廣播的函數

      };

      $scope.$on('handleBroadcast', function() {

      $scope.message = sharedService.message;

      });

      }

      function ControllerOne($scope, sharedService) {

      $scope.$on('handleBroadcast', function() {

      $scope.message = 'ONE: ' + sharedService.message;

      });

      }

      function ControllerTwo($scope, sharedService) {

      $scope.$on('handleBroadcast', function() {

      $scope.message = 'TWO: ' + sharedService.message;

      });

      }

      ControllerZero.$inject = ['$scope', 'mySharedService'];

      ControllerOne.$inject = ['$scope', 'mySharedService'];

      ControllerTwo.$inject = ['$scope', 'mySharedService'];

      作者:南航

      鏈接:http://www.lxweimin.com/p/959cb6bb7036

      來源:簡書

      著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

    • 最后編輯于
      ?著作權歸作者所有,轉載或內容合作請聯系作者
      平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

      推薦閱讀更多精彩內容

      • Angular面試題 一、ng-show/ng-hide與ng-if的區別? 第一點區別是,ng-if在后面表達式...
        w_zhuan閱讀 5,569評論 0 26
      • 1、angularjs的幾大特性是什么? 雙向數據綁定、依賴注入、模板、指令、MVC/MVVM 2、列舉幾種常見的...
        2e9a10d418ab閱讀 1,398評論 0 10
      • AngularJS是什么 AngularJS的官方文檔這樣介紹它: 完全使用JavaScript編寫的客戶端技術。...
        oWSQo閱讀 1,343評論 0 10
      • 1. 傾聽不僅僅聽聲音,也要聽無聲的東西。 2. 何謂污泥,于身心不潔的,皆是污泥。 3. 因為愛你,所以我無所畏...
        達爾藍色海風閱讀 370評論 0 3
      • 書里講,世上有三類人-----男人,女人和已婚男人。想想是很有道理的,單從我們家的那位來看,完全可證明這一點。 從...
        錦瑟_db50閱讀 258評論 1 1