用ES6编写AngularJS程序是怎样一种体验

AngularJS不用我赘述,前端开发人员一定耳熟能详。有人称之为MVWhatever框架,意思是使用AngularJS,你可以参考任意范式进行应用开发,无论是MVC、还是MVVVM都信手拈来,只要你懂,范式在AngularJS手下,都可以轻松适配。

随着各种现代浏览器、以及node对ES6的支持,已经有越来越多的ES6特性可以在程序中使用,她们给开发过程带来的便利不言而喻,举个小例子,我想从一个数组里找一些符合条件的数据,放入另一个数组内,过去我们这么写:

var list = [],
i;

for (i = 0; i
如果改用数组的高阶函数,再配合ES6的arrow function,代码可以简洁如斯:

var list = originalList.filter(item => item.gender === 'male');

console.log(list); //符合条件的新数组
既然有如此优雅的语法糖能让我们的开发变得high到爆,那过去我们认为屌炸天的AngularJS(现在也屌炸天,只不过还有Angular2, React, VUE横空出世)是不是可以用ES6来写?少年不要怀疑,真的可以哦![br]UI设计

一个良好、快速、简洁的starter工具有利于我们对ES6编写AngularJS的深入认知,所以我要用一个骨架生成器generator-es6-angular来创建新项目,该generator是依托于yeoman的脚手架。

安装yo

npm install -g yo
请注意前缀sudo,如果你使用的是unix like操作系统的话

安装generator-es6-angular

npm install -g generator-es6-angular
请注意前缀sudo,如果你使用的是unix like操作系统的话

使用generator-es6-angular创建项目

先找个你喜欢的目录,然后运行下面的命令,因为一会新项目会直接创建在该目录下。

yo es6-angular
上面命令回车后,生成器会问你如下问题,老实作答即可(注意: 对单页应用没经验的孩纸,在Use html5 model这个问题上,请选择No; 当问你Which registry would you use?时,国内用户选择第一个淘宝镜像安装速度会快很多)

UI设计

当命令执行完毕后,你就能在当前目录下看到刚才创建的项目了,本例中我使用的project name是es6-demo。

开启调试之旅

进入刚创建的项目目录

cd es6-demo

启动调试服务

npm start
然后你就可以在http://localhost:8080下,看到刚创建的项目的运行效果了:

UI设计

项目简介

骨架结构

es6-demo
├── css
├── etc
├── img
├── js
| ├── features
| │   ├──common
| │   │   ├── logical
| │   │   └── ui
| │   ├── about
| │   │   ├── controller
| │   │   └── partials
| │   └── home
| │   ├── controller
| │   ├── partials
| │   └── service
| └── fw
| ├── config
| ├── ext
| ├── init
| ├── lib
| └── service
├── index.html_vm
├── package.json
├── webpack.config.dev.js
├── webpack.config.prod.js

  1. css, 这个不用多说吧,里面有个main.css,自己随便改改看嘛。我这里没有默认引入less或者sass理由非常简单,留给开发人员选择自己喜爱的工具

  2. etc, 一些公共配置性内容,可以放在这里,方便查找、通用

  3. img, 用我多说么?放图片的啦

  4. js, 分为features和fw两大部分。这个内容略多,我后面详述吧。

  5. index.html_vm, 单页应用html模版,最终的html会由webpack根据这个模版生成

  6. package.json, 项目的npm描述文件,那些具体的工具命令(譬如刚才用过的npm start,都在这里面定义好了)

  7. webpack.config.dev.js, 开发、调试环境使用的webpack配置

  8. webpack.config.prod.js, 正式运行环境使用的webpack配置。npm run release命令会用这个配置,生成的结果都会给文件名加hash,javascript文件也会压缩。

可用工具介绍

  1. npm start, 启动调试服务器,使用webpack.config.dev.js作为webpack配置,不直接生成物理文件,直接内存级别响应调试服务器资源请求。而且内置hot reload,不用重启服务,修改源码,浏览器即可刷新看到新效果

  2. npm run release, 使用webpack.config.prod.js作为webpack配置,生成压缩、去缓存化的bundle文件到es6-demo/build目录下。也就是说,如果你要发布到生产环境或者其它什么测试环境,你应该提供的是es6-demo/build目录下生成的那堆东西,而不是源码。

  3. npm run dev, 使用webpack.config.dev.js作为webpack配置,生成物理文件。

js目录介绍

features

common

那些通用的逻辑、UI组件可以通通放在这里,譬如为了演示方便,我已经在features/common/ui里写了一个Autofocus.js的指令,拿去用,不要客气。

features/common/logical里也默认放了一个公共的Header,就是最上面的那个导航。

about[br]home

这两个就是纯粹为了演示“功能 路由”这个小原则而做的,你可以分别在这两个feature下找到一个Routes.js,里面的内容就描述了该功能对应一个(或多个)路由,是何等的easy。至于最后这个路由会怎样被这个骨架使用,小伙伴们,好好研究哦!

fw

这里面都是些所谓"框架"级别的设置,有兴趣的话挨个儿打开瞧瞧嘛,没什么大不了的。

特别注意,大部分时候,你的开发都应该围绕features目录展开,之所以叫fw,就是和具体业务无关,除非你需要修改框架启动逻辑,路由控制系统。。。,否则不需要动这里的内容

源码介绍

js/index.js

入口文件

/

  • 这里连用两个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();

});

