angular1.x + ES6开发风格记录
angular1.x和ES6開發風格
一、Module
- 把各功能模塊的具體實現代碼獨立出來。
- module機制作為一個殼子,對功能模塊進行封裝。
- 每個功能分組,使用一個總的殼子來包裝,減少上級模塊的引用成本。
- 每個殼子文件把module的name屬性export出去。
export default class ServiceA {}
serviceB的實現,service/b.js
export default class ServiceB {}
moduleA的殼子定義,moduleA.js
import ServiceA from './services/a';
import ServiceB from './services/b';
export default angular.module('moduleA'[])
.service('ServiceA', ServiceA)
.service('ServiceB', ServiceB)
.name;
存在一個moduleB要使用moduleA:
import moduleA from './moduleA';
export default angular.module('moduleB', [moduleA]).name;
二、Controller
<div ng-controller="AppCtrl as app">
<div ng-bing="app.name"></div>
<button ng-click="app.getName">get app name</button>
</div>
controller AppCtrl.js
export default class AppCtrl {
constructor() {
this.name = 'angualr$es6';
}
getName() {
return this.name;
}
}
module
import AppCtrl from './AppCtrl';
export default angular.module('app', [])
.controller('AppCtrl', AppCtrl)
.name;
三、Component(Directive)
export default class DirectiveA {}
DDO上面的東西大致可以分為兩類,屬性和方法,所以就在構造函數里這樣定義:
constructor() {
this.template = template;
this.restrict = 'E';
}
接下來就是controller和link,compile等函數了,比如controller,可以實現一個普通的controller類,然后賦值到controller屬性上來:
this.controller = ControllerA;
寫directive的時候,盡量使用controllerAs這樣的語法,這樣controller可以清晰一些,不必注入$scope,而且還可以使用bingToController屬性,把在指令attr上定義的值或方法傳遞到controller實例上來。接下來我們使用三種方法來定義指令
1、定義一個類(ddo),然后在定義指令的工廠函數中返回這個類的實例。
import template from '../template/calendar.html';
import CalendarCtrl from '../controllers/calendar'; import '../css/calendar.css'; export default class CalendarDirective{
constructor() {
this.template = template;
this.restrict = 'E'; this.controller = CalendarCtrl;
this.controllerAs = 'calendarCtrl';
this.bingToController = true; this.scope = {
minDate: '=',
maxDate: '=',
selecteDate: '=',
dateClick: '&'
};
} link(scope) {
//這個地方引入了scope,應盡量避免這種做法,
//但是搬到controller寫成setter,又會在constructor之前執行
scope.$watch('calendarCtrl.selecteDate', newDate => {
if(newDate) {
scope.calendarCtrl.calendar.year = newDate.getFullYear();
scope.calendarCtrl.calendar.month = newDate.getMonth();
scope.calendarCtrl.calendar.date = newDate.getDate(); }
});
}
}
然后在module定義的地方:
import CalendarDirective from './directives/calendar';
export default angular.module('components.form.calendar', [])
.directive('snCalendar', () => new CalendarDirective())
.name;
2、直接定義一個ddo對象,然后傳給指令
// DatePickerCtrl.js
export default class DatePickerCtrl {
$onInit() {
this.date = `${this.year}-${this.month}`;
} getMonth() {
...
} getYear() {
...
}
}
注意,這里先寫了controller而不是link/compile方法,原因在于一個數據驅動的組件體系下,我們應該盡量減少對DOM操作,因此理想狀態下,組件是不需要link或compile方法的,而且controller在語義上更貼合mvvm架構。
import template from './date-picker-tpl.html';
import controller from './DatePickerCtrl'; const ddo = {
restrict: 'E',
template, //es6對象簡寫
controller,
controllerAs: '$ctrl',
bingToController: {
year: '=',
month: '='
} }; export default angular.module('components.datePicker', [])
.directive('dataPicker', ddo)
.name;
在整個系統設計中只有index.js(定義模塊的地方)是框架可識別的,其它地方的業務邏輯都不應該出現框架的影子,這樣方便移植。
3、component
1.5版本還給組件定義了相對完整的生命周期鉤子,而且提供了單向數據流的方式,以上例子可以寫成下面這樣子:
//DirectiveController.js
export class DirectiveController {
$onInit() { } $onChanges(changesObj) { } $onDestroy() { } $postLink() { }
} //index.js
import template from './date-picker-tpl.html';
import controller from './DatePickerCtrl'; const ddo = {
template,
controller,
bindings: {
year: '<',
month: '<'
}
}; export default angular.module('components.datepicker', [])
.component('datePicker', ddo)
.name;
4、服務
export default class ServiceA {}
serviceA的模塊包裝器moduleA的實現
import ServiceA from './service/a';
export angular.module('moduleA', [])
.service('ServiceA', ServiceA)
.name;
factoryA的實現,factory/a.js
import EntityA from './model/a';
export default function FactoryA {
return new EntityA();
}
factoryA的模塊包裝器moduleA的實現
import FactoryA from './factory/a';
export angular.module('modeuleA', [])
.factory('FactoryA', FactoryA)
.name;
對于依賴注入我們可以通過以下方式來實現:
export default class ControllerA {
constructor(ServiceA) {
this.serviceA = ServiceA;
}
}
ControllerA.$inject = ['ServiceA'];
import ControllerA from './controllers/a';
export angular.module('moduleA', [])
.controller('ControllerA', ControllerA);
對于constant和value,可以直接使用一個常量來代替。
export const VERSION = '1.0.0';
5、filter
import { dateFormatter } './transformers';
export default class Controller {
constructor() {
this.data = [1,2,3,4];
this.currency = this.data
.filter(v => v < 4)
.map(v => '$' + v);
this.date = Date.now();
this.today = dateFormatter(this.date);
}
}
6、消除$scope,淡化框架概念
1、controller的注入
<div ng-controller="TestCtrl as testCtrl">
<input ng-model="testCtrl.aaa">
</div>
xxx.controller("TestCtrl", [function() {
this.aaa = 1;
}]);
實際上框架會做一些事情:
$scope.testCtrl = new TestCtrl();
對于這一塊,把那個function換成ES6的類就可以了。
2、依賴屬性的計算
$scope.$watch("a", function(val) {
$scope.b = val + 1;
});
我們可以直接使用ES5的setter和getter來定義就可以了。
class A {
set a(val) { //a改變b就跟著改變
this.b = val + 1;
}
}
如果有多個變量要觀察,例如
$scope.$watchGroup(["firstName", "lastName"], function(val) {
$scope.fullName = val.join(",");
});
我們可以這樣寫
class Controller {
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
html
<input type="text" ng-model="$ctrl.firstName">
<input type="text" ng-model="$ctrl.lastName"> <span ng-bind="$ctrl.fullName"></span>
3、事件冒泡和廣播
在$scope上,另外一套常用的東西是$emit,$broadcast,$on,這些API其實是有爭議的,因為如果說做組件的事件傳遞,應當以組件為單位進行通信,而不是在另外一套體系中。所以我們也可以不用它,比較直接的東西通過directive的attr來傳遞,更普遍的東西用全局的類似Flux的派發機制去通信。
根作用域的問題也是一樣,盡量不要去使用它,對于一個應用中全局存在的東西,我們有各種策略去處理,不必糾結于$rootScope。
4、指令中$scope
7、總結
參考鏈接:
總結
以上是生活随笔為你收集整理的angular1.x + ES6开发风格记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 上传工程到github
- 下一篇: spring transaction源码