Webpack 啓動後會從配置的 Entry 出發,解析出文件中的導入語句,再遞歸的解析。
在遇到導入語句時 Webpack 會作兩件事情:html
require('react')
導入語句對應的文件是 ./node_modules/react/react.js
,require('./util')
對應的文件是 ./util.js
。以上兩件事情雖然對於處理一個文件很是快,可是當項目大了之後文件量會變的很是多,這時候構建速度慢的問題就會暴露出來。
雖然以上兩件事情沒法避免,但須要儘可能減小以上兩件事情的發生,以提升速度。node
接下來一一介紹能夠優化它們的途徑。react
因爲 Loader 對文件的轉換操做很耗時,須要讓儘量少的文件被 Loader 處理。webpack
在2-3 Module 中介紹過在使用 Loader 時能夠經過 test
、 include
、 exclude
三個配置項來命中 Loader 要應用規則的文件。
爲了儘量少的讓文件被 Loader 處理,能夠經過 include
去命中只有哪些文件須要被處理。git
以採用 ES6 的項目爲例,在配置 babel-loader 時,能夠這樣:github
module.exports = { module: { rules: [ { // 若是項目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提高正則表達式性能 test: /\.js$/, // babel-loader 支持緩存轉換出的結果,經過 cacheDirectory 選項開啓 use: ['babel-loader?cacheDirectory'], // 只對項目根目錄下的 src 目錄中的文件採用 babel-loader include: path.resolve(__dirname, 'src'), }, ] }, };
你能夠適當的調整項目的目錄結構,以方便在配置 Loader 時經過
include
去縮小命中範圍。
在2-4 Resolve 中介紹過 resolve.modules
用於配置 Webpack 去哪些目錄下尋找第三方模塊。web
resolve.modules
的默認值是 ['node_modules']
,含義是先去當前目錄下的 ./node_modules
目錄下去找想找的模塊,若是沒找到就去上一級目錄 ../node_modules
中找,再沒有就去 ../../node_modules
中找,以此類推,這和 Node.js 的模塊尋找機制很類似。正則表達式
當安裝的第三方模塊都放在項目根目錄下的 ./node_modules
目錄下時,沒有必要按照默認的方式去一層層的尋找,能夠指明存放第三方模塊的絕對路徑,以減小尋找,配置以下:npm
module.exports = { resolve: { // 使用絕對路徑指明第三方模塊存放的位置,以減小搜索步驟 // 其中 __dirname 表示當前工做目錄,也就是項目根目錄 modules: [path.resolve(__dirname, 'node_modules')] }, };
在2-4 Resolve 中介紹過 resolve.mainFields
用於配置第三方模塊使用哪一個入口文件。json
安裝的第三方模塊中都會有一個 package.json
文件用於描述這個模塊的屬性,其中有些字段用於描述入口文件在哪裏,resolve.mainFields
用於配置採用哪一個字段做爲入口文件的描述。
能夠存在多個字段描述入口文件的緣由是由於有些模塊能夠同時用在多個環境中,準對不一樣的運行環境須要使用不一樣的代碼。
以 isomorphic-fetch 爲例,它是 fetch API 的一個實現,但可同時用於瀏覽器和 Node.js 環境。
它的 package.json
中就有2個入口文件描述字段:
{ "browser": "fetch-npm-browserify.js", "main": "fetch-npm-node.js" }
isomorphic-fetch 在不一樣的運行環境下使用不一樣的代碼是由於 fetch API 的實現機制不同,在瀏覽器中經過原生的fetch
或者XMLHttpRequest
實現,在 Node.js 中經過http
模塊實現。
resolve.mainFields
的默認值和當前的 target
配置有關係,對應關係以下:
target
爲 web
或者 webworker
時,值是 ["browser", "module", "main"]
target
爲其它狀況時,值是 ["module", "main"]
以 target
等於 web
爲例,Webpack 會先採用第三方模塊中的 browser
字段去尋找模塊的入口文件,若是不存在就採用 module
字段,以此類推。
爲了減小搜索步驟,在你明確第三方模塊的入口文件描述字段時,你能夠把它設置的儘可能少。
因爲大多數第三方模塊都採用 main
字段去描述入口文件的位置,能夠這樣配置 Webpack:
module.exports = { resolve: { // 只採用 main 字段做爲入口文件描述字段,以減小搜索步驟 mainFields: ['main'], }, };
使用本方法優化時,你須要考慮到全部運行時依賴的第三方模塊的入口文件描述字段,就算有一個模塊搞錯了均可能會形成構建出的代碼沒法正常運行。
在2-4 Resolve 中介紹過 resolve.alias
配置項經過別名來把原導入路徑映射成一個新的導入路徑。
在實戰項目中常常會依賴一些龐大的第三方模塊,以 React 庫爲例,安裝到 node_modules
目錄下的 React 庫的目錄結構以下:
├── dist │ ├── react.js │ └── react.min.js ├── lib │ ... 還有幾十個文件被忽略 │ ├── LinkedStateMixin.js │ ├── createClass.js │ └── React.js ├── package.json └── react.js
能夠看到發佈出去的 React 庫中包含兩套代碼:
lib
目錄下,以 package.json
中指定的入口文件 react.js
爲模塊的入口。dist/react.js
是用於開發環境,裏面包含檢查和警告的代碼。dist/react.min.js
是用於線上環境,被最小化了。默認狀況下 Webpack 會從入口文件 ./node_modules/react/react.js
開始遞歸的解析和處理依賴的幾十個文件,這會時一個耗時的操做。
經過配置 resolve.alias
可讓 Webpack 在處理 React 庫時,直接使用單獨完整的 react.min.js
文件,從而跳過耗時的遞歸解析操做。
相關 Webpack 配置以下:
module.exports = { resolve: { // 使用 alias 把導入 react 的語句換成直接使用單獨完整的 react.min.js 文件, // 減小耗時的遞歸解析操做 alias: { 'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'), } }, };
除了 React 庫外,大多數庫發佈到 Npm 倉庫中時都會包含打包好的完整文件,對於這些庫你也能夠對它們配置 alias。可是對於有些庫使用本優化方法後會影響到後面要講的使用 Tree-Shaking 去除無效代碼的優化,由於打包好的完整文件中有部分代碼你的項目可能永遠用不上。
通常對總體性比較強的庫採用本方法優化,由於完整文件中的代碼是一個總體,每一行都是不可或缺的。
可是對於一些工具類的庫,例如 lodash,你的項目可能只用到了其中幾個工具函數,你就不能使用本方法去優化,由於這會致使你的輸出代碼中包含不少永遠不會執行的代碼。
在導入語句沒帶文件後綴時,Webpack 會自動帶上後綴後去嘗試詢問文件是否存在。
在2-4 Resolve 中介紹過 resolve.extensions
用於配置在嘗試過程當中用到的後綴列表,默認是:
extensions: ['.js', '.json']
也就是說當遇到 require('./data')
這樣的導入語句時,Webpack 會先去尋找 ./data.js
文件,若是該文件不存在就去尋找 ./data.json
文件,若是仍是找不到就報錯。
若是這個列表越長,或者正確的後綴在越後面,就會形成嘗試的次數越多,因此 resolve.extensions
的配置也會影響到構建的性能。
在配置 resolve.extensions
時你須要遵照如下幾點,以作到儘量的優化構建性能:
require('./data')
寫成 require('./data.json')
。相關 Webpack 配置以下:
module.exports = { resolve: { // 儘量的減小後綴嘗試的可能性 extensions: ['js'], }, };
在2-3 Module 中介紹過 module.noParse
配置項可讓 Webpack 忽略對部分沒采用模塊化的文件的遞歸解析處理,這樣作的好處是能提升構建性能。
緣由是一些庫,例如 jQuery 、ChartJS, 它們龐大又沒有采用模塊化標準,讓 Webpack 去解析這些文件耗時又沒有意義。
在上面的 優化 resolve.alias 配置 中講到單獨完整的 react.min.js
文件就沒有采用模塊化,讓咱們來經過配置 module.noParse
忽略對 react.min.js
文件的遞歸解析處理,
相關 Webpack 配置以下:
const path = require('path'); module.exports = { module: { // 獨完整的 `react.min.js` 文件就沒有采用模塊化,忽略對 `react.min.js` 文件的遞歸解析處理 noParse: [/react\.min\.js$/], }, };
注意被忽略掉的文件裏不該該包含import
、require
、define
等模塊化語句,否則會致使構建出的代碼中包含沒法在瀏覽器環境下執行的模塊化語句。
以上就是全部和縮小文件搜索範圍相關的構建性能優化了,在根據本身項目的須要去按照以上方法改造後,你的構建速度必定會有所提高。
本實例 提供項目完整代碼