07_04_加載Javascript(webpack book)

Loading JavaScript(加載Javascript)

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

Using Babel with Webpack Configuration(使用babel與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}

Setting Up babel-loader(設置 babel-loader)

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的預設進行處理。

Setting Up .babelrc (設置 .babelrc)

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.
尤爲要注意函數是如何發生改變的。您能夠嘗試不一樣的瀏覽器定義和語言特性,以查看輸出是如何根據選擇進行更改的。

Polyfilling Features("填坑"特性)

<翻譯略,主要是針對傳統瀏覽器的兼容問題>

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] }.

Babel Tips(Babel小竅門)

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同樣, .babelrcJSON5 做爲它的配置格式,這樣就能夠在代碼中包含註釋,使用單引號等等。

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}

Babel Plugins(Babel插件)

Perhaps the greatest thing about Babel is that it's possible to extend with plugins:
或許Babel最偉大之處就在於它容許用插件擴展:

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-registerbabel-cli 來鏈接 Babel和 Node。若是你想只經過Babel而不用Webpack來執行你的代碼,這些包會很是方便。

Enabling Presets and Plugins per Environment(根據環境打開預設和插件)

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_ENVBABEL_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配置相匹配,不然你所指望的功能可能不會應用到構建上 .

Setting Up TypeScript(配置Typescript)

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對其進行處理.

Setting Up Flow(配置流程)

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}

Conclusion

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:(總結)

  • Babel gives you control over what browsers to support. It can compile ES2015+ features to a form the older browser understand. babel-preset-env is valuable as it can choose which features to compile and which polyfills to enable based on your browser definition.
  • Babel 幫你能控制支持什麼瀏覽器。它能夠把ES2015+的特性編譯成傳統瀏覽器能夠識別的格式。 babel-preset-env 是有價值的,由於它能基於瀏覽器判斷哪一個功能須要編譯,哪一個功能須要用Poyfill來幫助瀏覽器實現功能。
  • Babel allows you to use experimental language features. You can find numerous plugins that improve development experience and the production build through optimizations.
  • Babel 讓你能使用試驗中的語言特性。你能夠找到大量的插件經過優化來改善開發體驗和生產環境構建。
  • Babel functionality can be enabled per development target. This way you can be sure you are using the correct plugins at the right place.
  • 每一個開發目標均可以啓用Babel功能。這樣能夠確保在正確的地方使用了正確的插件。
  • Besides Babel, webpack supports other solutions like TypeScript or Flow. Flow can complement Babel while TypeScript represents an entire language compiling to JavaScript.
  • 除了Babel,webpack還支持其餘的解決方案,如:TypeScript和Flow。Flow能夠補充Babel,而TypeScript則表明了編譯成JavaScript的別一種語言。
相關文章
相關標籤/搜索