angular1.x + ES6开发风格记录
来源:互联网 发布:爱奇艺获取网站源码 编辑:程序博客网 时间:2024/06/09 20:44
angular1.x和ES6开发风格
一、Module
ES6有自己的模块机制,所以我们要通过使用ES6的模块机制来淡化ng的框架,使得各业务逻辑层的看不出框架的痕迹,具体的做法是:
- 把各功能模块的具体实现代码独立出来。
- module机制作为一个壳子,对功能模块进行封装。
- 每个功能分组,使用一个总的壳子来包装,减少上级模块的引用成本。
- 每个壳子文件把module的name属性export出去。
举例来说,我们有一个moduleA,里面有serviceA,serviceB,那么,就有这样一些文件:
serviceA的实现,service/a.js
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
ng1.2开始提供了controllerAs的语法,自此Controller终于能变成一个纯净的ViewModel(视图模型)了,而不像之前一样混入过多的$scope痕迹。
例如:
HTML
<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)
指令主要包含了一个ddo(Directive Definition Object),所以本质上这是一个对象,我们可以给它构建一个类。
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对象,然后传给指令
同样以datepicker组件为例,先定义一个controller
// DatePickerCtrl.jsexport 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之后提供了一个新的语法moduleInstance.component,它是moduleInstance.directive的高级封装版,提供了更简洁的语法,同时也是未来组件应用的趋势。例如
bingToController -> bindings的变化,而且默认controllerAs = ‘$ctrl’,但是它只能定义自定义标签,不能定义增强属性,而且component定义的组件都是isolated scope。
//DirectiveController.jsexport class DirectiveController {$onInit() {}$onChanges(changesObj) {}$onDestroy() {}$postLink() {}}//index.jsimport 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、服务
先来说一下在ES6中通过factory和service来定义服务的方式。
serviceA的实现,service/a.js
export default class ServiceA {}serviceA的模块包装器moduleA的实现
import ServiceA from './service/a';export angular.module('moduleA', []) .service('ServiceA', ServiceA) .name;
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;
controller/a.js
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,可以直接使用一个常量来代替。
Contant.js
export const VERSION = '1.0.0';
5、filter
angular中filter做的事情有两类:过滤和格式化,归结起来就是一种数据的变换工作,过度使用filter会让你的额代码在不自知的情况下走向混乱。所以我们可以自己去写一系列的transformer来做数据处理。
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的注入
1.2之后有了controllerAS的语法,我们可以这么写。
<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,$watchGroup,$watchCollection,还有$eval(作用域上的表达式求值)这类东西,我们必须想到对它们的替代办法。
一个$watch的典型例子
$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}`; }}
<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、总结
对于整个系统而言,除了angular.module,angular.controller,angular.component,angular.directive,angular.config,angular.run以外,都应该实现成与框架无关的,我们的业务模型和数据模型应该可以脱离框架而运作,当做完这层之后,上层迁移到各种框架就只剩下体力活了。
一个可伸缩的系统构架,确保下层业务模型/数据模型的纯净都是有必要的,这样才能提供上层随意变化的可能,任何模式下的应用开发,都应具备这样一个能力。
参考链接:
https://segmentfault.com/a/1190000005352995?utm_source=tuicool&utm_medium=referral
https://github.com/xufei/blog/issues/29
阅读全文
0 0
- angular1.x + ES6开发风格记录
- webpack+es6+angular1.x项目构建
- Angular1.x规范
- angular1.x的directive
- Angular1.x启动分析
- angular1.x select 常用技巧
- Angular1.x安装与配置
- Angular1
- angular1
- ionic3.x开发记录
- 搞定angular1.x——自定义指令
- 搞定angular1.x——复杂指令
- 简单实现 angular1.x 双向数据绑定
- angular1.x $watch 监听的简单应用
- angular1.x 简单的全选功能
- angular1.x 指令 全选(checkbox)
- javascript es6编码风格
- javascript es6 编程风格
- js操作select option
- org.apache.commons.lang.StringUtils;的时候出错解决方法
- 深度学习Matlab工具箱代码注释——cnnapplygrads.m
- 安卓一键去广告工具
- oracle中pivot和unpivot函数用法举例
- angular1.x + ES6开发风格记录
- Ohana Cleans Up
- eclipse打开,莫名报一堆错(Syntax error on tokens, delete these tokens )
- POJ3304(计算几何基础-判断线段与直线相交)
- 百练_2752:字符串数组排序问题
- SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
- No module named 'cv2'出错
- 低功耗蓝牙cc2541学习笔记之协议栈工作原理
- GitHub上排名前100的Android开源库介绍