Getting MEAN
getting MEAN
MEAN是一个Javascript平台的现代Web开发框架总称,它是MongoDB + Express +AngularJS + NodeJS 四个框架的第一个字母组合。它与传统LAMP一样是一种全套开发工具的简称。
- MongoDB是一个使用JSON风格存储的数据库,非常适合javascript。(JSON是JS数据格式)
- ExpressJS是一个Web应用框架,提供有帮助的组件和模块帮助建立一个网站应用。
- AngularJS是一个前端MVC框架。
- Node.js是一个并发 异步 事件驱动的Javascript服务器后端开发平台。
Angular
AngluarJS 1.x的迭代之路
2012年6月,发布了1.0.0版本,集成了angular应用习以为常的诸多概念:
- 使用MVVM这种高效开发界面的技术
- 引入Directive指令来扩展HTML的语义
- �引入Controller和Scope来构建应用逻辑
- 引入Service、Factory和Provider等来为公共和数据逻辑提供抽象
- 基于ngModel双向数据绑定的表单格式化和验证
2013年11月,整合了1.1版本功能的1.2版本带来的功能和调整如下:
- 把ngRouter路由抽取到单独模块中
- 引入ControllerAs语法糖,使得模板中的数据绑定更加清晰(而不是分不清层次的Scope)
- ngRepeat支持track by功能
- $http等服务遵从Promise/A+标准
2014年10月,在1.3版本中,angular宣布不再支持IE8,同时提供了如下新功能:
- 单次绑定,在模板中使用::的表达式
- ngMessage,利用该模块来更好地做表单检验消息提示
- ng-model-options对ngModel更细力度的控制
2015年5月底,Angular团队终于发布了1.4版本,该版本中有超过400条提交,同时优化了文档,修改了100多处bug,以及新增了30多个新特性,其中包括:
- 重写动画模块,修复大量的遗留bug
- Router模块,为Angular1.x和Angular提供了强大并且一致的路由方案
2016年2月份,Angular团队又发布了AngularJS1.5版本,该版本的主题就是要和Angular做进一步整合,提供更接近于Angular应用的书写体验,如组件式开发、定义组件指令、生命周期钩子等等。
初生的Angular2
2014年3月份,官方博客就有提及在为新的Angular做设计和开发,所有设计文档都公布在Google云盘中,博客宣称该框架有以下特点:优先为移动应用设计、更快速的变化检测、ES6原生模块化引入、采用状态、支持整合认证授权和缓存视图的新路由、有持久化支持离线使用的存储层,等等。
2014年9月,Angular首次亮相,它的接口和变化在很多AngluarJS1.x开发者中引起了不小的争议,这些变化包括:
- 引入Component,统一控制器和模板,向Web Component标准看齐。
- 引入AtScript,对TypeScript和ES6做语法增强,添加可选运行时类型和注解,来帮助大型团队开发复杂应用。
- 移除Scope概念。
这些改变抛弃了AngluarJS1.x这几年来的一些历史包袱,让经验老到的开发团队能够重新设计,结合Angluar1.x的经验教训和外界引入的思潮(React的Virtual DOM方案分离出的渲染来获得性能提升和平台扩展性,向Web Component的标准看齐等等)。它成就了现在具有高性能、高开发效率、丰富扩展能力特点的Angluar。
快速发展的Angular2+
2016年5月,Angluar发布rc1版本,正式进入发行候选阶段。6月中旬发布rc2版本,它的动画模板从底层支持多平台,合入了超过100项社区贡献的代码。一周后rc3发布,把新路由项目合入主代码库中。7月初发布rc4版本,官方对暴露出来的公共接口进行了清理,并且大幅度提升了测试的灵活性和易用性。8月中的rc5版本中,又引入了不少新特性,如路由支持懒加载、组件和服务支持Ahead-of-Time(AoT)编译、新的NgModules封装方式等。在随后的9月,官方先后发布了rc6和rc7版本,其中r6为表单引入更多功能(如validator指令绑定等),同时增强了国际化支持;而rc7主要集中在问题的修复上。
通过7次rc版本的迭代,Angular已经基本趋于稳定:
- 通过广泛的使用场景验证,为外部提供的API接口不会再有变更。
- 框架本身被反复优化,已经能提供更好的开发体验、更小的加载包以及更快的性能等等。
在AngularJS2发布后大约6个月,下一个大的更新也发布了:Angluar4,或者Angular v4。其主要feature有:
- router增加ParamMap,可以使用标准的js语法来访问。
- 把动画模块从@angluar/core模块中独立出来,通过@angular/platform-browser/animations的BrowserAnimationsModule提供。
- ngif 增加else
- 通过NgComponentOutlet指令创建动态Component
- 使用TypeScript2.1/2.2,增加Angular应用的类型安全性和提供ngc编译速度。
- 提供Angular Universal实现在浏览器外渲染,比如web server。
- 更快更好。
经过14个月的2次重要迭代,Angluar5终于在2017年1月发布了。其承诺更好的代码共享能力,更新的HttpClient,更快的重构,重点是更容易创建渐进式web应用。主要feature有:
- 移除不必要代码,优化构建
- 添加Angluar通用的状态转移API和DOM支持
- Angluar编译器增加支持增量编译
- 增加跨浏览器的标准化
- 提供一个新的HttpClient
- component和directive可支持多个名称
- router支持整个生命周期的events
- ReflectiveInjector 被 StaticInjector替代, 对大多数开发者来说可减少应用体积
- CLI 1.5 默认创建 Angular 5 的项目
- RXJS更新到5.5.2
Angluar6。。。
Angular核心概念
模块 Modules
组件 Components
模板 Templates
依赖注入 Dependency Injection
服务 Service
数据绑定 Data Binding
指令 Directives
Typescript 介绍
Typescript相当于Javascript的超集,实现了ECMAScript的标准,并在此基础上做了进一步增强,主要有类型校验、接口、装饰器等特性,这使得代码编写更加规范化,也更有利于项目维护。
TS,ES7,ES6,ES5关系为什么要用TS
从开发效率上看,虽然需要多写一些类型定义代码,但TS在VSCode、WebStorm等IDE下可以做到智能提示,智能感知bug,同时我们项目常用的一些第三方类库框架都有TS类型声明,我们也可以给那些没有TS类型声明的稳定模块写声明文件,这在团队协作项目中可以提升整体的开发效率。
从可维护性上看,长期迭代维护的项目开发和维护的成员会有很多,团队成员水平会有差异,而软件具有熵的特质,长期迭代维护的项目总会遇到可维护性逐渐降低的问题,有了强类型约束和静态检查,以及智能IDE的帮助下,可以降低软件腐化的速度,提升可维护性,且在重构时,强类型和静态类型检查会帮上大忙,甚至有了类型定义,会不经意间增加重构的频率(更安全、放心)。
从线上运行时质量上看,很多bug都是由于一些调用方和被调用方(如组件模块间的协作、接口或函数的调用)的数据格式不匹配引起的,由于TS有编译期的静态检查,让我们的bug尽可能消灭在编译器,加上IDE有智能纠错,编码时就能提前感知bug的存在,我们的线上运行时质量会更为稳定可控。
面向对象编程增强
访问权限控制
接口,组合继承和实现接口的方式使面向对象编程更为灵活、可扩展性更好。
泛型,泛型(模板)在传统面向对象编程语言中是很常见的概念了,在代码逻辑是通用模式化的,参数可以是动态类型的场景下比较有用
类型系统
模块系统增强,支持命名空间,在管理复杂模块的内部时比较有用
TS作者在最近微软Build大会给出的一个图:
image.png
如图,Web和Node平台的JS始终与JS最新规范有一段距离,Web平台的距离更远,TS可以填充这个间隙,让使用者在Web和Node平台都能用上最新的Feature,用上优雅的JS,提高生产力。【Anders Hejlsberg: What's new in TypeScript? 2017】
响应式编程RxJS
在计算领域,响应式编程一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
在大家熟悉的MVVM中,存在一种M(Model)到V(View)的绑定关系,当model变化时view也随之变化,体现了响应式编程中面向变化传播的思想。
监听一系列事件流并对这一系列事件流进行映射、过滤和合并等处理后,再响应整个事件流的回调,这过程便属于面向数据流的编程。比如ReactiveX的编程范式中,数据流被封装在一个叫作Observable的对象实例中,通过观察者模式,对数据流进行统一的订阅(Subscribe),并在中间插入像filter()这样的操作函数,从而对Obervable所封装的数据进行过滤处理。
实例代码如下:myObservable.filter(fn).subscribe(callback);
通过这个例子可以看到响应式编程清楚地表达了动态的异步数据流,而相关的计算模型也自动地将变化的值通过数据流的方式进行了传播。响应式编程需要描述数据流,而不是单个点的数据变量,我们需要把数据的每个变化汇聚成一个数据流。如果说传统编程方式是基于离散的点,那么响应式编程就是线。
核心概念
在Rx中,最核心的概念就是Observable,应用中产生的异步数据都需要先包装成Observable对象,其作用就是把异步的数据变换成数据流的形式。
Rx在结合了观察者模式的同时,还结合了函数式编程和迭代器模式的思想,其中Operator就是对这两种编程思想的重要体现。在Rx中,每一个Observable对象,都可以通过某个operator对象进行变换、过滤、合并和监听等操作。可以进行链式调用。
Observable作为观察者模式的被观察者,需要一个方法来订阅它,subscribe就是这样一个方法。除此还有
Observer
Subscription
Subject
等等
一个简单的示例:
用户在文本框进行输入时,需要对用户的输入进行实时监听,每当用户输入一个新的字符时,发送请求到服务器。有以下限制条件:
用户输入停顿500ms后才发送到服务器;
保证返回顺序;
let inputSelector = document.querySelector('input');
Rx.Observable.formEvent(inputSelector, 'keyup')
.debounceTime(500)
.switchMap(event => getRecommend(evnet.target.value))
.subscribe(callback);
RxJS和Promis的对比
RxJS的Observable可以通过toPromise()方法把原有的Observable对象转为Promise对象,所以能用Promise的场景RxJS都适用,RxJS是作为Promise的超集存在。
Observable能以流的形式响应多个异步事件,有forkJoin合并请求,Promise的其他一系列操作,RxJS都有对应的解决方案。
Angluar中的RxJS
HTTP服务中的get/post/request等方法会返回一个Observable对象,以及路由中的events,params等也是Observable对象。
addSuit(suit: Suit): Observable<Suit> {
return this.http.post<Suit>('/api/suit', suit);
}
this.suitService.addSuit(this.addSuitForm.value).subscribe(
res => {
this.suits.push(res);
this.addSuitForm.reset();
this.toast.setMessage('item added successfully.', 'success');
},
error => console.log(error)
);
MongoDB
MongoDB
强大、灵活、易于扩展的通用型数据库。能扩展出非常多的功能,比如二级索引、范围查询、排序、聚合以及地理空间索引。
- MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
- 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。
- 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
- 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
- Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
- Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
- Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
- Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
- MongoDB安装简单。
Mongoose
mongoose是nodeJS提供连接 mongodb的一个库,遵循模板式方法, 能够对你输入的数据进行自动处理。
Mongoose是NodeJS的驱动,不能作为其他语言的驱动。Mongoose有两个特点
- 通过关系型数据库的思想来设计非关系型数据库
-
基于mongodb驱动,简化操作
image.png
Mongooose中,有三个比较重要的概念,分别是Schema、Model、Entity。它们的关系是:Schema生成Model,Model创造Document,Model和Document都可对数据库操作造成影响,但Model比Document更具操作性
- Schema:用于定义数据库的结构。 类似创建表时的数据定义(不仅仅可以定义文档的结构和属性,还可以定义文档的实例方法、静态模型方法、复合索引等),每个
Schema
会映射到mongodb中的一个collection,Schema
不具备操作数据库的能力 - Model:由Schema编译而成的构造器,具有抽象属性和行为,可以对数据库进行增删查改。Model的每一个实例(instance)就是一个文档document
- Document:是由Model创建的实体,它的操作也会影响数据库
数据库设计
实体关系如下所示:
实体关系关系型数据库设计如下:
关系型数据库设计文档型数据库设计如下:
agentSchema
const agentSchema = new mongoose.Schema({
name: { type: String, unique: true, trim: true },
price: Number,
discountPrice: Number,
}, { timestamps: true });
memberSchema
const memberSchema = new mongoose.Schema({
bid: { type: String, unique: true, trim: true },
username: { type: String, unique: true, trim: true },
name: String,
_agent: { type: mongoose.Schema.Types.ObjectId, ref: 'Agent' }
}, { timestamps: true });
topicSchema
const memSchema = new mongoose.Schema({
userId: String,
username: String,
name: String,
donePageCount: Number,
areaCount: Number,
});
const topicSchema = new mongoose.Schema({
no: { type: Number, unique: true },
name: { type: String, unique: true, trim: true },
shortName: String,
total: Number,
assessment: Number,
passTime: { type: Date },
note: String,
mems: [memSchema],
}, { timestamps: true });
orderSchema
const memOSchema = new mongoose.Schema({
_mem: { type: mongoose.Schema.Types.ObjectId, ref: 'Member' },
userId: String,
username: String,
name: String,
income: Number,
areaCount: Number,
isBelong: Boolean,
});
const agentOSchema = new mongoose.Schema({
_agent: { type: mongoose.Schema.Types.ObjectId, ref: 'Agent' },
name: String,
income: Number,
areaCount: Number,
});
const orderSchema = new mongoose.Schema({
name: { type: String, unique: true, trim: true },
income: Number,
areaCount: Number,
memList: [memOSchema],
agentList: [agentOSchema],
}, { timestamps: true });