ES6时代的

前端新架构

By

       汤桂川  @广发证券

About me

汤桂川

爱Coding,也爱Traveling

@ 腾讯 - AlloyTeam 前端工程师

  • WebQQ and Smart QQ
  • PC QQ and QQ群
  • 手机QQ:吃喝玩乐,兴趣部落

@ 广发证券 - 前端技术专家

  • 广发微店 - 易淘金
  • 广发大数据日志平台
  • 金钥匙 - 金融界的嘀嘀打车

Agenda - 大纲

① ES6介绍

  • JavaScript简史
  • 重点新特性
  • Coding Style
  • Best Practice

② ES6实战开发

  • Angular - ES6 

  • Angular 2.0

  • Babel

  • jspm, Webpack, Gulp

  • Linting,  Debug , Test

  • React, Node, Meteor - ES6

③ 发展趋势

  • ES7
  • Babel support
  • Trending

Part 1 - ES6介绍

 

  • 1995 LiveScript发布      随后改名为 JavaScript
  • 1997 ECMAScript 1 
  • 1999 ES3 (大变化)
  • ES4 流产 (harmony)
  • 2009  ES5(ES3.1)
  • 2015. 06. 17   ES6  ECMAScript 2015 

JavaScript简史

Brendan Eich  @Netscape

->1993年的小鲜肉

ECMAScript 5 在浏览器的实现情况

数据来源:http://kangax.github.io/compat-table/es5/

ECMAScript 6 在浏览器的实现情况

ES6 重点新特性

爱看规范的男生运气不会太差。

-- 乔布斯??

声明 - Declarations

let  && const 

var num = 222;

if (true) {
  num = 666; 
  let num;
  console.log('此时的num是什么:' + num );
}
  • 块级作用域
  • 作用域不提升
  • 不允许重复声明
  • const 常量
  • const vs immutable
for (var i=0; i<6; i++) {
  setTimeout(function() {
    console.log(i)
  },0);
}
for (let i=0; i<6; i++) {
  setTimeout(function() {
    console.log(i)
  },0);
}

Arrows

Syntax

const name = 'object name';
let obj = {
  name,
  init() {
    // Lexical this
    $.on('click', () => this.name);
  }
};
  • C# 3.0 lambda 
  • Lexical this
  • 单行隐性return value
  • No arguments object
// 以上代码等同于 ES5
var name = 'object name';
var obj = {
  name: name,
  init: function init() {
    var _this = this;
    
    $.on('click', function () {
      return _this.name;
    });
  }
};

严格模式下,模块里顶层的this是undefined

 

//改造一下上面例子,this变成什么?
let obj = {
    init: ()=> this
};

注意点(keng) :

// 上面代码等同于:
var obj = {
    init: function test() {
        return undefined;
    }
};

Template String

Syntax

// 多行字符串,引号
var multiStr = `In 'JavaScript' this is
                not legal.`;

// 字符串插值
var name = "QCon", time = "today";
console.log(`Hello ${name},
how are you ${time}?`);

注意点(keng) ?

Classes

Syntax

class Animal {
  constructor(name) {
    this.name = name;
  }
  say(msg){
    alert(`${this.name} says ${msg}`);
  }
  static staticMethod() {
    return '不能被实例继承,子类可继承父类';
  }
}
class Panda extends Animal {
  say(msg = 'hi') {
    super(msg); // 调用父类say方法
  }
}
let panda = new Panda('Tuan');
panda.say(); // 'Tuan says hi'
panda.say('Ooooh'); // 'Tuan says Ooooh'
  • constructor
  • this
  • default
  • extends
  • super
  • 静态方法
// 上面代码转换为ES5 
'use strict';

