前端性能构建优化-vue2项目dist瘦身

bj.png

二.png
1. speed-measure-webpack-plugin
使用步骤:

# 安装分析工具
npm install speed-measure-webpack-plugin

// vue.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
  // 添加分析工具
  chainWebpack: config => {
    config.plugin('speed-measure-webpack-plugin').use(SpeedMeasurePlugin).end()
  }
}

2. webpack-bundle-analyzer
使用步骤:
# 安装分析工具
npm install webpack-bundle-analyzer --save-dev
// vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  configureWebpack: {
    plugins: [new BundleAnalyzerPlugin()]
  }
}
image.png
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
     .rule('images')
     .use('image-webpack-loader')
     .loader('image-webpack-loader')
     .options({
         mozjpeg: { progressive: true, quality: 65 },
         optipng: { enabled: true },
         pngquant: { quality: [0.65, 0.9], speed: 4 },
         gifsicle: { interlaced: false },
         webp: { quality: 75 }
     })
  }
}
WebP 格式
尽量使用 webp 格式(体积比 jpg/png 小很多,且支持透明)。
可以做 渐进式降级,不支持 webp 的浏览器使用原始 png/jpg。

SVG 替代
对于小图标,优先使用 SVG 或者 iconfont,而不是 png,还可以使用base64内联小于2K的小图标。
Vue 项目里可用 vue-svg-loader 直接把 svg 当组件用。
2-2. 打包策略优化
小图片转 Base64 内联

webpack 的 url-loader 可以把小于某个阈值(如 8kb)的图片转为 base64,减少请求数。

过大的图片不要内联,否则反而增大 bundle 体积。


// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('images')
      .use('url-loader')
      .tap(options => {
        return {
          ...options,
          limit: 8192, // 8kb 以下转为 base64
          name: 'img/[name].[hash:7].[ext]'
        }
      })
  }
}
静态资源走 CDN

对于大图片,打包时不放入 bundle,而是上传到 CDN,代码中引用远程 URL。

可以结合 publicPath 配置,自动替换为 CDN 地址。


// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? 'https://cdn.xxx.com/' : '/'
}
分包处理(独立存放到 static/ 目录)

在 Vue2(Webpack 构建)项目中,所有放在 src/assets/ 的图片都会被 webpack 当作模块处理,它们会被打包进 JS 或 CSS chunk 中。 如果图片体积大、数量多,会导致:

打包体积增大
构建速度变慢
浏览器首屏下载 JS 时间增加

而放在 static/ 目录下的资源不会被 webpack 打包,而是原封不动地复制到 dist/static/ 里,构建时直接引用即可。

这样做可以:
   1.减少 webpack 打包压力
   2.提高构建速度
   3.避免大图片进入 JS chunk,提升页面加载性能
#懒加载 / 按需加载
      使用 v-lazy 或 vue-lazyload 实现图片懒加载,减少首屏资源加载压力。
雪碧图 (Sprite)
     对于很多小图标,可以用 spritesmith 合并成雪碧图,减少 http 请求数。
组件化封装图标
       经常使用的图片资源(比如状态图标),可以抽成 Vue 组件,改用字体图标或 svg。

2. 组件库按需加载
// babel.config.js
module.exports = {
  plugins: [
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ]
  ]
}
image.png
// vue.config.js
module.exports = {
  configureWebpack: {
     optimization: {
            splitChunks: {
                cacheGroups: {
                    vendor: {
                        test: /[\\/]node_modules[\\/]/, // 匹配规则:所有node_modules目录下的模块(第三方依赖)
                        name: 'chunk-vendors', // 输出的chunk文件名(如:chunk-vendors.xxx.js)
                        chunks: 'all', // 处理所有类型的chunk(入口+异步)
                        priority: 100, // 优先级:100(最高,会优先于其他缓存组匹配)
                        minSize: 100 * 1024, // 至少100KB才拆分
                        reuseExistingChunk: true // 复用已有chunk:若模块已被拆分到其他chunk,不再重复拆分
                    },
                    elementUI: {
                        name: 'chunk-elementUI', // 输出的chunk文件名(如:chunk-elementUI.xxx.js)
                        priority: 60, // 优先级
                        test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // 匹配规则:element-ui模块(包括别名如_element-ui)
                        reuseExistingChunk: true
                    },
                    echarts: {
                        name: 'chunk-echarts', // 输出的chunk文件名
                        priority: 55, // 优先级
                        test: /[\\/]node_modules[\\/]_?echarts(.*)/, // 匹配规则:echarts模块(包括别名)
                        reuseExistingChunk: true
                    },
                    common: {
                        name: 'chunk-common', // 输出的chunk文件名(如:chunk-common.xxx.js)
                        minChunks: 2, // 最小引用次数:模块需被≥3个chunk引用,才会拆分到这里(比如3个页面共享的组件)
                        priority: 50, // 优先级
                        reuseExistingChunk: true // 复用已有chunk:避免重复拆分
                    }
                }
            }
        }
    }
}
3-3. 动态 import 结合组件懒加载: 除了按路由进行懒加载外,还可以在组件内部使用 import() 动态加载其他组件。这样可以把一个大组件拆分成多个小组件,按需加载。


export default {
  components: {
    // 动态导入另一个组件
    MyComponent: () => import(/* webpackChunkName: "my-component" */ './MyComponent.vue')
  }
}


4. 依赖清理与替代
4-1. 检查未使用的依赖
使用工具:depcheck


npm install -g depcheck
depcheck
它会输出:

Unused dependencies(项目没使用的)

Missing dependencies(代码中引用但没安装的)

