webpack + vite packaging optimization

Packaging optimization

    • webpack optimization
      • 1. Depend on conversion and compatible with lower version browsers
      • 2. Close sourceMap in production environment
      • 3. Modify the packaging output directory name and store static resources
      • 4. Modify icon
      • 5. Modify webpack configuration
        • 5-1. The configuration written here can overwrite the default configuration of the scaffolding.
        • 5-2. Everything written here is not configured by default, and the original configuration of the scaffold will not be overwritten.
      • 6. Complete configuration
    • vite optimization

webpack optimization

1. Depend on conversion, compatible with lower version browsers

//Convert dependencies
transpileDependencies: true,

2. Close sourceMap in production environment

// Close sourceMap in production
productionSourceMap: false,

3. Modification of packaging output directory name and storage of static resources

outputDir: 'bundle', // Directory of packaged files (default is dist)
assetsDir: 'static', // The static resource (js, css, img, fonts) directory of outputDir defaults to '' and there is no separate directory js/css/img in the root directory.

4. Modify icon

// Modify the browser icon. If you do not add the following, modifying the browser icon will not take effect.
pwa: {<!-- -->
    iconPaths: {<!-- -->
        favicon32: 'favicon.ico',
        favicon16: 'favicon.ico',
        appleTouchIcon: 'favicon.ico',
        maskIcon: 'favicon.ico',
        msTileImage: 'favicon.ico',
    }
}

5. Modify webpack configuration

5-1. The configuration written here can overwrite the default configuration of the scaffold
chainWebpack: config => {<!-- -->
    config.optimization.minimizer('terser').tap(args => {<!-- -->

        // Delete the comments and printing in the code to reduce the code size.
        args.forEach(item => {<!-- -->
            if (item.hasOwnProperty('terserOptions')) {<!-- -->
                Object.assign(item['terserOptions'].compress, {<!-- -->
                    drop_debugger: true,
                    drop_console: true,
                    pure_funcs: ['console.log']
                })
            }
            item['terserOptions']['format'] = {<!-- -->
                comments: false
            }
        })
        return args
    })


    // Enable gzip compression
    if (process.env.NODE_ENV === "production") {<!-- -->
        config.plugin('CompressionPlugin').use(
            new CompressionWebpackPlugin({<!-- -->
                test: /\.(js|css|less|scss|html)$/, // Compress css, scss, less, and html
                threshold: 10240, // Compress files exceeding 10kb
                deleteOriginalAssets: false, // Do not delete source files
                minRatio: 0.8, // Minimum compression ratio 0.8
                algorithm: 'gzip'
            })
        )
    }
}

5-2. Everything written here is not configured by default, and the original configuration of the scaffold will not be overwritten
configureWebpack: {<!-- -->
   // code splitting
   optimization: {<!-- -->
       splitChunks: {<!-- -->
           chunks: "all",

           // Define a cache group to extract third-party packages
           cacheGroups: {<!-- -->
               elementUI: {<!-- -->
                   //Extracted name
                   name: "element-chunk-vendors",
                   // Find it in the node_modules package
                   test: /[\/]node_modules[\/]_?element-ui(.*)/,
                   // Weight, the larger the weight, the priority will be packaged
                   priority: 30,
               },
               vue: {<!-- -->
                   name: "vue-chunk-vendors",
                   test: /[\/]node_modules[\/]vue(.*)[\/]/,
                   chunks: "initial",
                   priority: 20,
                   reuseExistingChunk: true,
               },
               vueRouter: {<!-- -->
                   name: "vueRouter-chunk-vendors",
                   test: /[\/]node_modules[\/]vue-router(.*)[\/]/,
                   chunks: "initial",
                   priority: 19,
               },
               vuex: {<!-- -->
                   name: "vuex-chunk-vendors",
                   test: /[\/]node_modules[\/]vuex(.*)[\/]/,
                   chunks: "initial",
                   priority: 18,
               },
               echarts: {<!-- -->
                   name: "echarts-chunk-vendors",
                   test: /[\/]node_modules[\/]echarts(.*)[\/]/,
                   chunks: "initial",
                   priority: 17,
               },
               // Don’t forget to leave the rest alone.
               libs: {<!-- -->
                   name: "chunk-libs-vendors",
                   test: /[\/]node_modules[\/]/,
                   priority: 1, // The lowest weight, give priority to the previous content
                   chunks: "initial",
               },

               // For the code you write, if it is reused and meets the following configuration, it will be extracted and packaged separately, such as the package under utils
               default: {<!-- -->
                   //Other configurations that are not written will use the above default values.
                   test: /[\/]src(.*)[\/]/,
                   name: "common-chunk",
                   minSize: 20000, // If it exceeds 20kb, it will be unpacked
                   minChunks: 2, // Will be unpacked if referenced twice
                   priority: -10,
                   reuseExistingChunk: true
               }
           }
       }
   },

   //Configure alias
   resolve: {<!-- -->
       alias: {<!-- -->
           "#": path.resolve(__dirname, "src")
       }
   },

// Analysis plug-in
   plugins: [
       new BundleAnalyzer({<!-- -->
           analyzerMode: 'server',
           analyzerHost: '127.0.0.1',
           analyzerPort: 8088,
           reportFilename: 'report.html',
           defaultSizes: 'parsed',
           openAnalyzer: true,
           generateStatsFile: false,
           statsFilename: 'state.json',
           statsOptions: null,
           logLevel: 'info'
       })
   ]
}