function _inherits(subClass, superClass) { 
if (typeof superClass !== 'function' && superClass !== null) { 
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); 
} subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); 
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ superClass; 
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Animal = (function () {
  function Animal(name) {
    _classCallCheck(this, Animal);
    this.name = name;
  }
  Animal.prototype.say = function say(msg) {
    alert(this.name + ' says ' + msg);
  };
  Animal.staticMethod = function staticMethod() {
    return '不能被实例继承,子类可继承父类';
  };
  return Animal;
})();
var Panda = (function (_Animal) {
  _inherits(Panda, _Animal);
  function Panda() {
    _classCallCheck(this, Panda);
    _Animal.apply(this, arguments);
  }
  Panda.prototype.say = function say() {
    var msg = arguments.length <= 0 || arguments[0] === undefined ? 'hi' : arguments[0];
    _Animal.prototype.say.call(this, msg); // 调用父类say方法
  };
  return Panda;
})(Animal);
var panda = new Panda('Tuan');
panda.say(); // 'Tuan says hi'
panda.say('Ooooh'); // 'Tuan says Ooooh'
// 好
class SomeClass {
  constructor() {
    // constructor
  }

  get aval() {
    // public getter
  }

  set aval(val) {
    // public setter
  }

  doSth() {
    // 公用方法
  }

  get _aval() {
    // private getter
  }

  set _aval() {
    // private setter
  }

  _doSth() {
    // 私有方法
  }
}

Modules

Syntax

// 模块导入
import { lightRed } from './colors';
import { Component } from 'angular2/core';
import * as UserModule from './user.module';

let userCtrl = UserModule.ctrl;
// 模块导出
export { userCtrl };
export class App{
  constructor() {}
};
export default lightRed;

AMD, CommonJS, UMD

More Detail in: 下一代模块化的js

  • vs CommonJS
  • 循环依赖?
  • Native, HTTP 2
var webpackOptions = {
  resolve: {
     root: [
         path.resolve(__dirname, "../src/app"),//根路径
         path.resolve(__dirname, "../../public-modules")
     ],
     extensions: ['', '.js'],
     alias: {
        functions:  "utils/functions.js" // 配置需要的文件别名
     }
  }
};

//import {resolveInject} from '../../../../public-modules/utils/functions';
import {resolveInject} from 'functions';

ES6Features

查看详情:  ES6features(中文版)

More Info About ES6:

Part 2 - ES6实战开发

Workflow:

  • Angular - ES6
  • Babel
  •  Webpack & Gulp & JSPM
  • ESlint
  • Debug & Unit Test
  • React , Node, Meteor - ES6

Angular - ES6

⬇️

Babel

⬇️

 Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

Angular.js

  • Angular ES5
  • Angular ES6
  • Angular 2.0

Angular ES6 VS ES5

angular.module('myapp', ['localInfos'])
.service('locationService', ['localInfo', '$q',
 function(localInfo, $q) {
    $scope.localInfo = localInfo;

    // 打印当前状态
    $scope.printInfo = function(msg) {
       msg = msg || 'unknown';
       console.log('State:' + msg);  
    };

    // 寻找当前位置
    $scope.findLocation = function() {
      var deferred = $q.defer();
      var self = this;
      
      navigator.geolocation.getCurrentPosition(function(pos) {
        pos = pos || {};
        self.printInfo('Location');
        self.localInfo.save(pos);
        deferred.resolve('You are at ' + pos.coords.latitude);
      }, deferred.reject);

      return deferred.promise;
    };
  }
]);

ES5 Angular 1.x Service

angular.module('myapp', ['localInfos'])
.service('locationService', LocationService);

class LocationService {
    constructor(localInfo, $q) {
        'ngInject';
        this.localInfo = localInfo;
        this.$q = $q;
    }

    // 打印当前状态
    printInfo(msg = 'unknown') {
       console.log(`State: ${msg}`);  
    }

    // 寻找位置
    findLocation() {
        return this.$q((resolve, reject) => {

            navigator.geolocation.getCurrentPosition((pos = {}) => {
                this.printInfo('Location');
                this.localInfo.save(pos);
                resolve(`You are at ${pos.coords.latitude}`);
            }, reject);
        });
    }
}

