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