關於 AngularJS 框架的使用有哪些經驗值得分享?
分享的時候遲緩地發現知乎專欄是使用AngularJS的。最近也用AngularJS做了幾個小項目。似乎國內目前AngularJS的使用者還是挺少的,但是這又是個很不錯的框架,所以希望知乎專欄的前端工程師能分享下你們使用AngularJS的經驗。
我比較感興趣的是,怎麼解決搜索引擎的問題?AngularJS測試工具的使用經驗?謝謝!
謝提醒,之前還沒注意到這個。
最近做的某個項目的 UI 部分 Mobile Campus(Google Drive 可能需要跨牆)
代碼:https://github.com/morlay/angular-mobile-ui然後,說說我的一些做法。可能不夠完善,畢竟還在折騰中。
## DOM 的整體 or 零散
首先是這篇神貼:
javascript - How do I "think in AngularJS" if I have a jQuery background?AngularJS 與 jQuery 等傳統操作 DOM 的思想有所不同,
對於 jQuery 等,一般是先有完整 DOM 然後在這些 DOM 的基礎上進行二次調教。
而 AngularJS 等框架則是 根據 數據模型 以及其對應的 DOM 模版,然後通過模版像搭積木那樣組合頁面。顯然的,前者在 SEO 上有天然優勢;而後者,搜索引擎還只能拿到某個模版,而無內容。
暫時沒想到有什麼特別好的解決方案,或許,對於內容頁,可以繼續使用傳統方式,而只在需要更多交互的地方應用 AngularJS,特別是在移動端應用上。同理適用於各種 前端的 MVC 框架,後端只要為前端提供數據介面,而不再需要為其拼接 HTML.
## 模塊化
AngularJS 也是遵循 AMD 的。(AMD 是啥,參考:使用 AMD、CommonJS 及 ES Harmony 編寫模塊化的 JavaScript)
雖然它也可以按照傳統代碼方式來寫(其首頁介紹的用法 AngularJS — Superheroic JavaScript MVW Framework),但是,既然都提供了這麼一種模塊的方法,為何不用上呢 (參考下他已有的較成熟衍生庫 https://github.com/angular-ui/bootstrap)。
angular.module("app", [
"moduleA",
"moduleB",
])
.controller("MainCtrl", [
"$scope",
function ($scope) {
}]);
而且,這種寫法還可以方便做代碼的合併與壓縮,在後面 Grunt 自動化 一節中,就會提到使用 Nodejs/Grunt 來自動的做這些事情。
## 可復用模版 or 業務邏輯模版
今年 Google 開發者大會中 提到的 Polymer(Welcome -
Polymer)
這貨讓人感覺像是 Angular Directives 的進化。
而 Directives 做的事,就是把一堆 DOM 封裝為一條或者一組 自定義的 HTML標籤,作為可復用的模版,以供組裝業務調用。 Demos 可參看:Angular directives for Twitter"s Bootstrap
當然,為了方便修改,很多時候在做 directive 的時候需要將 template 用 templateUrl 代替,
不用擔心文件的碎片化,不利於前端載入 Grunt 自動化 一節 會提到如何合併這些碎片化的 模版。Directives 是作為可復用的模版,
而業務邏輯則是一般是一個業務對應一個 html 及其的 controller.## 作用域間的通信
上節提到了一個 html 及其的 controller,一個完整應用自然會包含很多的業務子塊。
自然會有很多很多 cotrollers.AngularJS 提供了 方法, $scope 或者 $rootScope 的 $broadcast $emit / $on
$scope.$emit("eventA",msg);
$scope.$broadcast("eventA",msg);
$rootScope.$on("eventA",function(event,msg){
console.log(msg);
});
至於他們之間的差別,可參考這個 Demo, Chrome F12 ,你可以看到結果。
https://googledrive.com/host/0Bwdui5aYcEA9SzN2WDJ4cXZRTTA/index.html## 數據池
除了作用域間的傳值外,還有個方法是統一的管理一個數據池,
對於沒有業務交叉的 controller,若是有公有數據的需要,都從這個數據池中取,而這個數據池更可以直接作為和後端數據的統一交互口,及本地緩存管理的地方。## Grunt 自動化
Grunt(Grunt: The JavaScript Task Runner)出現以後,我是發現 Git 上面基本上前端相關的項目上都多了個 Gruntfile.js,可見他確實好用。
不太喜歡大多數項目中把所有任務都丟在一個文件里的方式。
所以,利用 node.js 的特性,將任務集也分解開來。在 https://github.com/morlay/angular-mobile-ui 這裡可以看到對應的代碼。這裡只說說,如何按照 angular module 的依存關係自動合併對應文件。https://github.com/morlay/angular-mobile-ui/blob/develop/grunt/subTasks/angular-concat-modules.js
首先是模塊的命名,使得它能夠和它的路徑一致性。
第二,除了特殊的,全局公用的模塊外,
其他模塊在各自業務組件中建立引用關係。避免載入多餘的模塊。通過 `grunt angular-concat-modules` 和 `grunt script-uglify`
合併壓縮自動完成。當然,這裡更是直接做了任務,`grunt release` 一條指令搞定一切。
而,對於 angular 模版轉換為 js 有現成的 grunt-angular-templates 可用,
這裡不細說了,詳看代碼。## 測試工具
最後的,關於測試工具,官方有提供 Karma - Spectacular Test Runner for Javascript
但沒用過,也不知道怎麼用,希望有同行給予補充與介紹。其他的,在 API 文檔里寫得挺詳細的。
P.S.
至於知乎,並非完全 Angular 的方式,希望能夠進一步,至少在不需要 SEO 的部分。繼續折騰去了,還有很多東西要弄。1. 面向數據編程
使用angularjs的思路簡單:1)界面元素綁定數據,2)操作處理數據
我用這個思路編寫了一個冒泡演算法的可視化,實現十分簡單,想清楚如何綁定數據後,接下來的工作就只是操作處理數據了。
冒泡排序演算法的可視化相比如果使用jQuery,jQuery的思想是面向界面元素編程,簡單的說如何定位和操作界面(網頁)元素,大量精力放在了界面元素的操作上,而應該是重點的數據處理卻在這個思路體系中佔了很小的位置。
2. 可測試模塊化編程
這個思路有兩點,1)模塊化 2)可測試angularjs雖然可以很傻瓜式的編程,但是更加推薦的還是模塊化編程,而且是可測試模塊化編程。它的教程也一直把如何測試模塊的方式也寫了進去。
angularjs剛出來的時候,最詬病的地方,就是測試困難,也是backbone和其他框架使用者對它的質疑。後來angularjs team花了很大的精力去解釋angularjs如何測試,如何依賴注入,如何提高angularjs的可靠性。後來把官方網站把所有的例子上,都包括了相應的測試代碼。很好的回應了難以測試的質疑。
angularjs,本身有很多模塊,而且推薦以模塊的形式編寫angularjs插件。很大一塊是教人如何寫service,route和Declarative , 這些之間又是相互獨立的模塊,有可以把這些模塊有機的組合在一起。
3. 前端模板引擎
angularjs本身是很好的前端模板引擎,未來發展就是後端的mvc產生json視圖(view)作為前端的模型(model),而整個前端的mvc是後端的視圖(view),中間通信就靠json。這樣前後端高度解耦,可以完全達到模塊化設計的要求。這樣前端只要知道後端產生數據的結構,給了一些數據樣本,就可以直接開發了,而無需等待後端代碼完成。
這個答案是我來說明網站開發的未來趨勢:
請簡單說明 angularJS 有什麼用?-----暫時先寫到這裡,有了更多感悟再寫----程序員技術網站 stackoverflow 有一則精彩問答,地址:javascript - "Thinking in AngularJS" if I have a jQuery background?
How do I 「think in AngularJS」 if I have a jQuery background?
該問題至今已集結了 4538 個贊,其中 Josh David Miller 的答案得票最高,其文洋洋洒洒,循循善誘,娓娓道來,眾望所歸地榮獲 7200 多贊,這篇文章將他的西文翻譯成國語,以饗讀者。
問題摘要
我可以熟練使用jQuery進行客戶端應用的開發,但是現在我希望開始使用Angular.js。哪位能描述一下這個過程中必要的模式變化嗎?希望您的答案能夠圍繞下面這些具體的問題:
我如何對客戶端web應用進行不同方式的架構和設計?它們之間最大的區別是什麼?(譯者註:指jQuery和Angular.js)
有什麼是我不該做或者不該使用的;而又有什麼是我應該做或者應該使用的呢?
有沒有一些服務端的考量/約束呢?
我在尋找的就是一個關於jQuery和Angular.js之間的詳細的比較。
1. 不要先設計頁面,然後再使用DOM操作來改變它的展現
在jQuery中,你通常會設計一個頁面,然後再給它動態效果。這是因為jQuery的設計就是為了擴充DOM並在這個簡單的前提下瘋狂的生長的。
但是在AngularJS里,必須從頭開始就在頭腦中思考架構。必須從你想要完成的功能開始,然後設計應用程序,最後來設計視圖,而非「我有這麼一個DOM片段,我想讓他可以實現XXX效果」。
2. 不要用AngularJS來加強jQuery
類似的,不要以這樣的思維開始:用jQuery來做X,Y和Z,然後只需要把AngularJS的models和controllers加在這上面。這在剛開始的時候顯得非常誘人,這也是為什麼我總是建議AngularJS的新手完全不使用jQuery,至少不要在習慣使用「Angular Way」開發之前這麼做。
我在郵件列表裡看到很多開發者使用150或200行代碼的jQuery插件創造出這些複雜的解決方案,然後使用一堆callback函數以及$apply把它粘合到AngularJS里,看起來複雜難懂;但是他們最終還是把它搞定了!問題是在大多數情況下這些jQuery插件可以使用很少的AngularJS代碼重寫,而且所有的一切都很簡單直接容易理解。
這裡的底線是:
當你選擇解決方案時,首先「think in AngularJS」;如果想不出一個解決方案,去社區求助;如果還是沒有簡單的解決方案,再考慮使用jQuery。但是不要讓jQuery成為你的拐杖,導致你永遠無法真正掌握AngularJS。
3. 總是以架構的角度思考
首先要知道Single-page應用是應用,不是網頁。所以我們除了像一個客戶端開發者般思考外,還需要像一個伺服器端開發者一樣思考。我們必須考慮如何把我們的應用分割成獨立的,可擴展且可測試的組件。
那麼如何做到呢?如何「think in AngularJS」?這裡有一些基本原則,對比jQuery。
視圖是「Official Record」
在jQuery里,我們編程改變視圖。我們會將一個下拉菜單定義為一個ul :
&
在jQuery里,我們會在應用邏輯里這樣啟用這個下拉菜單:
$(".main-menu").dropdownMenu();
當我們只關注視圖,這裡不會立即明顯的體現出任何(業務)功能。對於小型應用,這沒什麼不妥。但是在規模較大的應用中,事情就會變得難以理解且難以維護。
而在AngularJS里,視圖是基於視圖的功能。ul聲明就會像這樣:
&
這兩種方式做了同樣的東西,但是在AngularJS的版本里任何人看到這個模版都可以知道將會發生什麼事。不論何時一個新成員加入開發團隊,他看到這個就會知道有一個叫做dropdownMenu的directive作用在這個標籤上;他不需要靠直覺去猜測代碼的功能或者去看任何代碼。視圖本身告訴我們會發生什麼事。清晰多了。
首次接觸AngularJS的開發者通常會問這樣一個問題:如何找到所有的某類元素然後給它們加上一個directive。但當我們告訴他:別這麼做時,他總會顯得非常的驚愕。而不這麼做的原因是這是一種半jQuery半AngularJS的方式,這麼做不好。這裡的問題在於開發者嘗試在AngularJS的環境里「do jQuery」。這麼做總會有一些問題。視圖是official record。在一個directive外,絕不要改變DOM。所有的directive都應用在試圖上,意圖非常清晰。
記住:
不要設計,然後寫標籤。你需要架構,然後設計。
數據綁定
這是到現在為止最酷的AngularJS特性。這個特性使得前面提到的很多DOM操作都顯得不再需要。AngularJS會自動更新視圖,所以你自己不用這麼做!在jQuery里,我們響應事件然後更新內容,就像這樣:
$.ajax({
url: "/myEndpoint.json",
success: function ( data, status ) {
$("ul#log").append("&
}
});
對應的視圖:
&
除了要考慮多個方面,我們也會遇到前面視圖中的問題。但是更重要的是,需要手動引用並更新一個DOM節點。如果我們想要刪除一個log條目,也需要針對DOM編碼。那麼如何脫離DOM來測試這個邏輯?如果想要改變展現形式怎麼辦?
這有一點凌亂瑣碎。但是在AngularJS里,可以這樣來實現:
$http("/myEndpoint.json").then(function (response) {
$scope.log.push({
msg: "Data Received!"
});
});
視圖看起來是這個樣子的:
&
但是其實還可以這樣來做:
&
&&
現在如果我們想使用Bootstrap的alert boxes,而不是一個無序列表,根本不需要改變任何的controller代碼!更重要的是,不論log在何處或如何被更新,視圖便會隨之更新。自動的。巧妙!
儘管我沒有在這裡展示,數據綁定其實是雙向的。所以這些log信息在視圖裡也可以是可編輯的。只需要這麼做:
&&
& &
&
簡單快樂。
清晰的模型(Model)層
在jQuery里,DOM在一定程度上扮演了模型的角色。但在AngularJS中,我們有一個獨立的模型層可以靈活的管理。完全與視圖獨立。這有助於上述的數據綁定,維護了關注點的分離(獨立的考慮視圖和模型),並且引入了更好的可測性。後面還會提到這點。
關注點分離
上面所有的內容都與這個願景相關:保持你的關注點分離。視圖負責展現將要發生的事情;模型表現數據;有一個service層來實現可復用的任務;在directive裡面進行DOM操作和擴展;使用controller來把上面的東西粘合起來。這在其他的答案里也有敘述,我在這裡只增加關於可測試性的內容,在後面的一個段落里詳述。
依賴注入
依賴注入幫我們實現了關注點分離。如果你來自一個伺服器語言(java或php),可能對這個概念已經非常熟悉,但是如果你是一個來自jQuery的客戶端開發者,這個概念可能看起來有點傻而多餘。但其實不是的。。。
大體來講,DI意味著可以非常自由的聲明組件,然後在另一個組件里,只需要請求一個該組件的實例,就可以得到它。不需要知道(關心)載入順序,或者文件位置,或類似的事情。這種強大可能不會立刻顯現,但是我只提供一個(常見。。)的例子:測試。
就說在你的應用里,我們需要一個服務通過REST API來實現伺服器端存儲,並且根據不同的應用狀態,也有可能使用(客戶端)本地存儲。當我們運行controller的測試時,不希望必須和伺服器交互 —— 畢竟是在測試controller邏輯。我們可以只添加一個與本來使用的service同名的mock service,injector會確保controller自動得到假的那個service —— controller不會也不需要知道有什麼不同。
說起測試……
4. 總是 —— 測試驅動開發
這其實是關於架構的第3節。但是它太重要了,所以我把它單獨拿出來作為一個頂級段落。
在所有那些你見過,用過或寫過的jQuery插件中,有多少是有測試集的?不多,因為jQuery經不起測試的考驗。但是AngularJS可以。
在jQuery中,唯一的測試方式通常是獨立地創建附帶sample/demo頁面的組件,然後我們的測試在這個頁面上做DOM操作。所以我們必須獨立的開發一個組件,然後集成到應用里。多不方便!在使用jQuery開發時,太多的時間,我們挑選迭代而非測試驅動開發。誰又能責怪我們呢?
但是因為有了關注點分離,我們可以在AngularJS中迭代地做測試驅動開發!例如,想要一個超級簡單的directive來展現我們的當前路徑。可以在視圖裡聲明:
&Hello&
OK,現在可以寫一個測試:
it("should add "active" when the route changes", inject(function () {
var elm = $compile("&Hello&")($scope);
$location.path("/not-matching");
expect(elm.hasClass("active")).toBeFalsey();
$location.path("/hello");
expect(elm.hasClass("active")).toBeTruthy();
}));
執行這個測試來確認它是失敗的。然後我們可以開始寫這個directive了:
.directive("whenActive", function ($location) {
return {
scope: true,
link: function (scope, element, attrs) {
scope.$on("$routeChangeSuccess", function () {
if ($location.path() == element.attr("href")) {
element.addClass("active");
} else {
element.removeClass("active");
}
});
}
};
});
測試現在通過了,然後我們的menu按照請求的方式執行。開發過程既是迭代的也是測試驅動的。太酷了。
5. 概念上,Directives並不是打包的jQuery
你經常會聽到「只在directive里做DOM操作」。這是必需的。請給它應有的尊重!
但讓我們再深入一點……
一些directive僅僅裝飾了視圖中已經存在的東西(想想ngClass)並且因此有時候僅僅直接做完DOM操作然後就完事了。但是如果一個directive像一個「widget」並且有一個模版,那麼它也要做到關注點分離。也就是說,模版本身也應該很大程度上與其link和controller實現保持獨立。
AngularJS擁有一整套工具使這個過程非常簡單;有了ngClass我們可以動態地更新class;ngBind使得我們可以做雙向數據綁定。ngShow和ngHide可編程地展示和隱藏一個元素;以及更多地 —— 包括那些我們自己寫的。換句話說,我們可以做到任何DOM操作能實現的特性。DOM操作越少,directive就越容易測試,也越容易給它們添加樣式,在未來也越容易擁抱變化,並且更加的可復用和發布。
我見過很多AngularJS新手,把一堆jQuery扔到directive里。換句話說,他們認為「因為不能在controller里做DOM操作,就把那些代碼弄到directive里好了」。雖然這麼做確實好一些,但是依然是錯誤的。
回想一下我們在第3節里寫的那個logger。即使要把它放在一個directive里,我們依然希望用「Angular Way」來做。它依然沒有任何DOM操作!有很多時候DOM操作是必要的,但其實比你想的要少得多!在應用里的任何地方做DOM操作之前,問問你自己是不是真的需要這麼做。有可能有更好的方式。
這裡有一個示例,展示出了我見過最多的一種模式。我們想做一個可以toggle的按鈕。(注意:這個例子有一點牽強、啰嗦,這是為了表達出使用同樣方式處理問題的更複雜的情況。)
.directive("myDirective", function () {
return {
template: "&Toggle me!&",
link: function (scope, element, attrs) {
var on = false;
$(element).click(function () {
if (on) {
$(element).removeClass("active");
} else {
$(element).addClass("active");
}
on = !on;
});
}
};
});
這裡有一些錯誤的地方。首先,jQeury根本沒必要出現。我們在這裡做的事情都根本用不著jQuery!其次,即使已經將jQuery用在了頁面上,也沒有理由用在這裡。第三,即使假設這個directive依賴jQuery來工作,jqLite(angular.element)在載入後總會使用jQuery!所以我們沒必要使用$ —— 用angular.element就夠了。第四,和第三條緊密關聯,jqLite元素不需要被$封裝 —— 傳到link里的元素本來就會是一個jQuery元素!第五,我們在前面段落中說過,為什麼要把模版的東西混到邏輯里?
這個directive可以(即使是更複雜的情況下!)寫得更簡單:
.directive("myDirective", function () {
return {
scope: true,
template: "&Toggle me!&",
link: function (scope, element, attrs) {
scope.on = false;
scope.toggle = function () {
scope.on = !$scope.on;
};
}
};
});
再一次地,模版就在模版里,當有樣式需求時,你(或你的用戶)可以輕鬆的換掉它,不用去碰邏輯。重用性 —— boom!
當然還有其他的好處,像測試 —— 很簡單!不論模版中有什麼,directive的內部API從來不會被碰到,所以重構也很容易。可以不碰directive就做到任意改變模版。不論你怎麼改,測試總是通過的。
所以如果directive不僅僅是一組類似jQuery的函數,那他們是什麼?Directive實際是HTML的擴展。如果HTML沒有做你需要它做的事情,你就寫一個directive來實現,然後就像使用HTML一樣使用它。
換句話說,如果AngularJS庫沒有做的一些事情,想想開發團隊會如何完成它來配合ngClick,ngClass等。
總結
不要用jQuery.連include也不要。它會讓你停滯不前。如果遇到一個你認為已經知道如何使用jQuery來解決的問題,在使用$之前,試試想想如何在AngularJS的限制下解決它。如果你不知道,問!20次中的19次,最好的方式不需要jQuery。如果嘗試使用jQuery會增加你的工作量。
最後,放上本屌的微信公眾號二維碼,【我為朝露誰苦多】微信號:【hxzqlh】。
http://weixin.qq.com/r/3kz378TEK5lUrYCM9xnM (二維碼自動識別)
怎麼解決搜索引擎的問題?
在接收到請求後,判斷如果是來自搜索引擎的抓蟲,就展示該 URL 應該顯示的內容。
而如果是用戶請求,那麼這邊應該輸出指定的 Web MVC 框架的入口,框架再通過 URL 判斷需要載入的內容通過 API 向後端請求,得到數據後,渲染。簡單講:
抓蟲:輸出內容完整的 HTML
用戶:輸出沒有內容的 HTML,然後 JS 自己再通過 API 請求內容渲染。貼一下 jsgen 處理抓蟲的代碼:var robotReg = new RegExp("Baiduspider|Googlebot|BingBot|Slurp!", "i");
if (robotReg.test(req.useragent)) {
//是搜索引擎爬蟲,輸出靜態頁面
}
關於SEO友好的,可以看看這個 Google On pushState Versus Hashbang (#!)
怎麼解決搜索引擎的問題?Making AJAX Applications Crawlable
國內的確用的不多,但我們是一個,規模算大,用的人100左右,代碼100w行以上,以後還會增加。十多個系統,金融類核心系統。
經驗個人積累一點,可能和做互聯網應用尤其是spa不太一樣。
1. 業務類系統,趕快用吧,代碼量少一半一般,越複雜優勢越明顯2. 性能很多坑,網上的方案不夠用的,ie8下很多不能直接the angular way,延遲載入是王道,多次bootstrap也常有,直接改源碼常有
3. ui控制項太少,完整套的要團隊自己開發維護,個性化需求更需要從最底層定製
4. 官方的檢驗機制要吃透,複雜檢驗需求變化很多,要設計足夠靈活,用watch $error方式又有性能坑
5. 指令是個好東西,重客戶端組件化的方向,要理解透,用好
6. 模型設計上,服務端的貧血,充血之分這裡可以借鑒。另外模型分類命名空間很重要
如果你的頁面不複雜,就沒那麼多講究了
最後,seo的,建議html兩套,angular有類似工具推薦個angularjs的debug工具:https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk
看看這本關於AngularJS的書:http://www.topitbooks.com/pro-angularjs-experts-voice-web-development-1492.html
操作dom最好用directive+jqlite,最好用ng寫好的指令,不要用原生……還有最好放棄jquery的邏輯思維
用Yeoman(generator-angular)可以快速搭建angular工程,用shell快速添加directive、view、service等。
推薦閱讀:
※求助,用angularJs實現下圖功能?
※有了 Angular 之類的 MVC/MVVM 框架是不是可以不學 DOM 了?或者只需簡單了解一下 DOM?
※Angular js 初學者該看什麼書啊? ?
※如何評價 Angular 2 發布 Beta 版本?
TAG:AngularJS |