ES6 Angular 1.x Service

// 'app/index.js'
import angular from 'angular';
import UserService from './user.service';
import UserController from './user.controller';

angular.module('myApp',[])
.factory('userService', UserService)
.controller('userController', UserController);

使用gulp-inject进行自动import与组装angular模块

Note: 文件遵循自动化命名规范(如: shop.controller.auto.js)

Angular with es6 In Depth

// 变成自动注入与Angular模块组装
import ShopController from './shop.controller.auto';

angular.module('shop',[])
.controller('shopController', ShopController);
// "shop/shop.module.js"
import { ShopController } from './shop/shop.controller';
import { ShopService } from './shop/shop.service';
 
let ctrl = ShopController;
let svc = ShopService.init; 
 
export { svc };
export { ctrl };

另一种按页面进行分模块导入导出,

并可DIY暴露特定方法的模式

// 调用方 'index.js'
import * as ShopModules from './shop/shop.module';
 
angular.module('microshop',[])
.factory('shopSvc', ShopModules.svc)
.controller('shopCtrl', ShopModules.ctrl);
// 'user.service.js'
class UserService {
    constructor($http) {
        'ngInject';

        this.$http = $http;
        this.userUrl = '/serve/user/';
    }
    getUser(name = 'Hanmeimei') {
        return this.$http.get(this.userUrl + name)
            .then((response) => response.data);
    }
}

export default UserService;

'ngInject': 使用ng-annotate进行DI依赖自动注入

<div ng-app="myApp" ng-strict-di>

'ng-strict-di': missing DI annotations; ng 1.3 or later

Angular ES6 VS 2.0

Angular 2.0

  • Why

  • What

 

 

  • When

AtScript vs TypeScript

No $scope, controller, default 2-way data binding

No $watch, $apply: Zone.js

(events) , [properties]

 

 迁徙计划:

       Angular 1.4 Router

       Angular 1.5 Component

// Angular 2.0
import { UnicornHorn } from "../animals/unicorn";
class UnicornHype {
    constructor(unicornHorn:UnicornHorn) {
        this.horn = unicornHorn;
    }
    findUnicorn() {
        return new Promise((resolve, reject) => {
            navigator.geolocation.getCurrentPosition((pos) => {
                this.horn.thrust();
                resolve(`The unicorn is at ${pos.coords.latitude}`);
            }, reject);
        });
    }
}
// Angular ES6
angular.module('test', ['unicorn']).service('unicornHype', UnicornHype);
class UnicornHype {
    constructor(unicornHorn, $q) {
        'ngInject';    
        this.horn = unicornHorn;
        this.$q = $q;
    }
    findUnicorn() {
        return this.$q((resolve, reject) => {
            navigator.geolocation.getCurrentPosition((pos) => {
                this.horn.thrust();
                resolve(`The unicorn is at ${pos.coords.latitude}`);
            }, reject);
        });
    }
}

差异在哪儿?

完美

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

1.Compatibility(支持度高):

2.生态圈与社区成熟:

  • WHY

Eg:引擎优化

// ES6 
(...args) => args


// 转换为ES5
(function () {
  for (
        var _len = arguments.length, 
        args = Array(_len),
         _key = 0; 
        _key < _len;
         _key++
      ) {
    
    args[_key] = arguments[_key];
  }

  return args;
});

Babel中有哪些优雅的实现?

More Information:

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM 

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

// gulp与babel结合

var gulp = require("gulp");
var babel = require("gulp-babel");

gulp.task("default", function () {

  return gulp.src("src/app.js")

    .pipe(babel())

    .pipe(gulp.dest("dist"));
});

More Information:

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

        -- ES6,AMD,CommonJS...

        -- babel-runtime

How

What

<!-- index.html -->

<script src="jspm_packages/system.js">
</script>
<script src="config.js"></script>
<script>
  System.import('./app/index');