6. Complete configuration

const path = require("path")
const {<!-- --> defineConfig } = require('@vue/cli-service')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = defineConfig({<!-- -->
    // Convert dependencies
    transpileDependencies: true,

    //Production close sourceMap
    productionSourceMap: false,

    outputDir: 'bundle', // Directory of packaged files (default is dist)
    assetsDir: 'static', // The static resource (js, css, img, fonts) directory of outputDir defaults to '' and there is no separate directory js/css/img in the root directory.

    //Modify the browser icon
    pwa: {<!-- -->
        iconPaths: {<!-- -->
            favicon32: 'favicon.ico',
            favicon16: 'favicon.ico',
            appleTouchIcon: 'favicon.ico',
            maskIcon: 'favicon.ico',
            msTileImage: 'favicon.ico',
        }
    },


    // webpack configuration (the configuration written here can override the default configuration of the scaffolding)
    chainWebpack: config => {<!-- -->
        config.optimization.minimizer('terser').tap(args => {<!-- -->

            // Delete the comments and printing in the code to reduce the code size.
            args.forEach(item => {<!-- -->
                if (item.hasOwnProperty('terserOptions')) {<!-- -->
                    Object.assign(item['terserOptions'].compress, {<!-- -->
                        drop_debugger: true,
                        drop_console: true,
                        pure_funcs: ['console.log']
                    })
                }
                item['terserOptions']['format'] = {<!-- -->
                    comments: false
                }
            })
            return args
        })


        // Enable gzip compression, the corresponding nginx also needs to be configured
        if (process.env.NODE_ENV === "production") {<!-- -->
            config.plugin('CompressionPlugin').use(
                new CompressionWebpackPlugin({<!-- -->
                    test: /\.(js|css|less|scss|html)$/, // Compress css, scss, less, and html
                    threshold: 10240, // Compress files exceeding 10kb
                    deleteOriginalAssets: false, // Do not delete source files
                    minRatio: 0.8, // Minimum compression ratio 0.8
                    algorithm: 'gzip'
                })
            )
        }
    },

    // webpack configuration (the ones written here are not configured by default, and the original configuration of the scaffold will not be overwritten)
    configureWebpack: {<!-- -->
        // code splitting
        optimization: {<!-- -->
            splitChunks: {<!-- -->
                chunks: "all",

                // Define a cache group to extract third-party packages
                cacheGroups: {<!-- -->
                    elementUI: {<!-- -->
                        //Extracted name
                        name: "element-chunk-vendors",
                        // Find it in the node_modules package
                        test: /[\/]node_modules[\/]_?element-ui(.*)/,
                        // Weight, the larger the weight, the priority will be packaged
                        priority: 30,
                    },
                    vue: {<!-- -->
                        name: "vue-chunk-vendors",
                        test: /[\/]node_modules[\/]vue(.*)[\/]/,
                        chunks: "initial",
                        priority: 20,
                        reuseExistingChunk: true,
                    },
                    vueRouter: {<!-- -->
                        name: "vueRouter-chunk-vendors",
                        test: /[\/]node_modules[\/]vue-router(.*)[\/]/,
                        chunks: "initial",
                        priority: 19,
                    },
                    vuex: {<!-- -->
                        name: "vuex-chunk-vendors",
                        test: /[\/]node_modules[\/]vuex(.*)[\/]/,
                        chunks: "initial",
                        priority: 18,
                    },
                    echarts: {<!-- -->
                        name: "echarts-chunk-vendors",
                        test: /[\/]node_modules[\/]echarts(.*)[\/]/,
                        chunks: "initial",
                        priority: 17,
                    },
                    // Don’t forget to leave the rest alone.
                    libs: {<!-- -->
                        name: "chunk-libs-vendors",
                        test: /[\/]node_modules[\/]/,
                        priority: 1, // The lowest weight, give priority to the previous content
                        chunks: "initial",
                    },

                    // For the code you write, if it is reused and meets the following configuration, it will be extracted and packaged separately, such as the package under utils
                    default: {<!-- -->
                        //Other configurations that are not written will use the above default values.
                        test: /[\/]src(.*)[\/]/,
                        name: "common-chunk",
                        minSize: 20000, // If it exceeds 20kb, it will be unpacked
                        minChunks: 2, // Will be unpacked if referenced twice
                        priority: -10,
                        reuseExistingChunk: true
                    }
                }
            }
        },

        //Configure alias
        resolve: {<!-- -->
            alias: {<!-- -->
                "#": path.resolve(__dirname, "src")
            }
        },


        plugins: [
            new BundleAnalyzer({<!-- -->
                analyzerMode: 'server',
                analyzerHost: '127.0.0.1',
                analyzerPort: 8088,
                reportFilename: 'report.html',
                defaultSizes: 'parsed',
                openAnalyzer: true,
                generateStatsFile: false,
                statsFilename: 'state.json',
                statsOptions: null,
                logLevel: 'info'
            })
        ]
    }
})

