1. 程式人生 > >Babel中的stage-0,stage-1,stage-2以及stage-3

Babel中的stage-0,stage-1,stage-2以及stage-3

.get else hub 到你 resolv cti component else if 便在

大家知道,將ES6代碼編譯為ES5時,我們常用到Babel這個編譯工具。大家參考一些網上的文章或者官方文檔,裏面常會建議大家在.babelrc中輸入如下代碼:

技術分享 技術分享
{
    "presets": [
      "es2015",
      "react",
      "stage-0"
    ],
    "plugins": []
  }
技術分享 技術分享

我們現在來說明下這個配置文件是什麽意思。首先,這個配置文件是針對babel 6的。Babel 6做了一系列模塊化,不像Babel 5一樣把所有的內容都加載。比如需要編譯ES6,我們需要設置presets為"es2015",也就是預先加載es6編譯的相關模塊,如果需要編譯jsx,需要預先加載"react"這個模塊。那問題來了,這個"stage-0"又代表什麽呢? 有了"react-0",是否又有諸如"stage-1", "stage-2"等等呢?

事實上, ”stage-0"是對ES7一些提案的支持,Babel通過插件的方式引入,讓Babel可以編譯ES7代碼。當然由於ES7沒有定下來,所以這些功能隨時肯能被廢棄掉的。現在我們來一一分析裏面都有什麽。

1. 法力無邊的stage-0

為什麽說“stage-0” 法力無邊呢,因為它包含stage-1, stage-2以及stage-3的所有功能,同時還另外支持如下兩個功能插件:

  • transform-do-expressions
  • transform-function-bind

用過React的同學可能知道,jsx對條件表達式支持的不是太好,你不能很方便的使用if/else表達式,要麽你使用三元表達,要麽用函數。例如你不能寫如下的代碼:

技術分享 技術分享
var App = React.createClass({

    render(){
        let { color } = this.props;

        return (
            <div className="parents">
                {
                    if(color == ‘blue‘) { 
                        <BlueComponent/>; 
                    }else if(color == ‘red‘) { 
                        <RedComponent/>; 
                    }else { 
                        <GreenComponent/>; }
                    }
                }
            </div>
        )
    }
})
技術分享 技術分享

在React中你只能寫成這樣:

技術分享 技術分享
var App = React.createClass({

    render(){
        let { color } = this.props;


        const getColoredComponent = color => {
            if(color === ‘blue‘) { return <BlueComponent/>; }
            if(color === ‘red‘) { return <RedComponent/>; }
            if(color === ‘green‘) { return <GreenComponent/>; }
        }


        return (
            <div className="parents">
                { getColoredComponent(color) }
            </div>
        )
    }
})
技術分享 技術分享

transform-do-expressions 這個插件就是為了方便在 jsx寫if/else表達式而提出的,我們可以重寫下代碼。

技術分享 技術分享
var App = React.createClass({

    render(){
        let { color } = this.props;

        return (
            <div className="parents">
                {do {
                    if(color == ‘blue‘) { 
                        <BlueComponent/>; 
                    }else if(color == ‘red‘) { 
                        <RedComponent/>; 
                    }else { 
                        <GreenComponent/>; }
                    }
                }}
            </div>
        )
    }
})
技術分享 技術分享

再說說 transform-function-bind, 這個插件其實就是提供過 :: 這個操作符來方便快速切換上下文, 如下面的代碼:

技術分享 技術分享
obj::func
// is equivalent to:
func.bind(obj)

obj::func(val)
// is equivalent to:
func.call(obj, val)

::obj.func(val)
// is equivalent to:
func.call(obj, val)

// 再來一個復雜點的樣例

const box = {
  weight: 2,
  getWeight() { return this.weight; },
};

const { getWeight } = box;

console.log(box.getWeight()); // prints ‘2‘

const bigBox = { weight: 10 };
console.log(bigBox::getWeight()); // prints ‘10‘

// Can be chained:
function add(val) { return this + val; }

console.log(bigBox::getWeight()::add(5)); // prints ‘15‘
技術分享 技術分享

如果想更屌點,還可以寫出更牛逼的代碼:

技術分享 技術分享
const { map, filter } = Array.prototype;

let sslUrls = document.querySelectorAll(‘a‘)
                ::map(node => node.href)
                ::filter(href => href.substring(0, 5) === ‘https‘);

console.log(sslUrls);
技術分享 技術分享

2. 包羅萬象的stage-1

stage-1除了包含stage-2和stage-3,還包含了下面4個插件:

  • transform-class-constructor-call (Deprecated)
  • transform-class-properties
  • transform-decoratorsdisabled pending proposal update
  • transform-export-extensions

今天就到這裏了,改天我們接著分析下"stage-2"和"stage-3", 它們又有什麽新的特性呢,讓我們拭目以待吧。

深藏不露的stage-2

