《Webpack5 核心原理与应用实践》学习笔记-> webpack性能优化技巧

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 《Webpack5 核心原理与应用实践》学习笔记-> webpack性能优化技巧


之前讲的是构建性能,这里是细粒度更高的性能优化,主要是还是体现在构建速度提升上,之前主要是对标生产环境,这里主要是对标开发环境。


使用最新版


使用最新版的webpacknode,新版本对比旧版本一定是更优的,没有谁说越更新越菜鸡的吧,新版本的更新不一定体现在性能优化上,还有会有新特性的增加。


但是需要注意的是不要盲目升级,例如新版本做了破坏性的变更,使用的插件或依赖不支持新版本的特性,等等一些问题都是需要考虑的。


新版本的更新一定是要了解新版本增加了什么特性,这些特性对于项目是否有影响,影响的强度是否能承受等,考虑好升级带来的利弊,再确定是否需要升级。


下面就是新版本的一些特性讲解了。


lazyCompilation


Webpack 5.17.0 之后引入实验特性 lazyCompilation,用于实现 entry 或异步引用模块的按需编译。


在以前如果webpack配置entry是多入口的情况,那么编译时是会将所有的都进行编辑,实际上这些入口你可能只会用到一到两个,其他的你根本就用不上,这种情况不仅浪费构建时间,同时也耗费内存,lazyCompilation的出现就是解决这个问题的,用法草鸡简单:


module.exports = {
    // 忽略其他配置
    experiments: {
        lazyCompilation: true
    }
}

它的作用是如果你定义的entry没有被访问,或者使用异步加载模块(const xxx = import('./xxx.js'))的方式,那么这些模块不会立即被构建,而是在你实际请求到这个模块的时候才会进行构建。


注意:这个不是懒加载,可以理解为懒构建,很明显这个功能不是提供给生产环境使用的,同时这个功能也是实验性功能,后面可能会发生较大的改动,建议使用该功能的锁定版本。


此外,lazyCompilation 支持如下参数:


  • backend: 设置后端服务信息,一般保持默认值即可;
  • entries:设置是否对 entry 启动按需编译特性;
  • imports:设置是否对异步模块启动按需编译特性;
  • test:支持正则表达式,用于声明对那些异步模块启动按需编译特性。


约束 Loader 执行范围


loader都打交道很长时间了,作用就是解析各种不同类型的文件,但是往往我们在使用一些第三方库的时候,库的作者都替我们把资源编译好了,我们如果再去解析就是在浪费性能了。


例如在node_modules下面,我们可以看到所以我们使用到的第三方依赖库,node_modules也是动辄上百兆。


但是通常情况下我们是不需要对node_modules下的文件进行编译的,所以我们可以使用module.rules.exclude排除掉node_modules下的编译:


module.exports = {
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                use: ["babel-loader"],
            },
        ],
    },
}

使用这个属性webpack看到是这个文件是这个目录下面的就会直接跳过,也就不会触发对应的loader的处理。


这里的属性值可以是字符串正则表达式数组函数condition


一般看到exclude都会想到配套的includeinclude的使用方法和exclude相同,作用相反,同时exclude优先级高于include:


module.exports = {
    module: {
        rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                include: /node_modules/, // 无效
                use: ["babel-loader"],
            },
        ],
    },
}

使用 noParse 跳过文件编译


很多npm的库作者已经帮你提前编译好库的内容了,一般都在 node_modules/lib/dist下面,这个时候我们就不需要再二次编译了:


module.exports = {
    module: {
        noParse: /lodash|vue/,
    },
};

这里需要注意的是并不是所有库的作者都给你提供了编译后的资源,需要自行确认,其次由于跳过了编译,无法发现包中的错误,也无法标记导出值。


开发模式禁用产物优化


webpack默认提供了很多产物优化方案,但是这些都是需要算力支持的,在开发环境中我们其实并不需要关系这些优化的东西,只有在生产环境中为了减少包的体积我们才需要,因此,开发模式下建议关闭这一类优化功能,如下:


  • 确保 mode='development'mode = 'none',关闭默认优化策略;
  • optimization.minimize 保持默认值或 false,关闭代码压缩;
  • optimization.concatenateModules 保持默认值或 false,关闭模块合并;
  • optimization.splitChunks 保持默认值或 false,关闭代码分包;
  • optimization.usedExports 保持默认值或 false,关闭 Tree-shaking 功能;


最终,建议开发环境配置如:


module.exports = {
  mode: "development",
  optimization: {
    removeAvailableModules: false,
    removeEmptyChunks: false,
    splitChunks: false,
    minimize: false,
    concatenateModules: false,
    usedExports: false,
  },
};

最小化 watch 监控范围


watchnpx webpack --watch命令)会持续监听文件是否发生改变,如果发生改变会就执行rebuild,但是往往很多文件我们项目持续到结束都不会发生修改,例如node_modules下的文件(万恶之首,又不能干掉,哈哈哈),所以我们需要对他取消监控:


module.exports = {
    watchOptions: {
        ignored: /node_modules/
    },
};

跳过 tseslint 检查


上面讲的都是怎么减少构建的内容来提升性能,但是还有性能点可能不在编译时,还有运行时(应该是开发时),tseslint在开发时都会进行检查,ts做类型推导,eslint做代码检查,这些都是需要吃算力的。


对于ts可以使用fork-ts-checker-webpack-plugin插件来将类型推导放到子进程中执行:


