Webpack:從 v1 升級到 v2 或 v3

2023-06-01 15:00 更新

以下各節(jié)描述從 webpack 1 到 webpack 2 的重大變化。

resolve.root, resolve.fallback, resolve.modulesDirectories

這些選項被一個單獨的選項 ?resolve.modules? 取代。更多用法請查看 解析 。

  resolve: {
-   root: path.join(__dirname, "src")
+   modules: [
+     path.join(__dirname, "src"),
+     "node_modules"
+   ]
  }

resolve.extensions

此選項不再需要傳一個空字符串。此行為被遷移到 ?resolve.enforceExtension? 。更多用法請查看 解析 。

resolve.*

這里更改了幾個 API 。由于不常用,不在這里詳細列出。更多用法請查看 解析 。

module.loaders is now module.rules

舊的 loader 配置被更強大的 rules 系統(tǒng)取代,后者允許配置 loader 以及其他更多選項。 為了兼容舊版,?module.loaders? 語法仍然有效,舊的屬性名依然可以被解析。 新的命名約定更易于理解,并且是升級配置使用 ?module.rules? 的好理由。

  module: {
-   loaders: [
+   rules: [
      {
        test: /\.css$/,
-       loaders: [
-         "style-loader",
-         "css-loader?modules=true"
+       use: [
+         {
+           loader: "style-loader"
+         },
+         {
+           loader: "css-loader",
+           options: {
+             modules: true
+           }
+         }
        ]
      },
      {
        test: /\.jsx$/,
        loader: "babel-loader", // 在這里不要使用 "use"
        options: {
          // ...
        }
      }
    ]
  }

鏈式 loaders

就像在 webpack 1 中,loader 可以鏈式調(diào)用,上一個 loader 的輸出被作為輸入傳給下一個 loader。 使用 rule.use 配置選項,?use? 可以設(shè)置為一個 loader 數(shù)組。 在 webpack 1 中,loader 通常被用 ?!? 連寫。這一寫法在 webpack 2 中只在使用舊的選項 ?module.loaders? 時才有效。

  module: {
-   loaders: [{
+   rules: [{
      test: /\.less$/,
-     loader: "style-loader!css-loader!less-loader"
+     use: [
+       "style-loader",
+       "css-loader",
+       "less-loader"
+     ]
    }]
  }

已移除 ?-loader? 模塊名稱自動擴展

在引用 loader 時,不能再省略 ?-loader? 后綴了:

  module: {
    rules: [
      {
        use: [
-         "style",
+         "style-loader",
-         "css",
+         "css-loader",
-         "less",
+         "less-loader",
        ]
      }
    ]
  }

你仍然可以通過配置 ?resolveLoader.moduleExtensions? 配置選項,啟用這一舊有行為,但是我們不推薦這么做。

+ resolveLoader: {
+   moduleExtensions: ["-loader"]
+ }

json-loader 不再需要手動添加

如果沒有為 JSON 文件配置 loader,webpack 將自動嘗試通過 [?json-loader?] 加載 JSON 文件。

  module: {
    rules: [
-     {
-       test: /\.json/,
-       loader: "json-loader"
-     }
    ]
  }

我們決定這樣做 是為了消除 webpack、 node.js 和 browserify 之間的環(huán)境差異。

配置中的 loader 默認相對于 context 進行解析

在 webpack 1 中,默認配置下 loader 解析相對于被匹配的文件。然而,在 webpack 2 中,默認配置下 loader 解析相對于 ?context? 選項。

這解決了「在使用 ?npm link? 或引用 ?context? 上下文目錄之外的模塊時,loader 所導(dǎo)致的模塊重復(fù)載入」的問題。

你可以移除掉那些為解決此問題的 hack 方案了:

  module: {
    rules: [
      {
        // ...
-       loader: require.resolve("my-loader")
+       loader: "my-loader"
      }
    ]
  },
  resolveLoader: {
-   root: path.resolve(__dirname, "node_modules")
  }

module.preLoaders and module.postLoaders were removed:

  module: {
-   preLoaders: [
+   rules: [
      {
        test: /\.js$/,
+       enforce: "pre",
        loader: "eslint-loader"
      }
    ]
  }

UglifyJsPlugin sourceMap

?UglifyJsPlugin? 的 ?sourceMap? 選項現(xiàn)在默認為 ?false? 而不是 ?true?。這意味著如果你在壓縮代碼時啟用了 ?source map?,或者想要讓 ?uglifyjs? 的警告能夠?qū)?yīng)到正確的代碼行,你需要將 ?UglifyJsPlugin? 的 ?sourceMap? 設(shè)為 ?true? 。

  devtool: "source-map",
  plugins: [
    new UglifyJsPlugin({
+     sourceMap: true
    })
  ]

UglifyJsPlugin warnings

  devtool: "source-map",
  plugins: [
    new UglifyJsPlugin({
+     compress: {
+       warnings: true
+     }
    })
  ]

UglifyJsPlugin minimize loaders

?UglifyJsPlugin? 不再壓縮 loaders。在未來很長一段時間里,需要通過設(shè)置 ?minimize:true? 來壓縮 loaders。參考 loader 文檔里的相關(guān)選項。

loaders 的壓縮模式將在 webpack 3 或后續(xù)版本中取消。

為了兼容舊的 loaders,loaders 可以通過插件來切換到壓縮模式:

  plugins: [
+   new webpack.LoaderOptionsPlugin({
+     minimize: true
+   })
  ]

DedupePlugin has been removed

不再需要 ?webpack.optimize.DedupePlugin?。請從配置中移除。

BannerPlugin - 破壞性改動

?BannerPlugin? 不再接受兩個參數(shù),而是只接受單獨的 options 對象。

  plugins: [
-    new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
+    new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
  ]

默認加載 OccurrenceOrderPlugin

?OccurrenceOrderPlugin? 現(xiàn)在默認啟用,并已重命名(在webpack 1 中為 ?OccurenceOrderPlugin)? 。 因此,請確保從你的配置中刪除該插件:

  plugins: [
    // webpack 1
-   new webpack.optimize.OccurenceOrderPlugin()
    // webpack 2
-   new webpack.optimize.OccurrenceOrderPlugin()
  ]

ExtractTextWebpackPlugin - 破壞性改動

ExtractTextPlugin 需要使用版本 2,才能在 webpack 2 下正常運行 。

?npm install --save-dev extract-text-webpack-plugin?

這一插件的配置變化主要體現(xiàn)在語法上。

ExtractTextPlugin.extract

module: {
  rules: [
    {
      test: /.css$/,
-      loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })
+      use: ExtractTextPlugin.extract({
+        fallback: "style-loader",
+        use: "css-loader",
+        publicPath: "/dist"
+      })
    }
  ]
}

new ExtractTextPlugin({options})

plugins: [
-  new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false })
+  new ExtractTextPlugin({
+    filename: "bundle.css",
+    disable: false,
+    allChunks: true
+  })
]

