關鍵要點
$onInit
和$onDestroy
等生命週期事件有效管理組件的設置和拆卸,確保資源得到適當的初始化和清理。 $scope
進行交互,這符合Angular 2的實踐,並提高了組件的模塊化和可重用性。 本文由Mark Brown和Jurgen Van de Moere共同評審。感謝所有SitePoint的同行評審員,使SitePoint的內容達到最佳狀態!
2017年1月10日:文章更新,澄清了關於單向綁定的部分,並添加了關於單次綁定的信息。 ---
在Angular 1中,組件是創建自定義HTML元素的機制。過去可以使用Angular指令實現這一點,但組件建立在對Angular的各種改進之上,並強制執行構建和設計方面的最佳實踐。
在本文中,我們將深入探討組件的設計以及如何在應用程序中使用它們。如果您尚未開始在Angular 1中使用組件,您可以閱讀我們最近的教程中關於它們的語法和設計的介紹。我的目標是概述一些最佳實踐,這些實踐將提高應用程序的質量。
還應該注意的是,通過新的組件API,Angular 2的許多最佳實踐都被引入到Angular 1中,使您可以構建更容易以後重構的應用程序。 Angular 2影響了我們思考和設計Angular 1組件的方式,但仍然存在許多明顯的差異。 Angular 1仍然是構建應用程序的非常強大的工具,因此我相信即使您不打算或尚未準備好遷移到Angular 2,投資使用組件改進您的應用程序也是值得的。
組件的設計應考慮許多關鍵特性,以使其成為應用程序的強大構建塊。我們將更詳細地探討每一個特性,但以下是組件應遵循的主要概念。
現在讓我們首先了解為什麼以及如何將組件與應用程序的其餘部分隔離和封裝。
Angular 1功能的演變是為了啟用隔離和封裝的組件,這是有充分理由的。一些早期的應用程序與$scope
和嵌套控制器的使用高度耦合。最初Angular沒有提供解決方案,但現在有了。
好的組件不會暴露其內部邏輯。由於它們的設計方式,這很容易實現。但是,除非絕對必要(例如發出/廣播事件),否則應避免濫用組件使用$scope
。
組件應承擔單一角色。這對於可測試性、可重用性和簡單性非常重要。最好創建額外的組件,而不是超載單個組件。這並不意味著您不會擁有更大或更複雜的組件,它只是意味著每個組件都應專注於其主要工作。
我根據組件在應用程序中的作用將其分為四個主要組,以幫助您考慮如何設計組件。構建這些不同類型的組件沒有不同的語法——只需要考慮組件所承擔的特定角色即可。
這些類型基於我5年以上的使用Angular的經驗。您可以選擇略微不同的組織方式,但根本概念是確保您的組件具有明確的角色。
只有一個應用程序組件可以充當應用程序的根。您可以將其視為在Web應用程序的主體中只有一個組件,所有其他邏輯都通過它加載。
<code>> <app>></app>> > </code>
這主要推薦用於Angular 2設計一致性,因此如果您希望遷移,將來會更容易。它還通過將應用程序的所有根內容移動到單個組件中(而不是將其中一些內容放在index.html文件中)來幫助測試。應用程序組件還為您提供了一個進行應用程序實例化的地方,因此您不必在應用程序運行方法中執行此操作,從而增強了可測試性並減少了對$rootScope
的依賴。
此組件應盡可能簡單。如果可能,它可能只包含模板,而不包含任何綁定或控制器。但是,它不會替換ng-app
或引導應用程序的需要。
過去,我們在ui-router狀態(或ngRoute路由)中鏈接控制器和模板。現在可以將路由直接鏈接到組件,因此組件仍然是控制器和模板配對的地方,但也有可路由的優點。
例如,使用ui-router,這就是我們鏈接模板和控制器的方式。
<code>> <app>></app>> > </code>
現在,您可以將URL直接鏈接到組件。
<code>$stateProvider.state('mystate', { url: '/', templateUrl: 'views/mystate.html', controller: MyStateController }); </code>
這些組件可以從路由參數(例如項目ID)綁定數據,它們的作用是專注於設置路由以加載所需的其它組件。這種對路由定義的看似微小的更改實際上對於Angular 2遷移能力非常重要,但在Angular 1.5中也同樣重要,因為它可以在組件級別更好地封裝模板和控制器。
Angular 1實際上有兩個路由模塊,ngRoute和ngComponentRouter。只有ngComponentRouter支持組件,但它也已棄用。我認為最好的辦法是使用ui-router。
您為應用程序構建的大多數唯一組件都是有狀態的。在這裡,您實際上將放置應用程序業務邏輯、發出HTTP請求、處理表單和其他有狀態任務。這些組件可能對您的應用程序來說是獨一無二的,它們專注於維護數據而不是視覺呈現。
假設您有一個控制器加載用戶配置文件數據以顯示,並且還有一個相應的模板(此處未顯示)在指令中鏈接在一起。此代碼段可能是完成這項工作的最基本的控制器。
<code>$stateProvider.state('mystate', { url: '/', component: 'mystate' }); </code>
使用組件,您可以比以前更好地設計它。理想情況下,您還應該使用服務而不是直接在控制器中使用$http
。
<code>.controller('ProfileCtrl', function ($scope, $http) { $http.get('/api/profile').then(function (data) { $scope.profile = data; }); }) .directive('profile', function() { return { templateUrl: 'views/profile.html', controller: 'ProfileCtrl' } }) </code>
現在您有一個加載自身數據的組件,因此使其成為有狀態的。這些類型的組件類似於路由組件,只是它們可能不用於鏈接到單個路由。
有狀態組件將使用其他(無狀態)組件來實際呈現UI。此外,您仍然希望使用服務,而不是將數據訪問邏輯直接放在控制器中。
無狀態組件專注於渲染而不管理業務邏輯,並且不必對任何特定應用程序都是唯一的。例如,大多數用於UI元素(例如表單控件、卡片等)的組件也不會處理加載數據或保存表單之類的邏輯。它們旨在高度模塊化、可重用和隔離。
如果無狀態組件只顯示數據或控制模板中的所有內容,則可能不需要控制器。它們將接受來自有狀態組件的輸入。此示例從有狀態組件(上面的profile示例)獲取值並顯示頭像。
<code>.component('profile', { templateUrl: 'views/profile.html', controller: function($http) { var vm = this; // 当组件准备好时调用,见下文 vm.$onInit = function() { $http.get('/api/profile').then(function (data) { vm.profile = data; }); }; } }) </code>
要使用它,有狀態組件將通過屬性傳遞用戶名,如下所示:<avatar username="vm.profile.username">.</avatar>
您使用的許多庫都是無狀態組件(和可能的服務)的集合。它們當然可以接受配置以修改其行為,但它們並非旨在負責其自身之外的邏輯。
這並不是組件的新功能,但在組件中使用它通常很明智。單向綁定的目的是避免將更多工作加載到摘要周期中,這是應用程序性能的主要因素。數據現在流入組件而無需查看其外部(這會導致當今存在的一些耦合問題),並且組件可以簡單地根據該輸入呈現自身。這種設計也適用於Angular 2,這有助於未來的遷移。
在此示例中,title屬性僅根據提供的初始值綁定到組件一次。如果title由某個外部參與者更改,則它不會反映在組件中。表示綁定為單向的語法是使用
<code>> <app>></app>> > </code>
當title屬性更改時,組件仍將更新,我們將介紹如何偵聽title屬性的更改。建議您盡可能使用單向綁定。
Angular還能夠單次綁定數據,因此您可以優化摘要周期。本質上,Angular將等待提供第一個非未定義的值到綁定中,綁定該值,然後(一旦所有綁定都已解析)從摘要周期中刪除相關的觀察者。這意味著特定綁定不會向未來的摘要循環添加任何處理時間。
這是通過在綁定表達式前面加上::
來完成的。如果您知道輸入綁定在生命週期中不會更改,則只有這樣做才有意義。在此示例中,如果title是單向綁定,它將繼續在組件內部更新,但此處的綁定不會更新,因為我們將其指定為單次綁定。
<code>$stateProvider.state('mystate', { url: '/', templateUrl: 'views/mystate.html', controller: MyStateController }); </code>
您可能已經註意到$onInit
函數作為一項新功能。組件具有生命週期和相應的事件,您應該使用這些事件來幫助管理組件的某些方面。
$onInit()
組件生命週期的第一步是初始化。此事件在控制器和綁定初始化後運行。您幾乎總是應該使用此方法進行組件設置或初始化。它將確保在運行之前所有值都可用於組件。如果您直接在控制器中訪問綁定值,則不能保證這些值可用。
<code>$stateProvider.state('mystate', { url: '/', component: 'mystate' }); </code>
$postLink()
下一步是鏈接模板中的任何子元素。當組件初始化時,不能保證它也已渲染在模板中使用的任何子元素。如果您需要以任何方式操作DOM,這一點很重要。一個重要的警告是,異步加載的模板在該事件觸發時可能尚未加載。您可以始終使用模板緩存解決方案來確保模板始終可用。
<code>> <app>></app>> > </code>
$onChanges()
當組件處於活動狀態時,它可能需要對輸入值的更改做出反應。單向綁定仍將更新您的組件,但我們有一個新的$onChanges
事件綁定來偵聽輸入何時更改。
對於此示例,假設向組件提供了產品標題和說明。您可以檢測到如下所示的更改。您可以查看傳遞給函數的對象,該對象具有映射到可用綁定的對象,其中包含當前值和先前值。
<code>$stateProvider.state('mystate', { url: '/', templateUrl: 'views/mystate.html', controller: MyStateController }); </code>
$onDestroy()
最後階段是從頁面中移除組件。此事件在控制器及其作用域銷毀之前運行。重要的是清理組件可能創建的或持有內存的任何內容,例如事件偵聽器、觀察者或其他DOM元素。
<code>$stateProvider.state('mystate', { url: '/', component: 'mystate' }); </code>
要使用一組數據配置和初始化組件,組件應使用綁定來接受這些值。這有時被認為是組件API,這只是描述組件接受輸入的方式的不同方法。
這裡的挑戰是為綁定提供簡潔但清晰的名稱。有時開發人員試圖縮短名稱以使其非常簡潔,但這對於組件的使用來說是危險的。假設我們有一個接受股票代碼作為輸入的組件,以下哪一個更好?
<code>.controller('ProfileCtrl', function ($scope, $http) { $http.get('/api/profile').then(function (data) { $scope.profile = data; }); }) .directive('profile', function() { return { templateUrl: 'views/profile.html', controller: 'ProfileCtrl' } }) </code>
希望您認為symbol
更好。有時開發人員也喜歡作為避免名稱衝突的一種方式來為組件和綁定添加前綴。為組件添加前綴是明智的,例如md-toolbar
是Material工具欄,但為所有綁定添加前綴會變得冗長,應避免。
為了與其他組件通信,組件應發出自定義事件。有很多使用服務和雙向數據綁定的示例來同步組件之間的數
據,但事件是更好的設計選擇。事件作為與頁面通信的一種方式效率要高得多(並且是JavaScript語言以及它在Angular 2中的工作方式的基礎部分,這並非巧合)。
Angular中的事件可以使用$emit
(向上到作用域樹)或$broadcast
(向下到作用域樹)。這是一個快速示例事件的實際應用。
<code>.component('profile', { templateUrl: 'views/profile.html', controller: function($http) { var vm = this; // 当组件准备好时调用,见下文 vm.$onInit = function() { $http.get('/api/profile').then(function (data) { vm.profile = data; }); }; } }) </code>
您需要在組件之間進行通信的主要有兩種情況:在您了解的組件之間以及您不了解的組件之間。為了說明這種區別,讓我們假設我們有一組組件來幫助管理頁面上的選項卡,以及一個具有相應幫助頁面鏈接的工具欄。
<code>.component('avatar', { template: '<img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173975605585460.png" class="lazy" ng- alt="建造優質角1.5組件的指南" >', bindings: { username: ' }, controllerAs: 'vm' }) </code>
在這種情況下,my-tabs
和my-tab
組件可能彼此了解,因為它們協同工作以創建一組三個不同的選項卡。但是,my-toolbar
組件超出了它們的認知範圍。
每當選擇不同的選項卡(這將是my-tab
組件實例上的一個事件)時,my-tabs
組件都需要知道,以便它可以調整選項卡的顯示以顯示該實例。 my-tab
組件可以向上發出事件到父my-tabs
組件。這種類型的通信就像兩個協同工作以創建單個功能(選項卡式界面)的組件之間的內部通信。
但是,如果my-toolbar
想要知道當前選擇了哪個選項卡,以便它可以根據可見內容更改幫助按鈕呢? my-tab
事件永遠不會到達my-toolbar
,因為它不是父級。因此,另一個選擇是使用$rootScope
向下發出整個組件樹的事件,這允許任何組件偵聽和React。這裡潛在的缺點是您的事件現在到達每個控制器,如果另一個組件使用相同的事件名稱,您可能會觸發意外的影響。
確定哪種方法適合您的用例,但是每當另一個組件可能需要了解事件時,您可能都希望使用第二個選項向整個組件樹發出事件。
現在可以使用組件編寫Angular 1應用程序,這改變了我們編寫應用程序的最佳實踐和性質。這是為了更好,但僅僅使用組件並不一定比您以前使用的更好。在構建Angular 1組件時,請記住以下要點。
$onChanges
生命週期事件來觀察更改。 您是否在Angular 1.x應用程序中使用組件?或者,您是否要等到改用Angular 2?我很樂意在下面的評論中聽到您的經驗。
Angular 1.5組件是用於創建指令的更簡單、更直觀的API。雖然指令功能強大,但由於其靈活性,它們可能難以使用。另一方面,組件具有更簡單的配置,並且旨在用於構建UI元素。它們還促進了單向數據綁定和生命週期掛鉤的使用,這可以導致更可預測的數據流和更輕鬆的調試。
可以在Angular 1.5組件中使用bindings
屬性實現單向數據綁定。
生命週期掛鉤是在組件生命週期的特定點調用的函數。 Angular 1.5引入了幾個生命週期掛鉤,例如$onInit
、$onChanges
、$onDestroy
和$postLink
。這些掛鉤可用於執行諸如初始化數據、清理資源或對綁定更改做出React之類的任務。
可以在Angular 1.5中使用綁定和事件實現組件之間的通信。父到子的通信可以使用綁定來完成,而子到父的通信可以使用事件來完成。這促進了單向數據流,這可以使您的應用程序更容易理解。
從Angular 1.5中的指令遷移到組件涉及幾個步驟。首先,用組件定義替換指令定義對象。然後,用生命週期掛鉤替換鏈接函數。最後,用單向數據綁定和事件替換雙向數據綁定。
Angular 1.5中的組件比指令提供了幾個好處。它們具有更簡單的API,促進了單向數據綁定和單向數據流,並提供了生命週期掛鉤。這些功能可以使您的代碼更易於理解、調試和維護。
可以在Angular 1.5組件中使用組件定義中的transclude
選項實現轉錄。這允許您在組件的模板中插入自定義內容,這對於創建可重用的UI元素非常有用。
可以在Angular 1.5組件中使用具有對象語法的transclude
選項實現多插槽轉錄。這允許您在組件的模板中定義多個轉錄插槽,這些插槽可以用自定義內容填充。
$onChanges
生命週期掛鉤? 每當更新單向綁定時,都會調用Angular 1.5組件中的$onChanges
生命週期掛鉤。它接收一個更改對象,其中包含綁定的當前值和先前值。這可用於對綁定更改做出React,並執行諸如更新組件狀態或獲取數據之類的任務。
$postLink
生命週期掛鉤? 在組件的元素及其子元素鏈接後,會調用Angular 1.5組件中的$postLink
生命週期掛鉤。這可用於執行需要訪問組件的DOM元素的任務,例如設置事件偵聽器或操作DOM。
以上是建造優質角1.5組件的指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!