Invalid devDependencies(开发依赖不需要的)

处理方式:
对于确定没用的依赖,删除:
npm uninstall 包名
对于误报(比如某些动态 import 或 require),可以手动忽略或添加注释解释。

4-2. 检查重复依赖版本
有时多个包依赖同一库(例如 lodash 不同版本),会重复打包。

**使用工具


npm dedupe
可以手动在 package.json 中锁定版本,比如:


"resolutions": {
  "lodash": "4.17.21"
}


4-3. 替换更轻量依赖

# 移除冗余依赖
npm uninstall video.js jspdf

# 安装轻量替代
npm install pdf-lib day.js --save


5. 构建速度优化
5-1. Webpack 构建层面优化
1. 使用缓存与并行构建

启用持久化缓存


// vue.config.js
const path = require('path');
module.exports = {
  configureWebpack: {
    cache: {
      type: 'filesystem', // 文件系统缓存(Webpack 5 原生支持)
      buildDependencies: {
        config: [__filename],
      },
    },
  },
};
chainWebpack: config => {
    // 确保缓存生效
    config.cache(true);
  }
};
作用:Webpack 5 内置的文件系统级缓存, 避免每次构建都重新编译未变更的模块。构建速度提升:10% ~ 60%。

开启多进程打包(thread-loader)


// vue.config.js
const threadLoader = require('thread-loader');

threadLoader.warmup({}, ['babel-loader', 'vue-loader']);

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('js')
      .use('thread-loader')
      .loader('thread-loader')
      .before('babel-loader');
  },
};
 特别适合多核 CPU 环境。对大型项目(上千个文件)效果明显。

5-2、Loader / Plugin 优化
1. 减少 Babel 转译范围

// JavaScript/TypeScript 文件
 config.module
       .rule('js')
       .test(/\.js$/)
       .include
       .add(path.resolve('src'))
       .add(path.resolve('node_modules/element-ui')) // 只处理需要的 node_modules
       .end()
       .exclude
       .add(/node_modules/) // 默认排除所有 node_modules
       .end()
  
// Vue 文件
 config.module
       .rule('vue')
       .test(/\.vue$/)
       .use('cache-loader')
       .loader('cache-loader')
       .options({
           cacheDirectory: path.resolve('.cache/vue-loader')
       })
避免对 node_modules 进行 Babel 转译(极大耗时)。若有特例库需转译,可单独指定。



2. 使用更快的压缩插件

替换默认的 terser-webpack-plugin 为更快的压缩器,如 esbuild 或 swc。


npm install esbuild-loader -D

// vue.config.js
const { EsbuildPlugin } = require('esbuild-loader');

module.exports = {
  configureWebpack: {
    optimization: {
      minimizer: [
        new EsbuildPlugin({
          target: 'es2015',
          css: true,
        }),
      ],
    },
  },
};
构建时间可缩短 30%+,压缩性能几乎相同。

3. 缩小解析范围(resolve 优化)


module.exports = {
  configureWebpack: {
    resolve: {
      symlinks: false,
      extensions: ['.js', '.vue', '.json'],
      alias: {
        '@': path.resolve(__dirname, 'src'),
      },
    },
  },
};
减少模块解析深度,加速依赖定位。
6. 生产环境强化
开启GZIP压缩

在打包阶段生成 .gz 文件,通过服务器返回压缩后的静态资源,减少网络传输体积。


// vue.config.js
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {
  configureWebpack: {
    plugins: [
      new CompressionPlugin({
        algorithm: 'gzip',
        threshold: 10240 // 超过10KB的文件才压缩
      })
    ]
  }
}
资源体积可减少约 60%~80%

提升首屏加载速度、降低带宽占用

建议同时在 Nginx 中启用 gzip_static 支持

去除 console 与 debugger

防止生产环境控制台输出敏感信息,减少无用日志、减小包体积。


// vue.config.js
module.exports = {
  configureWebpack: {
    terserOptions: {
      compress: {
        drop_console: true,   // 删除 console 语句
        drop_debugger: true   // 删除 debugger
      }
    }
  }
}
清理开发日志,缩小构建体积

避免泄露接口、环境变量等信息

提升执行性能,控制台更干净

移除源码与 map 文件

防止打包后的 .map 文件暴露源码,提升安全性。


// vue.config.js
module.exports = {
  productionSourceMap: false
}
禁止生成 .map 文件,防止源码反编译
减少构建体积与加载请求
总结.png
避坑.png

附录:核心配置代码

vue.config.js
const { defineConfig } = require('@vue/cli-service')
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = defineConfig({
  productionSourceMap: false,
  transpileDependencies: true,
  parallel: require('os').cpus().length > 1,
  
  configureWebpack: {
    cache: {
      type: 'filesystem',
      buildDependencies: {
        config: [__filename]
      }
    },
    plugins: [new CompressionPlugin()],
    optimization: {
      splitChunks: {
        chunks: 'all',
        minSize: 20000,
        cacheGroups: {
          vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
          }
        }
      }
    }
  },
  
  chainWebpack: config => {
    // 图片压缩
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ /* 参数配置 */ })
    
    // 多线程处理
    config.module
      .rule('js')
      .use('thread-loader')
      .loader('thread-loader')
      .options({ workers: 3 })
  }
})


路由懒加载示例
const routes = [
  {
    path: '/dashboard',
    component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
    meta: { preload: true }  // 重要路由添加预加载标识
  },
  {
    path: '/report/:id',
    component: () => import(/* webpackChunkName: "report" */ '@/views/Report.vue')
  }
]
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容