全動態(tài) require 現(xiàn)在默認會失敗

只有一個表達式的依賴(例如 ?require(expr)?)將創(chuàng)建一個空的 context 而不是一個完整目錄的 context。

這樣的代碼應(yīng)該進行重構(gòu),因為它不能與 ES2015 模塊一起使用。如果你確定不會有 ES2015 模塊,你可以使用 ?ContextReplacementPlugin? 來指示 compiler 進行正確的解析。

在 CLI 和配置中使用自定義參數(shù)

如果你之前濫用 CLI 來傳自定義參數(shù)到配置中,比如:

?webpack --custom-stuff?

// webpack.config.js
var customStuff = process.argv.indexOf('--custom-stuff') >= 0;
/* ... */
module.exports = config;

你將會發(fā)現(xiàn)新版中不再允許這么做。CLI 現(xiàn)在更加嚴格了。

替代地,現(xiàn)在提供了一個接口來傳遞參數(shù)給配置。我們應(yīng)該采用這種新方式,在未來許多工具將可能依賴于此。

?webpack --env.customStuff?

module.exports = function (env) {
  var customStuff = env.customStuff;
  /* ... */
  return config;
};

詳見 CLI 。

require.ensure and AMD require are asynchronous

現(xiàn)在這些函數(shù)總是異步的,而不是當(dāng) chunk 已經(jīng)加載完成的時候同步調(diào)用它們的回調(diào)函數(shù) (callback) 。

