loveky的流水账

Note: 目前虽然JDF已经支持使用ES6开发脚本,但线上使用还在前期摸索阶段,请大家根据自己项目实际情况评估使用情况 最近利用业余时间给JDF增加了ES6代码的支持。背后的原理是在项目构建阶段利用Babel将.babel文件转译成ES5代码。关于Babel的更多使用方法可以参考其官网的配置文档。 下面开始正式介绍在利用ES6特性开发前的准备工作: - 升级JDF到最新版本(>= 1.8.2) - 进入JDF项目目录,安装基本的Babel preset和plugin。关于preset和plugin的更多说明请参考文档 npm install babel-preset-es2015 npm install babel-plugin-transform-es3-member-expression-literals npm install babel-plugin-transform-es3-property-literals 配置项目的.gitignore文件,忽略node_modules目录。在文件中添加以下内容: **/node_modules/ 引入es6-base.js(包含 es5-shim和babel-polyfill) 至此,所有的准备工作就完成了。接下来就可以利用ES6的各种新特性开发了。需要注意的是所有包含了ES6特性的脚本文件扩展名必须是.babel,否则JDF是不会对其进行编译的。 最后是一份简单的FAQ: Q: ES6都有哪些新特性,有没有推荐的学习资料? 当然有,中文版的有阮一峰出品的ECMAScript 6入门。 英文的有: - ES6 In Depth来自Mozilla团队博客的一系列文章,每篇讲解一个新特性。建议按时间顺序阅读。 - Understanding ECMAScript 6来自Nicholas C. Zakas大神的一本ES6小书。 Q: 我想使用额外的Babel plugin或preset该怎样配置? 只需在package.json中指定需要额外引入的plugin或preset,例如: "babel": { "plugins": ["syntax-async-functions","transform-regenerator"], "presets": ["stage-0"] } 并在JDF项目的目录中安装相关npm package即可。 Q: .babel文件编译出来的代码我看不懂,出了问题如何调试呢? 我们看到的经过Babel转译后的JS是这样的: 这里包含了大量的Babel生成的代码,非常不利于调试。不过不用担心,JDF在本地开发模式中启用了sourceMap,你只需在Chrome开发者工具中的Sources Tab中在你要调试的JS文件目录下找到同名的.babel文件即可。相关JS中的报错信息,都会被映射到转译前的.babel文件的对应位置。 关于sourceMap的更多信息请参考这里。
本文翻译自JavaScript Template Syntax 表达式与表达式修饰符 ${expr} ${expr|modifier} ${expr|modifier1|modifier2|...|modifierN} ${expr|modifier1:argExpr1_1} ${expr|modifier1:argExpr1_1,argExpr1_2,...,argExpr1_N} ${expr|modifier1:argExpr1_1,argExpr1_2|...|modifierN:argExprN_1,argExprN_2,...,argExprN_M} expr可以是不包含右花括号}的任何合法JavaScript表达式 修饰符的格式是modifierName[:argExpr1[,argExpr2[,argExprN]]] argExpr可以是任何合法的expr Examples: ${customer.firstName} ${customer.firstName|capitalize} ${customer.firstName|default:"no name"|capitalize} ${article.getCreationDate()|default:new Date()|toCalenderControl:"YYYY.MM.DD",true,"Creation Date"} ${(lastQuarter.calcRevenue() - fixedCosts) / 1000000} 这里有一份内置的修饰符列表,你也可以通过TrimPath提供的API来创建自定义的修饰符。 表达式还可以写成${% customer.firstName %}这种形式,多出的%字符允许你的表达式中出现花括号}。例如: Visit our ${% emitLink('Solutions and Products', { color: 'red', blink: false }) %} 表达式中的空白字符是可选的,你也可以写成下面的格式: ${%customer.firstName%} ${%customer.firstName|capitalize%} 声明 声明标签可以嵌套使用。 控制流 {if testExpr} {elseif testExpr} {else} {/if} testExpr可以是任何不包含}的JavaScript表达式 testExpr不需要被(和)包围 示例: ... Read More
本文译自https://docs.angularjs.org/guide/di 依赖注入 依赖注入(DI)是一种解决组件如何获取其依赖这一问题的设计模式。 Angular injector子系统负责创建组件,解决它们的依赖,并按要求将它们提供给其他组件。 如果想深入了解DI,可以查看依赖注入的Wikipedia以及Martin Fowler的这篇Inversion of Control。 DI in a Nutshell 一个组件要想获取它的依赖可以通过以下三种途径: 直接创建这个依赖的一个实例,通常使用new操作符 通过全局变量查找已经被创建的依赖 依赖可以作为参数传递给需要它的组件 前两种获取依赖的方式并不理想,因为这会导致依赖关系被硬编码进代码中。这会导致更新依赖关系变得很复杂。在测试时尤其如此,我们通常会提供mock过的依赖关系以隔离不同组件间的测试,硬编码会导致每次测试都需要修改相关的源码。 相比之下,第三种方式是最可行的,因为它将解决依赖关系的责任从组件中移除。依赖只是简单的作为参数传递给组件: function SomeClass(greeter) { this.greeter = greeter; } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); } 在上边的例子中,SomeClass并不关心如何创建或找到greeter,它只是在初始化时从参数列表里取出需要的依赖即可。 但这样做的问题是,寻找/创建依赖的责任被转交给了调用SomeClass的代码。 为了统一管理依赖关系,每个Angular应用都有一个injector。injector是一个服务定位器,它负责查找或创建依赖。 下面是一个使用injector服务的例子: // Provide the wiring information in a module var myModule = angular.module('myModule', []); 下面的代码告诉injector如何创建greeter服务。需要注意的是greeter又依赖于$window服务。greeter实际上是一个包含greet方法的对象。 myModule.factory('greeter', function($window) { return { greet: function(text) { $window.alert(text); } }; }); 下面的代码演示了如何创建一个injector并通过它来请求greeter服务: ... Read More
数据的双向绑定可能是Angular最为人们熟知的特性之一。举个最简单的例子 可以在result页面中看到,每当在input中输入时,$scope中对应model的值也改变了。反之,当用户点击Set按钮在$scope中更新了model的值时,input输入框中的内容也对应更新了。 所有的这些魔法只需要我们在input元素上指定一个ng-model属性。可见ng-model这个directive是双向绑定这一特性不可缺少一点。本文就继续从源代码入手看看ngModel的实现方式以及其它directive是如何与ngModel交互的。 ngModel被定义在input.js。从名字上可看出,该文件还定义input这个directive,从这点也可以看出ngModel与input之间的紧密关系。 注: 文中所有涉及到的AngularJS源码均来自angular-1.2.8版本。 var ngModelDirective = function() { return { require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { // notify others, especially parent forms var modelCtrl = ctrls[0], formCtrl = ctrls[1] || nullFormCtrl; formCtrl.$addControl(modelCtrl); scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } }; }; 可以看出ngModel依赖于ngModelController以及可选的formController。ngModel在link方法中只做了2件事: 如果声明了ngModel的元素出现在一个form中,那么就向上层form注册自身 注册了$destroy事件的监听器,该事件触发时告知上层form移除对自身的引用 link方法如此简单以至于我们还没看到关于数据绑定的任何信息。看来问题的答案都藏在ngModelController中了。接下来看看ngModelController中都发生了什么。 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', function($scope, $exceptionHandler, $attr, $element, $parse) { this. ... Read More

Angular中的Controller
11 April 2014

在Angular中,一个Controller是一个JavaScript构造函数。当一个Controller通过ng-controller附加到某个DOM元素上时,Angular会使用相应Controller的构造函数来初始化一个新的Controller对象。一个新的child scope会被创建并作为可注入的参数(就是我们看到的$scope)传递给该Controller的构造函数。 使用Controller: 为$scope对象设置初始状态 为$scope对象添加行为 不要使用Controller: 修改DOM —— Controllers应该只负责业务逻辑。将任何UI的逻辑放进Controller都会极大地破坏其可测试性。Angular中可以使用数据绑定用来更新页面中的数据或使用directive来封装DOM操作。 格式化input数据 —— 使用angular form controls 过滤输出数据 —— 使用Angular中的filter机制 跨Controller之间的代码或状态共享 —— 使用Angular中的service机制 管理其他组件的生命周期