ES6时代的
前端新架构
By
汤桂川 @广发证券
About me
汤桂川
- Github : @lightningtgc
- Weibo : @汤桂川_gc
- Zhihu : @汤桂川
- Email : tangguichuan@gmail.com
爱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;
}
};
More detail in:
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) ?
More detail in:
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
- map + set
- weakmap + weakset
- enhanced object literals - 增强对象字面量
- proxies - 代理
- symbols - 第七种数据类型
- binary and octal literals
- unicode - 字符串扩展
- reflect api - 反射api
- tail calls - 尾调用
查看详情: 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 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);
});
}
}
差异在哪儿?
完美
More Info about Angular 2.0:
- 广发证券技术博客
- https://slides.com/tangguichuan/dissect-angular2/#
- Angualr.js 2.0 官网
- Try angular2 today
- asm.js , Flow
- Change detection in angular 2
- Angular2 template syntax(ng-model)
- Building applications in Angular
- The core concepts of angular 2
Angular - ES6
⬇️
Babel
⬇️
Webpack & Gulp & JSPM
⬇️
ESlint
⬇️
Debug & Unit Test
⬇️
React , Node, Meteor - ES6
1.Compatibility(支持度高):
2.生态圈与社区成熟:
-
WHY
Eg:引擎优化
More Detail:
// 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
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
- Support Babel Transformed: ES7...
- 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
More Info:
Angular - ES6
⬇️
Babel
⬇️
Webpack & Gulp & JSPM
⬇️
ESlint
⬇️
Debug & Unit Test
⬇️
React , Node, Meteor - ES6
- 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
- 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
- es7.comprehensions - [ for (i of [ 1, 2, 3]) i*i ]
- es7.classProperties - 静态类属性
- es7.doExpressions
- es7.functionBind - ::
Stage 1
- es7.decorators - 装饰器语法 Angular 2
- es7.exportExtensions
- es7.trailingFunctionCommas
Stage 2
Babel 支持
Stage 3
- es7.async-await - koa2
使用Babel玩转ES7 - 插件导入
- 开启某特性支持
{
"plugins": [
["transform-async-to-module-method", {
"module": "bluebird",
"method": "coroutine"
}]
]
}
More Information:
<thank-you author="汤桂川" [date]="'2016.04'">
</thank-you>
- Github : @lightningtgc
- Weibo : @汤桂川_gc
- Zhihu : @汤桂川
- Email : tangguichuan@gmail.com
Homework
- 在Babel线上平台编写ES6代码,并观察转换后的代码是怎么样的,是否有问题?
- 使用ES6语法改写一个不少于200行代码的文件(可拆解为多个文件)
- 将这些ES6文件用Babel,gulp等工具或脚手架 运行起来
- 制造错误,看看怎么来调试ES6的代码
ES6时代的前端新架构
By Tang Guichuan
ES6时代的前端新架构
ES6时代的前端新架构- es6 architecture
- 2,343