vue开启splitChunks分包处理

本文介绍了如何在Vue项目中修改`vue.config.js`文件,重点关注了生产环境配置,如使用HappyPack进行多线程编译,压缩插件(如UglifyJsPlugin和compression-webpack-plugin)以提高性能,以及优化CSS和图片处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

修改vue.config.js

//vue.config.js

const os = require('os');
const path = require('path');
const { HashedModuleIdsPlugin } = require('webpack');
const WebpackBar = require('webpackbar');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const HappyPack = require('happypack');
const CompressionPlugin = require('compression-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash')

// 开辟一个线程池,拿到系统CPU的核数,happypack 将编译工作利用所有线程
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const isProduction = process.env.NODE_ENV !== 'development';
const port = process.env.port || process.env.npm_config_port || 8998; // 端口
const themePath = process.env.VUE_APP_THEME ? `-${ process.env.VUE_APP_THEME }` : '';

module.exports = {
  publicPath: process.env.PATH_SUFFIX,
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  parallel: require('os').cpus().length > 1, // cpu核数
  devServer: {
    port: port,
    open: true,
    proxy: {
      '/api': {
        target: process.env.VUE_APP_URL,
        changeOrgin: true,
        pathRewrite: {
          '^/api': ''
        }
      },
      '/dapi': {
        target: process.env.VUE_APP_URL,
        changeOrgin: true,
        pathRewrite: {
          '^/dapi': ''
        }
      },
    },
  },
  pluginOptions: {
    'style-resources-loader': {
      preProcessor: 'less',
      patterns: [ path.resolve(__dirname, `src/assets/less/index${ themePath }.less`) ] // 引入全局样式变量
    }
  },
  css: {
    extract: true,
    sourceMap: false,
    requireModuleExtension: true
  },
  configureWebpack: {
    externals: {
      'AMap': 'AMap', // 高德地图配置
      'AMapUI': 'AMapUI', // 高德地图AMapUI
    },
    resolve: {
      modules: [ path.resolve((__dirname, './src')) ],
      alias: {
        'vue$': 'vue/dist/vue.esm.js',
        '@': path.resolve((__dirname, './src')),
        '@api': path.resolve((__dirname, './src/api')),
        '@utils': path.resolve((__dirname, './src/utils')),
        'vuex': path.resolve(__dirname, './node_modules/vuex/dist/vuex.min.js'),
        'vue-router': path.resolve(__dirname, './node_modules/vue-router/dist/vue-router.min.js'),
        'axios': path.resolve(__dirname, './node_modules/axios/dist/axios.min.js')
      }
    },
    performance: {
      'maxEntrypointSize': 10000000,
      'maxAssetSize': 30000000,
      // 只给出 js 文件的性能提示
      assetFilter: function (assetFilename) {
        return assetFilename.endsWith('.js')
      }
    },
    optimization: {
      minimizer: [
        new HashedModuleIdsPlugin(),
        new UglifyJsPlugin({ // 代码压缩
          uglifyOptions: {
            output: {
              comments: false, // 删除注释
              beautify: false, // 最紧凑的输出
            },
            compress: {
              drop_debugger: true, //清除 debugger 语句
              drop_console: true, //清除console语句
              pure_funcs: [ 'console.log' ],
              collapse_vars: true,
              reduce_vars: true
            }
          },
          sourceMap: false,
          parallel: true,
          cache: true,
          include: /\/src/,
          // include: path.resolve((__dirname, './src')),
          chunkFilter: chunk => {
            // `vendor` 块不压缩
            if (chunk.name === 'vendor') {
              return false;
            }

            return true;
          },
        })
      ],
      splitChunks: {
        chunks: 'all', // 可选值:all,async 和 initial。all功能最强大,所以咱们就使用all
        maxAsyncRequests: 10, // 每个异步加载模块最多能被拆分的数量
        maxInitialRequests: Infinity, // 每个入口和它的同步依赖最多能被拆分的数量 Infinity 10
        enforceSizeThreshold: 50000, // 强制执行拆分的体积阈值并忽略其他限制
        cacheGroups: {
          libs: { // 第三方库
            name: 'chunk-libs',
            test: /[\\/]node_modules[\\/]/, // 请注意'[\\/]'的用法,是具有跨平台兼容性的路径分隔符
            priority: 10, // 优先级,执行顺序就是权重从高到低
            chunks: 'initial' // 只打包最初依赖的第三方
          },
          elementUI: { // 把 elementUI 单独分包
            name: 'chunk-elementUI',
            test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
            priority: 12, // 权重必须比 libs 大,不然会被打包进 libs 里
            reuseExistingChunk: true,
            enforce: true
          },
          xlsxJs: {
            name: 'chunk-xlsxJs',
            test: /[\\/]node_modules[\\/]_?xlsx(.*)/,
            priority: 13,
            reuseExistingChunk: true,
            enforce: true
          },
          pdfJs: {
            name: 'chunk-pdfJs',
            test: /[\\/]node_modules[\\/]_?pdf+(.*)/,
            priority: 14,
            reuseExistingChunk: true,
            enforce: true
          },
          bpmnJs: {
            name: 'chunk-bpmnJs',
            test: /[\\/]node_modules[\\/]_?bpmn(.*)/,
            priority: 15,
            reuseExistingChunk: true,
            enforce: true
          },
          // commons: {
          //   name: 'chunk-commons',
          //   minChunks: 2, // 拆分前,这个模块至少被不同 chunk 引用的次数
          //   priority: 20,
          //   reuseExistingChunk: true
          // },
          // commons: {
          //   test: /[\\/]node_modules[\\/]/,
          //   name(module) {
          //     const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
          //     return `npm.${ packageName.replace('@', '') }`;
          //   },
          // },
          components: {
            chunks: 'all',
            test: /[\\/]src[\\/]components[\\/]/,
            priority: 21,
            name: 'chunk-components',
          },
          mixin: {
            chunks: 'all',
            test: /[\\/]src[\\/]mixin[\\/]/,
            priority: 22,
            name: 'chunk-mixin',
          },
          orderCenter: {
            chunks: 'all',
            test: /[\\/]src[\\/]views[\\/]orderCenter[\\/]/,
            name: 'chunk-orderCenter',
            priority: 23,
          },
          vehicleCenter: {
            chunks: 'all',
            test: /[\\/]src[\\/]views[\\/]vehicleCenter[\\/]/,
            name: 'chunk-vehicleCenter',
            priority: 24,
          },
          workFlow: {
            chunks: 'all',
            test: /[\\/]src[\\/]views[\\/]workFlow[\\/]/,
            name: 'chunk-workFlow',
            priority: 25,
          },
          systemSettings: {
            chunks: 'all',
            test: /[\\/]src[\\/]views[\\/]systemSettings[\\/]/,
            name: 'chunk-systemSettings',
            priority: 26,
          },
          commonsJS: {
            chunks: 'all',
            test: /[\\/]src[\\/]js[\\/]/,
            name: 'chunk-commonsJS',
            minChunks: 2,
            maxInitialRequests: 5,
            minSize: 0,
            priority: 27,
          },
          styles: {
            name: 'styles',
            test: /[\\/]src[\\/]\.(sa|sc|c)ss$/,
            chunks: 'all',
            priority: 28,
            enforce: true
          },
          svgIcon: {
            name: 'chunk-svgIcon',
            // 函数匹配示例,把 svg 单独拆出来
            test(module) {
              // `module.resource` 是文件的绝对路径
              // 用`path.sep` 代替 / or \,以便跨平台兼容
              // const path = require('path') // path 一般会在配置文件引入,此处只是说明 path 的来源,实际并不用加上
              return (
                module.resource &&
                module.resource.endsWith('.svg') &&
                module.resource.includes(`${ path.sep }icons${ path.sep }`)
              )
            },
            priority: 29
          },
        }
      }
    },
    plugins: [
      new WebpackMd5Hash(),
      new WebpackBar(),
      new HardSourceWebpackPlugin({
        root: process.cwd(),
        directories: [],
        environmentHash: {
          root: process.cwd(),
          directories: [],
          // 配置了files 的主要原因是解决配置更新,cache 不生效了的问题
          // 配置后有包的变化,plugin 会重新构建一部分 cache
          files: [ 'package.json', 'yarn.lock' ]
        }
      }),
      new HappyPack({
        id: 'happybabel',
        loaders: [ 'babel-loader?cacheDirectory=true' ],
        threadPool: happyThreadPool
      }),
      new HappyPack({
        id: 'vue',
        loaders: [ 'vue-loader?cacheDirectory=true' ],
        //共享进程池
        threadPool: happyThreadPool
      }),
      new HappyPack({
        id: 'styles',
        loaders: [ 'css-loader?cacheDirectory=true', 'less-loader?cacheDirectory=true' ],
        //共享进程池
        threadPool: happyThreadPool
      })
    ],
    devtool: 'source-map'
  },
  chainWebpack: config => {
    // 避免无用查找
    config.resolve.extensions.clear()
      .add('.js')
      .add('.vue')
      .add('.json')
    if (isProduction) { // 生产环境相关配置
      // 去掉console-log
      config.optimization.minimizer('terser').tap(args => {
        args[0].terserOptions.compress.drop_console = true
        return args
      })
      const productionGzipExtensions = [ 'js', 'css', 'json', 'ico', 'html', 'svg', 'webp' ]
      config.plugin('compressionPlugin')
        .use(new CompressionPlugin({
          test: new RegExp(
            `\\.(${ productionGzipExtensions.join('|') })$`
          ),
          algorithm: 'gzip', // 使用gzip压缩
          threshold: 10240, //  对10K以上的数据进行压缩
          minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
          filename: '[path].gz[query]',
          // filename: '[path][base].gz', // 压缩后的文件名
          deleteOriginalAssets: false // 删除未压缩的文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false
        }));
      // 临时添加
      config.plugin('CssMinimizerPlugin').use(new CssMinimizerPlugin()) // css压缩
      config.plugin('speedMeasurePlugin').use(new SpeedMeasurePlugin())
      // 移除 prefetch 插件
      config.plugins.delete('prefetch');
      // 移除 preload 插件
      config.plugins.delete('preload');
    }
    config.module
      .rule('images')
      .use('url-loader')
      .options({
        limit: 10240, // 稍微改大了点
      })
      .end()
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        progressive: true,
        optimizationLevel: 7,
        interlaced: false,
        mozjpeg: { progressive: true, quality: 50 }, // 压缩JPEG图像
        optipng: { enabled: false }, // 压缩PNG图像
        // pngquant: { quality: [ 0.65, 0.9 ], speed: 4 }, // 压缩PNG图像
        gifsicle: { interlaced: false }, // 压缩GIF图像
        webp: { quality: 75 } // 压缩webp
        // bypassOnDebug: true
      })
      .end();
  }
}