// After the packaging analysis tool is added, you need to add:
 "build": "vue-cli-service build",
 "build:analyze": "cross-env NODE_ENV=production npm_config_report=true vue-cli-service build"

https://juejin.cn/post/6951297954770583565

vite optimization

import {<!-- --> defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {<!-- --> ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import viteCompression from "vite-plugin-compression";


// https://vitejs.dev/config/
export default defineConfig({<!-- -->
    plugins: [
        vue(),

        Components({<!-- -->//Customized module
            dirs: ['src/components'],
            extensions: ['vue', 'ts'],
            resolvers: [ElementPlusResolver()]
        }),
        AutoImport({<!-- --> // The plug-in automatically imports related dependent libraries
            //After installing these two lines, you will find that you no longer need to import ref, reactive, etc. in the component.
            imports: ['vue', 'vue-router'],
            // Optional, used to automatically import component types
            dts: 'src/components.d.ts'
        }),

        // Enable gzip compression
        viteCompression({<!-- -->
            verbose: true,
            disable: false,
            threshold: 10240,
            algorithm: 'gzip',
            ext: '.gz',
        })
    ],

    // Pack
    build: {<!-- -->
        //Package and delete console and debugger
        minify: "terser",
        terserOptions: {<!-- -->
            compress: {<!-- -->
                drop_console: true,
                drop_debugger: true
            }
        },

        rollupOptions: {<!-- -->
            output: {<!-- --> //Static resource classification and packaging
                chunkFileNames: 'static/js/[name]-[hash].js',
                entryFileNames: 'static/js/[name]-[hash].js',
                assetFileNames: 'static/[ext]/[name]-[hash].[ext]',

                manualChunks(id: string) {<!-- -->
                    // node_modules unpacking
                    if (id.indexOf('node_modules') !== -1) {<!-- -->
                        if (id.indexOf('vue') !== -1) {<!-- -->
                            return id.toString().split('node_modules/')[1].split('/')[0].toString();
                        }
                        if (id.indexOf('vue-router') !== -1) {<!-- -->
                            return id.toString().split('node_modules/')[1].split('/')[0].toString();
                        }
                        if (id.indexOf('pinia') !== -1) {<!-- -->
                            return id.toString().split('node_modules/')[1].split('/')[0].toString();
                        }
                        if (id.indexOf('element-plus') !== -1) {<!-- -->
                            return id.toString().split('node_modules/')[1].split('/')[0].toString();
                        }
                    }
                }

            }

        }
    }
});