AngularJS不用我贅述,前端開發人員必定耳熟能詳。有人稱之爲MVWhatever
框架,意思是使用AngularJS
,你能夠參考任意範式進行應用開發,不管是MVC
、仍是MVVVM
都信手拈來,只要你懂,範式在AngularJS
手下,均可以輕鬆適配。javascript
隨着各類現代瀏覽器、以及node對ES6的支持,已經有愈來愈多的ES6
特性能夠在程序中使用,她們給開發過程帶來的便利不言而喻,舉個小例子,我想從一個數組裏找一些符合條件的數據,放入另外一個數組內,過去咱們這麼寫:css
var list = [], i; for (i = 0; i < originalList.length; i++) { var item = originalList[i]; if (item.gender === 'male') { list.push(item); } } console.log(list); //符合條件的新數組
若是改用數組的高階函數,再配合ES6
的arrow function,代碼能夠簡潔如斯:html
const list = originalList.filter(item => item.gender === 'male'); console.log(list); //符合條件的新數組
既然有如此優雅的語法糖能讓咱們的開發變得high到爆,那過去咱們認爲屌炸天的AngularJS
(如今也屌炸天,只不過還有Angular2
, React
, vue
橫空出世)是否是能夠用ES6
來寫?少年不要懷疑,真的能夠哦!前端
一個良好、快速、簡潔的starter
工具備利於咱們對ES6
編寫AngularJS
的深刻認知,因此我要用一個骨架生成器generator-es6-angular來建立新項目,該generator
是依託於yeoman的腳手架。vue
yo
npm install -g yo
請注意前綴
sudo
,若是你使用的是unix
like操做系統的話html5
generator-es6-angular
npm install -g generator-es6-angular
請注意前綴
sudo
,若是你使用的是unix
like操做系統的話java
generator-es6-angular
建立項目先找個你喜歡的目錄,而後運行下面的命令,由於一會新項目會直接建立在該目錄下。node
yo es6-angular
上面命令回車後,生成器會問你以下問題,老實做答便可(注意: 對單頁應用沒經驗的孩紙,在Use html5 model
這個問題上,請選擇No
; 當問你Which registry would you use?
時,國內用戶選擇第一個淘寶鏡像安裝速度會快不少)webpack
當命令執行完畢後,你就能在當前目錄下看到剛纔建立的項目了,本例中我使用的project name
是es6-demo
。git
#進入剛建立的項目目錄 cd es6-demo #啓動調試服務 npm start
而後你就能夠在http://localhost:8080下,看到剛建立的項目的運行效果了:
es6-demo ├── etc │ └── config.js ├── img │ └── ... ├── js │ ├── features │ │ ├── about │ │ │ ├── components │ │ │ │ ├── about.co │ │ │ │ ├── about.css │ │ │ │ └── subs │ │ │ │ ├── more │ │ │ │ │ ├── index.co │ │ │ │ │ └── more.css │ │ │ │ └── why │ │ │ │ ├── index.co │ │ │ │ └── why.css │ │ │ ├── main.js │ │ │ └── routes.js │ │ ├── common │ │ │ ├── components │ │ │ │ ├── main.js │ │ │ │ ├── menu.co │ │ │ │ └── menu.css │ │ │ ├── directives │ │ │ │ ├── autofocus.js │ │ │ │ └── main.js │ │ │ ├── main.js │ │ │ └── runners │ │ │ ├── main.js │ │ │ └── routeIndicator.js │ │ ├── home │ │ │ ├── components │ │ │ │ ├── home.co │ │ │ │ └── home.css │ │ │ │ │ │ │ ├── main.js │ │ │ ├── routes.js │ │ │ └── service │ │ │ └── HomeService.js │ │ └── main.js │ ├── fw │ │ ├── config │ │ │ ├── SSOConfig.js │ │ │ ├── main.js │ │ │ └── routerConfig.js │ │ ├── ext │ │ │ └── main.js │ │ ├── helper │ │ │ ├── event.js │ │ │ ├── ngDeclare.js │ │ │ └── object.js │ │ └── value │ │ ├── main.js │ │ └── routesValue.js │ ├── application.co │ ├── application.css │ ├── index.js │ └── main.js ├── index.html_vm ├── package.json ├── postcss.config.js ├── webpack.config.js └── webpack.config.prod.js
etc
, 一些公共配置性內容,能夠放在這裏,方便查找、通用
img
, 用我多說麼?放圖片的啦
js
, 分爲features
和fw
兩大部分。這個內容略多,我後面詳述吧。
index.html_vm
, 單頁應用html
模版,最終的html
會由webpack
根據這個模版生成
package.json
, 項目的npm
描述文件,那些具體的工具命令(譬如剛纔用過的npm start
,都在這裏面定義好了)
postcss.config.js
, postcss
的配置文件
webpack.config.js
, 開發、調試環境使用的webpack
配置
webpack.config.prod.js
, 正式運行環境使用的webpack
配置。npm run release
命令會用這個配置,生成的結果都會給文件名加hash
,javascript
文件也會壓縮。
npm start
, 啓動調試服務器,使用webpack.config.dev.js
做爲webpack
配置,不直接生成物理文件,直接內存級別響應調試服務器資源請求。並且內置hot reload
,不用重啓服務,修改源碼,瀏覽器便可刷新看到新效果
npm run release
, 使用webpack.config.prod.js
做爲webpack
配置,生成壓縮、去緩存化的bundle
文件到es6-demo/build
目錄下。也就是說,若是你要發佈到生產環境或者其它什麼測試環境,你應該提供的是es6-demo/build
目錄下生成的那堆東西,而不是源碼。
js
目錄介紹common
那些通用的組件、指令、過濾器、服務。。。統統應該放在這裏,譬如爲了演示方便,我已經在features/common/directives
裏寫了一個autofocus.js
的指令,拿去用,不要客氣。代碼以下:
export default { type: 'directive',//聲明這是一個指令 name: 'autofocus',//聲明指令名 //聲明指令構造函數,詳見:https://docs.angularjs.org/api/ng/type/angular.Module#directive directiveFactory: function() { 'ngInject'; return { restrict: 'A', link($scope, element) { element[0].focus(); } }; } };
同時固然也能夠聲明諸如:組件、過濾器之類的公共工具,詳見:common
about
home
這兩個就是純粹爲了演示「功能 <對應> 路由」這個小原則而作的,你能夠分別在這兩個feature
下找到一個routes.js
,裏面的內容就描述了該功能對應一個(或多個)路由,是何等的easy。至於最後這個路由會怎樣被這個骨架使用,小夥伴們,好好研究哦!
這裏面都是些所謂"框架"級別的設置,有興趣的話挨個兒打開瞧瞧嘛,沒什麼大不了的。
特別注意,大部分時候,你的開發都應該圍繞
features
目錄展開,之因此叫fw
,就是和具體業務無關,除非你須要修改框架啓動邏輯,路由控制系統。。。,不然不須要動這裏的內容
入口文件
/** * * 這裏連用兩個ensure,是webpack的特性,能夠強制在bundle時將內容拆成兩個部分 * 而後兩個部分還並行加載 * */ //第一個部分是一個很小的spinner,在並行加載兩個chunk時,這個很是小的部分90%會競速成功 //因而你就看到了傳說中的loading動畫 require.ensure(['splash-screen/dist/splash.min.css', 'splash-screen'], function(require) { require('splash-screen/dist/splash.min.css').use(); require('splash-screen').Splash.enable('circular'); }); //因爲這裏是真正的業務,代碼多了太多,因此體積也更大,加載也更慢,因而在這個chunk加載完成前 //有個美好的loading動畫,要比生硬的白屏更優雅。 //放心,這個chunk加載完後,loading動畫也會被銷燬 require.ensure(['css/main.css', 'splash-screen', './main'], function(require) { require('css/main.css').use(); //這裏啓動了真正的「框架」 var App = require('./main').default; (new App()).run(); });
「框架」啓動器
//引入依賴部分 import angular from 'angular'; //引入Object幫助庫 import {pluck} from './fw/helper/object'; //引入feature註冊工具 import {declareFeatures, declareValues, declareDirectives, declareComponents, declareRunners, declareFilters} from './fw/helper/ngDeclare'; //引入三方依賴 import Extensions from './fw/ext/main'; //引入項目配置 import Configurators from './fw/config/main'; //引入項目常量設置 import Values from './fw/value/main'; //引入features import Things from './features/main'; //引入根組件 import Application from './application'; //引入啓動spinner控制器 import {Splash} from 'splash-screen'; class App { constructor() { //這裏至關於ng-app的名字 this.appName = 'es6-demo'; //找到全部的features this.features = Things.filter(t => t.type === 'feature' && t.name); } //檢查項目基本設置 validate() { if (!this.features || this.features.length === 0) { return console.warn('No features loaded'); } const modNames = pluck(this.features, 'name').sort(); for (let i = 0; i < modNames.length - 1; i++) { if (modNames[i] === modNames[i + 1]) { throw new Error('Duplicated Module: [ ' + modNames[i] + ' ], you have to specify another name'); } } } //從features實例中提取AngularJS module name //並將這些name做爲es6-demo的依賴 //會在下面createApp時用到 findDependencies() { this.depends = [...Extensions, ...this.features.map(f => f.name)]; } //建立angular應用 createApp() { declareFeatures(this.features); this.app = angular.module(this.appName, this.depends); this.app.component('application', Application); } //配置es6-demo configApp() { Configurators.forEach(Configurator => { this.app.config(Configurator.config); }); } //註冊fw下的「框架」級service registerServices() { declareValues(this.app, Values); declareDirectives(this.app, Things.filter(t => t.type === 'directive')); declareComponents(this.app, Things.filter(t => t.type === 'component')); declareRunners(this.app, Things.filter(t => t.type === 'runner')); declareFilters(this.app, Things.filter(t => t.type === 'filter')); } //看到了麼,這裏我會銷燬loading動畫,並作了容錯 //也就是說,即使你遇到了那微乎其微的情況,loading動畫比業務的chunk加載還慢 //我也會默默的把它收拾掉的 destroySplash() { Splash.destroy(); require('splash-screen/dist/splash.min.css').unuse(); setTimeout(() => { if (Splash.isRunning()) { this.destroySplash(); } }, 100); } //啓動AngularJS app launch() { angular.bootstrap(document, [this.appName]); } //順序激活全部模塊 run() { this.validate(); this.findDependencies(); this.createApp(); this.configApp(); this.registerServices(); this.destroySplash(); this.launch(); } } export default App;
features/home/main.js
//引入路由 import routes from './routes'; //引入全部本feature中要用到的組件 import home from './components/home'; import logo from './components/subs/logo'; import description from './components/subs/description'; import github from './components/subs/github'; import todoApp from './components/subs/todo'; import footer from './components/subs/footer'; //引入本feature中要用到的service import HomeService from './service/HomeService'; export default { type: 'feature',//聲明該模塊是一個feature name: 'home',//聲明feature的名字,必須的 routes,//倒入路由 component: {//註冊全部用到的組件 home, logo, description, github, todoApp, footer }, service: {//註冊全部用到的service HomeService } };
簡單到沒朋友
export default [ { id: 'home',//爲該路由起一個惟一標識符 isDefault: true,//聲明是否爲默認路由 when: '/home',//路由路徑 template: '<home></home>'//路由對應組件 } ];
//引入該組件對應的css,注意這裏不會有像vue那樣的做用域, //不過能幫助你分離css內容,也不錯的 import './home.css'; //導出組件聲明對象 export default { template: ` <logo></logo> <description></description> <github></github> <todo-app list="$ctrl.todos" loading="$ctrl.loading" on-toggle="$ctrl.toggleTodo(todo)" on-add="$ctrl.addTodo(todo)" on-archive="$ctrl.archive()"></todo-app> <footer></footer> `, controller: class { //下面是依賴注入的關鍵,經過https://github.com/schmod/babel-plugin-angularjs-annotate實現 /*@ngInject*/ constructor(HomeService) { this.HomeService = HomeService; this.todos = []; this.loading = true; } $onInit() { this.HomeService .getInitTodos() .then(todos => { this.todos = todos; this.loading = false; }); } addTodo(todo) { this.todos = [todo, ...this.todos]; } toggleTodo(todo) { this.todos = this.todos.map(t => { if (t.txt !== todo.txt) { return t; } return { finished: !todo.finished, txt: t.txt }; }); } archive() { this.todos = this.todos.filter(todo => !todo.finished); } $onDestroy() {} } };