webpack 學習筆記:實戰之 babel 編碼

zhangbao發表於2020-09-27

使用 webpack 直接解析下面的檔案會出問題。

class Counter extends HTMLElement {
    x = 0;

    clicked() {
        this.x++
        window.requestAnimationFrame(this.render.bind(this))
    }

    constructor() {
        super()
        this.onclick = this.clicked.bind(this)
        this.x = 0
    }

    connectedCallback() {
        this.render()
    }

    render() {
        this.textContent = this.x.toString()
    }
}

window.customElements.define('num-counter', Counter)

報錯資訊如下:

$ npx webpack --watch

webpack is watching the files…

Hash: d8bc5ae1b88c0918c1c1
Version: webpack 4.44.2
Time: 67ms
Built at: 2020-09-26 9:00:13 ├F10: PM┤
     Asset       Size  Chunks             Chunk Names
 bundle.js   3.87 KiB       0  [emitted]  main
index.html  276 bytes          [emitted]  
Entrypoint main = bundle.js
[0] ./src/index.js 303 bytes {0} [built] [failed] [1 error]

ERROR in ./src/index.js 2:6
Module parse failed: Unexpected token (2:6)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| class Counter extends HTMLElement {
>     x = 0;
| 
|     clicked() {
Child HtmlWebpackCompiler:
     1 asset
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [0] ../node_modules/html-webpack-plugin/lib/loader.js!./src/template.html 505 bytes {0} [built]

報錯出在 index.js 第二行的第 6 個字元。

ERROR in ./src/index.js 2:6

也就是在下面這個地方:

class Counter extends HTMLElement {
    x = 0; // ← 這裡
    // ...
}

查詢得知,webpack 內部使用的 parser acorn 只支援 stage 4 階段的提案。而我這裡使用的 class (public) field declarations 的特性 目前還處於 stage 3 階段,因此出錯。這時就要藉助 babel-loader 來轉碼了。

首先安裝依賴:

$ npm install -D babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties

然後配置 webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            // See: https://webpack.js.org/loaders/babel-loader/
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: ['@babel/plugin-proposal-class-properties']
                    }
                }
            }
        ]
    },
    plugins: [
        // See: https://webpack.js.org/plugins/html-webpack-plugin/
        new HtmlWebpackPlugin({ template: './src/template.html' })
    ],
    mode: 'none'
};

重新編譯,就不會有問題了。

$ npx webpack --watch

webpack is watching the files…

Hash: 651e138f726765848707
Version: webpack 4.44.2
Time: 802ms
Built at: 2020-09-26 10:31:09 ├F10: PM┤
     Asset       Size  Chunks             Chunk Names
 bundle.js   8.82 KiB       0  [emitted]  main
index.html  276 bytes          [emitted]  
Entrypoint main = bundle.js
[0] ./src/index.js 5.25 KiB {0} [built]
Child HtmlWebpackCompiler:
     1 asset
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [0] ../node_modules/html-webpack-plugin/lib/loader.js!./src/template.html 505 bytes {0} [built]

最終得到的 bundle 檔案如下(簡要):

var Counter = /*#__PURE__*/function (_HTMLElement) {
  _inherits(Counter, _HTMLElement);

  var _super = _createSuper(Counter);

  _createClass(Counter, [{
    key: "clicked",
    value: function clicked() {
      this.x++;
      window.requestAnimationFrame(this.render.bind(this));
    }
  }]);

  function Counter() {
    var _this;

    _classCallCheck(this, Counter);

    _this = _super.call(this);

    _defineProperty(_assertThisInitialized(_this), "x", 0);

    _this.onclick = _this.clicked.bind(_assertThisInitialized(_this));
    _this.x = 0;
    return _this;
  }

  _createClass(Counter, [{
    key: "connectedCallback",
    value: function connectedCallback() {
      this.render();
    }
  }, {
    key: "render",
    value: function render() {
      this.textContent = this.x.toString();
    }
  }]);

  return Counter;
}( /*#__PURE__*/_wrapNativeSuper(HTMLElement));

window.customElements.define('num-counter', Counter);

Tip:

上面的 @babel/plugin-proposal-class-properties plugin 是解決問題的關鍵。如果只是配置了 babel-loader 的 presets option,依然會報錯。

$ npx webpack --watch

webpack is watching the files…

Hash: 2bb64eb81b2f5899f55f
Version: webpack 4.44.2
Time: 748ms
Built at: 2020-09-26 10:24:22 ├F10: PM┤
     Asset       Size  Chunks             Chunk Names
 bundle.js   7.78 KiB       0  [emitted]  main
index.html  276 bytes          [emitted]
Entrypoint main = bundle.js
[0] ./src/index.js 4.2 KiB {0} [built] [failed] [1 error]

ERROR in ./src/index.js
Module build failed (from ../node_modules/babel-loader/lib/index.js):
SyntaxError: E:\projects\tc39-proposal-demos\proposal-class-fields\src\index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (2:7):

  1 | class Counter extends HTMLElement {
> 2 |     x = 0;
    |       ^
  3 | 
  4 |     clicked() {
  5 |         this.x++

Add @babel/plugin-proposal-class-properties (https://git.io/vb4SL) to the 'plugins' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-class-properties (https://git.io/vb4yQ) to the 'plugins' section to enable parsing.
    at Parser._raise (E:\projects\tc39-proposal-demos\node_modules\@babel\parser\lib\index.js:766:17)

(完)

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章