如何衡量angular工程師水平

1. ng-if跟ng-show/hide的區別有哪些?
2. ng-repeat迭代數組的時候,如果數組中有相同值,會有什麼問題,如何解決?
3. ng-click中寫的表達式,能使用JS原生對象上的方法,比如Math.max之類的嗎?爲什麼?
4. {{now | 'yyyy-MM-dd'}}這種表達式裏面,豎線和後面的參數通過什麼方式可以自定義?
5. factory和service,provider是什麼關係?

1. angular的數據綁定採用什麼機制?詳述原理
2. 兩個平級界面塊a和b,如果a中觸發一個事件,有哪些方式能讓b知道,詳述原理
3. 一個angular應用應當如何良好地分層?
4. angular應用常用哪些路由庫,各自的區別是什麼?
5. 如果通過angular的directive規劃一套全組件化體系,可能遇到哪些挑戰?
6. 分屬不同團隊進行開發的angular應用,如果要做整合,可能會遇到哪些問題,如何解決?
7. angular的缺點有哪些?
8. 如何看待angular 1.2中引入的controller as 語法?
9. 詳述angular的“依賴注入”
10. 如何看待angular 2……
從小問題開始:

1. ng-if跟ng-show/hide的區別有哪些?

第一點區別是,ng-if 在後面表達式爲 true 的時候才創建這個 dom 節點,ng-show 是初始時就創建了,用 display:block 和 display:none 來控制顯示和不顯示。


第二點區別是,ng-if 會(隱式地)產生新作用域,ng-switch 、 ng-include 等會動態創建一塊界面的也是如此。


2. ng-repeat迭代數組的時候,如果數組中有相同值,會有什麼問題,如何解決?


會提示 Duplicates in a repeater are not allowed. 加 track by $index 可解決。當然,也可以 trace by 任何一個普通的值,只要能唯一性標識數組中的每一項即可(建立 dom 和數據之間的關聯)。


3. ng-click中寫的表達式,能使用JS原生對象上的方法,比如Math.max之類的嗎?爲什麼?


不可以。只要是在頁面中,就不能直接調用原生的 JS 方法,因爲這些並不存在於與頁面對應的 Controller 的 $scope 中。除非在 $scope 中添加了這個函數:


$scope.parseInt = function(x){
    return parseInt(x);
}

4. {{now | 'yyyy-MM-dd'}}這種表達式裏面,豎線和後面的參數通過什麼方式可以自定義?


定義方式:

app.filter('過濾器名稱',function(){
    return function(需要過濾的對象, 過濾器參數1, 過濾器參數2, ...){
        //...做一些事情   
        return 處理後的對象;
    }
}); 

使用方式有兩種,一種是直接在頁面裏:

<p>{{now | date : 'yyyy-MM-dd'}}</p>

一種是在 js 裏面用:

// $filter('過濾器名稱')(需要過濾的對象, 參數1, 參數2,...)
$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');

5. factory和service,provider是什麼關係?

factory 把 service 的方法和數據放在一個對象裏,並返回這個對象;service 通過構造函數方式創建 service,返回一個實例化對象;provider 創建一個可通過 config 配置的 service。

從底層實現上來看,service 調用了 factory,返回其實例;factory 調用了 provider,將其定義的內容放在 $get 中返回。factory 和 service 功能類似,只不過 factory 是普通 function,可以返回任何東西(return 的都可以被訪問,所以那些私有變量怎麼寫你懂的);service 是構造器,可以不返回(綁定到 this 的都可以被訪問);provider 是加強版 factory,返回一個可配置的 factory。



1. angular的數據綁定採用什麼機制?詳述原理

髒檢查機制。

Angular 在 scope 模型上設置了一個監聽隊列,用來監聽數據變化並更新 view 。每次綁定一個東西到 view 上時 AngularJS 就會往 $watch 隊列裏插入一條 $watch,用來檢測它監視的 model 裏是否有變化的東西。當瀏覽器接收到可以被 angular context 處理的事件時,$digest 循環就會觸發,遍歷所有的 $watch,最後更新 dom。

舉個栗子:

<button ng-click="val=val+1">increase 1</button>

