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() {} } };