Webpack processes ES2015 module definitions by default and transforms them into code. It does not transform specific syntax, such as const
, though. The resulting code can be problematic especially in the older browsers.
Webpack默認狀況下處理ES015模塊定義並轉換成代碼。但它 不能 轉換特殊語法, 如const
。生成的代碼可能會有問題,尤爲是對於傳統瀏覽器。 javascript
To get a better idea of the default transform, consider the example output below (npm run build -- --devtool false --mode development
):
爲了更好的理解默認轉換,參考下面示例的輸出(npm run build -- --devtool false --mode development
):java
dist/main.jsnode
... /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = ((text = "Hello world") => { const element = document.createElement("div"); element.className = "pure-button"; element.innerHTML = text; return element; }); ...
The problem can be worked around by processing the code through Babel, a famous JavaScript compiler that supports ES2015+ features and more. It resembles ESLint in that it's built on top of presets and plugins. Presets are collections of plugins, and you can define your own as well.<br/>
用Babel來處理代碼能夠繞過這個問題, 它是一個知名的Javascript編譯器,支持ES015+特性和其餘一些功能. 它與ESLint相似,也是在預設(presets)和插件之上構建。預設(Presets)是插件的集合,你也能夠本身定義<br/>react
T> Given sometimes extending existing presets is not enough, modify-babel-preset allows you to go a step further and configure the base preset in a more flexible way.<br/>
T> 鑑於有時擴展示有預設是不夠的, modify-babel-preset 讓你能更進一步並以更靈活的方式配置基本預設。webpack
Even though Babel can be used standalone, as you can see in the SurviveJS - Maintenance book, you can hook it up with webpack as well. During development, it can make sense to skip processing if you are using language features supported by your browser.<br/>
即便Babel能夠單獨使用,正如你在 SurvieJS-Maintenance書中所看到的,你也能夠把它和Webpack鏈接起來。在開發的時候,若是你的瀏覽器支持語言特性,跳過這步處理仍是不錯的。git
Skipping processing is a good option primarily if you don't rely on any custom language features and work using a modern browser. Processing through Babel becomes almost a necessity when you compile your code for production, though.<br/>
若是不依賴特定的語言特性並工做在現代瀏覽器上,跳過處理是一個不錯的選擇。可是當你爲生產環境編譯代碼時,經過Babel處理源碼幾乎是一個必選項。es6
You can use Babel with webpack through babel-loader. It can pick up project level Babel configuration, or you can configure it at the webpack loader itself. babel-webpack-plugin is another lesser-known option.<br/>
你能夠經過babel-loader在Webpack中使用Babel。它可使用項目級別的配置,或者在Webpack的加載器中配置它。babel-webpack-plugin 是另外一個相對不太知名的選擇。github
Connecting Babel with a project allows you to process webpack configuration through it. To achieve this, name your webpack configuration using the webpack.config.babel.js convention. interpret package enables this, and it supports other compilers as well.
將Babel與項目相連,就能經過它來處理Webpack配置了。要實現這個功能,根據約定用 webpack.config.babel.js 命名你的Webpack配置文件. interpret 包實現了這個功能,它還支持其餘編譯器。web
T> Given that Node supports the ES2015 specification well these days, you can use a lot of ES2015 features without having to process configuration through Babel.<br/>
T> 考慮到近來 Node supports the ES2015 specification well , 你能夠直接使用不少ES2015的特性而沒必要經過Babel來處理配置了。typescript
W> If you use webpack.config.babel.js, take care with the "modules": false,
setting. If you want to use ES2015 modules, you could skip the setting in your global Babel configuration and then configure it per environment as discussed below.
W> 若是使用webpack.config.babel.js, 請注意配置項"modules": false,
. 若是想使用ES017的模塊特性,能夠跳過Babel的全局配置,而後像下面那樣根據不一樣環境的須要單獨配置。
{pagebreak}
The first step towards configuring Babel to work with webpack is to set up babel-loader. It takes the code and turns it into a format older browsers can understand. Install babel-loader and include its peer dependency @babel/core:
讓Babel和Webpack連攜工做的第一步是配置babel-loader。它將代碼轉換成傳統瀏覽器可以識別的格式。安裝 babel-loader 以及它的同儕依賴(peer dependency) @babel/core。
npm install babel-loader @babel/core --save-dev
As usual, let's define a function for Babel:
和往常同樣,咱們爲Babel定義一個函數:
webpack.parts.js
exports.loadJavaScript = ({ include, exclude } = {}) => ({ module: { rules: [ { test: /\.js$/, include, exclude, use: "babel-loader", }, ], }, });
Next, you need to connect this to the main configuration. If you are using a modern browser for development, you can consider processing only the production code through Babel. It's used for both production and development environments in this case. Also, only application code is processed through Babel.<br/>
下面,你須要把這個函數與主配置聯接。若是在使用現代瀏覽器開發,能夠只有在處理生產環境代碼時使用Babel。在這個例子中,生產環境和開發環境同時使用了Babel。而且,只有應用代碼才經過Babel處理。
{pagebreak}
Adjust as below:
以下調整:
webpack.config.js
const commonConfig = merge([ ... leanpub-start-insert parts.loadJavaScript({ include: PATHS.app }), leanpub-end-insert ]);
Even though you have Babel installed and set up, you are still missing one bit: Babel configuration. The configuration can be set up using a .babelrc dotfile as then other tooling can use the same.
即便安裝了Babel,並進行了配置,仍是差了一點:Babel的配置。可使用 .babelrc 點文件設置配置,其餘工具也可使用相同的點文件。
W> If you try to import files outside of your configuration root directory and then process them through babel-loader, this fails. It's a known issue, and there are workarounds including maintaining .babelrc at a higher level in the project and resolving against Babel presets through require.resolve
at webpack configuration.
W> 若是從配置的根目錄 之外 的地方導入文件這會致使 babel-loader 處理失敗。這是一個已和的問題 a known issue, 不過有其餘的方法來解決,包括在項目中把 .babelrc 級別設置得更高並經過Webpack配置中的 require.resolve
對Babel的預設進行處理。
At a minimum, you need babel-preset-env. It's a Babel preset that enables the required plugins based on the optional environment definition you pass to it.<br/>
你最少須要 babel-preset-env。 它是一個Babel預設,根據傳遞給它的可選環境定義啓用所需的插件。
Install the preset first:
首先安裝預設:
npm install @babel/preset-env --save-dev
To make Babel aware of the preset, you need to write a .babelrc. Given webpack supports ES2015 modules out of the box, you can tell Babel to skip processing them. Jumping over this step would break webpack's HMR mechanism although the production build would still work. You can also constrain the build output to work only in recent versions of Chrome.
建立一個 .babelrc 來讓它注意到預設。 假設Webpack直接支持ES2015 模塊特性(Module),可讓Babel直接跳過處理他們。儘管生產環境構建還會工做,可是跳過這一步會破壞Webpack的HMR機制。您還能夠限制構建輸出只能在最新版本的Chrome中工做。
Adjust the target definition as you like. As long as you follow browserslist, it should work. Here's a sample configuration:
若是願意的話能夠調整目標定義。只要按照browserslist,應該可以工做。下面是一個配置的示例:
.babelrc
{ "presets": [ [ "@babel/preset-env", { "modules": false, } ] ] }
If you execute npm run build -- --devtool false --mode development
now and examine dist/main.js, you will see something different based on your .browserslistrc
file.
若是你如今執行 npm run build -- --devtool false --mode development
並檢查 dist/main.js, 基於 .browserslistrc
文件這裏發生了一些改變.
{pagebreak}
Try to include only a definition like IE 8
there, and the code should change accordingly:
試着在裏面僅包含一個定義如 IE 8
,代碼也發生了相應的改變:
dist/main.js
... /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (function () { var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "Hello world"; var element = document.createElement("div"); element.className = "pure-button"; element.innerHTML = text; return element; }); ...
Note especially how the function was transformed. You can try out different browser definitions and language features to see how the output changes based on the selection.
尤爲要注意函數是如何發生改變的。您能夠嘗試不一樣的瀏覽器定義和語言特性,以查看輸出是如何根據選擇進行更改的。
<翻譯略,主要是針對傳統瀏覽器的兼容問題>
babel-preset-env allows you to polyfill certain language features for older browsers. For this to work, you should enable its useBuiltIns
option ("useBuiltIns": true
) and install babel-polyfill. You have to include it in your project either through an import or an entry (app: ["babel-polyfill", PATHS.app]
). babel-preset-env rewrites the import based on your browser definition and loads only the polyfills that are needed.
babel-polyfill pollutes the global scope with objects like Promise
. Given this can be problematic for library authors, there's transform-runtime option. It can be enabled as a Babel plugin, and it avoids the problem of globals by rewriting the code in such way that they aren't be needed.
W> Certain webpack features, such as Code Splitting, write Promise
based code to webpack's bootstrap after webpack has processed loaders. The problem can be solved by applying a shim before your application code is executed. Example: entry: { app: ["core-js/es6/promise", PATHS.app] }
.
There are other possible .babelrc options beyond the ones covered here. Like ESLint, .babelrc supports JSON5 as its configuration format meaning you can include comments in your source, use single quoted strings, and so on.
除了介紹的選項,還有其餘一些.babelrc options. 和ESLint同樣, .babelrc 用JSON5 做爲它的配置格式,這樣就能夠在代碼中包含註釋,使用單引號等等。
Sometimes you want to use experimental features that fit your project. Although you can find a lot of them within so-called stage presets, it's a good idea to enable them one by one and even organize them to a preset of their own unless you are working on a throwaway project. If you expect your project to live a long time, it's better to document the features you are using well.
有時要在項目中使用一些試驗中的特性。儘管能夠找到不少所謂的階段性預設,可是最好仍是一個一個地啓用它們,甚至將它們組織成本身的預設,除非您正在處理一個一次性項目。若是想讓你的項目活得長點兒,最好文檔化你使用得比較好的特性。
Babel isn't the only option although it's the most popular one. Buble by Rich Harris is another compiler worth checking out. There's experimental buble-loader that allows you to use it with webpack. Buble doesn't support ES2015 modules, but that's not a problem as webpack provides that functionality.
Babel儘管深受歡迎但不是惟一選擇。Rich Harris的Buble 是另外一個值得一試的編譯器。 有個試驗用的加載器buble-loader
{pagebreak}
Perhaps the greatest thing about Babel is that it's possible to extend with plugins:
或許Babel最偉大之處就在於它容許用插件擴展:
import { Button } from "antd";
instead of pointing to the module through an exact path.import { Button } from "antd";
而不用明確的指定模塊的路徑。console.warn
to functions that have @deprecate
annotation in their comment.@deprecate
標誌的函數添加console.warn
.console.log
calls with information about invocation context, so it's easier to see where they logged.console.log
的調用註釋中 , 這樣就能很容易得找到在哪裏被調用的了.propType
related code from your production build. It also allows component authors to generate code that's wrapped so that setting environment at DefinePlugin
can kick in as discussed in the book.propType
相關的代碼刪除掉. 它還容許組件做者生成封裝的代碼,以便在' DefinePlugin '設置環境,如書中討論的那樣。T> It's possible to connect Babel with Node through babel-register or babel-cli. These packages can be handy if you want to execute your code through Babel without using webpack.
T> 能夠經過babel-register 或 babel-cli 來鏈接 Babel和 Node。若是你想只經過Babel而不用Webpack來執行你的代碼,這些包會很是方便。
Babel allows you to control which presets and plugins are used per environment through its env option. You can manage Babel's behavior per build target this way.
經過env option, Babel讓你可以根據環境控制哪些預設和插件可用. 能夠根據構建目標的不一樣來管理Babel的行爲.
env
checks both NODE_ENV
and BABEL_ENV
and adds functionality to your build based on that. If BABEL_ENV
is set, it overrides any possible NODE_ENV
.env
會同時檢查 NODE_ENV
和 BABEL_ENV
並基於此向你的構建中添加功能。 若是 BABEL_ENV
被設置, 它將覆蓋全部可能的 NODE_ENV
.
Consider the example below:
參考下面的例子:
.babelrc
{ ... "env": { "development": { "plugins": [ "annotate-console-log" ] } } }
Any shared presets and plugins are available to all targets still. env
allows you to specialize your Babel configuration further.
任何共享的預設和插件對目標仍然可用。env
讓你進一步定製你的Babel配置。
{pagebreak}
It's possible to pass the webpack environment to Babel with a tweak:
經過微調,能夠將webpack環境傳遞給Babel:
webpack.config.js
module.exports = mode => { leanpub-start-insert process.env.BABEL_ENV = mode; leanpub-end-insert ... };
T> The way env
works is subtle. Consider logging env
and make sure it matches your Babel configuration or otherwise the functionality you expect is not applied to your build.
T> env
運行方式比較微妙. 考慮將env
記錄到日誌中,以確保它與你的Babel配置相匹配,不然你所指望的功能可能不會應用到構建上 .
Microsoft's TypeScript is a compiled language that follows a similar setup as Babel. The neat thing is that in addition to JavaScript, it can emit type definitions. A good editor can pick those up and provide enhanced editing experience. Stronger typing is valuable for development as it becomes easier to state your type contracts.
微軟的 TypeScript 是一種編譯語言,它的配置方式與Babel相似。 除了兼容Javascript以外,它還可使用類型定義。一個好的編輯器除了可使用這些特性還能加強編寫體驗。更強的類型對於開發頗有價值,由於它更容易聲明類型契約。
Compared to Facebook's type checker Flow, TypeScript is a more secure option. As a result, you find more premade type definitions for it, and overall, the quality of support should be better.
與Facebook的類型檢查程序流相比,TypeScript是一個更安全的選擇。所以,您會發現它有更多預先定義的類型,總的來講,支持的質量應該更好。
You can use TypeScript with webpack using the following loaders:
能夠經過下面的加載器在Webpack中使用Typescript:
T> There's a TypeScript parser for ESLint. It's also possible to lint it through tslint.
T> 有一個 TypeScript parser for ESLint. 也能夠經過 tslint對其進行處理.
Flow performs static analysis based on your code and its type annotations. You have to install it as a separate tool and then run it against your code. There's flow-status-webpack-plugin that allows you to run it through webpack during development.
Flow 基於代碼和類型註釋進行靜態分析. 你不得不把它安裝爲一個獨立的工具並在你的代碼上運行它。有一個 flow-status-webpack-plugin 包可讓你在開發時經過Webpack執行它.
If you use React, the React specific Babel preset does most of the work through babel-plugin-syntax-flow. It can strip Flow annotations and convert your code into a format that is possible to transpile further.
若是在使用React, 經過包 babel-plugin-syntax-flow, Webpack特定的Babel預設完成了大部分工做。它能夠剝離流注釋,並將代碼轉換爲能夠進一步轉換的格式。
There's also babel-plugin-typecheck that allows you to perform runtime checks based on your Flow annotations. flow-runtime goes a notch further and provides more functionality. These approaches complement Flow static checker and allow you to catch even more issues.
還有一個包 babel-plugin-typecheck 能夠基於流注釋進行運行時檢查. flow-runtime 更進一步,提供了更多的功能。這些方法補充了流靜態檢查器,並容許您捕獲更多的問題。
T> flow-coverage-report shows how much of your code is covered by Flow type annotations.
T> flow-coverage-report 顯示流類型註釋覆蓋了多少代碼。
{pagebreak}
Babel has become an indispensable tool for developers given it bridges the standard with older browsers. Even if you targeted modern browsers, transforming through Babel is an option.
Babel已經成爲開發人員不可或缺的工具,由於它鏈接了標準與舊瀏覽器。即便是針對現代瀏覽器,經過Babel進行轉換也是一種選擇。
To recap:(總結)