资讯专栏INFORMATION COLUMN

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

lastSeries / 1009人阅读

摘要:不用我赘述,前端开发人员一定耳熟能详。命令会用这个配置,生成的结果都会给文件名加,文件也会压缩。可用工具介绍启动调试服务器,使用作为配置,不直接生成物理文件,直接内存级别响应调试服务器资源请求。

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

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

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,代码可以简洁如斯:

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的脚手架。

安装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?时,国内用户选择第一个淘宝镜像安装速度会快很多)

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

开启调试之旅
#进入刚创建的项目目录
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, 分为featuresfw两大部分。这个内容略多,我后面详述吧。

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命令会用这个配置,生成的结果都会给文件名加hashjavascript文件也会压缩。

可用工具介绍

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目录介绍 features

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。至于最后这个路由会怎样被这个骨架使用,小伙伴们,好好研究哦!

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";
//引入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;
用ES6写Feature

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
    }
};
用ES6写路由

简单到没朋友

export default [
    {
        id: "home",//为该路由起一个唯一标识符
        isDefault: true,//声明是否为默认路由
        when: "/home",//路由路径
        template: ""//路由对应组件
    }
];
用ES6写组件
//引入该组件对应的css,注意这里不会有像vue那样的作用域,
//不过能帮助你分离css内容,也不错的
import "./home.css";

//导出组件声明对象
export default {
    template: `
        
        
        
        
        
`, 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() {} } };

最后,你可能还有其它问题,直接来看看这里,或者Github上给我提issue也未尝不可呢

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79410.html

相关文章

  • 前端相关汇总

    摘要:简介前端发展迅速,开发者富有的创造力不断的给前端生态注入新生命,各种库框架工程化构建工具层出不穷,眼花缭乱,不盲目追求前沿技术,学习框架和库在满足自己开发需求的基础上,然后最好可以对源码进行调研,了解和深入实现原理,从中可以获得更多的收获随 showImg(https://segmentfault.com/img/remote/1460000016784101?w=936&h=397)...

    BenCHou 评论0 收藏0
  • JavaScript 就要统治世界了?

    摘要:欢迎使用中文文档架构概览是网易项目团队开发的一个基于进行开发的应用层框架,提供了一个轻量级的容器来编写简单可维护的。 JavaScript 可以……嘛,不就是操作一下 DOM,可以让元素飞来飞去吗JavaScript 是……不就是用 jQuery 让网页动起来,顶多就是再用用 Ajax 和后端进行一下数据交换吗JavaScript 是一门……最讨厌和鄙视这种弱类型不需要编译的脚本语言...

    AbnerMing 评论0 收藏0
  • 2019,开发者应该学习的16个JavaScript框架

    摘要:它不仅从前端移动到后端,我们也开始看到它用于机器学习和增强现实,简称。由于其高使用率,年的现状调查将其称为采用的安全技术。机器学习框架在年的开发者峰会上,宣布了他们的机器学习框架的实现,称为。更高级别的用于在之上构建机器学习模型。 2019,开发者应该学习的16个JavaScript框架 showImg(https://segmentfault.com/img/remote/14600...

    Harpsichord1207 评论0 收藏0
  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • AngularJS简述

    流行框架 简介 angularjs是一款非常优秀的前端高级JS框架,由谷歌团队开发维护,能够快速构建单页web应用,化繁为简 无论是angularjs还是jQuery都是用原生JS封装的 库:对代码进行封装,调用封装的方法,简化操作 传统方式是用get方式获取元素,然后点方法 jQuery库实现了对获取方式的封装,对方法的封装 框架:提供代码书写规则,按照规则去写代码,框架会帮我们实现响应的功能...

    Jason 评论0 收藏0

发表评论

0条评论

lastSeries

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<