const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
    module: {
        rules: [{
            test: /.ts$/,
            use: [
                {
                    loader: 'ts-loader',
                    options: {
                        // 设置为“仅编译”,关闭类型检查
                        transpileOnly: true
                    }
                }
            ],
        }, ],
    },
    plugins:[
        // fork 出子进程,专门用于执行类型检查
        new ForkTsCheckerWebpackPlugin()
    ]
};

对于eslint可以使用eslint-webpack-plugin代替eslint-loader


const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
    plugins: [
        new ESLintPlugin(options)
    ],
};

个人更推荐使用ide的插件或其他附件功能实现他们的特性,例如WebStorm对于上面两者的检测是自带的,当然vscode也是有对应的插件。


慎用 source-map


source-map是很消耗性能的一个东东,但是呢在开发环境中还是需要的,生产环境中是不需要有的,所以建议:


  • 开发环境使用 eval ,确保最佳编译速度;
  • 生产环境使用 source-map,获取最高质量。


source-map的属性值在之前的文章有讲过,所以这里就没有详细的。


设置 resolve 缩小搜索范围


resolve.extensions


这个属性让我们在使用importrequire可以忽略后缀,例如:import('./a'),找到的可能是./a.js./a.json./a.wasm./a/index.js./a/index.json./a/index.wasm、 但是需要做到这些程序还是需要做很多推导工作的。


上面列举的这些都是webpack默认支持的,我们可以使用这个属性扩展我们自己的想要的,例如ts,但是不宜过多,同时可以指定文件后缀名来跳过解析。


resolve.modules


这个属性用来告诉 webpack 解析模块时应该搜索的目录,默认的情况下是从根目录下寻找node_modules目录,如果没找到就往上层找,直到全局。


一般情况下,一个项目都是控制node_modules的范围的,所以这个功能实用性不高,建议关闭:


const path = require('path');
module.exports = {
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')],
    },
};

resolve.mainFiles


这个属性其实和resolve.extensions有点对应,import('./a')如果不带后缀其实就是一个目录,但是这种情况下webpack是要找到对应的文件的,所以这里对应的文件名就是通过这个属性来配置:


module.exports = {
    resolve: {
        mainFiles: ['index'],
    },
};

默认情况下是index,所以上面resolve.extensions属性中就会出现index.xxx了,这里告诉这个属性当然是希望能尽量减少对这个属性的配置。


总结


webpack构建性能这一块一直都被诟病,所以这一块也是一个挑战,对于性能优化建议先使用性能分析工具,来分析性能瓶颈,再通过对应的手段来进行性能优化。


目录
相关文章
|
1月前
|
监控 前端开发 JavaScript
Webpack 中 HMR 插件的工作原理
【10月更文挑战第23天】可以进一步深入探讨 HMR 工作原理的具体细节、不同场景下的应用案例,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解 HMR 插件的工作原理,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的 HMR 技术和最佳实践。
|
1月前
|
缓存 前端开发 JavaScript
Webpack 动态加载的原理
【10月更文挑战第23天】Webpack 动态加载通过巧妙的机制和策略,实现了模块的按需加载和高效运行,提升了应用程序的性能和用户体验。同时,它也为前端开发提供了更大的灵活性和可扩展性,适应了不断变化的业务需求和技术发展。
|
1月前
|
缓存 前端开发 JavaScript
Webpack 4 和 Webpack 5 区别?
【10月更文挑战第23天】随着时间的推移,Webpack 可能会继续发展和演进,未来的版本可能会带来更多的新特性和改进。保持对技术发展的关注和学习,将有助于我们更好地应对不断变化的前端开发环境。
|
1月前
|
缓存 前端开发 JavaScript
webpack 原理
【10月更文挑战第23天】Webpack 原理是一个复杂但又非常重要的体系。它通过模块解析、依赖管理、加载器和插件的协作,实现了对各种模块的高效打包和处理,为现代前端项目的开发和部署提供了强大的支持。同时,通过代码分割、按需加载、热模块替换等功能,提升了应用程序的性能和用户体验。随着前端技术的不断发展,Webpack 也在不断演进和完善,以适应不断变化的需求和挑战。
|
2月前
|
缓存 前端开发 JavaScript
Webpack 打包的基本原理
【10月更文挑战第5天】
|
2月前
|
缓存 前端开发 JavaScript
Webpack技术深度解析:模块打包与性能优化
【10月更文挑战第13天】Webpack技术深度解析:模块打包与性能优化
|
3月前
|
JavaScript 前端开发
手写一个简易bundler打包工具带你了解Webpack原理
该文章通过手写一个简易的打包工具bundler,帮助读者理解Webpack的工作原理,包括模块解析、依赖关系构建、转换源代码以及生成最终输出文件的整个流程。
|
4月前
|
缓存 前端开发 JavaScript
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
214 1
|
7月前
|
缓存 资源调度 监控
Webpack 5新特性详解与性能优化实践
Webpack 5通过确定性的Chunk ID、模块ID和导出ID实现了长期缓存,这意味着相同的输入将始终产生相同的输出。这样,当你的用户再次访问更新后的网站时,浏览器可以重用旧的缓存,而不是重新下载所有资源。
93 2
|
7月前
|
API 开发工具 开发者
webpack热更新原理
Webpack的Hot Module Replacement(HMR)提升开发效率,无需刷新页面即可更新模块。开启HMR需在配置中设`devServer.hot: true`。Webpack构建时插入HMR Runtime,通过WebSocket监听并处理文件变化。当模块改变,Webpack发送更新到浏览器,HMR Runtime找到对应模块进行热替换,保持应用状态。开发者可利用`module.hot` API处理热替换逻辑。