摘要:他們即不是指令,也不應該使用組件代替指令,除非你正在用控制器升級模板指令,組件還包含數(shù)據(jù)事件的輸入與輸出,生命周期鉤子和使用單向數(shù)據(jù)流以及從父組件上獲取數(shù)據(jù)的事件對象。
關鍵詞 架構, 文件結構, 組件, 單向數(shù)據(jù)流以及最佳實踐
來自 @toddmotto 團隊的編碼指南
Angular 的編碼風格以及架構已經使用ES2015進行重寫,這些在Angular 1.5+的變化可以更好幫助您的更好的升級到Angular2.。
這份指南包括了新的單向數(shù)據(jù)流,事件委托,組件架構和組件路由。
老版本的指南你可以在這里找到, 在這里你能看到最新的.
模塊架構Angular 中的每一個模塊都是一個模塊組件。一個模塊組件囊括了邏輯,模版,路由和子組件。
Module 基本概念在模塊的設計直接反映到我們的文件夾結構,從而保證我們項目的可維護性和可預測性。
我們最好應該有三個高層次的模塊:根,組件和常用模塊。根模塊定義了用于啟動App和相應的模板的基本架子。
然后,我們導入需要依賴的組件和通用模塊。組件和通用模塊然后需要低級別的組件模塊,其中包含我們的組件,控制器,服務,指令,過濾器和給可重復使用的功能進行測試。
根模塊會啟動一個根組件,整個組件主要定義了整個應用的基本的元素和路由出口,例如使用ui-view和ui-router。
// app.component.js const AppComponent = { template: `Hello world ` }; export default AppComponent;
我們導入AppComponent并且使用.component("app",AppComponent)完成注冊即表示一個根模塊創(chuàng)建完成。
更進一步我們會導入一些子模塊(組件和通用模塊)用于引入相關的組件。
// app.js import angular from "angular"; import uiRouter from "angular-ui-router"; import AppComponent from "./app.component"; import Components from "./components/components"; import Common from "./common/common"; const root = angular .module("app", [ Components, Common, uiRouter ]) .component("app", AppComponent); export default root;組件模塊
一個組件模塊就是引用所有課重復使用的組件容器。上面我們可以了解我們如何導入組件和將它們注入到根模塊,
這樣我么可以有一個地方導入所有應用程序需要的組件。
我們要求這些模塊從所有其它模塊分離出來,這樣這些模塊可以應用到其它的應用程序中。
import angular from "angular"; import Calendar from "./calendar"; import Events from "./events"; const components = angular .module("app.components", [ Calendar, Events ]) .name; export default components;公共模塊
公共模塊為所有的應用提供一些特殊組件的引用,我們不希望它能夠在另一個應用程序中使用。比如布局,導航和頁腳。
前面我們已經知道如何導入Common并將其注入到根模塊,而這里就是我們導入所有通用組件的地方。
import angular from "angular"; import Nav from "./nav"; import Footer from "./footer"; const common = angular .module("app.common", [ Nav, Footer ]) .name; export default common;低級別的模塊
低層次的模塊是一些獨立的組件,它們包含邏輯和功能。這些將分別定義成模塊,被引入到較高層次模塊中,
比如一個組件或通用模塊。一定要記住每次創(chuàng)建一個新的模塊時(并非引用),記得在export中添加后綴。你會注意到路由定義也是在這里,我們將在隨后的部分講到它。
import angular from "angular"; import uiRouter from "angular-ui-router"; import CalendarComponent from "./calendar.component"; const calendar = angular .module("calendar", [ uiRouter ]) .component("calendar", CalendarComponent) .config(($stateProvider, $urlRouterProvider) => { $stateProvider .state("calendar", { url: "/calendar", component: "calendar" }); $urlRouterProvider.otherwise("/"); }) .name; export default calendar;文件命名規(guī)范
使用小寫并保持命名的簡介, 比如使用組件名稱時, e.g. calendar.*.js*, calendar-grid.*.js - 將名稱放到中間. 使用 index.js 作為模塊的定義文件 ,這樣你就可以直接通過目錄引入了。
index.js calendar.controller.js calendar.component.js calendar.service.js calendar.directive.js calendar.filter.js calendar.spec.js
返回目錄
易于擴展的文件結構文件目錄結構實際上十分重要,它有利于我們更好的擴展和預測。下面的例子展示了模塊組件的基本架構。
├── app/ │ ├── components/ │ │ ├── calendar/ │ │ │ ├── index.js │ │ │ ├── calendar.controller.js │ │ │ ├── calendar.component.js │ │ │ ├── calendar.service.js │ │ │ ├── calendar.spec.js │ │ │ └── calendar-grid/ │ │ │ ├── index.js │ │ │ ├── calendar-grid.controller.js │ │ │ ├── calendar-grid.component.js │ │ │ ├── calendar-grid.directive.js │ │ │ ├── calendar-grid.filter.js │ │ │ └── calendar-grid.spec.js │ │ └── events/ │ │ ├── index.js │ │ ├── events.controller.js │ │ ├── events.component.js │ │ ├── events.directive.js │ │ ├── events.service.js │ │ ├── events.spec.js │ │ └── events-signup/ │ │ ├── index.js │ │ ├── events-signup.controller.js │ │ ├── events-signup.component.js │ │ ├── events-signup.service.js │ │ └── events-signup.spec.js │ ├── common/ │ │ ├── nav/ │ │ │ ├── index.js │ │ │ ├── nav.controller.js │ │ │ ├── nav.component.js │ │ │ ├── nav.service.js │ │ │ └── nav.spec.js │ │ └── footer/ │ │ ├── index.js │ │ ├── footer.controller.js │ │ ├── footer.component.js │ │ ├── footer.service.js │ │ └── footer.spec.js │ ├── app.js │ └── app.component.js └── index.html
頂級目錄 僅僅包含了 index.html 以及 app/, 而在app/目錄中則包含了我們要用到的組件,公共模塊,以及低級別的模塊。
組件 組件的基本概念組件實際上就是帶有控制器的模板。他們即不是指令,也不應該使用組件代替指令,除非你正在用控制器升級“模板指令”,
組件還包含數(shù)據(jù)事件的輸入與輸出,生命周期鉤子和使用單向數(shù)據(jù)流以及從父組件上獲取數(shù)據(jù)的事件對象。
從父組件獲取數(shù)據(jù)備份。這些都是在Angular 1.5及以上推出的新標準。
我們創(chuàng)建的一切模板,控制器都可能是一個組件,它們可能是是有狀態(tài)的,無狀態(tài)或路由組件。
你可以把一個“部件”作為一個完整的一段代碼,而不僅僅是.component()定義的對象。
讓我們來探討一些組件最佳實踐和建議,然后你應該可以明白如何組織他們。
下面是一些.component()你可能會使用到的屬性 :
Property | Support |
---|---|
bindings | Yes, 僅僅使用 "@", "<", "&" |
controller | Yes |
controllerAs | Yes, 默認 $ctrl |
require | Yes (new Object syntax) |
template | Yes |
templateUrl | Yes |
transclude | Yes |
控制器應該只與組件一起使用。如果你覺得你需要一個控制器,你真正需要的可能是一個無狀態(tài)的組件來管理特定的行為。
這里有一些使用Class構建controller的建議:
始終使用 constructor 用于依賴注入;
不要直接導出 Class,導出它的名稱,并允許$inject;
如果你需訪問到 scope 里的語法,使用箭頭函數(shù);
另外關于箭頭 函數(shù), let ctrl = this; 也是可以接受的,當然這更取決于使用場景;
綁定到所有公共函數(shù)到Class上;
適當?shù)睦蒙芷诘囊恍┿^子, $onInit, $onChanges, $postLink 以及$onDestroy。
注意: $onChanges 是 $onInit之后調用的, 這里 擴展閱讀 有更深一步的講解。
在$onInit使用require 以便引用繼承的邏輯;
不要覆蓋默認 $ctrl 使用controllerAs 起的別名, 當然也不要在別的地方使用 controllerAs
One-way dataflow and Events單向數(shù)據(jù)流已經在Angular1.5中引入了,并且重新定義了組件之間的通信。
關于單向數(shù)據(jù)流的一些小建議:
在組件接受數(shù)據(jù),始終使用 單向數(shù)據(jù)綁定符號"<"
不要再使用 "="雙向的數(shù)據(jù)綁定的語法
擁有綁定的組件應該使用$ onChanges克隆單向綁定的數(shù)據(jù)阻止對象通過引用傳遞和更新原數(shù)據(jù)
使用 $event 作為一個父級方法中的的一個函數(shù)參數(shù)(參見有狀態(tài)的例子 $ctrl.addTodo($event))
傳遞一個$event: {} 從無狀態(tài)的組件中進行備份(參見無狀態(tài)的例子 this.onAddTodo).
Bonus: 使用 包裹 .value() 的 EventEmitter 以便遷到Angular2 , 避免手動創(chuàng)建一個 $event
為什么? 這和Angular2類似并且保持組件的一致性.并且可以讓狀態(tài)可預測。
有狀態(tài)的組件什么是“有狀態(tài)的組件”
獲取狀態(tài),通過服務與后端API通信
不直接發(fā)生狀態(tài)變化
渲染發(fā)生狀態(tài)變化的子組件
作為一個組件容器的引用
下面的是一個狀態(tài)組件案例,它和一個低級別的模塊組件共同完成(這只是演示,為了精簡省略的一些代碼)
/* ----- todo/todo.component.js ----- */ import controller from "./todo.controller"; const TodoComponent = { controller, template: `` }; export default TodoComponent; /* ----- todo/todo.controller.js ----- */ class TodoController { constructor(TodoService) { this.todoService = TodoService; } $onInit() { this.newTodo = { title: "", selected: false }; this.todos = []; this.todoService.getTodos.then(response => this.todos = response); } addTodo({ todo }) { if (!todo) return; this.todos.unshift(todo); this.newTodo = { title: "", selected: false }; } } TodoController.$inject = ["TodoService"]; export default TodoController; /* ----- todo/index.js ----- */ import angular from "angular"; import TodoComponent from "./todo.component"; const todo = angular .module("todo", []) .component("todo", TodoComponent) .name; export default todo;
這個例子顯示了一個有狀態(tài)的組件,在控制器哪通過服務獲取狀態(tài),然后再將它傳遞給無狀態(tài)的子組件。注意這里并沒有在模版使用指令比如ng-repeat以及其他指令,相反,數(shù)據(jù)和功能委托到
什么是無狀態(tài)的組件
使用bindings: {} 定義了輸入和輸出;
數(shù)據(jù)通過屬性綁定進入到組件內
數(shù)據(jù)通過事件離開組件
狀態(tài)變化,會將數(shù)據(jù)進行備份 (比如觸發(fā)點擊和提交事件)
并不需要關心的數(shù)據(jù)來自哪里
可高度重復利用的組件
也被稱作無聲活著表面組件
下面是一個無狀態(tài)組件的例子 (我們使用
/* ----- todo/todo-form/todo-form.component.js ----- */ import controller from "./todo-form.controller"; const TodoFormComponent = { bindings: { todo: "<", onAddTodo: "&" }, controller, template: `` }; export default TodoFormComponent; /* ----- todo/todo-form/todo-form.controller.js ----- */ class TodoFormController { constructor(EventEmitter) {} $onChanges(changes) { if (changes.todo) { this.todo = Object.assign({}, this.todo); } } onSubmit() { if (!this.todo.title) return; // with EventEmitter wrapper this.onAddTodo( EventEmitter({ todo: this.todo }); ); // without EventEmitter wrapper this.onAddTodo({ $event: { todo: this.todo } }); } } TodoFormController.$inject = ["EventEmitter"]; export default TodoFormController; /* ----- todo/todo-form/index.js ----- */ import angular from "angular"; import TodoFormComponent from "./todo-form.component"; const todoForm = angular .module("todo") .component("todo", TodoFormComponent) .value("EventEmitter", payload => ({ $event: payload}); export default todoForm;
請注意
在這個例子中,$onChanges周期鉤子 產生一個this.todo的對象克隆并重新分配它,這意味著原數(shù)據(jù)不受影響,直到我們提交表單,沿著單向數(shù)據(jù)流的新的綁定語法"<" 。
什么是路由組件
它本質上是個有狀態(tài)的組件,具備路由定義
沒有router.js 文件
*我們使用路由組件去定義它自己的路由邏輯
*數(shù)據(jù)流入到組件是通過路由分解獲得 (當然在控制器中我們通過服務獲得)
在這個例子中,我們將利用現(xiàn)有
/* ----- todo/todo.component.js ----- */ import controller from "./todo.controller"; const TodoComponent = { bindings: { todoData: "<" }, controller, template: `指令 基本概念` }; export default TodoComponent; /* ----- todo/todo.controller.js ----- */ class TodoController { constructor() {} $onInit() { this.newTodo = { title: "", selected: false }; } $onChanges(changes) { if (changes.todoData) { this.todos = Object.assign({}, this.todoData); } } addTodo({ todo }) { if (!todo) return; this.todos.unshift(todo); this.newTodo = { title: "", selected: false }; } } export default TodoController; /* ----- todo/todo.service.js ----- */ class TodoService { constructor($http) { this.$http = $http; } getTodos() { return this.$http.get("/api/todos").then(response => response.data); } } TodoService.$inject = ["$http"]; export default TodoService; /* ----- todo/index.js ----- */ import angular from "angular"; import TodoComponent from "./todo.component"; import TodoService from "./todo.service"; const todo = angular .module("todo", []) .component("todo", TodoComponent) .service("TodoService", TodoService) .config(($stateProvider, $urlRouterProvider) => { $stateProvider .state("todos", { url: "/todos", component: "todo", resolve: { todoData: TodoService => TodoService.getTodos(); } }); $urlRouterProvider.otherwise("/"); }) .name; export default todo;
指令給予了我們的模板,scope ,與控制器綁定,鏈接和許多其他的事情。這些的使用使我們慎重考慮 .component()的存在。指令不應在聲明模板和控制器了,或者通過綁定接收數(shù)據(jù)。指令應該僅僅是為了裝飾DOM使用。這樣,就意味著擴展現(xiàn)有的HTML - 如果用.component()創(chuàng)建。簡而言之,如果你需要自定義DOM事件/ API和邏輯,使用一個指令并將其綁定到一個組件內的模板。如果你需要的足夠的數(shù)量的 DOM變化,postLink生命周期鉤子值得考慮,但是這并不是遷移所有的的DOM操作。你可以給一個無需Angular的地方使用directive
使用指令的小建議:
不要使用模板 ,scope,控制器
一直設置 restrict: "A"
在需要的地方使用 compile and link
記得 $scope.$on("$destroy", fn) 進行銷毀和事件解除;
返回目錄
推薦的屬性由于指令支持了大多數(shù) .component() 的語法 (模板指令就是最原始的組件), 建議限制指令中的的 Object,以及避免使用錯誤的指令方法。
Property | Use it? | Why |
---|---|---|
bindToController | No | 在組件中使用 bindings |
compile | Yes | 預編譯 DOM 操作/事件 |
controller | No | 使用一個組件 |
controllerAs | No | 使用一個組件 |
link functions | Yes | 對于 DOM操作/事件 的前后 |
multiElement | Yes | 文檔 |
priority | Yes | 文檔 |
require | No | 使用一個組件 |
restrict | Yes | 定義一個組件并使用 A |
scope | No | 使用一個組件 |
template | No | 使用一個組件 |
templateNamespace | Yes (if you must) | See docs |
templateUrl | No | 使用一個組件 |
transclude | No | 使用一個組件 |
下面有幾個使用es2015和指令的方法,無論是帶有箭頭函數(shù),更容易的操作,或使用ES2015Class。記住選擇最適合自己或者團隊的方法,并且記住 Angular 2中使用 Class.
下面是一個恒在箭頭函數(shù)的表達式()=>({})使用常量的例子,它返回一個對象面(注意里面與.directive的使用差異()):
/* ----- todo/todo-autofocus.directive.js ----- */ import angular from "angular"; const TodoAutoFocus = ($timeout) => ({ restrict: "A", link($scope, $element, $attrs) { $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => { if (!newValue) { return; } $timeout(() => $element[0].focus()); }); } }); TodoAutoFocus.$inject = ["$timeout"]; export default TodoAutoFocus; /* ----- todo/index.js ----- */ import angular from "angular"; import TodoComponent from "./todo.component"; import TodoAutofocus from "./todo-autofocus.directive"; const todo = angular .module("todo", []) .component("todo", TodoComponent) .directive("todoAutofocus", TodoAutoFocus) .name; export default todo;
或者用ES2015 Class(注意在注冊指令時手動調用 new TodoAutoFocus)來創(chuàng)建對象:
/* ----- todo/todo-autofocus.directive.js ----- */ import angular from "angular"; class TodoAutoFocus { constructor() { this.restrict = "A"; } link($scope, $element, $attrs) { $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => { if (!newValue) { return; } $timeout(() => $element[0].focus()); }); } } TodoAutoFocus.$inject = ["$timeout"]; export default TodoAutoFocus; /* ----- todo/index.js ----- */ import angular from "angular"; import TodoComponent from "./todo.component"; import TodoAutofocus from "./todo-autofocus.directive"; const todo = angular .module("todo", []) .component("todo", TodoComponent) .directive("todoAutofocus", () => new TodoAutoFocus) .name; export default todo;服務 基本理論
服務本質上是包含業(yè)務邏輯的容器,而我們的組件不應該直接進行請求。服務包含其它內置或外部服務,如$http,我們可以隨時隨地的在應用程序注入到組件控制器。我們在開發(fā)服務有兩種方式,.service() 以及 .factory()。使用ES2015Class,我們應該只使用.service(),通過$inject完成依賴注入。
構建服務Class下面的
/* ----- todo/todo.service.js ----- */ class TodoService { constructor($http) { this.$http = $http; } getTodos() { return this.$http.get("/api/todos").then(response => response.data); } } TodoService.$inject = ["$http"]; export default TodoService; /* ----- todo/index.js ----- */ import angular from "angular"; import TodoComponent from "./todo.component"; import TodoService from "./todo.service"; const todo = angular .module("todo", []) .component("todo", TodoComponent) .service("TodoService", TodoService) .name; export default todo;
返回目錄
ES2015 以及相關工具使用 Babel 將ES2015進行轉換為當前瀏覽器所支持的代碼
考慮使用 TypeScript 讓你更好的遷移到Angular2
使用 ui-router latest alpha (查看 Readme) 如果你希望支持路由鉆
你可能會在 template: "
考慮使用 Webpack 來編譯es2016的代碼
使用 ngAnnotate 自動完成 $inject 屬性注入
如何使用ngAnnotate with ES6
狀態(tài)管理考慮使用 Redux 用于 數(shù)據(jù)管理.
Angular Redux
資源理解 .component() 方法
使用 "require" 與 $onInit
理解生命周期鉤子, $onInit, $onChange, $postLink, $onDestroy
在路由中使用 "resolve"
Redux 以及 Angular 狀態(tài)管理
文檔關于Angular API Angular documentation.
Github: https://github.com/JackPu/angular-styleguide/blob/master/i18n/zh-cn.md
感謝 @toddmotto 許可
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79732.html
摘要:技術棧概述大名,顧名思義是在年月正式發(fā)布的一套標準。小名,意為第六次變更。本項目,選擇的是的推薦配置,唯一注意的是全局變量中把的關鍵詞加上。項目結構公共組件目錄,放一些二次封裝的等等片段式的。項目的公用樣式目錄。 技術棧概述 ES2015(ES6) 大名ES2015,顧名思義是 ECMAScript 在2015年6月正式發(fā)布的一套標準。小名ES6,意為ECMAScript第六次變更。(...
摘要:組件還包含數(shù)據(jù)事件的輸入與輸出,生命周期鉤子和使用單向數(shù)據(jù)流以及從父組件上獲取數(shù)據(jù)的事件對象備份。 說明:參照了Angular1.x+es2015的中文翻譯,并將個人覺得不合適、不正確的地方進行了修改,歡迎批評指正。 架構,文件結構,組件,單向數(shù)據(jù)流以及最佳實踐 來自@toddmotto團隊的實用編碼指南 Angular 的編碼風格以及架構已經使用ES2015進行重寫,這些在Angul...
學習的過程中收藏了這些優(yōu)秀教程和的項目,希望對你有幫助。 github地址, 有不錯的就更新 官方文檔 中文指南 初級教程 webpack-howto 作者:Pete Hunt Webpack 入門指迷 作者:題葉 webpack-demos 作者:ruanyf 一小時包教會 —— webpack 入門指南 作者:VaJoy Larn webpack 入門及實踐 作者:...
摘要:可以在不必打斷其它業(yè)務的前提下,升級應用程序,因為這項工作可以多人協(xié)作完成,在一段時間內逐漸鋪開,下面就方案展開說明主要依賴提供模塊。在混合式應用中,我們同時運行了兩個版本的。這意味著我們至少需要和各提供一個模塊。 angular1.x 升級 angular2+ 方案 我給大家提供的是angular1.x和angular5并行,增量式升級方案,這樣大家可以循序漸進升級自己的應用,不想看...
摘要:由進行開發(fā)和維護,代發(fā)布于年月,現(xiàn)在主要是。狀態(tài)是只讀的,只能通過來改變,以避免競爭條件這也有助于調試。文件大小為,而為,為。請記住,性能基準只能作為考慮的附注,而不是作為判斷標準。使用的人員報告說,他們永遠不必閱讀庫的源代碼。 本文當時寫在本地,發(fā)現(xiàn)換電腦很不是方便,在這里記錄下。 angular,react & vue 2018/07/23 2016年,對于JavaScript來說...
閱讀 2529·2023-04-26 02:57
閱讀 1410·2023-04-25 21:40
閱讀 2173·2021-11-24 09:39
閱讀 3564·2021-08-30 09:49
閱讀 764·2019-08-30 15:54
閱讀 1172·2019-08-30 15:52
閱讀 2080·2019-08-30 15:44
閱讀 1278·2019-08-28 18:27