当包过大时,如果我们更新一小部分的包内容,那么整个包都需要重新加载,如果我们把这个包拆分,那么我们仅仅需要重新加载发生内容变更的包,而不是所有包,有效的利用了缓存。
拆分 node_modules
很多情况下,我们不需要手动拆分包,可以使用 optimization.splitChunks :
const path = require('path'); module.exports = { entry: path.resolve(__dirname, 'src/index.js'), output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash].js', }, optimization: { splitChunks: { // 对所有的包进行拆分 chunks: 'all', }, }, };
我们不必制定拆包策略, chunks: all 会自动将 node_modules 中的所有内容放入一个名为 vendors〜main.js 的文件中。
拆分业务代码
module.exports = { entry: { main: path.resolve(__dirname, 'src/index.js'), ProductList: path.resolve(__dirname, 'src/ProductList/ProductList.js'), ProductPage: path.resolve(__dirname, 'src/ProductPage/ProductPage.js'), Icon: path.resolve(__dirname, 'src/Icon/Icon.js'), }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash:8].js', }, };
采用多入口的方式,当有业务代码更新时,更新相应的包即可
拆分第三方库
const path = require('path'); const webpack = require('webpack'); module.exports = { entry: path.resolve(__dirname, 'src/index.js'), output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash].js', }, optimization: { runtimeChunk: 'single', splitChunks: { chunks: 'all', maxInitialRequests: Infinity, minSize: 0, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name(module) { // 获取第三方包名 const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]; // npm 软件包名称是 URL 安全的,但是某些服务器不喜欢@符号 return `npm.${packageName.replace('@', '')}`; }, }, }, }, }, };
当第三方包更新时,仅更新相应的包即可。
注意,当包太多时,浏览器会发起更多的请求,并且当文件过小时,对代码压缩也有影响。
动态加载
现在我们已经对包拆分的很彻底了,但以上的拆分仅仅是对浏览器缓存方面的优化,减小首屏加载时间,实际上我们也可以使用按需加载的方式来进一步拆分,减小首屏加载时间:
import React, { useState, useEffect } from 'react'; import './index.scss' function Main() { const [NeighborPage, setNeighborPage] = useState(null) useEffect(() => { import('../neighbor').then(({ default: component }) => { setNeighborPage(React.createElement(component)) }); }, []) return NeighborPage ? NeighborPage : <div>Loading...</div>; } export default Main
七、配置 plugin
配置 Plugin 去处理及优化其它的需求,
module.exports = { plugins: [ // 优化 require new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en|zh/), // 用于提升构建速度 createHappyPlugin('happy-babel', [{ loader: 'babel-loader', options: { presets: ['@babel/preset-env', "@babel/preset-react"], plugins: [ ['@babel/plugin-proposal-class-properties', { loose: true }] ], // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启 cacheDirectory: true, // Save disk space when time isn't as important cacheCompression: true, compact: true, } }]) ] }
常用 plugins:
html-webpack-plugin :生成 html 文件,并将包添加到 html 中
webpack-parallel-uglify-plugin :压缩 js(多进程并行处理压缩)
happypack :多线程loader,用于提升构建速度
hard-source-webpack-plugin :为模块提供中间缓存步骤,显著提高打包速度
webpack-merge :合并 webpack 配置
mini-css-extract-plugin :抽离 css
optimize-css-assets-webpack-plugin :压缩 css
add-asset-html-webpack-plugin :将 JavaScript 或 CSS 资产添加到 html-webpack-plugin 生成的 HTML 中
更多插件可见:plugins
八、配置devtool:source map
配置 webpack 如何生成 Source Map,用来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度:
生产环境:默认为 null ,一般不设置( none )或 nosources-source-map
开发环境:默认为 eval ,一般设置为 eval 、 cheap-eval-source-map 、 cheap-module-eval-source-map
策略为: