摘要:一個高度可復用的組件則可以被稱為控件,是可以多帶帶投稿項目庫的。行為的定制化通過參數綁定實現組件行為的定制化。組件被銷毀時調用。當有組件復用的情況時請使用標識指定接收對象模型另外最好給事件名添加組件前綴。
轉自自己在開源中國的博客:https://my.oschina.net/u/7247...
angular 1 也要面向組件編程前端組件化是前端開發模式中一個不可逆轉的趨勢,三大主要前端框架 angular 2 react vue 都不約而同的把組件化編程作為自己的一大賣點,angular 1 作為一個歷史相對悠久的框架,在私生子 angular 2 的推動下,終于也搭上了組件化編程的末班車,公司里那些老項目終于也有機會體驗組件化編程的滋味。
angular 1 的組件化之路angular 1 中類似組件化的編程思想其實很早就有,只不過那時候不叫組件,而叫指令(Directive),指定 restrict: "E" 后這個指令就與如今組件的用法很相似了。angular 1.5 中,又將指令根據 angular 2 的類似概念加以限制,脫胎為如今的組件(Components)。
組件的特點官方文檔列舉了組件和指令的不同點。除此之外,一個規范的組件還應符合以下幾個特點。
組件的標簽名稱必須包含中劃線
組件擁有良好的生命周期
組件有自包含性
組件有自封閉性
組件有可復用性
組件可以被定制化
下面依次說明。
組件的名稱規范與指令不同,組件必須是一個元素,HTML 對于這一點有特殊的規范。
HTML 規范把帶有中劃線的標簽留給開發者使用,這樣形成的元素又稱作自定義元素(Custom Element)。我們雖然沒有用到自定義元素的概念,但兩者的行為是相似的。我們應該符合這一標準。
這一點規范對應到 angular 1 中即為:組件名稱必須帶有駝峰形式。
例如:
module.component("dialog", { // ... });
這是不對的。HTML 規范已經定義了 dialog 這個標準元素,重復使用標簽名可能導致我們自定義的組件行為和標準元素的行為混雜到一起,導致奇葩 bug;而且如果這樣做也間接導致開發者不能使用原生的 dialog 標簽。
另外,就算現在標準沒有定義某個元素,不代表將來不會定義。我們的程序既然跑在瀏覽器里,就要按規矩辦事。這是一種合法的寫法:
module.component("customDialog", { // ... });組件的自包含性
一個設計良好的組件一定有它自己的行為和默認樣式。
默認行為默認行為在 angular 1 中用控制器(Controller)定義。
function CustomDialogController($service) { this.someField = 123; this.someMethod = function someMethod() { } } CustomDialogController.$inject = ["$service"]; module.component("customDialog", { controller: CustomDialogController, template: require("./customDialogTemplate.html"), });
因為組件默認啟用 controllerAs,所有變量和函數都是綁定到 this 上的,所以你也可以使用 ES2015 的 class 語法來組織代碼:
class CustomDialogController { constructor($service) { } someMethod() { } } CustomDialogController.$inject = ["$service"]; module.component("customDialog", { controller: CustomDialogController, template: require("./customDialogTemplate.html"), });
這樣做有一個問題就是其他函數不能使用 constructor 里注入的服務(Service),只能通過 this 中轉一次。我個人的做法是這樣:
class CustomDialogController { constructor($service) { this.services = { $service }; } someMethod() { const { $service } = this.services; } } // 下略
建議對于邏輯相對簡單的組件的控制器使用 function 定義,復雜的組件使用 class 定義,后者代碼的層次要更為清晰易讀。
默認樣式組件的默認樣式直接使用樣式表指定。
custom-dialog { display: block; // ... }
對于所有瀏覽器不認識的標簽,默認都是內聯元素(display: inline),對于組件來說通常不是想要的。所以自定義的組件通常至少要有 display: (inline-)block 來改變元素的默認顯示方式。
組件的自封閉性自封閉性包含兩個方面:數據的自封閉性和樣式的自封閉性。
數據的自封閉性angular 1 中,組件自身的 scope 已經是隔離的(isolate),即組件的 scope 不繼承自父級 scope(__proto__ 為 null)。除此之外,一個規范的組件不應該直接使用外部的數據,因為這樣會破壞組件的可復用性。舉幾個例子:
$rootScope
$root、$parent(模板中)
路由參數
localStorage、sessionStorage
這些數據都應該通過參數綁定 binding 傳入。如果組件是路由插件生成,那么可以用 resolve。
其次,參數綁定不應使用雙向綁定 =,規范的組件不應(直接)修改組件外部傳入的數據。官方推薦的參數綁定方式有兩種
< 單向綁定,綁定可變數據。通常用于給組件傳遞數據
@ 字符串綁定,綁定字符串。通常用于指定組件行為
對于單向綁定對象的情況,由于是引用傳遞,也不應該修改對象內部的屬性。
遇到要向外部傳值的情況,推薦使用 ngModel 或 事件綁定(下面會提到)
樣式的自封閉性組件間的樣式不應該互相干擾,這一點可以簡單的通過 scss 的樣式嵌套(Nesting)實現:
custom-dialog { display: block; // ... .title { // ... } .body { // ... } }
這樣可以簡單的把組件的內置樣式表限制在組件內部,從而避免樣式外溢。但是這種方法對在組件內部的其他組件不起效果。如果這個組件的模板中還引用了別的組件,或者這個組件被定義為可嵌入的(transclude),那么可以考慮加類名前綴:
custom-dialog { display: block; .custom-dialog { &-title { // .. } &-body { } } }組件的可復用性
組件為復用而生,擁有良好自封閉性的組件必然是可復用的,因為這個組件不受任何外部因素干擾。組件的復用形式包括
一個頁面中使用多次
在多個頁面中使用
ng-repeat
自己套自己(遞歸樹結構)
整個源代碼拷貝到其他項目中
等等。一個高度可復用的組件則可以被稱為控件,是可以多帶帶投稿 npm 項目庫的。
當然,有些組件(比如多帶帶的頁面)可能復用需求沒那么高,可以視組件的復用程度不同,從組件的自封閉性和整體代碼量做一些取舍。
組件的定制化一個高度可復用的組件一定可以被定制。
行為的定制化通過參數綁定實現組件行為的定制化。例如:
module.component("customDialog", { template: require("./customDialogTemplate.html"), transclude: true, bindings: { title: "@", modal: "<", }, });
出于使用方便的考慮,定制用的參數都是可選的,組件內部實現應該給每個定制參數設定默認值。
樣式的定制化組件風格定制可以使用 class 判斷。
custom-dialog { display: block; // ... .title { font-size: 16px; // ... } &.big { .title { font-size: 24px; } } }
使用時
深度定制樣式比較好的方式是 CSS 屬性(CSS Variable,注意不是 SCSS 屬性)。
custom-dialog { display: block; // ... .title { font-size: 16px; color: var(--dialog-title-color, #333); // ... } &.big { .title { font-size: 24px; } } }
這時只需要文檔中說明標題顏色使用 --dialog-title-color 這個 CSS 變量就好,外部使用不依賴于組件內部 DOM 實現。使用時
.mydialog { --dialog-title-color: red; }組件的生命周期
從創建至銷毀,組件有自己的生命周期(lifecycle),而不像指令那樣把 scope 作為生命周期。常用的回調函數如下:
$onInit():組件被初始化時調用。與 constructor 不同,angular 1 確保 $onInit 被調用時組件的所有參數綁定都被正確賦值。
$onChanges(changeObj):組件參數綁定值被改變時調用。用于監聽綁定值的變化,初次綁定時也會調用這個函數。
$onDestroy():組件被銷毀時調用。用于清理內部資源如 $interval 等。
這些函數也是綁定在 this 上的。如果 controller 使用 ES2015 的 class 定義方式,可以這么寫:
class CustomDialogController { constructor() {} onInit() {} onChanges({ prop1, prop2 }) {} onDestroy() {} }組件間的通信
組件間通信是一個讓很多人頭疼的問題,通常有這樣 3 種情況
子 -> 父這種情況有標準的實現方式:事件綁定。例如
class CustomDialogController { close($value) { this.hide = true; this.onClose({ $value }); } } module.component("customDialog", { controller: CustomDialogController, template: require("./customDialogTemplate.html"), bindings: { onClose: "&", }, });
使用時:
這種方式也可以用于子組件向父組件傳值。
父 -> 子用于觸發子組件的某個動作。除了改變某個在子組件內部監聽變化的綁定參數值外,行之有效的方式就只有事件廣播。
子組件先監聽某個事件
$scope.$on("custom-dialog--close", () => this.close());
父組件發送廣播
$scope.$broadcast("custom-dialog--close");
切記:事件是全局性的。當有組件復用的情況時請使用標識指定接收對象(BUS 模型);另外最好給事件名添加組件前綴。
同級組件請通過父級組件中轉
子 -> 某全局性組件這個顯示 Notification 時最常用。遇到這種情況時,可以封裝服務(Service)。例如:
module.component("globalNotification", { controller: class GlobalNotificationController { constructor(notificationService) { notificationService.component = this; } show(props) { // ... } } }); module.factory("notify", function NotifyService() { return { warn(msg) { this.show({ type: "warn", text: msg }); } error(msg) { this.show({ type: "error", text: msg }); } } });
方案并不完美。如果有更好的建議歡迎提出。
結語有人可能問既然三大前端框架都是組件化的,何必還要在 angular 1 上實現。殊不知 angular 1 的組件誕生的初衷就是為了減少向 angular 2 遷移的難度。機會總是留給有準備的人,哪天老板大發慈悲表示給你把代碼重寫的時間,你卻看著項目里滿屏的 $scope.abc = xxx 不知所措,這豈不是悲劇。。。
完文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94954.html
摘要:目前已經在大大小小多個線上產品中使用了,也收集了一些有效的建議好了,該看下一個最簡單的組件長什么樣吧免費領取驗證碼內容安全短信發送直播點播體驗包及云服務器等套餐更多網易技術產品運營經驗分享請訪問網易云社區。文章來源網易云社區 本文由作者鄭海波授權網易云社區發布。 此文摘自regularjs的指南, 目前指南正在全面更新, 把老文檔的【接口/語法部分】統一放到了獨立的 Reference...
摘要:在該版本發布之后,開發團隊并不會繼續發布新的特性,而會著眼于進行重大的錯誤修復。發布每六個星期,團隊就會創建新的分支作為發布通道,本文即是對新近發布的版本進行簡要介紹。 showImg(https://segmentfault.com/img/remote/1460000013229009); 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱...
摘要:每個框架類庫被大量用戶大規模使用都說明其戳中了開發者的剛需。但是未執行完的情況下發生人機交互雖然不會報腳本錯誤,但是嚴重影響用戶體驗開發者們被各種爽到之后,這個問題已經被拋到了九霄云外。 寫在前面 因為zepto、jQuery2.x.x和Nuclear都是為現代瀏覽器而出現,不兼容IE8,適合現代瀏覽器的web開發或者移動web/hybrid開發。每個框架類庫被大量用戶大規模使用都說明...
閱讀 3016·2021-10-08 10:18
閱讀 732·2019-08-30 15:54
閱讀 1067·2019-08-29 18:43
閱讀 2441·2019-08-29 15:33
閱讀 1305·2019-08-29 15:29
閱讀 1604·2019-08-29 13:29
閱讀 1026·2019-08-26 13:46
閱讀 1701·2019-08-26 11:55