click 時會產生一次更新的操作(至少觸發兩次 $digest 循環)

  • 按下按鈕
  • 瀏覽器接收到一個事件,進入到 angular context
  • $digest 循環開始執行,查詢每個 $watch 是否變化
  • 由於監視 $scope.val 的 $watch 報告了變化,因此強制再執行一次 $digest 循環
  • 新的 $digest 循環未檢測到變化
  • 瀏覽器拿回控制器,更新 $scope.val 新值對應的 dom

$digest 循環的上限是 10 次(超過 10次後拋出一個異常,防止無限循環)。


2. 兩個平級界面塊a和b,如果a中觸發一個事件,有哪些方式能讓b知道,詳述原理


這個問題換一種說法就是,如何在平級界面模塊間進行通信。有兩種方法,一種是共用服務,一種是基於事件。


a. 共用服務


在 Angular 中,通過 factory 可以生成一個單例對象,在需要通信的模塊 a 和 b 中注入這個對象即可。


b. 基於事件


這個又分兩種方式


第一種是藉助父 controller。在子 controller 中向父 controller 觸發($emit)一個事件,然後在父 controller 中監聽($on)事件,再廣播($broadcast)給子 controller ,這樣通過事件攜帶的參數,實現了數據經過父 controller,在同級 controller 之間傳播。


第二種是藉助 $rootScope。每個 Angular 應用默認有一個根作用域 $rootScope, 根作用域位於最頂層,從它往下掛着各級作用域。所以,如果子控制器直接使用 $rootScope 廣播和接收事件,那麼就可實現同級之間的通信。


3. 一個angular應用應當如何良好地分層?


分兩個方面講


a. 目錄結構的劃分


對於小型項目,可以按照文件類型組織,比如


css
js
  controllers
  models
  services
  filters
templates  

但是對於規模較大的項目,最好按業務模塊劃分,比如


css
modules
  account
    controllers
    models
    services
    filters
    templates
  disk
    controllers
    models
    services
    filters
    templates

modules 下最好再有一個 common 目錄來存放公共的東西。


b. 邏輯代碼的劃分


作爲一個 MVVM 框架,Angular 應用本身就應該按照 模型,視圖模型(控制器),視圖來劃分。


這裏邏輯代碼的拆分,主要是指儘量讓 controller 這一層很薄。提取共用的邏輯到 service 中 (比如後臺數據的請求,數據的共享和緩存,基於事件的模塊間通信等),提取共用的界面操作到 directive 中(比如將日期選擇、分頁等封裝成組件等),提取共用的格式化操作到 filter 中等等。


在複雜的應用中,也可以爲實體建立對應的構造函數,比如硬盤(Disk)模塊,可能有列表、新建、詳情這樣幾個視圖,並分別對應的有 controller,那麼可以建一個 Disk 構造函數,裏面完成數據的增刪改查和驗證操作,有跟 Disk 相關的 controller,就注入 Disk 構造器並生成一個實例,這個實例就具備了增刪改查和驗證方法。這樣既層次分明,又實現了複用(讓 controller 層更薄了)。


4. angular應用常用哪些路由庫,各自的區別是什麼?


Angular1.x 中常用 ngRoute 和 ui.router,還有一種爲 Angular2 設計的 new router(面向組件)。後面那個沒在實際項目中用過,就不講了。


無論是 ngRoute 還是 ui.router,作爲框架額外的附加功能,都必須以 模塊依賴 的形式被引入。


兩者區別是:


ngRoute 模塊是 Angular 自帶的路由模塊,而 ui.router 模塊是基於 ngRoute模塊開發的第三方模塊。


ui.router 是基於 state (狀態)的, ngRoute 是基於 url 的,ui.router模塊具有更強大的功能,主要體現在視圖的嵌套方面。


使用 ui.router 能夠定義有明確父子關係的路由,並通過 ui-view 指令將子路由模版插入到父路由模板的 <div ui-view></div> 中去,從而實現視圖嵌套。而在 ngRoute 中不能這樣定義,如果同時在父子視圖中 使用了 <div ng-view></div> 會陷入死循環。


5. 如果通過angular的directive規劃一套全組件化體系,可能遇到哪些挑戰?


沒有自己用 directive 做過一全套組件,講不出。


能想到的一點是,組件如何與外界進行數據的交互,以及如何通過簡單的配置就能使用吧。


6. 分屬不同團隊進行開發的angular應用,如果要做整合,可能會遇到哪些問題,如何解決?


可能會遇到不同模塊之間的衝突。比如一個團隊所有的開發在 moduleA 下進行,另一團隊開發的代碼在 moduleB 下:


