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
從開發效率上看,雖然需要多寫一些類型定義代碼,但TS在VSCode、WebStorm等IDE下可以做到智能提示,智能感知bug,同時我們項目常用的一些第三方類庫框架都有TS類型聲明,我們也可以給那些沒有TS類型聲明的穩定模塊寫聲明文件,這在團隊協作項目中可以提升整體的開發效率。
從可維護性上看,長期迭代維護的項目開發和維護的成員會有很多,團隊成員水平會有差異,而軟件具有熵的特質,長期迭代維護的項目總會遇到可維護性逐漸降低的問題,有了強類型約束和靜態檢查,以及智能IDE的幫助下,可以降低軟件腐化的速度,提升可維護性,且在重構時,強類型和靜態類型檢查會幫上大忙,甚至有了類型定義,會不經意間增加重構的頻率(更安全、放心)。
從線上運行時質量上看,很多bug都是由于一些調用方和被調用方(如組件模塊間的協作、接口或函數的調用)的數據格式不匹配引起的,由于TS有編譯期的靜態檢查,讓我們的bug盡可能消滅在編譯器,加上IDE有智能糾錯,編碼時就能提前感知bug的存在,我們的線上運行時質量會更為穩定可控。
面向對象編程增強
訪問權限控制
接口,組合繼承和實現接口的方式使面向對象編程更為靈活、可擴展性更好。
泛型,泛型(模板)在傳統面向對象編程語言中是很常見的概念了,在代碼邏輯是通用模式化的,參數可以是動態類型的場景下比較有用
類型系統
模塊系統增強,支持命名空間,在管理復雜模塊的內部時比較有用
TS作者在最近微軟Build大會給出的一個圖:
如圖,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 });