</script>
// config.js
System.config({
  "baseURL": "/",
  "transpiler": "babel",
  "paths": {
    "*": "*.js",
    "github:*": "jspm_packages/github/*.js"
  }
});
  • Package Manager

        -- local git repos, Github, NPM, Bower

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

ESLint

The pluggable linting utility for JavaScript and JSX

  • Why


  • How
  1. Support Babel    Transformed: ES7...
  2. Error Location Info

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

HOW

  • - > ES5
  • Source Maps
  • Runtime Transpiler

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

Tools

  • jasmine.js
  • karma.js

Setting up the test environment:

  • package.json
  • karma.config.js
  • test-context.js

ES6 化的单元测试

// karma.config.js
module.exports = function(config) {
    config.set({
        browsers: ['PhantomJS'],
        frameworks: ['jasmine'],
        webpack: {
            module: {
                loaders: [{
                    test: /\.js/, 
                    loader: 'babel-loader', 
                    exclude: /node_modules/ 
                }]
            },
            watch: true
        }
    });
};
// test.js
export class Calculator{

    add(op1,op2){
        return op1 + op2;
    }

    subtract(op1,op2){
        return op1 - op2;
    }
}

测试的目标文件如下:


// calculator-spec.js
import {Calculator} from './calculator';

describe('Calculator', () => {
   it('should add two numbers', () => {
       let calculator = new Calculator();
       let sum = calculator.add(1,4);
       expect(sum).toBe(5);
   });
    it('should subtract two numbers', () => {
        let calculator = new Calculator();
        let sum = calculator.subtract(4,1);
        expect(sum).toBe(3);
    });
});

ES6化的Unit Test 文件:

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = { 
    initialCount: React.PropTypes.number 
};
Counter.defaultProps = { initialCount: 0 };

Tools for react, jsx

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React Node, Meteor - ES6

           No more --harmony (Node v0.12)

 

 

  • shipping
  • staged
  • in progress

Angular - ES6

⬇️

Babel

⬇️

Webpack & Gulp & JSPM

⬇️

ESlint

⬇️

Debug & Unit Test

⬇️

React , Node, Meteor - ES6

 ES6 is now the official JavaScript

  of the Meteor platform.

 

 Every new Meteor project now uses ES6

  by default, in every JS file. 

Projects and Info

Part 3 - 发展趋势

标准是怎样炼成的

方案生命周期 - stages:

   Stage 0  : Strawman  建议方案

   Stage  1  : Proposal     提案       

   Stage  2 :  Draft           草案          

   Stage  3 :  Candidate  候选方案   

   Stage  4 :  Finished     定案

EMCAScript 2016

  1. Array.prototype.includes 

 

2. Exponentiation Operator (乘方)

[1,2,3,NaN].includes(NaN) === true
[1,2,3, -0].includes(0) === true
['a', 'b', 'c'].includes('a', 'b') === true
// 数字vs字母vs数字字符串混合
let cubed = 3 ** 4;  // 3*3*3*3

Stage 0

Stage 1

Stage 2

Babel 支持

Stage 3

使用Babel玩转ES7 - 插件导入

  • 开启某特性支持
{
  "plugins": [
    ["transform-async-to-module-method", {
      "module": "bluebird",
      "method": "coroutine"
    }]
  ]
}

More Information:

<thank-you  author="汤桂川" [date]="'2016.04'">

</thank-you>

Homework

  1. 在Babel线上平台编写ES6代码,并观察转换后的代码是怎么样的,是否有问题?
  2. 使用ES6语法改写一个不少于200行代码的文件(可拆解为多个文件)
  3. 将这些ES6文件用Babel,gulp等工具或脚手架 运行起来
  4. 制造错误,看看怎么来调试ES6的代码

ES6时代的前端新架构

By Tang Guichuan

ES6时代的前端新架构

ES6时代的前端新架构- es6 architecture

  • 2,343