行业知识

轻松玩 webpack,使你的装包速率提高 90%

日期:2021-02-05
我要分享
提升 webpack 搭建速率的第一步是了解将活力集中化在哪儿里。大家能够根据speed-measure-webpack-plugin精确测量你的 webpack 搭建期内每个环节花销的時间:
// 剖析装包時间 const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); const smp = new SpeedMeasurePlugin(); // ... module.exports = smp.wrap(prodWebpackConfig) 拷贝编码
特殊的新项目,都是有自身特殊的特性搭建短板,下边大家对装包的每个阶段开展提升。 二、剖析危害装包速率阶段 在「窥视基本原理:笔写一个 JavaScript 装包器」中,大家早已详细介绍过,装包便是从通道文档刚开始将全部的依靠控制模块装包到一个文档中的全过程,自然,在装包全过程中涉及到各种各样编译程序、提升全过程。 装包全过程中,普遍危害搭建速率的地区有什么喃? 1. 刚开始装包,大家必须获得全部的依靠控制模块 检索全部的依靠项,这必须占有一定的時间,即检索時间,那麼大家就明确了: 大家必须提升的第一个時间便是检索時间。 2. 分析全部的依靠控制模块(分析成访问器可运作的编码) webpack 依据大家配备的 loader 分析相对的文档。平时开发设计中大家必须应用 loader 对 js ,css ,照片,字体样式等文档做变换实际操作,而且变换的文档数据信息量也是是非非常大。因为 js 单进程的特点促使这种变换实际操作不可以高并发解决文档,只是必须一个个文档开展解决。 大家必须提升的第二个時间便是分析時间。 3. 将全部的依靠控制模块装包到一个文档 将全部分析进行的编码,装包到一个文档中,以便使访问器载入的包升级(减少黑屏時间),因此 webpack 会对编码开展提升。 JS 缩小是公布编译程序的最终环节,一般 webpack 必须卡好一会,它是由于缩小 JS 必须先将编码分析成 AST 英语的语法树,随后必须依据繁杂的标准去剖析和解决 AST,最终将 AST 复原成 JS,这一全过程涉及到到很多测算,因而较为用时,装包就非常容易卡住。 大家必须提升的第三个時间便是缩小時间。 4. 二次装包 当变更新项目中一个小小的的文档时,大家必须再次装包,全部的文档都务必要再次装包,必须花销同第一次装包同样的時间,但新项目广州中山大学一部分文档也没有变动,特别是在是第三方库。 大家必须提升的第四个時间便是二次装包時间。 三、 提升分析時间 - 打开多过程装包 运作在 Node.js 以上的 webpack 是单进程方式的,换句话说,webpack 装包只有逐一文档解决,当 webpack 必须装包很多文档时,装包時间便会较为悠长。 1. thread-loader(webpack4 官方网强烈推荐) 把这一 loader 置放在别的 loader 以前, 置放在这里个 loader 以后的 loader 便会在一个独立的 worker【worker pool】 池里运作,一个worker 便是一个nodeJS 过程【node.js proces】,每一个独立过程解决時间限制为600Ms,每个过程的数据信息互换也会限定在这里个時间内。 thread-loader 应用起來也十分简易,要是把 thread-loader 置放在别的 loader 以前, 那 thread-loader 以后的 loader 便会在一个独立的 worker 池(worker pool)中运作。
module.exports = { // ... module: { rules: [
 { test: /\.js$/, exclude: /node_modules/, // 建立一个 js worker 池 use: [ 'thread-loader', 'babel-loader' ] 
 { test: /\.s?css$/, exclude: /node_modules/, // 建立一个 css worker 池 use: [ 'style-loader', 'thread-loader',
 { loader: 'css-loader', options: { modules: true, localIdentName: '[name]__[local]--[hash:base64:5]', importLoaders: 1 }
 }, 'postcss-loader' ]
 } // ... ] // ... } // ... } 拷贝编码
留意:thread-loader 放到了 style-loader 以后,它是由于 thread-loader 后的 loader 无法存储文档也无法获得 webpack 的选择项设定。 官方网上说每一个 worker 大约必须花销 600Ms ,因此官方网以便避免起动 worker 时的高延迟时间,出示了对 worker 池的提升:加热
// ... const threadLoader = require('thread-loader'); const jsWorkerPool = { // options // 造成的 worker 的总数,默认设置是 (cpu 关键数 - 1) // 当 require('os').cpus() 是 undefined 时,则为 1 workers: 2, // 闲置不用时定时执行删掉 worker 过程 // 默认设置为 500Ms // 能够设定为无限大, 那样在监控方式(--watch)下能够维持 worker 不断存有 poolTimeout: 2000 }; const cssWorkerPool = { // 一个 worker 过程中并行处理实行工作中的总数 // 默认设置为 20 workerParallelJobs: 2, poolTimeout: 2000 };
threadLoader.warmup(jsWorkerPool, ['babel-loader']);
threadLoader.warmup(cssWorkerPool, ['css-loader', 'postcss-loader']); module.exports = { // ... module: { rules: [
 { test: /\.js$/, exclude: /node_modules/, use: [
 { loader: 'thread-loader', options: jsWorkerPool
 }, 'babel-loader' ]
 { test: /\.s?css$/, exclude: /node_modules/, use: [ 'style-loader',
 { loader: 'thread-loader', options: cssWorkerPool
 { loader: 'css-loader', options: { modules: true, localIdentName: '[name]__[local]--[hash:base64:5]', importLoaders: 1 }
 }, 'postcss-loader' ]
 } // ... ] // ... } // ... } 拷贝编码
留意:请仅在用时的 loader 上应用。 2. HappyPack 在webpack搭建全过程中,具体上消耗時间大多数数用在 loader 分析变换及其编码的缩小中,HappyPack 可运用多过程对文档开展装包(默认设置cpu核数-1),对多核cpu运用率高些。HappyPack 可让 Webpack 同一時间解决好几个每日任务,充分发挥多核 CPU 的工作能力,将每日任务溶解给好几个子过程去高并发的实行,子过程解决完后,再把結果推送给主过程。 happypack 的解决构思是将原来的 webpack 对 loader 的实行全过程从单一过程的方式拓展多过程方式,本来的步骤维持不会改变。应用 HappyPack 也是有一些限定,它只适配一部分流行的 loader,实际能够查询官方网得出的 适配性目录。 留意:Ahmad Amireh 强烈推荐应用 thread-loader,并公布将已不再次维护保养 happypack,因此不强烈推荐应用它
const path = require('path') const webpack = require("webpack"); const HappyPack = require('happypack'); // 多过程loader // node 出示的系统软件实际操作控制模块 const os = require('os'); // 结构出共享资源过程池,依据系统软件的核心总数,特定过程池数量,还可以别的总数 const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); const createHappyPlugin = (id, loaders) = new HappyPack({ // 用唯一的标志符 id 来意味着当今的 HappyPack 是用于解决一类特殊的文档 id: id, // 怎样解决 .js 文档,使用方法和 Loader 配备中一样 loaders: loaders, // 其他配备项(可选择) // 意味着共享资源过程池,即好几个 HappyPack 案例都应用同一个共享资源过程池中的子过程好去处理每日任务,防止止資源占有过量 threadPool: happyThreadPool, // 是不是容许 HappyPack 輸出系统日志,默认设置是 true verbose: true // threads:意味着打开好多个子过程好去处理这一种类的文档,默认设置是3个,种类务必是整数金额 }); const clientWebpackConfig = { // ... module: { rules: [
 { test: /\.(js|jsx)$/, // 把对 .js .jsx 文档的解决转送给 id 为 happy-babel 的 HappyPack 案例 use: ["happypack/loader?id=happy-babel"], // 清除 node_modules 文件目录下的文档 // node_modules 文件目录下的文档全是选用的 ES5 英语的语法,没必需再根据 Babel 去变换 exclude: /node_modules/,
 }, // ... plugins: [
 createHappyPlugin('happy-babel', [{ loader: 'babel-loader', options: { presets: ['@babel/preset-env', "@babel/preset-react"], plugins: [
 ["import", { "libraryName": "antd", "style": true }],
 ['@babel/plugin-proposal-class-properties',{loose:true}]
 ], cacheDirectory: true, // Save disk space when time isn't as important cacheCompression: true, compact: true,
 }]), // ... ]
} 拷贝编码
留意,当新项目较钟头,多过程装包反倒会使装包速率很慢。 四、有效运用缓存文件(减少持续搭建時间,提升原始搭建時间) 应用 webpack 缓存文件的方式有几类,比如应用cache-loader,HardSourceWebpackPlugin或babel-loader的cacheDirectory标示。 全部这种缓存文件方式都是有起动的花销。 再次运作期内在当地节约的時间非常大,可是原始(冷)运作具体上面更慢。 假如你的新项目生产制造版本号每一次都务必开展原始搭建得话,缓存文件会提升搭建時间,缓减你的速率。假如并不是,那他们便会大大的减缩你二次搭建的時间。 1. cache-loader cache-loader 和 thread-loader 一样,应用起來也非常简单,只是必须在一些特性花销很大的 loader 以前加上此 loader,以将結果缓存文件到硬盘里,明显提高二次搭建速率。
module.exports = { module: { rules: [
 { test: /\.ext$/, use: ['cache-loader', ...loaders], include: path.resolve('src'),
}; 拷贝编码
⚠️ 一定要注意,储存和载入这种缓存文件文档会出现一些時间花销,因此请只对特性花销很大的 loader 应用此 loader。 2. HardSourceWebpackPlugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') const clientWebpackConfig = { // ... plugins: [ new HardSourceWebpackPlugin({ // cacheDirectory是在髙速缓存文件载入。默认设置状况下,将缓存文件储存在node_modules下的文件目录中 // 'node_modules/.cache/hard-source/[confighash]' cacheDirectory: path.join(__dirname, './lib/.cache/hard-source/[confighash]'), // configHash在起动webpack案例时变换webpack配备, // 并且用于cacheDirectory为不一样的webpack配备搭建不一样的缓存文件 configHash: function(webpackConfig) { // node-object-hash on npm can be used to build this. return require('node-object-hash')({sort: false}).hash(webpackConfig);
 }, // 当载入器、软件、别的搭建时脚本制作或别的动态性依靠项产生变更时, // hard-source必须更换缓存文件以保证輸出恰当。 // environmentHash被用于明确这一点。假如散列与此前的搭建不一样,则将应用新的缓存文件 environmentHash: { root: process.cwd(), directories: [], files: ['package-lock.json', 'yarn.lock'],
 }, // An object. : { // 'none' or 'test'. mode: 'none', // 'debug', 'log', 'info', 'warn', or 'error'. level: 'debug',
 }, // Clean up large, old caches automatically. cachePrune: { // Caches younger than `maxAge` are not considered for deletion. They must // be at least this (default: 2 days) old in milliseconds. maxAge: 2 * 24 * 60 * 60 * 1000, // All caches together must be larger than `sizeThreshold` before any // caches will be deleted. Together they must be at least this // (default: 50 MB) big in bytes. sizeThreshold: 50 * 1024 * 1024 },
 }), new HardSourceWebpackPlugin.ExcludeModulePlugin([
 { test: /.*\.DS_Store/ }
} 拷贝编码
五、提升缩小時间
webpack3 起动装包时再加--optimize-minimize,那样 Webpack 会全自动给你引入一个含有默认设置配备的 UglifyJSPlugin 。
module.exports = { optimization: { minimize: true,
} 拷贝编码
缩小 JavaScript 编码必须先把编码分析成用 Object 抽象性表明的 AST 英语的语法树,再去运用各种各样标准剖析和解决 AST,造成这一全过程测算量极大,用时十分多。但UglifyJsPlugin是单进程,因此大家可使用ParallelUglifyPlugin。 ParallelUglifyPlugin软件完成了多过程缩小,ParallelUglifyPlugin会打开好几个子过程,把对好几个文档的缩小工作中分派给好几个子过程去进行,每一个子过程实际上還是根据UglifyJS去缩小编码,可是变为了并行处理实行。 因此ParallelUglifyPlugin能迅速的进行对好几个文档的缩小工作中。 2. webpack4 webpack4 中webpack.optimize.UglifyJsPlugin已被废料。 都不强烈推荐应用 ParallelUglifyPlugin,新项目基本处在没有人维护保养的环节,issue 没有人解决,pr没有人合拼。 webpack4 默认设置内嵌应用terser-webpack-plugin软件缩小提升编码,而该软件应用terser来变小JavaScript。 terser 是啥? 说白了 terser,官方网得出的界定是: 用以 ES6+ 的 JavaScript pressor(缩小器)专用工具包。 为何 webpack 挑选 terser? 已不维护保养 uglify-es ,而且 uglify-js 不兼容 ES6 +。 terser 是 uglify-es 的一个支系,关键保存了与 uglify-es 和 [email protected] 的 API 和 CLI 适配性。
module.exports = { optimization: { minimizer: [ new TerserPlugin({ parallel: true,
}; 拷贝编码
能够明显加速搭建速率,因而明显强烈推荐打开多过程 六、提升检索時间- 变小文档检索范畴 减少无须要的编译程序工作中 webpack 装包时,会从配备的entry开启,分析通道文档的导进句子,再递归的分析,在碰到导进句子时 webpack 会做2件事情:
依据导进句子寻找相匹配的要导进的文档。比如require('react')导进句子相匹配的文档是./node_modules/react/react.js,require('./util')相匹配的文档是./util.js。
依据寻找的要导进文档的后缀名,应用配备中的 Loader 好去处理文档。比如应用 ES6 开发设计的 JavaScript 文档必须应用 babel-loader 好去处理。
之上2件事情尽管针对解决一个文档十分快,可是当新项目变大之后文档量能变的十分多,这时候候搭建速率慢的难题便会曝露出去。 尽管之上2件事情没法防止,但必须尽可能降低之上2件事情的产生,以提升速率。 接下去逐一详细介绍能够提升他们的方式。 1. 提升 loader 配备 应用 Loader 时能够根据 test 、 include 、 exclude 三个配备项来命里 Loader 要运用标准的文档 2. 提升 resolve.module 配备 resolve.modules 用以配备 webpack 去哪里些文件目录下找寻第三方控制模块,resolve.modules 的默认设置值是 ['node_modules'],含意是先去当今文件目录下的 ./node_modules 文件目录下来找想找的控制模块,假如没寻找就要上一级文件目录 ../node_modules 中找,再沒有就要 ../../node_modules 中找,为此类推。 3. 提升 resolve.alias 配备 resolve.alias 配备项根据别称来把原导进相对路径投射成一个新的导进相对路径,降低用时的递归分析实际操作。 4. 提升 resolve.extensions 配备 在导进句子没带文档后缀名时,webpack 会依据 resolve.extension 全自动携带后缀名后去试着了解文档是不是存有,因此在配备 resolve.extensions应负将会留意下列几个方面: