background
The latest requirement is to embed h5 in the client webview and use vite2 X and vue3. When the first version was tested, it was found that after packaging, the total package size was more than 4M, so there was a lot of room for optimization.
Tool chain description
- Currently vite2 X is packaged based on rollup, not esbuild. See here
- Use rollup plugin visualizer for packaging analysis. After packaging, a stats will be generated by default in the root directory HTML file
- Use vite plugin imagemin for image compression. Each package will be compressed once, which will take up the time of construction and have space worthy of optimization. It will not be discussed here first
- @Vitejs / plugin legacy vite supports modern browsers that support ESM by default. This plug-in is used to be compatible with browsers that do not support ESM. See here
- The configuration files and scripts involved in this article are placed in my warehouse: https://github.com/Rockergmail/imagemin-script
Before optimization
vite.config.js is configured as follows
import { UserConfigExport, ConfigEnv } from 'vite'; import vue from '@vitejs/plugin-vue'; import { viteVConsole } from 'vite-plugin-vconsole'; import { resolve } from 'path'; import legacy from '@vitejs/plugin-legacy'; import { visualizer } from 'rollup-plugin-visualizer'; export default ({ mode }: ConfigEnv): UserConfigExport => { return { plugins: [ vue(), styleImport({ libs: [ { libraryName: 'vant', esModule: true, resolveStyle: (name) => `vant/es/${name}/style` } ] }), viteVConsole({ entry: resolve(__dirname, './src/main.ts').replace(/\\/g, '/'), localEnabled: mode !== 'prod', // dev environment enabled: mode !== 'prod', // build production config: { maxLogNumber: 1000, theme: 'light' } }), legacy({ targets: ['ie >= 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'], renderLegacyChunks: true, polyfills: [ 'es.symbol', 'es.array.filter', 'es.promise', 'es.promise.finally', 'es/map', 'es/set', 'es.array.for-each', 'es.object.define-properties', 'es.object.define-property', 'es.object.get-own-property-descriptor', 'es.object.get-own-property-descriptors', 'es.object.keys', 'es.object.to-string', 'web.dom-collections.for-each', 'esnext.global-this', 'esnext.string.match-all' ] }, visualizer() ], build: { target: 'es2015', outDir: './dist/', cssCodeSplit: true } }; };
The packaging output information is as follows:
dist/assets/polyfills-legacy.a0c5535b.js 51.67 KiB / gzip: 20.70 KiB dist/assets/index-legacy.a9899ade.js 150.73 KiB / gzip: 46.17 KiB dist/assets/vendor-legacy.6772670a.js 1896.50 KiB / gzip: 576.97 KiB dist/assets/mos_jj@3x.24678237.png 9.92 KiB dist/assets/mos_meituan@3x.91fce938.png 14.19 KiB dist/assets/mos_xiecheng@3x.0ca58b53.png 11.89 KiB dist/assets/mos_score_bg.eb2dc424.png 15.92 KiB dist/assets/mos_good.f267c228.png 36.00 KiB dist/assets/mos_bad.58afe320.png 28.50 KiB dist/assets/mos_bonus_points_bg@3x.e18c09a9.png 175.78 KiB dist/assets/mos_bg_null.8ae1448c.png 55.00 KiB dist/assets/mos_price_bad.919aaddf.png 46.72 KiB dist/assets/mos_price_good.563cd841.png 62.14 KiB dist/assets/mos_bg_copper.515f59b3.png 86.77 KiB dist/assets/mos_bg_silver.4e31d711.png 82.74 KiB dist/assets/mos_bg_gold.c1f61403.png 93.59 KiB dist/assets/mos_copper.483f2a6f.png 79.09 KiB dist/assets/mos_silver.7da7643a.png 71.10 KiB dist/assets/mos_bg_good1.d6390420.png 146.27 KiB dist/assets/mos_gold.fa42d3e3.png 72.31 KiB dist/assets/mos_bg_null2.01d64f5d.png 7.25 KiB dist/assets/mos_bg_gold2.64b6b5e9.png 18.66 KiB dist/assets/mos_bg_bad1.1ceacacc.png 176.29 KiB dist/assets/mos_null.753e5b08.png 35.62 KiB dist/assets/mos_index_bg@3x.3cf17a3f.png 210.25 KiB dist/assets/mos_bg_copper1.9941fb6f.png 136.03 KiB dist/assets/mos_bg_null1.aa018f69.png 74.54 KiB dist/assets/mos_bg_silver1.e4fc875c.png 149.86 KiB dist/assets/mos_bg_gold1.9e337e71.png 157.11 KiB dist/assets/mos_qa.8c12f693.png 440.17 KiB dist/assets/mos_applyfor_img_desc1@3x.b87c6d38.png 639.01 KiB dist/assets/mos_applyfor_img_desc2@3x.f63db53c.png 669.85 KiB dist/index.html 1.95 KiB dist/assets/index.fa1d3dc8.css 43.33 KiB / gzip: 5.95 KiB dist/assets/polyfills-modern.3ab1202f.js 17.48 KiB / gzip: 7.16 KiB dist/assets/index.1cfeddd0.js 93.03 KiB / gzip: 37.47 KiB dist/assets/vendor.d320968a.css 116.42 KiB / gzip: 37.62 KiB dist/assets/vendor.88c712d4.js 1730.56 KiB / gzip: 530.72 KiB (!) Some chunks are larger than 500 KiB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. Done in 60.29s.
From the packaged output information, we can see:
- vendor-legacy.6772670a.js after packaging, there are still 576.97 KiB after 1896.50 KiB / gzip
- vendor. 88c712d4. After JS packaging, there are still 530.72 KiB after 1730.56 KiB / gzip
- Individual picture resources are too large
- The construction took 79.07 seconds, so there is room for optimization
Packaging Optimization
Use the rollup plugin visualizer plug-in to generate stats after packaging HTML, we can see the following chart
It should be noted that the package size displayed by the rollup plugin visualizer icon is the size when chunk s are divided but not compressed.
It can be observed that:
- zender and echarts take up most of the space and need to get rid of it first
- vant/es and @vue have been packaged on demand, and there is no room for optimization
- loadash/es has repeatedly checked that it is not used in the business code. Why is it packaged?
Optimize echarts packaging
Because I'm not familiar with echarts, I hope it was encapsulated by others when I introduced it. I chose vue3 echarts library, but from the packaging results, I packed all echarts.
So we decided to replace vue3 echarts with official ones.
After packaging, zender and echarts are still in the vendor without treeshaking, and they are all imported (echart only uses radar components and canvas renderer, not svg renderer and other components)
It can be seen that lodash no longer exists, which was originally caused by vue3 echarts. But echarts still needs to exclude it from vendor s. Here are two ideas:
Use echarts as externals. When packaging, import * as echots from 'echots' will be processed into window ['echots'] (the processing results will be different according to the output library target. Here, only the browser side is considered, which is the processing result of AMD specification).
- This scheme has one disadvantage: it will still be packaged in full. Although it can be found on [echarts official website]( https://echarts.apache.org/zh... )Customization, but lack of flexibility. If the project needs to introduce other modules, it needs to be customized again.
- Pack ecarts as an independent chunk, which is the chunk after treeshaking
When you see vite's alarm during packaging, there are two schemes to package echarts as an independent chunk
(!) Some chunks are larger than 500 KiB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
import() dynamic import
Change the code to:
const { echartsUse = use, echartsInit = init } = import('echarts/core'); // Radar charts are introduced, and the suffix of charts is Chart const { RadarChart } = import('echarts/charts'); // Radar chart components are introduced, and the suffix of components is Component const { RadarComponent } = import('echarts/components'); // When introducing the Canvas renderer, note that the introduction of Canvas renderer or SVGRenderer is a necessary step const { CanvasRenderer } = import('echarts/renderers'); // Register required components
Output after packaging
dist/assets/core-legacy.fb1211d8.js 5.18 KiB / gzip: 2.33 KiB dist/assets/createSeriesData-legacy.a0c30666.js 5.94 KiB / gzip: 2.47 KiB dist/assets/renderers-legacy.b888ddd4.js 30.54 KiB / gzip: 11.63 KiB dist/assets/polyfills-legacy.a0c5535b.js 51.67 KiB / gzip: 20.70 KiB dist/assets/graphic-legacy.d43ff24a.js 113.12 KiB / gzip: 38.24 KiB dist/assets/index-legacy.158e4856.js 152.88 KiB / gzip: 46.49 KiB dist/assets/customGraphicKeyframeAnimation-legacy.171ce259.js 123.95 KiB / gzip: 41.24 KiB dist/assets/charts-legacy.d5f2354e.js 238.20 KiB / gzip: 77.00 KiB dist/assets/Axis-legacy.ff4470f0.js 258.47 KiB / gzip: 88.32 KiB dist/assets/components-legacy.86e016d9.js 226.12 KiB / gzip: 71.46 KiB dist/assets/vendor-legacy.5481a04c.js 644.12 KiB / gzip: 196.07 KiB dist/assets/core.afbe1b14.js 4.13 KiB / gzip: 1.93 KiB dist/assets/createSeriesData.ee016422.js 5.82 KiB / gzip: 2.42 KiB dist/assets/renderers.453d5aab.js 30.52 KiB / gzip: 11.53 KiB dist/assets/index.242992b7.css 45.04 KiB / gzip: 6.09 KiB dist/assets/polyfills-modern.3ab1202f.js 17.48 KiB / gzip: 7.16 KiB dist/assets/index.854b8d89.js 94.24 KiB / gzip: 37.96 KiB dist/assets/graphic.c1bb426e.js 111.41 KiB / gzip: 37.28 KiB dist/assets/vendor.d320968a.css 116.42 KiB / gzip: 37.62 KiB dist/assets/customGraphicKeyframeAnimation.0a83d4fc.js 123.80 KiB / gzip: 40.83 KiB dist/assets/charts.d2837108.js 237.92 KiB / gzip: 76.78 KiB dist/assets/components.df137286.js 225.47 KiB / gzip: 70.87 KiB dist/assets/Axis.804e702b.js 259.07 KiB / gzip: 87.15 KiB dist/assets/vendor.6476629b.js 489.00 KiB / gzip: 151.32 KiB
It can be seen that echarts are packaged independently, without occupying the space of the vendor, and are packaged according to the modules in echarts
However, we can still see that the modules not introduced are packaged: we can still see the svg renderer and components other than radar.
manualChunks
Change the code back:
// Radar charts are introduced, and the suffix of charts is Chart import { RadarChart } from 'echarts/charts'; // Radar chart components are introduced, and the suffix of components is Component import { RadarComponent } from 'echarts/components'; // When introducing the Canvas renderer, note that the introduction of Canvas renderer or SVGRenderer is a necessary step import { CanvasRenderer } from 'echarts/renderers';
In vite config. JS add
build: { rollupOptions: { output: { manualChunks: { echarts: ['echarts'] } } } }
Output when packaging
dist/assets/polyfills-legacy.a0c5535b.js 51.67 KiB / gzip: 20.70 KiB dist/assets/echarts-legacy.fa6a2a05.js 361.40 KiB / gzip: 120.08 KiB dist/assets/index-legacy.a57b6cea.js 812.68 KiB / gzip: 244.17 KiB dist/assets/polyfills-modern.3ab1202f.js 17.48 KiB / gzip: 7.16 KiB dist/assets/index.1cfae15f.css 159.75 KiB / gzip: 44.24 KiB dist/assets/echarts.c2408918.js 360.93 KiB / gzip: 119.38 KiB dist/assets/index.e28d5487.js 600.23 KiB / gzip: 191.01 KiB Done in 41.78s.
You can see that the contents of the vendor package are directly entered into the index package, and there are more echarts packages
The echarts package just packages the referenced components
Optimize legacy package
The configuration of legacy is copied from other projects, and the compatibility of IE > = 11 version is considered. This project only needs to consider the mobile terminal, so it doesn't need to consider ie. Just set it as the default configuration
legacy({ targets: ['defaults', 'not IE 11'] })
Output after packaging:
dist/assets/polyfills-legacy.749f4000.js 68.82 KiB / gzip: 27.57 KiB dist/assets/echarts-legacy.fa6a2a05.js 360.15 KiB / gzip: 119.61 KiB dist/assets/index-legacy.a57b6cea.js 758.60 KiB / gzip: 235.17 KiB Done in 32.22s.
Image compression optimization
Picture compression, in awesome-vite Find vite plugin imagemin and configure it as follows:
viteImagemin({ gifsicle: { optimizationLevel: 7, interlaced: false, }, optipng: { optimizationLevel: 7, }, mozjpeg: { quality: 20, }, pngquant: { quality: [0.8, 0.9], speed: 4, }, svgo: { plugins: [ { name: 'removeViewBox', }, { name: 'removeEmptyAttrs', active: false, }, ], }, })
Repackage to see the effect:
[vite-plugin-imagemin]- compressed image resource successfully: dist/assets/mos_meituan@3x.91fce938.png -73% 14.19kb / tiny: 3.85kb dist/assets/mos_jj@3x.24678237.png -66% 9.92kb / tiny: 3.45kb dist/assets/mos_bg_gold2.64b6b5e9.png -91% 18.66kb / tiny: 1.84kb dist/assets/mos_xiecheng@3x.0ca58b53.png -76% 11.89kb / tiny: 2.95kb dist/assets/mos_bg_null2.01d64f5d.png -91% 7.25kb / tiny: 0.71kb dist/assets/mos_good.f267c228.png -76% 36.00kb / tiny: 8.83kb dist/assets/mos_bad.58afe320.png -78% 28.50kb / tiny: 6.32kb dist/assets/mos_silver.7da7643a.png -79% 71.10kb / tiny: 14.95kb dist/assets/mos_copper.483f2a6f.png -81% 79.09kb / tiny: 15.76kb dist/assets/mos_gold.fa42d3e3.png -78% 72.31kb / tiny: 15.93kb dist/assets/mos_null.753e5b08.png -75% 35.62kb / tiny: 8.92kb dist/assets/mos_bonus_points_bg@3x.e18c09a9.png -91% 175.78kb / tiny: 16.21kb dist/assets/mos_score_bg.eb2dc424.png -18% 15.92kb / tiny: 13.06kb dist/assets/mos_bg_copper.515f59b3.png -76% 86.77kb / tiny: 21.26kb dist/assets/mos_bg_gold.c1f61403.png -74% 93.59kb / tiny: 24.36kb dist/assets/mos_bg_silver.4e31d711.png -72% 82.74kb / tiny: 23.70kb dist/assets/mos_price_bad.919aaddf.png -65% 46.72kb / tiny: 16.47kb dist/assets/mos_bg_null.8ae1448c.png -66% 55.00kb / tiny: 18.97kb dist/assets/mos_price_good.563cd841.png -72% 62.14kb / tiny: 17.86kb dist/assets/mos_bg_null1.aa018f69.png -48% 74.54kb / tiny: 38.92kb dist/assets/mos_qa.8c12f693.png -77% 440.17kb / tiny: 103.91kb dist/assets/mos_bg_gold1.9e337e71.png -66% 157.11kb / tiny: 54.89kb dist/assets/mos_bg_good1.d6390420.png -65% 146.27kb / tiny: 52.56kb dist/assets/mos_bg_copper1.9941fb6f.png -60% 136.03kb / tiny: 55.20kb dist/assets/mos_applyfor_img_desc2@3x.f63db53c.png -70% 669.85kb / tiny: 204.32kb dist/assets/mos_bg_bad1.1ceacacc.png -68% 176.29kb / tiny: 56.69kb dist/assets/mos_bg_silver1.e4fc875c.png -63% 149.86kb / tiny: 56.70kb dist/assets/mos_applyfor_img_desc1@3x.b87c6d38.png -71% 639.01kb / tiny: 189.78kb dist/assets/mos_index_bg@3x.3cf17a3f.png -80% 210.25kb / tiny: 42.16kb Done in 79.86s.67
In this way, the problem of excessive picture resources is solved. Picture resources can also be uploaded to CDN for acceleration.
There will be a problem here: image compression will be carried out every time.
Although the advantage is that it will not affect the original picture, it will lead to repeated work and occupy packaging time. Since compressed images are needed for production, there is no need to retain the source images. So I referred to it vite-plugin-imagemin and imagemin A script is written to realize the following functions:
- Automatically execute this script for image compression before packaging
- Generate an imagemin after compression map. JSON files record which images have been compressed and do not need secondary compression
- Before compressing, check whether the modification time value of the current picture is within In the imagemin file, if it is, it will be filtered. If it is not, it needs to be compressed
- The compressed image is processed to cover the original path
Yes, packages JSON adds a pre script, and when executing yarn build:test, it will automatically execute yarn prebuild:test first
"scripts": { "prebuild:test": "node scripts/imagemin.mjs", "build:test": "vite build --mode test" },
Here are scripts / imagemin MJS code
import { promises as fs } from 'fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { globby } from 'globby'; import chalk from 'chalk'; import convertToUnixPath from 'slash'; import ora from 'ora'; import imagemin from 'imagemin'; import imageminGifsicle from 'imagemin-gifsicle'; import imageminOptpng from 'imagemin-optipng'; import imageminMozjpeg from 'imagemin-mozjpeg'; import imageminPngquant from 'imagemin-pngquant'; import imageminSvgo from 'imagemin-svgo'; // 1. Create imagemin map. If the JSON cache table has been processed, it will not be processed. After processing, it will be updated to imagemin map. json // 2. You need to overwrite the original image. There are multiple folders under assets/images, so you need to solve the path problem of dest and use imagemin Buffer to rewrite // 3. Some pictures will become larger after compression. In this case, the written file will not be overwritten, but the cached file will be written, and the timestamp is the timestamp of the old file itself // 4. For more picture type plug-ins, see https://github.com/orgs/imagemin/repositories?type=all // Cache file let cacheFilename = '../imagemin.map.json'; // Picture file directory const input = ['src/assets/images/**/*.{jpg,png,svg,gif}']; // plug-in unit const plugins = [ imageminGifsicle({ optimizationLevel: 7, interlaced: false }), imageminOptpng({ optimizationLevel: 7 }), imageminMozjpeg({ quality: 80 }), imageminPngquant({ quality: [0.8, 0.9], speed: 4 }), imageminSvgo({ plugins: [ { name: 'removeViewBox' }, { name: 'removeEmptyAttrs', active: false } ] }) ]; const debug = false; let tinyMap = new Map(); let filePaths = []; let cache, cachePath; let handles = []; let time; const spinner = ora('Picture compression in progress...'); (async () => { const unixFilePaths = input.map((path) => convertToUnixPath(path)); cachePath = path.resolve( path.dirname(fileURLToPath(import.meta.url)), cacheFilename ); cache = await fs.readFile(cachePath); cache = JSON.parse(cache.toString() || '{}'); // Match file path by wildcard filePaths = await globby(unixFilePaths, { onlyFiles: true }); // If the file is not in imagemin map. JSON, then join the queue; // If the file is in.imagemin map. JSON, and the modification time is inconsistent, then join the queue; filePaths = await filter(filePaths, async (filePath) => { let ctimeMs = cache[filePath]; let mtimeMs = (await fs.stat(filePath)).mtimeMs; if (!ctimeMs) { debug && console.log(filePath + 'Not listed in cache'); tinyMap.set(filePath, { mtimeMs }); return true; // The system timestamp is larger than date Now() is more accurate, with three more digits after the decimal point, so it is considered to be an effective cache within 1ms } else { if (Math.abs(ctimeMs - mtimeMs) > 1) { debug && console.log(` ${filePath}Listed in the cache but expired, ${ctimeMs} ${mtimeMs} differ ${ ctimeMs - mtimeMs }`); tinyMap.set(filePath, { mtimeMs }); return true; } else { // debug && console. Log (filepath + 'listed in cache'); return false; } } }); debug && console.log(filePaths); await processFiles(); })(); // To process a single file, call imagemin Buffer processing async function processFile(filePath) { let buffer = await fs.readFile(filePath); let content; try { content = await imagemin.buffer(buffer, { plugins }); const size = content.byteLength, oldSize = buffer.byteLength; if (tinyMap.get(filePath)) { tinyMap.set(filePath, { ...tinyMap.get(filePath), size: size / 1024, oldSize: oldSize / 1024, ratio: size / oldSize - 1 }); } else { tinyMap.set(filePath, { size: size / 1024, oldSize: oldSize / 1024, ratio: size / oldSize - 1 }); } return content; } catch (error) { console.error('imagemin error:' + filePath); } } // Batch processing async function processFiles() { if (!filePaths.length) { return; } spinner.start(); time = Date.now(); handles = filePaths.map(async (filePath) => { let content = await processFile(filePath); return { filePath, content }; }); handles = await Promise.all(handles); await generateFiles(); } // Generate files and overwrite source files async function generateFiles() { if (handles.length) { handles = handles.map(async (item) => { const { filePath, content } = item; if (content) { if (tinyMap.get(filePath).ratio < 0) { await fs.writeFile(filePath, content); cache[filePath] = Date.now(); } else { // There is a situation that it becomes larger after compression. This situation does not cover the original image, but will be recorded in the cache table, and the recorded timestamp is the timestamp of the old file itself cache[filePath] = tinyMap.get(filePath).mtimeMs; } } }); handles = await Promise.all(handles); handleOutputLogger(); generateCache(); } } // Generate cache file async function generateCache() { await fs.writeFile(cachePath, Buffer.from(JSON.stringify(cache)), { encoding: 'utf-8' }); } // Output results function handleOutputLogger() { spinner.stop(); console.info('Picture compression succeeded'); time = (Date.now() - time) / 1000 + 's'; const keyLengths = Array.from(tinyMap.keys(), (name) => name.length); const valueLengths = Array.from( tinyMap.values(), (value) => `${Math.floor(100 * value.ratio)}`.length ); const maxKeyLength = Math.max(...keyLengths); const valueKeyLength = Math.max(...valueLengths); tinyMap.forEach((value, name) => { let { ratio } = value; const { size, oldSize } = value; ratio = Math.floor(100 * ratio); const fr = `${ratio}`; // There is a situation that it becomes larger after compression. This situation does not cover the original image, so this situation shows 0% const denseRatio = ratio > 0 ? // ? chalk.red(`+${fr}%`) chalk.green(`0%`) : ratio <= 0 ? chalk.green(`${fr}%`) : ''; const sizeStr = ratio <= 0 ? `${oldSize.toFixed(2)}kb / tiny: ${size.toFixed(2)}kb` : `${oldSize.toFixed(2)}kb / tiny: ${oldSize.toFixed(2)}kb`; console.info( chalk.dim( chalk.blueBright(name) + ' '.repeat(2 + maxKeyLength - name.length) + chalk.gray( `${denseRatio} ${' '.repeat(valueKeyLength - fr.length)}` ) + ' ' + chalk.dim(sizeStr) ) ); }); console.info('Total image compression time', time); } // The filter does not support asynchronous processing. Use map to simulate the filter // https://stackoverflow.com/questions/33355528/filtering-an-array-with-a-function-that-returns-a-promise/46842181#46842181 async function filter(arr, callback) { const fail = Symbol(); return ( await Promise.all( arr.map(async (item) => ((await callback(item)) ? item : fail)) ) ).filter((i) => i !== fail); }
The output of execution information is as follows
$ yarn build:test yarn run v1.22.17 warning package.json: No license field $ node scripts/imagemin.mjs Picture compression succeeded src/assets/images/comment/arrow_down_grey.png -53% 0.40kb / tiny: 0.19kb src/assets/images/mos/arrow_down_grey.png -53% 0.40kb / tiny: 0.19kb src/assets/images/mos/arrow_right_black.png -47% 0.36kb / tiny: 0.20kb src/assets/images/mos/back@3x.png -6% 0.30kb / tiny: 0.29kb src/assets/images/mos/basepoint-right.png -21% 0.21kb / tiny: 0.17kb src/assets/images/mos/back_left_black@3x.png -13% 0.31kb / tiny: 0.27kb src/assets/images/mos/bg_mos_improve@3x.png 0% 58.90kb / tiny: 58.90kb src/assets/images/mos/ic_0.png -16% 0.28kb / tiny: 0.24kb src/assets/images/mos/ic_0_active.png -17% 0.28kb / tiny: 0.24kb src/assets/images/mos/ic_1.png -24% 0.46kb / tiny: 0.36kb src/assets/images/mos/ic_1_active.png -26% 0.49kb / tiny: 0.36kb src/assets/images/mos/ic_2.png -21% 1.06kb / tiny: 0.85kb src/assets/images/mos/ic_2_active.png -15% 1.08kb / tiny: 0.92kb src/assets/images/mos/ic_3.png -21% 0.88kb / tiny: 0.70kb src/assets/images/mos/ic_3_active.png -18% 0.88kb / tiny: 0.73kb src/assets/images/mos/ic_4.png 0% 0.13kb / tiny: 0.13kb src/assets/images/mos/ic_4_active.png 0% 0.13kb / tiny: 0.13kb src/assets/images/mos/ic_5.png -14% 0.63kb / tiny: 0.54kb src/assets/images/mos/ic_5_active.png -12% 0.62kb / tiny: 0.55kb src/assets/images/mos/mos_addPhoto_bg@3x.png -83% 13.88kb / tiny: 2.42kb src/assets/images/mos/mos_add_photo@3x.png -79% 9.27kb / tiny: 2.02kb src/assets/images/mos/mos_applyfor_img_desc1@3x.png -71% 639.01kb / tiny: 190.52kb src/assets/images/mos/mos_applyfor_img_desc2@3x.png -70% 669.85kb / tiny: 205.11kb src/assets/images/mos/mos_applyFor_tip@3x.png -72% 1.55kb / tiny: 0.44kb src/assets/images/mos/mos_au@3x.png -82% 173.61kb / tiny: 31.70kb src/assets/images/mos/mos_bad.png -78% 28.50kb / tiny: 6.32kb src/assets/images/mos/mos_bg_bad1.png -68% 176.29kb / tiny: 56.82kb src/assets/images/mos/mos_bg_bad2.png -71% 1.62kb / tiny: 0.48kb src/assets/images/mos/mos_bg_copper.png -76% 86.77kb / tiny: 21.29kb src/assets/images/mos/mos_bg_copper1.png -60% 136.03kb / tiny: 55.38kb src/assets/images/mos/mos_bg_copper2.png -71% 1.54kb / tiny: 0.45kb src/assets/images/mos/mos_bg_gold.png -74% 93.59kb / tiny: 24.39kb src/assets/images/mos/mos_bg_gold1.png -66% 157.11kb / tiny: 54.96kb src/assets/images/mos/mos_bg_gold2.png -90% 18.66kb / tiny: 1.87kb src/assets/images/mos/mos_bg_good1.png -64% 146.27kb / tiny: 52.75kb src/assets/images/mos/mos_bg_good2.png -71% 1.48kb / tiny: 0.43kb src/assets/images/mos/mos_bg_null.png -66% 55.00kb / tiny: 19.00kb src/assets/images/mos/mos_bg_null1.png -48% 74.54kb / tiny: 39.06kb src/assets/images/mos/mos_bg_null2.png -91% 7.25kb / tiny: 0.72kb src/assets/images/mos/mos_bg_silver.png -72% 82.74kb / tiny: 23.72kb src/assets/images/mos/mos_bg_silver1.png -63% 149.86kb / tiny: 56.85kb src/assets/images/mos/mos_bg_silver2.png -72% 1.53kb / tiny: 0.44kb src/assets/images/mos/mos_bonus_add@3x.png -57% 1.69kb / tiny: 0.73kb src/assets/images/mos/mos_bonus_points_bg@3x.png -91% 175.78kb / tiny: 16.24kb src/assets/images/mos/mos_copper.png -81% 79.09kb / tiny: 15.78kb src/assets/images/mos/mos_gold.png -78% 72.31kb / tiny: 15.94kb src/assets/images/mos/mos_gold@3x.png -79% 148.05kb / tiny: 31.80kb src/assets/images/mos/mos_good.png -76% 36.00kb / tiny: 8.83kb src/assets/images/mos/mos_index_bg@3x.png -80% 210.25kb / tiny: 42.21kb src/assets/images/mos/mos_jj@3x.png -66% 9.92kb / tiny: 3.46kb src/assets/images/mos/mos_main_bg.png -71% 57.49kb / tiny: 16.94kb src/assets/images/mos/mos_main_bg1.png -72% 16.49kb / tiny: 4.72kb src/assets/images/mos/mos_main_bg2.png -64% 41.01kb / tiny: 14.95kb src/assets/images/mos/mos_meituan@3x.png -73% 14.19kb / tiny: 3.87kb src/assets/images/mos/mos_null.png -75% 35.62kb / tiny: 8.93kb src/assets/images/mos/mos_price_bad.png -65% 46.72kb / tiny: 16.49kb src/assets/images/mos/mos_price_good.png -72% 62.14kb / tiny: 17.88kb src/assets/images/mos/mos_qa.png -77% 440.17kb / tiny: 104.05kb src/assets/images/mos/mos_score_bg.png -18% 15.92kb / tiny: 13.07kb src/assets/images/mos/mos_shadow.png -45% 0.50kb / tiny: 0.28kb src/assets/images/mos/mos_silver.png -79% 71.10kb / tiny: 14.96kb src/assets/images/mos/mos_silver@3x.png -82% 159.11kb / tiny: 30.06kb src/assets/images/mos/mos_xiecheng@3x.png -76% 11.89kb / tiny: 2.95kb Total image compression time 122.469s $ vite build --mode test D:\wehotel-hyt-h5\src\main.ts test vite v2.8.4 building for test... transforming (1056) node_modules\call-bind\callBound.jsUse of eval is strongly discouraged, as it poses security risks and may cause issues with minification ✓ 1086 modules transformed. dist/assets/polyfills-legacy.749f4000.js 68.82 KiB / gzip: 27.57 KiB dist/assets/echarts-legacy.fa6a2a05.js 360.15 KiB / gzip: 119.61 KiB dist/assets/index-legacy.ac103818.js 773.37 KiB / gzip: 243.56 KiB (!) Some chunks are larger than 500 KiB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. dist/assets/mos_good.7bf63890.png 8.83 KiB dist/assets/mos_bad.6fa9e0dc.png 6.32 KiB dist/assets/mos_bonus_points_bg@3x.55ee32a1.png 16.24 KiB dist/assets/mos_copper.9a60b8a1.png 15.78 KiB dist/assets/mos_silver.f28117bd.png 14.96 KiB dist/assets/mos_bg_good1.f6f8697a.png 52.75 KiB dist/assets/mos_gold.31ebe01d.png 15.94 KiB dist/assets/mos_bg_bad1.72c4e590.png 56.82 KiB dist/assets/mos_null.10696c0f.png 8.93 KiB dist/assets/mos_bg_copper1.60acd92a.png 55.38 KiB dist/assets/mos_bg_silver1.92272192.png 56.85 KiB dist/assets/mos_bg_null1.357a30a0.png 39.06 KiB dist/assets/mos_bg_gold1.a15e5641.png 54.96 KiB dist/assets/mos_qa.51ee2b68.png 104.05 KiB dist/assets/mos_score_bg.0f990b30.png 13.07 KiB dist/assets/mos_bg_copper.db603582.png 21.29 KiB dist/assets/mos_bg_silver.a768f625.png 23.72 KiB dist/assets/mos_bg_gold.ddd22ca4.png 24.39 KiB dist/assets/mos_bg_null.ea3f4937.png 19.00 KiB dist/assets/mos_price_bad.5bafec2a.png 16.49 KiB dist/assets/mos_price_good.378eeaff.png 17.88 KiB dist/assets/mos_index_bg@3x.ceab3831.png 42.21 KiB dist/assets/mos_applyfor_img_desc1@3x.9d293ba9.png 190.52 KiB dist/assets/mos_applyfor_img_desc2@3x.1ec8156e.png 205.11 KiB dist/index.html 6.23 KiB dist/assets/index.cfc146ac.css 162.39 KiB / gzip: 44.63 KiB dist/assets/echarts.c2408918.js 360.93 KiB / gzip: 119.38 KiB dist/assets/index.1b17a995.js 612.49 KiB / gzip: 199.82 KiB (!) Some chunks are larger than 500 KiB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. Done in 159.94s.
It can be seen that the first packaging takes an additional 122.469s, and the second packaging takes an average of 159.94 - 122.469 = 37.47s without adding pictures. If a new picture is added later, only the new picture will be compressed.
This scheme also has disadvantages:
- Overwrite the original image. It's inconvenient to see the effect when setting the parameters of the compression plug-in. You need to back it up first
- The picture files matching the wildcard suffix in the specified directory will be compressed, instead of which pictures are referenced by the business component and which pictures need to be compressed
Remarks about imagemin:
- The imagemin package needs to be executed in node > = 12.20.0; It will be slow to download in China. You need to download it in package JSON with field "Resolutions": {"bin wrapper": "NPM: Bin wrapper China"}
- imagemin and several other packages are in ESM format and are not compatible with the commonjs specification, so it is better to change the suffix of the script to mjs, another scheme is in package JSON adds the field {"type": "module"}, but considering that other scripts need to use the commonjs specification, they don't do so
summary
Through packaging analysis, it is confirmed that image compression leads to long construction time (79.07 s), and the following factors lead to too large main package (568.19 kiB gzied in total: vendor 530.72 KiB gized, index 37.47 KiB gized):
- echarts full packaging
- Redundancy exists in legacy package
- lodash shouldn't exist
Solution:
- Through the manual chunks scheme, ecarts are packaged independently and on demand, reducing the volume of the main package
- Reduce legacy packages by removing redundant configurations
- Use the eckarts solution to solve the problem of lodash packaged due to the introduction of vue3 eckarts
- Through the script, compress the picture before construction, avoid repeated compression next time through caching, and optimize the construction process
achievements:
The main package is reduced from 568.19 kiB gized to 199.82 KiB gized, and the volume is reduced by 64.8%
The construction time is reduced from 79.07 s to 37.47 s, with a 52.6% reduction
Postscript
Although manual chunks is configured under the vue3 eckarts scheme, or manual chunks is configured under the import() scheme, eckarts can be packaged independently, but treeshaking is not performed