webpack实际实践优化项目

参考:
如何通过性能优化,将包的体积压缩了62.7%
雅虎35条
20210526-webpack深入学习,搭建和优化react项目

本文只专注于性能优化的这个部分。

总体来说分为两个方面:第一是开发环境中主要优化打包速度,第二是线上环境中主要优化分包大小。

打包阶段

使用speed-measure-webpack-plugin插件,显示打包过程中的每个步骤耗时大小。然后根据耗费的时长去优化。
比如下图:

安装cache-loader打包缓存
  {test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,use: [cacheLoader,{loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('img/[name].[hash:7].[ext]')}}]}
happypack .在打包的时候开启多线程打包
UglifyJsPlugin开启多线程优化时间,有两个方式,可以都试一下看看哪个效果好一点。
  1. 开启多线程
  // 在minimizer属性内添加// 自定义js优化配置,将会覆盖默认配置new UglifyJsPlugin({parallel: true,  //使用多进程并行运行来提高构建速度sourceMap: false,uglifyOptions: {warnings: false,compress: {unused: true,drop_debugger: true,drop_console: true, },output: {comments: false // 去掉注释}}})
  1. 使用:webpack-parallel-uglify-plugin
    上图可以看出UglifyJsPlugin这个包花费时间太长,可以使用相同效果的另一个包,开启多核同步压缩增加压缩的效率。
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
new ParallelUglifyPlugin({uglifyJS: {output: {beautify: false, // 是否保留空格和制表符,设置为不保留comments: false, // 是否保留代码中的注释,设置为不保留},compress: {drop_console: true, // 是否删除代码中的console语句,设置为删除collapse_vars: false, // 是否内嵌虽然已经定义了,但是只用到一次的变量,设置为不使用reduce_vars: false, // 是否提取出现了多次但是没有定义成变量去引用的静态值,设置为不转换},warnings: false // 是否在删除没有用到的代码时输出警告信息,设置为不警告},}),
线上优化阶段

这一部分主要在于分离包(组件懒加载、路径懒加载、按需引入)、合并包(optimization.splitChunks)、压缩包(NGINX: gzip)
具体可以看以前的文章20210526-webpack深入学习,搭建和优化react项目

使用webpack-bundle-analyzer插件,显示打包后的包大小、包组成,然后针对去处理。
将大包拆小:

异步引入,路由懒加载 分包 import('@/components/Index')
按需引入组件,看看是不是直接把一个依赖都引入进来了,比如lodash需要引入babel-plugin-lodash等。

优化代码和合并相同的包

看看analyzer显示的第三方包有没有重复引入、打包到不同包的情况,这样的话就比较浪费空间,最好将第三方包合并一下,这里就使用到 optimization.splitChunks

压缩代码、去除无用代码

引入Uglifyjs进行代码压缩(第一步时间优化已经做了) Uglifyjs还会进行Tree-shaking剔除无用代码。比如引入了第三方包却没有用上的情况也会删除。

在服务器上的优化
  1. 图片和其他文件进行无损压缩,并上传到cdn上。
    推荐网站:https://tinyjpg.com

  2. nginx配置开通gzip ,从线上拉取的代码能压缩2/3
    巨强。

Next项目的实际实践

一年半年前进行过一次next项目的webpack优化,那时候的优化方向主要是splitChunks分包的数据。
半年前的参考文章可看博客列表:结合Next项目实际认识webpack.splitChunks

Next版本:12.3.4(因为使用next-plugin-antd-less 只能支持到next12。。)
Node版本:16.20.2

推荐阅读:v12.3.X的Next文档

经过半年多的持续优化,在webpack这方面又有了新的优化目标:

  1. 发现treeShaking没有生效,引入但是没有使用的资源仍然打包进去了。
  2. console.log正式环境禁用。
  3. 同一个组件在不同的包里被反复打包,导致一个页面上被多次下载。
  4. 部署构建太慢,想要加快速度。

Treeshaking

结论:next使用terser-plugin做的压缩和摇树,本地构建不会开,但是在构建环境会使用,因此确实会把没有引用的组件shake出去,不会影响使用。

调研过程:

  1. 一开始从本地上看以为失效了,查看了很多terser有关的问题,列在如下:
    • https://github.com/vercel/next.js/issues/20804
    • https://github.com/vercel/next.js/issues/49742
  2. 推荐使用next13+可以解决这个问题,于是重新实验,但是本地情况下仍然没有shake出去。
  3. nvm安装(next13+必须要node18以上)教程:
    • https://blog.csdn.net/Cavin80/article/details/132831839
    • https://juejin.cn/post/7083026831263137800
  4. 按照方案二,增加terser的参数passes:2可以解决,但是next并没有开放terserOptions给我们,因此只能另辟蹊径,找了半天源码,找到了一个v12版本可行的options插入方法。注意这个为后续的drop_console方案提供了思路。(不过事实证明passes也没什么变化)
  // next v12版本的实验特性,用于配置swc压缩属性experimental: { swcMinifyDebugOptions: { compress: { passes: 2 } } },
  1. modularizeImports。经过不懈寻找,找到了这么一个属性,可以用于内部组件的按需引入。

    官方推荐按需引入来达到treeShaking效果:https://github.com/vercel/next.js/issues/45687

    https://vercel.com/blog/how-we-optimized-package-imports-in-next-js

  2. 最后发现:原来是没有部署到线上,正式构建环境是可以treeshaking成功的。


drop console

线上环境我们并不想显示意外的console,因此删除console也是一个需求。

next提供的没有起作用。

  compiler: {removeConsole: {exclude: ['error'],},},

根据上面对源码的研究,找到了插入terserOptions的方式,增加了如下代码,push到构建环境后就能正常生效了。

  experimental: {swcMinifyDebugOptions: { compress: { passes: 2, drop_console: true } },},

PS.中间有一段自行重新引入了terser-webpack-plugin没有成功。

        config.optimization.minimizer = [new TerserPlugin({parallel: true,sourceMap: true,terserOptions: {compress: { drop_console: isProduction },},}),];

部署构建太慢,想要加快速度。

  1. 引入速度衡量依赖speed-measure-webpack-plugin

    使用参考:https://github.com/vercel/next.js/discussions/33678

    config.plugins.push(smp);
    
  2. 分析:注意vscode setting原本默认的展示日志行数太少,可以通过 “terminal.integrated.scrollback”:1000000配置。

经过测试速度插件,可以看出耗时较久的是next\ no-loaders\ less-loader,前两个应该是next内部自行实现的(很无奈),那我们只能从less-loader下手了。

接下来是省流版:本项目使用next-plugin-antd-less已经不再维护,并且不支持多线程处理,因此费劲了九牛二虎之力仍然没有效果。作为一次探究过程放在下面。

打印出next的loader配置,经过对next-plugin-antd-less源码的研究,实际上对于css的处理next都是放在oneOf中的,我们要处理也要优先处理oneOf中的loader。

我考虑的方案是:

  1. 采用thread-loader添加多线程处理。
  2. 或者采用happyPack添加多线程处理。

const withConsole = (config) => {// console.log('----resolveLoader----\n',config.resolveLoader)// console.log('----rules----\n',config.module.rules)// console.log('---oneOf----\n',console.log(config.module.rules.map((rule) => Array.isArray(rule.oneOf) && rule)))return {...config,webpack(webpackConfig, nextConfig) {// const _oneOf = [];// webpackConfig.module.rules.forEach((rule) => {//   if (Array.isArray(rule)) {//     rule.forEach((loader) => {//       Array.isArray(loader.oneOf) && _oneOf.push(loader);//     });//   } else {//     Array.isArray(rule.oneOf) && _oneOf.push(rule);//   }// });// // console.log('---oneOf----\n');// _oneOf.forEach(rule=>{//   rule.oneOf.forEach(_d=>{//     if(_d.test?.toString() === /\.module\.(scss|sass)$/.toString()){//       console.log(//         '---sass----\n',//         '🌈',//         _d//       )//     }//     console.log(_d)//   })// })const config = nextConfig;console.log('---config----\n', config);return webpackConfig;},};
};
{test: /(antd\/.*?\/style|@ant-design).*(?<![.]js)$/,use: 'null-loader'},{ issuerLayer: 'edge-asset', type: 'asset/source' },{dependency: 'url',loader: 'next-middleware-asset-loader',type: 'javascript/auto',layer: 'edge-asset'},{test: /\.wasm$/,loader: 'next-middleware-wasm-loader',type: 'javascript/auto',resourceQuery: /module/i},{ test: /\.m?js/, resolve: { fullySpecified: false } },{test: /\.(js|cjs|mjs)$/,issuerLayer: 'api',parser: { url: true }},{oneOf: [[Object], [Object],[Object], [Object],[Object], [Object],[Object], [Object],[Object], [Object],[Object], [Object]]},{test: /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i,loader: 'next-image-loader',issuer: { not: /\.(css|scss|sass)$/ },dependency: { not: [Array] },options: { isServer: true, isDev: false, basePath: '', assetPrefix: '' }},{ oneOf: [ [Object], [Object] ] },{test: /\.+(js|jsx|mjs|ts|tsx)$/,use: [ 'thread-loader', [Object] ],include: [Function (anonymous)],type: 'javascript/auto'},{test: /\.(jpg|jpeg|png|svg|gif|ico|webp|jp2|avif)$/,issuer: /\.\w+(?<!(s?c|sa)ss)$/i,exclude: undefined,use: [ [Object] ]}
]
但是问题点在于,我打印出了所有的loader配置以后发现 找不到less-loader。。

然后我没有办法,把next-plugin-antd-less源码的插件引入了项目测试一下效果,在手动引入的代码文件中增加了thread-loader.

报错:UnhandledPromiseRejectionWarning: TypeError: loaderContext.getLogger is not a function
查找了一下这个问题是因为less-loader的版本问题导致的,降低了一下less-loader的版本。。。
https://github.com/webpack-contrib/thread-loader/issues/135

最后效果看起来确实少了5-6秒,但是不太值得。最终放弃了这个方案,以及happyPack也是同样的原因,做不到再把plugin引进源码文件再处理一次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/473403.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BIG DATA —— 大数据时代

大数据时代 [英] 维克托 迈尔 — 舍恩伯格 肯尼斯 库克耶 ◎ 著 盛杨燕 周涛◎译 《大数据时代》是国外大数据研究的先河之作&#xff0c;本书作者维克托迈尔舍恩伯格被誉为“大数据商业应用第一人”&#xff0c;他在书中前瞻性地指出&#xff0c;大数据带来的信息…

python-自动化篇-运维-网络-IP

文章目录 IP自我介绍IPy安装模块windowsLinux IPy介绍支持大多数 IP 地址格式IPv4 地址IPv6 地址网络掩码和前缀 派生网络地址将地址转换为字符串使用多个网络多网络计算方法 IP自我介绍 IP地址规划是网络设计中非常重要的一个环节&#xff0c;规划的好坏会直接影响路由协议算…

2024年【天津市安全员B证】考试技巧及天津市安全员B证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年天津市安全员B证考试技巧为正在备考天津市安全员B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的天津市安全员B证复审模拟考试祝您顺利通过天津市安全员B证考试。 1、【多选题】《建设行政处罚决定…

力扣刷题之旅:进阶篇(六)—— 图论与最短路径问题

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 --点击进入刷题地址 引言 在算法的广阔天地中&#xff0c;图论是一个非常重要的领域。…

《Go 简易速速上手小册》第8章:网络编程(2024 最新版)

文章目录 8.1 HTTP 客户端与服务端编程 - Go 语言的网络灯塔与探航船8.1.1 基础知识讲解服务端编程客户端编程 8.1.2 重点案例&#xff1a;简易博客服务服务端实现客户端实现运行示例 8.1.3 拓展案例 1&#xff1a;增加文章评论功能功能描述服务端实现客户端实现 8.1.4 拓展案例…

MATLAB导出图程序

本文将以代码的形式快速介绍MATLAB导出图到Paper 1 从simulation导出数 2 与simulation同源文件夹下创建导图m文件 代码如下&#xff1a; % 实验后的数据处理用 M-文件 % clear all % 清空工作空间 % close all      % 关闭所有图形窗口 % load adp.mat …

二叉树前序中序后序遍历(非递归)

大家好&#xff0c;又和大家见面啦&#xff01;今天我们一起去看一下二叉树的前序中序后序的遍历&#xff0c;相信这个对大家来说是信手拈来&#xff0c;但是&#xff0c;今天我们并不是使用常见的递归方式来解题&#xff0c;我们采用迭代方式解答。我们先看第一道前序遍历 1…

把Activity当做dialog对话框使用

1、引言 在安卓开发中对话框的使用是不可避免的&#xff0c;但是原生的对话框用起来总感觉差点意思&#xff0c;而且位置不好控制&#xff0c;在与界面的交互上也不够灵活&#xff0c;没有像activity那样的生命周期方法&#xff0c;以至于某些特殊的功能无法实现。此时我们就希…

MySQL 基础知识(八)之用户权限管理

目录 1 MySQL 权限管理概念 2 用户管理 2.1 创建用户 2.2 查看当前登录用户 2.3 修改用户名 2.4 删除用户 3 授予权限 3.1 授予用户管理员权限 3.2 授予用户数据库权限 3.3 授予用户表权限 3.4 授予用户列权限 4 查询权限 5 回收权限 1 MySQL 权限管理概念 关于 M…

太以假乱真了,大家小心

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

「算法」滑动窗口

前言 算法需要多刷题积累经验&#xff0c;所以我行文重心在于分析解题思路&#xff0c;理论知识部分会相对简略一些 正文 滑动窗口属于双指针&#xff0c;这两个指针是同向前行&#xff0c;它们所夹的区间就称为“窗口” 啥时候用滑动窗口&#xff1f; 题目涉及到“子序列…

数据结构之时空复杂度

一、前言 1&#xff09;什么是数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的 集合。 2&#xff09;什么是算法 算法(Algorithm):就是定义良好的计算过程&#xff0c;他取一个或一组的值为输入&am…