webpack 的底層配置項resolve主要和模塊解析的路徑相關,常見的例如mainFiles ,extensions等node
配置模塊依賴別名,常見的@解析到src文件夾,path.resolve自己是用來構造絕對路徑的,因此@總能解析到src目錄的路徑react
module.exports = { resolve: {alias: { '@': path.resolve(__dirname, './src'), }, }, };複製代碼
引入模塊必須添加拓展名,默認是falsewebpack
配置文件依賴後綴名git
使用resolve.extensions能夠配置文件的默認後綴名,默認是['.wasm', '.mjs', '.js', '.json']。當省略文件後綴名時,webpack 會嘗試對文件名依次添加後綴名並解析,若是找到相關後綴的文件就中止解析。例如import "./data",那麼 webpack 首先會去找文件夾是否存在./data.wasm的文件,找不到就換下一個後綴名./data.mjs,就這樣依次解析,若是最後都找不到就會報錯。github
須要注意的是默認的配置項不要刪除,不然會發生錯誤,如今在默認配置的基礎上加上jsx,即:web
module.exports = { resolve: {extensions: ['.wasm', '.mjs', '.js', '.json', 'jsx'], }, };複製代碼
下面是 CRA 內置的解析順序是這樣的:npm
const moduleFileExtensions = [ 'web.mjs', 'mjs', 'web.js', 'js', 'web.ts', 'ts', 'web.tsx', 'tsx', 'json', 'web.jsx', 'jsx', ];複製代碼
解析目錄時要使用的文件名,默認是['index'],我在 webpack V4.44.1 的版本使用中發現單純使用這個配置項並不會讓 webpack 去自動解析文件夾下的index命名的文件,而後我在 stackoverflow 上發現了這個問題的回答 —— webpack can't find module if file named jsx,對於從 webpack 4.36.1 起,還須要在babel-loader中去配置rule.resolve.extensions,而且這個配置項是決定性的,必須加上rule.resolve.extensions的配置纔會讓 webpack 自動解析文件夾的index文件,而且".js"也不能省略,不然 react fast refresh 會報錯。json
module.exports = { module: {rules: [ {test: /\.m?jsx?$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: {presets: [ ['@babel/preset-env', { modules: false, }, ], ['@babel/preset-react'], ],plugins: [ '@babel/plugin-proposal-class-properties', isDevelopment && require.resolve('react-refresh/babel'), ].filter(Boolean), }, },resolve: { extensions: ['.js', '.jsx'] }, //必須加上這個配置 }, ], }, };複製代碼
resolve.mainFields針對的是使用第三方庫的代碼如何進行解析,由於 JS 能夠編寫在瀏覽器中使用的代碼,也能夠編寫使用在 nodejs 中的代碼,或者一些通用的在服務端和瀏覽器均可以使用的代碼。有的第三方庫會針對不一樣的環境提供幾份打包代碼放在一塊兒打包,它們會在package.json文件中指定不一樣環境代碼的入口,例如:瀏覽器
{ "browser": "build/upstream.js", "module": "index"}複製代碼
通常狀況下,在搭建項目的時候,會經過 webpack 的配置項 target來指定 webpack 去指定當前項目運行在什麼樣的環境中,例如target: 'node'指定當前代碼最終運行在 nodejs 環境下。具體見 —— target 。babel
當 webpack 的target屬性設置成 webworker, web 或者沒有指定的時候,那麼mainFields的默認值就是['browser', 'module', 'main'],也就是 webpack 會最優先選擇browser屬性做爲入口去解析第三方庫的代碼,對應上文說的package.json裏面提供的"browser"屬性的字段值。
而對於其它指定的target的屬性值,例如node等,mainFields的默認值都是['module', 'main']。
告訴 webpack 解析模塊時應該搜索的目錄,默認是['node_modules']。webpack 經過路徑解析解析模塊的時候會首先查找項目根目錄的./node_modules文件夾去尋找模塊,若是找不到就往上一級目錄 ../node_modules 中去找。
有時候這個配置比alias還簡潔,比方說對於項目中封裝通用業務組件庫位於src/components下面,而後經過resolve.modules配置模塊解析規則以下
module.exports = { resolve: {modules: [path.resolve(__dirname, './src/components'), 'node_modules'], }, };複製代碼
這時候在編寫頁面的時候,若是import一個src/components下里面的組件,就不用帶任何路徑前綴直接寫文件夾就好了,nice!不過這時候的弊端就是若是你的組件和第三方庫的組件同名,就會把第三方庫的組件覆蓋掉了。
import { Button } from 'Button/index.jsx';複製代碼
這個配置加上上文提到的rule.resolve.extensions,結合在一塊兒就是神器,對於src/components的組件,例如src/components/Button,直接一句話就搞定,webpack 會自動查找src/components/Button文件夾下的index文件。
import { Button } from 'Button';複製代碼
定義解析模塊過程當中使用的插件,經常使用的應該是pnp-webpack-plugin。這個理要了解一下yarn pnp,簡單來講Plug'n'Play是替代node_modules依賴機制的方案,對於一個簡單項目來講,若是使用 pnp 替代 npm 的機制,能夠經過在package.json中配置便可,對於 webpack 項目可使用插件來支持 pnp 的模塊解析機制。例如,CRS 內部就是使用了pnp-webpack-plugin這個插件。
yarn add pnp-webpack-plugin -D複製代碼
const PnpWebpackPlugin = require('pnp-webpack-plugin');module.exports = { resolve: {plugins: [PnpWebpackPlugin], }, resolveLoader: {plugins: [PnpWebpackPlugin.moduleLoader(module)], }, };複製代碼
CRA 還使用了一個插件,是react-dev-utils/ModuleScopePlugin,這個插件的做用是阻止項目內部從src或者node_modules之外的地方引入模塊,由於這一般會引發混亂。通常來講項目代碼都會放在src中,通常也不會瞎搞隨便引用外面的文件夾。
resolveLoader的配置項和resolve相同,不過resolveLoader只用於解析特定的 loader 模塊,例如resolveLoader.moduleExtensions默認是['-loader'],也就是解析 loader 的時候,默認會自動添加-loader的後綴名。
module.rule.resolve在上文提到過,也就是在解析特定類型的模塊時使用的解析規則,如上文所說,它對於解析index命名的模塊是必須配置的項。
也可使用上述的resolve的其餘配置項,這些配置項會和頂層的resolve配置項進行合併。