上一篇通過TodoList的練習,目的是爲了讓大家理解ES6中各種新特性的實際用途。
最好的學習方法就是實踐,所以這節課結合實際項目,來更好的理解和掌握ES6的用途和使用場景,達到靈活運用的目的。
1、模塊化
以項目中普遍會有的config.js文件爲例,實現export導出:
const githubURL = "OUR GITHUB URL HERE";
const staticServer = "http://xxx.com";
const testsPath = `zaz-${type}-${name}/tests/index.htm?zaz[env]=tests`;
const name = "stalker";
const type = "mod";
const version = "0.0.1";
const state = "ok";
const description = "JavaScript API to deal with user data";
let globalpkg = null;
const config = {
_static: {
name,
version,
state,
description,
docs: `${githubURL}/pages/terra/zaz-${type}-${name}`,
source: `${githubURL}/Terra/zaz-${type}-${name}`,
tests: `${staticServer}/fe/${testsPath}`,
dependencies: ['mod.wilson']
}
};
export default config;
再在其他文件中通過import實現導入:
import config from './config';//導入ES6模塊
import { globalpkg } from './config';
import factory from './factory';
zaz.use((pkg) => {
"use strict";
config.dynamic.globalpkg = pkg;
pkg.require(['modFactory'], (modFactory) => {
modFactory.create(pkg.utils.deepMerge(config._static, factory));
});
});
使用ES6統一的模塊化規範,可以提高代碼的可讀性,更易於維護。
2、數組操作
import React,{Component} from 'react';
class RepeatArray extends Component{
constructor() {
super();
}
render(){
const names = ['Alice', 'Emily', 'Kate'];
return (
<div>
{
let p=document.querySelectorAll('p');
let pArr=Array.from(p);
pArr.forEach(function(item){
console.log(item.textContent);
});
Array.from(pArr,function(item){return item + 'ES6'});
}
</div>
);
}
}
使用Array.from 同時對每個元素進行操作。
3、模板字符串
常見的使用場景便是寫組件模板時使用:
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
可以在模板字符串中任意的嵌入變量, 調用函數等。
4、解構與擴展操作符
擴展操作符在父組件給子組件傳遞一批屬性的情境中更爲方便。
下面的例子把className以外的所有屬性傳遞給div標籤
class AutoloadingPostsGrid extends React.Component {
render() {
var {
className,
...others, // contains all properties of this.props except for className
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
使用react開發最常見的問題就是父組件要傳給子組件的屬性較多時比較麻煩
class MyComponent extends React.Component{
//假設MyComponent已經有了name和age屬性
render(){
return (
<SubComponent name={this.props.name} age={this.props.age}/>
)
}
}
使用擴展操作符可以變得很簡單
class MyComponent extends React.Component{
//假設MyComponent已經有了name和age屬性
render(){
return (
<SubComponent {...this.props}/>
)
}
}
上述方式是將父組件的所有屬性都傳遞下去,如果這其中有些屬性不需要傳遞呢?也很簡單
class MyComponent extends React.Component{
//假設MyComponent有很多屬性,而name屬性不需要傳遞給子組件
var {name,...MyProps}=this.props;
render(){
return (
<SubComponent {...Myprops}/>
)
}
}
上述方法最常用的場景就是父組件的class屬性需要被單獨提取出來作爲某個元素的class,而其他屬性需要傳遞給子組件。
在構建通用容器時,擴展屬性會非常有用。
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
5、Promise
場景一 : 所有圖片加載完再顯示在頁面上,避免頁面閃動。
function loadImg(src) {
return new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = src;
img.onload = function () {
resolve(img)
}
img.onerror = function (err) {
reject(err)
}
})
}
function showImgs(imgs) {
imgs.forEach(function(img){
document.body.appendChild(img);
})
}
Promise.all([
loadImg('https://example.com/pic1')
loadImg('https://example.com/pic2')
loadImg('https://example.com/pic3')
]).then(showImgs)
場景二: 多個資源中只要加載了其中一種就可。
{
function loadImg(src) {
return new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = src;
img.onload = function () {
resolve(img)
}
img.onerror = function (err) {
reject(err)
}
})
}
function showImgs(img) {
document.body.appendChild(img);
}
Promise.race([
loadImg('https://example.com/pic1')
loadImg('https://example.com/pic2')
loadImg('https://example.com/pic3')
]).then(showImgs)
}
6、Generator
在異步編程的解決方案中,Generator比Promise更高級些。
使用場景:抽獎次數邏輯控制、長輪詢(服務器請求報錯再次請求, 定時發送請求)
// 以前控制次數是在全局中申明,既不安全又影響性能
let draw = function (count) {
// 具體抽獎邏輯
console.info(`剩餘${count}次`)
}
let residue = function* (count) {
while (count > 0) {
count--
yield draw(count)
}
}
let start = residue(5)
let btn = document.createElement('button')
btn.id='start'
btn.textContent = '抽獎'
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click', function() {
start.next()
}, false)
}
{
// 長輪詢
let ajax = function* () {
yield new Promise(function(resolve, reject) {
setTimeout(() => {
resolve({code: 0})
}, 200);
})
}
let pull = function () {
let generator = ajax()
let step = generator.next()
step.value.then(function(d){
if (d.code !=0 ) {
setTimeout(() => {
console.info('wait')
pull()
}, 100);
} else {
console.info(d)
}
})
}
pull()
通過下面這張流程圖,再加深下對Generator的理解。
7、await
在項目中,有時會出現需要同時依賴多個接口,而且必須在這幾個請求都處理完後,才能開始處理數據的情況。我們可以在一個 async函數 中寫多個await 語法的請求,然後逐個處理,但是這樣效率太低了。
多個請求是可以並行執行的。這時就可以結合 Promise.all 高級方法來處理,可以同時發起多個請求,然後統一處理接口返回數據。
爲了方便,這裏演示同時請求2個url,多個的也是一樣的, 如下:
export default {
name: 'hello1',
data () {
return {
msg: 'Hello Vue.js',
info: {},
user1: {},
user2: {}
}
},
methods: {
async getUserInfo () {
try {
const res = await this.$http.get('http://aaa.com/userinfo');
this.info = res.data
} catch (e) {
console.log(e);
}
},
async get2UserInfo () {
try {
const res = await Promise.all([
this.$http.get('http://aaa.com/userinfo1'),
this.$http.get('http://aaa.com/userinfo2'),
])
this.user1 = res[0].data;
this.user2 = res[1].data;
} catch (e) {
console.log(e);
}
}
},
created () {
this.getUserInfo();
this.get2UserInfo();
}
}
再次運行項目,可以發現頁面在初始化的時候同時發起了3個請求,並正常渲染出了接口數據。
注意:這裏的 Promise.all() 的參數是一個函數執行隊列,它們會同時發起,然後都請求成功後,會將隊列的每個任務的結果組裝成一個結果數據,然後返回。
8、類操作
先實戰創建一個List類
import Utils from "./Utils.js";
class List {
constructor(title = "", items = [], isEditable = true, id = "") {
this.id = (id) ? id : Utils.guid();
this.title = title;
this.items = items;
this.isEditable = isEditable;
}
render() {
var html = `<div class="list" data-id="${this.id}" data-iseditable="${this.isEditable}">
<h2 class="list-title">${this.title}</h2>
${(this.isEditable) ? "<div class='btn delete-list danger' data-action='delete-list'>X</div>" : ""}
<ul class="items">`;
this.items.forEach(function(item) {
html += item.render();
});
html += `
</ul>
${(this.isEditable) ? "<div class='btn add-item success' data-action='add-item'>Add Item</div>" : ""}
</div>`;
return html;
}
getItemById(id) {
let item = this.items.filter(i => i.id === id);
return ((item.length) ? item[0] : null);
}
add(item) {
this.items.push(item);
}
remove(item) {
this.items = this.items.filter(i => (i.id !== item.id));
}
}
export default List;
在app.js中創建List實例:
import List from "./List.js";
import Utils from "./Utils.js";
import Status from "./Status.js";
class App {
constructor(lists = []) {
this.lists = lists;
}
getDueItems() {
let dueItems = [];
this.lists.forEach(function(list) {
list.items.forEach(function(item) {
if (item.date && item.status === Status.PENDING && Utils.dateDiffInDays(new Date(item.date), new Date()) > 0) {
dueItems.push(item);
}
});
});
return dueItems;
}
getItemById(id) {
const filterById = (function filterById(id1) {
return function(listItem) {
return listItem.id === id1;
};
}(id));
for (let i = 0; i < this.lists.length; i++) {
let item = this.lists[i].items.filter(filterById);
if (item.length) {
return item[0];
}
}
return null;
}
getListById(id) {
let list = this.lists.filter(l => l.id === id);
return ((list.length) ? list[0] : null);
}
render() {
let pastDueList = new List("Past Due Date", this.getDueItems(), false);
let html = `<div class="app">
<div class="btn add-list success" data-action="add-list">[+] Add List</div>
`;
this.lists.forEach(function(list) {
html += list.render();
});
html += pastDueList.render();
html += "</div>";
return html;
}
add(list) {
this.lists.push(list);
}
remove(list) {
this.lists = this.lists.filter(l => (l.id !== list.id));
}
}
export default App;
ES6中的類能讓我們可以用更簡明的語法實現繼承,也使代碼的可讀性變得更高。
9、Proxy
Proxy可以讓我們根據不同的業務邏輯進行相應的處理, 對原對象進行映射,生成新的對象,操作新對象的同時通過一定的規則修改原對象。
// 先定義一個函數
function validator(target,validator) {
return new Proxy(target, {
_validator: validator,
set(targer,key,value,proxy){
if (targer.hasOwnProperty(key)) {
let va = this._validator[key];
if (!!va(value)) {
return Reflect.set(target,key,value,proxy)
}else {
throw Error(`不能設置${key}到${value}`)
}
}else {
throw Error(`${key} 不存在`)
}
}
})
}
const personValidator={
name(val){
return typeof val === 'string'
},
age(val){
return typeof val === 'number' && val >18
}
}
class Person{
constructor(name,age) {
this.name = name
this.age = age
return validator(this, personValidator)
}
}
const person = new Person('lilei', 30)
console.info(person) // Proxy {name: "lilei", age: 30}
// person.name = 48 // Uncaught Error: 不能設置name到48
// console.info(person)
person.name = 'han mei mei'
console.info(person) // Proxy {name: "han mei mei", age: 30}
點評:使用Proxy進行數據校驗,將對象和驗證分離開,便於後期代碼的維護。
總結
本篇主要通過實際項目中的例子回顧ES6的知識點,幫大家梳理重點和難點。學會ES6語法不難,活學活用到項目纔是關鍵。
希望各位小夥伴能充分認識到ES6的強大,通過在實際工作中不斷地使用ES6,提升代碼質量和工作效率,這樣就能多一點喝茶看電影的時間。
最後祝大家都能成爲別人眼中的程序猿大牛,O(∩_∩)O哈哈~