开发体验
SourceMap
默认情况下,浏览器的js报错只能定位到打包后的js文件,无法定位到源代码
配置sourcemap后,可以直接定位到源代码
开发模式:打包编译速度快,只包含行映射,不包含列映射
module.exports = {
// 其他省略
mode: "development",
devtool: "cheap-module-source-map",
};
生产模式:包含行/列映射,但打包速度慢
module.exports = {
// 其他省略
mode: "production",
devtool: "source-map",
};
提升打包速度
模块热替换
hot module replacement, 在程序运行中,替换、添加或删除模块,而无需重新加载整个页面
module.exports = {
// 其他省略
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)
},
};
需要注意的是:
- css文件需要经过style-loader处理,不能单独提取css文件,文件哈希值变了
- js文件需要手动指明热模块替换的文件,在vue和react中,社区已经实现了相应的loader
-
// 判断是否支持HMR功能,js文件需要手动指定热替换文件 if (module.hot) { module.hot.accept("./js/add.js", function () { console.log('file changed') }); }
Oneof
默认情况下rules中的loader会逐个匹配,即使已经找到了匹配成功的loader。
Oneof可以保证只匹配一个loader,其余的不继续匹配了
Include/Exclude
只对文件处理/不处理,如在对js文件处理事,需要排除node_modules下面的文件
同时存在时,exclude优先级高于include
Cache
每次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包时速度就会更快
{
test: /.m?js$/i,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩
}
},
Thread
多进程打包,在js特别耗时的操作中使用。
减少代码体积
Tree shaking
production默认是开启了tree shaking的。需要注意的是tree shaking是依赖 es module分析的。webpack默认使用terser-webpack-plugin进行代码压缩,当需要自定义terser配置时,需要手动安装插件。
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};
webpack是没有删除未使用代码的,它这是根据依赖图,分析哪些模块和函数是没有被使用的,真正执行代码删除操作的是terser插件。
Babel
Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以将这些辅助代码作为一个独立模块,来避免重复引入。
@babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用
可以参考:zhuanlan.zhihu.com/p/147083132
{
test: /.m?js$/i,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
options: {
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
}
},
Image minimizer
压缩项目中静态图片,在线链接无法压缩。
image-minimizer-webpack-plugin: 用来压缩图片的插件
优化代码运行性能
Code split
分割文件 + 按需加载: 打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。
参考:webpack.docschina.org/plugins/spl…
-
为 Vendor 单独打包(Vendor 指第三方的库或者公共的基础组件,因为 Vendor 的变化比较少,单独打包利于缓存
-
为不同入口的业务代码打包,也就是代码分割异步加载(同理,也是为了缓存和加载速度)
-
为异步公共加载的代码打一个的包(如路由懒加载,每个路由下单独打包,import动态加载)
document.getElementById('webpack').onclick = () => { // webpackChunkName: "dynamic":这是webpack动态导入模块命名的方式 // "dynamic"将来就会作为[name]的值显示。 import(/* webpackChunkName: "dynamic" */ './js/dynamic.js').then(({ dynamicImport }) => { console.log('here') console.log(dynamicImport()) }) }
Preload/Prefetch
使用 import 动态语法来进行代码按需加载,但是其加载速度不够好,比如:是用户点击按钮时才加载这个资源的,如果资源体积很大,那么用户会感觉到明显卡顿效果。
我们想在浏览器空闲时间,加载后续需要使用的资源。我们就需要用上 Preload 或 Prefetch 技术。
-
两者均为加载资源,并不会“执行”
-
preload 告诉浏览器立即加载资源;prefetch 告诉浏览器在空闲时才开始加载资源;一般首屏资源用preload,路由资源用prefetch
-
Preload只能加载当前页面需要使用的资源,Prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源new PreloadWebpackPlugin({ rel: "preload", // preload兼容性更好 as: "script", // rel: 'prefetch' // prefetch兼容性更差 }),