?require.ensure? 現(xiàn)在依賴原生的 ?Promises? 。如果不支持 Promise 的環(huán)境中使用 ?require.ensure? ,你需要添加 polyfill. 。

通過 options 中配置 loader

你 不能再 通過 ?webpack.config.js? 的自定義屬性來配置 loader 。只能通過 ?options? 來配置。下面配置的 ?ts? 屬性在 webpack 2 下不再有效:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
      },
    ],
  },
  // 在 webpack 2 中無效
  ts: { transpileOnly: false }
};

什么是 ?options? ?

好問題。嚴格來說,有兩種辦法,都可以用來配置 webpack 的 loader。典型的 ?options? 被稱為 ?query?,是一個可以被添加到 loader 名之后的字符串。它比較像一個查詢字符串(query string),但是實際上有更強大的能力:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader?' + JSON.stringify({ transpileOnly: false }),
      },
    ],
  },
};

不過它也可以分開來,寫成一個單獨的對象,緊跟在 loader 屬性后面:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: { transpileOnly: false },
      },
    ],
  },
};

LoaderOptionsPlugin context

有的 loader 需要從配置中讀取一些 context 信息。在未來很長一段時間里,這將需要通過 loader options 傳入。詳見 loader 文檔的相關(guān)選項。

為了保持對舊 loaders 的兼容,這些信息可以通過插件傳進來:

  plugins: [
+   new webpack.LoaderOptionsPlugin({
+     options: {
+       context: __dirname
+     }
+   })
  ]

debug

在 webpack 1 中 ?debug? 選項可以將 loader 切換到調(diào)試模式 (debug mode) 。在未來很長一段時間里,這將需要通過 loader 選項傳遞。詳見 loader 文檔的相關(guān)選項。

loaders 的調(diào)試模式將在 webpack 3 或后續(xù)版本中取消。

為了保持對舊 loaders 的兼容,loader 可以通過插件來切換到調(diào)試模式:

- debug: true,
  plugins: [
+   new webpack.LoaderOptionsPlugin({
+     debug: true
+   })
  ]

ES2015 的代碼分割

在 webpack 1 中,可以使用 ?require.ensure()? 作為實現(xiàn)應(yīng)用程序的懶加載 chunks 的一種方法:

require.ensure([], function (require) {
  var foo = require('./module');
});

ES2015 模塊加載規(guī)范定義了 ?import()? 方法,可以在運行時 (runtime) 動態(tài)地加載 ES2015 模塊。webpack 將 ?import()? 作為分割點 (split-point) 并將所要請求的模塊 (requested module) 放置到一個單獨的 chunk 中。?import()? 接收模塊名作為參數(shù),并返回一個 Promise 。

function onClick() {
  import('./module')
    .then((module) => {
      return module.default;
    })
    .catch((err) => {
      console.log('Chunk loading failed');
    });
}

好消息是:如果加載 chunk 失敗,我們現(xiàn)在可以進行處理,因為現(xiàn)在它基于 ?Promise?。

動態(tài)表達式

可以傳遞部分表達式給 ?import()? 。這與 CommonJS 對表達式的處理方式一致(webpack 為所有可能匹配的文件創(chuàng)建 context](/plugins/context-replacement-plugin/)) )。

?import()? 為每一個可能的模塊創(chuàng)建獨立的 chunk。

function route(path, query) {
  return import(`./routes/${path}/route`).then(
    (route) => new route.Route(query)
  );
}
// 上面代碼為每個可能的路由創(chuàng)建獨立的 chunk

混合使用 ES2015、AMD 和 CommonJS

你可以自由混合使用三種模塊類型(甚至在同一個文件中)。在這個情況中 webpack 的行為和 babel 以及 node-eps 一致:

// CommonJS 調(diào)用 ES2015 模塊
var book = require('./book');

book.currentPage;
book.readPage();
book.default === 'This is a book';
// ES2015 模塊調(diào)用 CommonJS
import fs from 'fs'; // module.exports 映射到 default
import { readFileSync } from 'fs'; // 從返回對象(returned object+)中讀取命名的導(dǎo)出方法(named exports)

typeof fs.readFileSync === 'function';
typeof readFileSync === 'function';

值得注意的是,你需要讓 Babel 不解析這些模塊符號,從而讓 webpack 可以使用它們。你可以通過設(shè)置如下配置到 .babelrc 或 babel-loader 來實現(xiàn)這一點。

.babelrc

{
  "presets": [["es2015", { "modules": false }]]
}

Hints

不需要改變什么,但有機會改變。

模版字符串

webpack 現(xiàn)在支持表達式中的模板字符串了。這意味著你可以在 webpack 構(gòu)建中使用它們:

- require("./templates/" + name);
+ require(`./templates/${name}`);

配置中使用 Promise

webpack 現(xiàn)在支持在配置文件中返回 ?Promise? 了。這讓你能在配置文件中做異步處理。

webpack.config.js

module.exports = function () {
  return fetchLangs().then((lang) => ({
    entry: '...',
    // ...
    plugins: [new DefinePlugin({ LANGUAGE: lang })],
  }));
};

高級 loader 匹配

webpack 現(xiàn)在支持對 loader 進行更多方式的匹配。

module.exports = {
  //...
  module: {
    rules: [
      {
        resource: /filename/, // 匹配 "/path/filename.js"
        resourceQuery: /^\?querystring$/, // 匹配 "?querystring"
        issuer: /filename/, // 如果請求 "/path/filename.js" 則匹配 "/path/something.js"
      }
    ]
  }
};

更多的 CLI 參數(shù)項

你可以使用一些新的 CLI 參數(shù)項:

?--define process.env.NODE_ENV="production"? 見 ?DefinePlugin? 。

?--display-depth? 顯示每個模塊到入口的距離。

?--display-used-exports? 顯示一個模塊中被使用的 exports 信息。

?--display-max-modules? 設(shè)置輸出時顯示的模塊數(shù)量(默認是 15)。

?-p? 能夠定義 ?process.env.NODE_ENV? 為 ?"production"? 。

Loader 變更

以下變更僅影響 loader 的開發(fā)者。

Cacheable

Loaders 現(xiàn)在默認可被緩存。Loaders 如果不想被緩存,需要選擇不被緩存。

  // 緩存 loader
  module.exports = function(source) {
-   this.cacheable();
    return source;
  }
  // 不緩存 loader
  module.exports = function(source) {
+   this.cacheable(false);
    return source;
  }

復(fù)雜 options

webpack 1 只支持能夠「可 ?JSON.stringify? 的對象」作為 loader 的 options。

webpack 2 現(xiàn)在支持任意 JS 對象作為 loader 的 options。

webpack 2.2.1 之前(即從 2.0.0 到 2.2.0),使用復(fù)合 ?options? ,需要在 ?options? 對象上添加 ?ident?,允許它能夠被其他 loader 引用。這 在 2.2.1 中被刪除 ,因此目前的遷移不再需要使用 ?ident? 鍵。

{
  test: /\.ext/
  use: {
    loader: '...',
    options: {
-     ident: 'id',
      fn: () => require('./foo.js')
    }
  }
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號