Webpack 啟動後會從設定的 Entry 出發,解析出檔案中的導入語句,再遞歸的解析。本文主要介紹了Webpack優化-縮小文件搜尋範圍的相關知識,文中較詳細的給大家介紹了可以優化的途徑,需要的朋友可以參考下,希望能幫助到大家。
在遇到導入語句時 Webpack 會做兩件事:
1.依照導入語句去尋找對應的要導入的檔案。例如 require('react') 導入語句對應的檔案是 ./node_modules/react/react.js , require('./util') 對應的檔案是 ./util.js 。
2.根據找到的要導入檔案的後綴,使用設定中的 Loader 去處理檔案。例如使用 ES6 開發的 JavaScript 檔案就需要使用 babel-loader 去處理。
以上兩件事雖然對於處理一個檔案非常快,但是當專案大了以後檔案量會變的非常多,這時候建立速度慢的問題就會暴露出來。
雖然以上兩件事情無法避免,但需要盡量減少以上兩件事情的發生,以提高速度。
接下來一一介紹可以優化它們的途徑。
優化 loader 設定
由於 Loader 對檔案的轉換操作很耗時,需要讓盡可能少的檔案被 Loader 處理。
在 2-3 Module 中介紹過在使用 Loader 時可以透過 test 、 include 、 exclude 三個設定項來命中 Loader 要套用規則的檔案。
為了盡可能少的讓檔案被 Loader 處理,可以透過 include 去命中只有哪些檔案需要被處理。
以採用ES6 的專案為例,在設定babel-loader 時,可以這樣:
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 去縮小命中範圍。
優化 resolve.modules 配置
在 2-4 Resolve 中介紹過 resolve.modules 用於配置 Webpack 去哪些目錄下尋找第三方模組。
resolve.modules 的預設值是['node_modules'] ,意思是先去目前目錄下的./node_modules 目錄下去找想找的模組,如果沒找到就去上一級目錄../ node_modules 中找,再也沒有就去../../node_modules 中找,以此類推,這和Node.js 的模組尋找機制很相似。
當安裝的第三方模組都放在專案根目錄下的./node_modules 目錄下時,沒有必要按照預設的方式去一層層的尋找,可以指明存放第三方模組的絕對路徑,以減少尋找,配置如下:
module.exports = { resolve: { // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤 // 其中 __dirname 表示当前工作目录,也就是项目根目录 modules: [path.resolve(__dirname, 'node_modules')] }, };
優化resolve.mainFields 配置
在2-4 Resolve 中介紹過resolve.mainFields 用於設定第三方模組使用哪個入口檔案。
安裝的第三方模組中都會有一個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'], }, };
使用本方法最佳化時,你需要考慮到所有執行時間所依賴的第三方模組的入口文件描述字段,就算有一個模組搞錯了都可能會造成構建出的程式碼無法正常運作。
優化 resolve.alias 配置
在 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 库中包含两套代码:
一套是采用 CommonJS 规范的模块化代码,这些文件都放在 lib 目录下,以 package.json 中指定的入口文件 react.js 为模块的入口。
一套是把 React 所有相关的代码打包好的完整代码放到一个单独的文件中,这些代码没有采用模块化可以直接执行。其中 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 ,你的项目可能只用到了其中几个工具函数,你就不能使用本方法去优化,因为这会导致你的输出代码中包含很多永远不会执行的代码。
优化 resolve.extensions 配置
在导入语句没带文件后缀时,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'], }, };
优化 module.noParse 配置
在 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 等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。
相关推荐:
以上是實例詳解Webpack優化設定縮小檔案搜尋範圍的詳細內容。更多資訊請關注PHP中文網其他相關文章!