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
等模块化语句,否则会致使构建出的代码中包含没法在浏览器环境下执行的模块化语句。
以上就是全部和缩小文件搜索范围相关的构建性能优化了,在根据本身项目的须要去按照以上方法改造后,你的构建速度必定会有所提高。
本实例 提供项目完整代码