為什麽說 stage-2深藏不露呢,因為它很低調,低調到你可以忽略它,但事實上,它很有內涵的。它除了覆蓋stage-3的所有功能,還支持如下兩個插件:

  • syntax-trailing-function-commas
  • transform-object-reset-spread

syntax-trailing-function-commas

這個插件讓人一看覺得挺沒趣的,讓人甚至覺得它有點雞肋。因它不是對ES6功能的增加,而是為了增強代碼的可讀性和可修改性而提出的。如下面的代碼所示:


// 假設有如下的一個函數,它有兩個參數
function clownPuppiesEverywhere(
  param1,
  param2
) { /* ... */ }

clownPuppiesEverywhere(
  ‘foo‘,
  ‘bar‘
);

// 有一天,它需要變成3個參數,你需要這樣修改
function clownPuppiesEverywhere(
  param1,
- param2
+ param2, // 這一行得加一個逗號
+ param3  // 增加參數param3
) { /* ... */ }

clownPuppiesEverywhere(
  ‘foo‘,
- ‘bar‘
+ ‘bar‘, // 這裏的修改為逗號
+ ‘baz‘  // 增加新的參數
);

// 看到沒? 我們修改了4行代碼。。啊啊。修改了4行代碼。

修改了4行代碼,嗯嗯嗯。。追求高效的程序猿想想了,以後如果有更多參數了,我是不是要改等多行,得想想,代碼改的越少越好,於是有了下面的改動。。


// 我們來重新定義一下函數
function clownPuppiesEverywhere(
  param1,
  param2, // 註意這裏,我們加了一個逗號喲
) { /* ... */ }

clownPuppiesEverywhere(
  ‘foo‘,
  ‘bar‘, // 這裏我們也加了一個逗號
);

// 現在函數需要三個參數,我們來修改下
function clownPuppiesEverywhere(
  param1,
  param2,
+ param3, // 增加params3參數
) { /* ... */ }

clownPuppiesEverywhere(
  ‘foo‘,
  ‘bar‘,
+ ‘baz‘, // 增加第三個參數
);

// 叮叮當,我們只修改了兩行代碼就完成了,好開森

說實話吧,這個功能讓人有點很無語。不過程序猿對幹凈代碼的追求真的很讓人感動,還是值得鼓勵的。這個就是stage-2中"尾逗號函數”功能。哈哈哈哈。

transform-object-rest-spread

再來說transform-object-rest-spread, 其實它是對 ES6中解構賦值的一個擴展,因為ES6只支持對數組的解構賦值,對對象是不支持的。如下面的代碼所示:

// 獲取剩下的屬性

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

// 屬性展開
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }

大放異彩的stage3

為啥說stage3大放異彩呢?因為它支持大名鼎鼎的asyncawait, 這兩個哥們可是解決(Ajax)回調函數的終極解決方法呀!管你什麽異步,我都可以用同步的思維來寫,ES7裏面非常強悍的存在。總的來說,它包含如下兩個插件:

  • transform-async-to-generator
  • transform-exponentiation-operator

transform-async-to-generator

transform-async-to-generator主要用來支持ES7中的asyncawait, 我們可以寫出下面的代碼:

const sleep = (timeout)=>{
    return new Promise( (resolve, reject)=>{
        setTimeout(resolve, timeout)
    })
}

(async ()=>{
    console.time("async");
    await sleep(3000);
    console.timeEnd("async");
})()

再來一個實際點的例子

const fetchUsers = (user)=>{
    return window.fetch(`https://api.douban.com/v2/user/${user}`).then( res=>res.json())
}


const getUser = async (user) =>{
    let users = await fetchUsers(user);
    console.log( users);
}

console.log( getUser("flyingzl"))

提示: 由於asycnawait是ES7裏面的內容,現階段不建議使用。為了順利運行上面的代碼,建議用webpack進行編譯。

transform-exponentiation-operator

transform-exponentiation-operator這個插件算是一個語法糖,可以通過**這個符號來進行冪操作,想當於Math.pow(a,b)。如下面的樣例


// x ** y

let squared = 2 ** 2;
// 相當於: 2 * 2

let cubed = 2 ** 3;
// 相當於: 2 * 2 * 2


// x **= y

let a = 2;
a **= 2;
// 相當於: a = a * a;

let b = 3;
b **= 3;
// 相當於: b = b * b * b;

很簡單也很實用吧,哈。使用起來還是蠻方便的。

總結

通過以上的兩篇文章,我們了解了stage-0,state-1stage-2以及stage-3的區別。在進行實際開發時,可以更具需要來設置對應的stage。如果省事懶得折騰,一般設置為stage-0即可。如果為了防止開發人員使用某些太新的功能,我們可以限制到某個特定的stage即可。如果有其他問題,歡迎大家留言 :-)。 更詳細的請參考https://babeljs.io/docs/plugins/preset-stage-0/

Babel中的stage-0,stage-1,stage-2以及stage-3