js/main.js

“框架”启动器

//引入依赖部分
import angular from 'angular';
import Initializers from 'init/main';
import Extensions from 'ext/main';
import Configurators from 'config/main';
import Services from 'service/main';
import Features from 'features/main';
import {Splash} from 'splash-screen';

class App {

constructor() {    //这里相当于ng-app的名字    this.appName = 'es6-demo';    //实例化所有features    Features.forEach(function(Feature) {        this.push(new Feature());    }, this.features = []);}//从features实例中提取AngularJS module name//并将这些name作为es6-demo的依赖//会在下面createApp时用到findDependencies() {    this.depends = Extensions.slice(0);    var featureNames = this.features        .filter(feature => feature.export)        .map(feature => feature.export);    this.depends.push(...featureNames);}//激活初始化器,个别操作希望在AngularJS app启动前完成beforeStart() {    Initializers.forEach(function(Initializer) {        (new Initializer(this.features)).execute();    }, this);    this.features.forEach(feature => feature.beforeStart());}//创建es6-demo应用实例createApp() {    this.features.forEach(feature => feature.execute());    this.app = angular.module(this.appName, this.depends);}//配置es6-democonfigApp() {    Configurators.forEach(function(Configurator) {        (new Configurator(this.features, this.app)).execute();    }, this);}//注册fw下的“框架”级serviceregisterService() {    Services.forEach(function(Service) {        (new Service(this.features, this.app)).execute();    }, this);}//看到了么,这里我会销毁loading动画,并做了容错//也就是说,即便你遇到了那微乎其微的状况,loading动画比业务的chunk加载还慢//我也会默默的把它收拾掉的destroySplash() {    var _this = this;    Splash.destroy();    require('splash-screen/dist/splash.min.css').unuse();    setTimeout(function() {        if (Splash.isRunning()) {            _this.destroySplash();        }    }, 100);}//启动AngularJS applaunch() {    angular.bootstrap(document, [this.appName]);}//顺序激活所有模块run() {    this.findDependencies();    this.beforeStart();    this.createApp();    this.configApp();    this.registerService();    this.destroySplash();    this.launch();}

}

export default App;

用ES6写Feature

features/home/main.js

//一个feature的main.js负责管理该feature所用到的所有模块
import FeatureBase from 'lib/FeatureBase';
//引入路由
import Routes from './Routes';
//引入Controller
import HomeController from './controller/HomeController';
//引入service
import HomeService from './service/HomeService';

//继承自FeatureBase基类
class Feature extends FeatureBase {

constructor() {    //给feature一个名字,该名字在之前的findDependencies里用到    super('home');    //将路由绑定到该feature上    this.routes = Routes;}execute() {    //注册controller,下面我们继续讲如何写ES6版的controller    this.controller('HomeController', HomeController);    //注册service    this.service('HomeService', HomeService);}

}

//采用默认导出,别担心,再上一级调用方会正确处理的
export default Feature;

用ES6写路由

简单到没朋友

//引入路由对应的模版,还是因为webpack,将模版
//作为字符串引入,就是这么easy
import tpl from './partials/home.html';

export default [
{
id: 'home',
isDefault: true,//是否默认页面
when: '/home',//路由path
controller: 'HomeController',//处理该路由的controller
controllerAs: 'home',//模版里用的controller别名
template: tpl//模版字符串
}
];

用ES6写Controller

//ES6的引入机制,即便是图片资源,因为有了webpack,获
//取URL照样萌萌哒
import logoUrl from 'img/AngularJS-large.png';

//注意,我们已经开始用ES6的类来声明一个Controller了
class HomeController {

//这里我们用ng-annotate提供的注解方式,可以快捷注//入需要的依赖/*@ngInject*/constructor($scope, HomeService) {    this.$scope = $scope;    this.HomeService = HomeService;    this._init_();    this._destroy_();}_init_() {    this.logoUrl = logoUrl;    this.todos = [];    this.HomeService        .getInitTodos()        .then(todos => {            //又见arrow function            //注意这里的this            this.todos = todos;        });}addTodo(e) {    if (e.keyCode !== 13) {        return;    }    this.todos.push({txt: e.target.value});    e.target.value = '';}toggleTodo(todo) {    todo.finished = !todo.finished;}remaining() {    //计算剩余工作量,是不是还挺帅?    return this.todos.reduce((n, todo) => n + Number(!todo.finished), 0);}archive() {    //过滤,high不high    this.todos = this.todos.filter(todo => !todo.finished);}_destroy_() {    //如果有AngularJS未知的变量产生,记得手动销毁哦    this.$scope.$on('$destroy', function() {});}

}

//最后,采用默认导出
export default HomeController;
最后,你可能还有其它问题,直接来看看这里,或者Github上给我提issue也未尝不可呢

关键字:angularjs, es6, spa

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部