0x001 polyfill
我們都知道,js
總是一直存在着兼容性問題,雖然其他語言也存在着兼容性問題,比如c++
、java
,但那種兼容性是新特性在舊版本上的不兼容,js
則存在着各種奇形怪哉的不兼容。這其中有着非常複雜的歷史和時代的原因,並不加以累述。而解決兼容性問題的方法在以前只存在一種,那就是polyfill
。先說什麼是polyfill
,比如我們想要用數組的一個新的方法includes
,在較新版本的瀏覽器下,可以直接使用:
但是在舊的瀏覽器下,比如ie10
,就會報錯:
這種情況下我們可以通過自定義一個方法來解決:
function includesPolyfill(){
if(!Array.prototype.includes){
Array.prototype.includes=function(element){
for(var i=0; i<this.length; i++){
if(this[i]===element)return true
}
return false
}
}
}
這裏定義一個簡單的方法,添加到Array.prototype
上,爲了簡單,並沒有做太多的異常檢測,接着在代碼中引入以上方法並優先執行,就可以做到在不兼容這個方法的js
環境總直接調用Array.protorype.includes
方法了:
這就是polyfill
,但是polyfill
有其侷限性,對於可以用舊的方法實現的新特性,可以使用polyfill
來解決,比如Array.prototype.includes
,但是,對於一些無法使用舊方法實現的新特性、新語法,比如箭頭函數
、const
之類的,polyfill
就無能爲力了,這個時候需要使用另一種方法:預編譯
,或者說是語法轉換
。
0x002 預編譯
在之前的js
開發中,是沒有預編譯這個流程的,擼完js
就直接部署了,但是隨着前端工程化的推進,預編譯也就出現了,特別是typescript
之類的語言出現以後,編碼和發佈就不再是同一種方式了。
現在在發佈之前,總是需要打包,而打包有許多的流程,比如資源整合、代碼優化、壓縮混淆...而在其中對代碼的操作上,我們可以將新的語法轉化成舊的語法來達到對新語法的支持。
簡單的說就是,新語法
->編譯器
->舊語法
。
編譯器的作用就是將輸入的源碼中的新特性轉化成就語法,說白了就是字符串處理,比如對箭頭函數
的處理:var add=(num1, num2)=>num1+num2
,這段代碼在不兼容箭頭函數
的環境中,比如ie10
,是無法執行的
但是我們可以通過語法轉化、編譯處理,將源碼轉化成var add=function(num1, num2){return num1+num2}
,這樣在不支持箭頭函數的瀏覽器中就可以執行了
現在來實現一個簡單的編譯器,當然只支持箭頭函數
function translate(src){
let [_, name, args, body]=src.match(/\((.+)\)=>(.+)/)
return `function ${name}(${args}){return ${body}}`
}
爲了簡單,只是使用簡單的正則提取來做實驗,並且不做任何異常處理
translate('var add=(num1, num2)=>num1+num')
// var add=function(num1, num2){return num1+num2}
將轉化結果保存成文件,就可以在不兼容箭頭語法的環境中使用了。甚至我們可以在瀏覽器中嵌入這個編譯器,將源碼編譯之後使用Function
構造函數或者eval
來執行,達到執行新語法的作用,這種情況下,稱爲運行時編譯器
,當然一般不會這麼用。
0x003 使用babel
很明顯,不可能自己寫這麼一個編譯器,那還要不要做項目了?這時候只能藉助社區的力量了,babel
就是這麼一個東西,接下來將會使用babel
來解析箭頭函數
-
初始化一個項目
$ mk babel-demo $ cd babel-demo $ npm init -y
-
安裝
babel
:
注意:(babel7
以後babel
相關的庫基本都是放在@babel
命名空間下)$ npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-arrow-functions
-
@babel/core
:核心庫 -
@babel/cli
:命令行工具 -
@babel/plugin-transform-arrow-functions
:箭頭函數語法轉化插件
-
-
編寫代碼:
let add=(num1, num2)=>num1+num2
-
使用
babel
解析$ npx babel --plugins @babel/plugin-transform-arrow-functions index.js -o bundle.js
上面命令的意思是將
index.js
使用babel
轉化,並將結果放到bundle.js
中,執行之後,將會生成bundle
-
--plugins
:爲這次轉化添加插件支持 -
-o
:輸出文件
-
-
查看轉化結果
查看新生成的bundle.js
,可以發現,箭頭函數
被轉化成了普通的funciton
, 在任何環境中都支持。let add = function (num1, num2) { return num1 + num2; };
- 說明
所以,對於新特性,我們可以通過使用polyfill
,也可以通過語法轉化來達到兼容。
0x004 babel
配置文件
很明顯,使用babel cli
的侷限性很大,容易出錯、不直觀、繁瑣等,所以babel
還是支持配置文件的方式:
-
.babelrc
方式-
在項目新建
.babelrc
文件,並使用JSON
語法配置{ "presets": [...], "plugins": [...] }
-
直接寫在
package.json
的babel
節點{ "name": "my-package", "version": "1.0.0", "babel": { "presets": [ ... ], "plugins": [ ... ], } }
-
-
babel.config.js
方式module.exports = function () { const presets = [ ... ]; const plugins = [ ... ]; return { presets, plugins }; }
兩種方式大同小異,區別就是一個是動態的,一個是靜態的,推薦小項目就用.babelrc
,大項目就使用babel.config.js
0x005 babel
配置之plugin
plugin
是babel
中很重要的概念,可以說,plugin
纔是構成babel
擴展性的核心,各種各樣的plugin
構成了babel
的生態,可以在這裏看一些babel
的插件列表。
.babelrc
配置文件中配置插件
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
這時候我們再執行npx babel index.js -o bundle.js
,就可以不指定plugin
也能正常轉化箭頭函數了
如果一個plugin
可以配置參數,則可以這麼配置:
{
"plugins": [
["@babel/plugin-transform-arrow-functions", { "spec": true }]
]
}
0x006 babel
配置之preset
在一個項目中,我們總是要配置一大堆的插件,這個時候,就是preset
出馬的時候了,那什麼是preset
呢?其實就是預置插件列表
了,引入了一個preset
就包含了一個系列的plugin
比如preset-react
就包含了以下插件:
@babel/plugin-syntax-jsx
@babel/plugin-transform-react-jsx
@babel/plugin-transform-react-display-name
-
.babelrc
配置preset-react
{
"presets": ["@babel/preset-react"]
}
- 如果有配置項,就醬:
{
"presets": [
[
"@babel/preset-react",
{
"pragma": "dom", // default pragma is React.createElement
"pragmaFrag": "DomFrag", // default is React.Fragment
"throwIfNamespace": false // defaults to true
}
]
]
}
0x007 babel
和webpack
- 添加
webpack.config.js
const path=requrie('path')
module.exports={
entry:path.resolve(__dirname, 'index.js'),
output:{
path: path.resolve(__dirname, 'dist'),
filename:'bundle.js'
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
}
-
添加相關依賴
$ npm install --save-dev webpack webpack-cli babel-loader
-
webpack
:webpack
核心庫 -
webpack-cli
:webpack
命令行工具 -
babel-loader
:babel
的webpack loader
-
-
打包
$ npm webpack
-
查看編譯結果
省略無關的東西,可以看到,箭頭函數
也被轉化成了function
/***/ "./index.js": /*!******************!*\ !*** ./index.js ***! \******************/ /*! no static exports found */ /***/ (function(module, exports) { eval("let add = function (num1, num2) {\n return num1 + num2;\n};\n\nmodule.exports = add;\n\n//# sourceURL=webpack:///./index.js?"); /***/ }) /******/ });
0x008 支持es6
支持es6
可以使用@babel/present-env
來代替一系列的東西,還有許多的配置像,比如兼容的瀏覽器版本,具體可以看這裏
- 安裝依賴包
$ npm install --save-dev @babel/preset-env
-
配置
{ "plugins": ["@babel/present-env"] }
-
打包
$ npm webpack
-
查看效果
/***/ "./index.js": /*!******************!*\ !*** ./index.js ***! \******************/ /*! no static exports found */ /***/ (function(module, exports) { eval("let add = function (num1, num2) {\n return num1 + num2;\n};\n\nmodule.exports = add;\n\n//# sourceURL=webpack:///./index.js?"); /***/ }) /******/ });
0x009 總結
這只是babel
功能的一個小覽,瞭解一下babel
的基本使用和一些概念而已。