angular.module('myApp.moduleA', [])
    .factory('serviceA', function(){
        ...
    })

angular.module('myApp.moduleB', [])
    .factory('serviceA', function(){
        ...
    })    

angular.module('myApp', ['myApp.moduleA', 'myApp.moduleB']) 

會導致兩個 module 下面的 serviceA 發生了覆蓋。


貌似在 Angular1.x 中並沒有很好的解決辦法,所以最好在前期進行統一規劃,做好約定,嚴格按照約定開發,每個開發人員只寫特定區塊代碼。


7. angular的缺點有哪些?


a. 強約束


導致學習成本較高,對前端不友好。


但遵守 AngularJS 的約定時,生產力會很高,對 Java 程序員友好。


b. 不利於 SEO


因爲所有內容都是動態獲取並渲染生成的,搜索引擎沒法爬取。


一種解決辦法是,對於正常用戶的訪問,服務器響應 AngularJS 應用的內容;對於搜索引擎的訪問,則響應專門針對 SEO 的HTML頁面。


c. 性能問題


作爲 MVVM 框架,因爲實現了數據的雙向綁定,對於大數組、複雜對象會存在性能問題。


可以用來 優化 Angular 應用的性能 的辦法:


減少監控項(比如對不會變化的數據採用單向綁定)主動設置索引(指定 track by,簡單類型默認用自身當索引,對象默認使用 $$hashKey,比如改爲track by item.id)降低渲染數據量(比如分頁,或者每次取一小部分數據,根據需要再取)數據扁平化(比如對於樹狀結構,使用扁平化結構,構建一個 map 和樹狀數據,對樹操作時,由於跟扁平數據同一引用,樹狀數據變更會同步到原始的扁平數據)

d. 移動端


8. 如何看待angular 1.2中引入的controller as 語法?


在 angular 1.2 以前,在 view 上的任何綁定都是直接綁定在 $scope 上的。使用 controllerAs,不需要再注入 $scope,controller 變成了一個很簡單的 JavaScript 對象(POJO),一個更純粹的 ViewModel。


從源碼實現上來看,controllerAs 語法只是把 controller 這個對象的實例用 as 別名在 $scope 上創建了一個屬性。


if (directive.controllerAs) {
    locals.$scope[directive.controllerAs] = controllerInstance;
}

但是這樣做,除了上面提到的使 controller 更加 POJO 外,還可以避免遇到 AngularJS 作用域相關的一個坑(就是上文中 ng-if 產生一級作用域的坑,其實也是 javascript 原型鏈繼承中值類型繼承的坑。因爲使用 controllerAs 的話 view 上所有字段都綁定在一個引用的屬性上,比如 vm.xx,所以坑不再存在)。


不過不引入 $scope 會出現的一個問題是,導致 $emit、 $broadcast、 $on、$watch 等 $scope 下的方法無法使用。這些跟事件相關的操作可以封裝起來統一處理,或者在單個 controller 中引入 $scope,特殊對待。


9. 詳述angular的“依賴注入”


AngularJS 是通過構造函數的參數名字來推斷依賴服務名稱的,通過 toString() 來找到這個定義的 function 對應的字符串,然後用正則解析出其中的參數(依賴項),再去依賴映射中取到對應的依賴,實例化之後傳入。


因爲 AngularJS 的 injector 是假設函數的參數名就是依賴的名字,然後去查找依賴項,那如果像下面這樣簡單注入依賴,代碼壓縮後(參數被重命名了),就無法查找到依賴項了。


function myCtrl = ($scope, $http){
    ...
}

所以,通常會使用下面兩種方式注入依賴(對依賴添加的順序有要求)。


數組註釋法:


myApp.controller('myCtrl', ['$scope', '$http', function($scope, $http){
    ...
}])

顯式 $inject :


myApp.controller('myCtrl', myCtrl);
function myCtrl = ($scope, $http){
    ...
}
myCtrl.$inject = ['$scope', '$http'];

對於一個 DI 容器,必須具備三個要素:依賴項的註冊,依賴關係的聲明和對象的獲取。在 AngularJS 中,module 和 $provide 都可以提供依賴項的註冊;內置的 injector 可以獲取對象(自動完成依賴注入);依賴關係的聲明,就是上面的那兩種方式。


發佈了29 篇原創文章 · 獲贊 16 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章