<think>嗯,用户想知道Vue.js的性能优化方法、技巧和最佳实践。首先,我需要回忆一下Vue的相关知识。记得Vue的响应式系统可能会有性能问题,尤其是处理大型数据的时候。可能应该提到使用Object.freeze来冻结不需要响应式的数据,这样可以减少开销。 然后,组件优化方面,异步组件和懒加载应该很重要。路由懒加载可以用import()来实现,这样分包加载,减少初始加载时间。还有组件库的按需引入,比如Element-UI或Vant,用到的组件再导入,避免整个包都加载。 代码结构方面,keep-alive缓存组件状态,避免重复渲染。v-show和v-if的区别,前者适合频繁切换,后者适合条件变化少的情况。事件代理可能也有帮助,比如用@click.native在父组件处理事件,减少子组件的事件监听器数量。 构建优化,Webpack的配置,比如压缩代码、Tree Shaking、代码分割。生产环境去掉console和debugger,使用Gzip压缩,CDN加载第三方库。图片懒加载和压缩也是关键,尤其是移动端。 监控和分析工具,比如Chrome的Performance面板,Vue Devtools的性能追踪,还有Lighthouse评估整体性能。引用中提到的Vue 3新特性,比如Composition API,可能也有优化技巧,比如更细粒度的响应式控制。 还需要考虑SSR服务端渲染,但用户没有提到,可能暂时不需要深入。不过如果提到首屏加载时间的话,SSR或静态生成可能有帮助。另外,虚拟滚动对于长列表优化应该有用,比如vue-virtual-scroller库。 可能用户的应用场景是开发单页面应用,遇到性能瓶颈,比如加载慢、渲染卡顿。所以需要从代码、构建、运行时几个方面给出建议。注意引用中的参考内容,比如引用1提到压缩和合并技术,引用3提到Vue 3的新特性,引用4提到Chrome Performance分析步骤。 确保回答结构清晰,分点列出,每个技巧有代码示例,比如懒加载路由、按需引入组件、Object.freeze的使用。还要生成相关问题,比如如何分析Vue应用性能,Vue 3的优化新特性,SSR如何优化首屏加载等。</think>### Vue.js性能优化方法、技巧与最佳实践 以下是针对Vue.js应用性能优化的系统性解决方案,包含代码示例和原理说明: --- #### 一、响应式系统优化 1. **冻结非响应式数据** 对不需要Vue追踪变化的大型数据对象(如静态配置表),使用$Object.freeze()$避免响应式转换: ```javascript export default { data() { return { staticData: Object.freeze([...]) // 冻结数组/对象[^4] } } } ``` 2. **计算属性缓存** 优先使用$computed$替代$methods$,自动缓存计算结果: ```javascript computed: { filteredList() { // 仅当依赖项变化时重新计算 return this.list.filter(item => item.status) } } ``` --- #### 二、组件级优化 3. **异步组件与懒加载** 使用动态导入实现路由级代码分割: ```javascript const UserProfile = () => import('./UserProfile.vue') // 路由懒加载[^1] ``` 4. **组件按需引入** 对第三方组件库采用按需加载策略: ```javascript import { Button, Select } from 'element-ui' // 仅引入必要组件[^2] ``` 5. **虚拟滚动优化长列表** 使用$vue-virtual-scroller$处理超长列表: ```vue <RecycleScroller :items="bigData" :item-size="50"> <template v-slot="{ item }"> <div>{{ item.content }}</div> </template> </RecycleScroller> ``` --- #### 三、渲染优化 6. **合理使用v-show与v-if** - $v-show$:适合频繁切换显示状态(保留DOM) - $v-if$:适合条件变化较少的场景(销毁/重建DOM) 7. **事件代理优化** 使用事件修饰符进行事件委托: ```vue <div @click.native="handleClick"> <!-- 代理子元素事件 --> <child-component/> </div> ``` 8. **keep-alive缓存组件** 缓存高频切换的组件状态: ```vue <keep-alive include="HomePage"> <router-view></router-view> </keep-alive> ``` --- #### 四、构建优化 9. **Webpack配置优化** ```javascript module.exports = { productionSourceMap: false, // 关闭sourcemap configureWebpack: { optimization: { splitChunks: { // 代码分割策略 chunks: 'all' } } } } ``` 10. **资源压缩策略** - 使用$compression-webpack-plugin$开启Gzip压缩 - 通过$image-webpack-loader$自动压缩图片 - 使用CDN加载第三方库(配置$externals$) --- #### 五、性能监控 11. **Chrome Performance分析** 通过Chrome开发者工具: 1. 打开Performance面板 2. 录制用户操作过程 3. 分析脚本执行时间、内存占用等指标 12. **Vue Devtools检测** 使用组件渲染时间分析功能,识别低效组件。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值