vue项目chunk.js太大导致页面加载超时

Published on in 程序人生 with 0 views and 0 comments

一、问题

最近做了一个vue项目,打包后部署到nginx,页面访问时一直停留在加载页面,浏览器调试观察到chunk开头的多个js文件加载时间超过了30s还没有加载完,js文件大小有20多Mb

二、解决方案

2.1 安装插件

执行如下命令

npm install --save-dev webpack-bundle-analyzer
npm install babel-plugin-component -D

2.2 修改配置

先引入依赖

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const CompressionWebpackPlugin  = require('compression-webpack-plugin')

新增这两个插件,webpack-bundle-analyzer用于优化打包体积和依赖关系,compression-webpack-plugin静态资源进行 Gzip/Brotli 压缩

在configureWebpack配置项的plugins新增这两个插件配置

plugins: [
      // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
      new CompressionWebpackPlugin({
        cache: false,                   // 不启用文件缓存
        test: /\.(js|css|html)?$/i,     // 压缩文件格式
        filename: '[path].gz[query]',   // 压缩后的文件名
        algorithm: 'gzip',              // 使用gzip压缩
        minRatio: 0.8                   // 压缩率小于1才会压缩
      }),
      new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        openAnalyzer: false,
        reportFilename: 'bundle-report.html'
      })
    ],

2.3 打包

执行项目打包命令,在dist目录下会生成一个bundle-report.html页面,里面可以看到项目依赖和打包的文件大小

image.png

点击左上角小箭头,可以看到js和压缩后的gz文件大小

image.png

image.png

2.4 配置nginx

nginx新增gzip配置

    # Gzip 全局配置
    gzip  on;
    gzip_min_length  1k;
    gzip_comp_level  6;  # 1-9,6 是性能与压缩率的平衡点
    gzip_types  text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary  on;
    gzip_disable  "MSIE [1-6]\.";  # 禁用旧版 IE 的压缩
    gzip_proxied  any;  # 允许代理请求的压缩

配置完成重启nginx在访问就正常了,F12控制台可以看到各个文件大小和具体加载时间,比原来几十秒一分多钟好了很多

image.png

在响应中也可以看到content-encoding选项

image.png

2.5 文件参考

2.5.1 vue配置文件

'use strict'
const path = require('path')
//
const packageName = require('./package.json').name

function resolve(dir) {
  return path.join(__dirname, dir)
}
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const CompressionWebpackPlugin  = require('compression-webpack-plugin')

const name = process.env.VUE_APP_TITLE || '高碳排放行业电碳核算系统' // 网页标题

const port = process.env.port || process.env.npm_config_port || 80 // 端口
console.log("NODE_ENV =", process.env.NODE_ENV);
// vue.config.js 配置说明
//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
// 这里只列一部分,具体配置参考文档
module.exports = {
  // 部署生产环境和开发环境下的URL。
  // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
  publicPath: process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test" ? "/ecap/" : "/",
  // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
  outputDir: 'dist',
  // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
  assetsDir: 'static',
  // 是否开启eslint保存检测,有效值:ture | false | 'error'
  lintOnSave: process.env.NODE_ENV === 'development',
  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
  productionSourceMap: false,
  // webpack-dev-server 相关配置
  devServer: {
    host: '0.0.0.0',
    port: port,
    open: true,
    historyApiFallback: true,
    proxy: {
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        target: `http://localhost:8081`,
        // target: `http://192.168.2.52:30800`,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: ''
        }
      },
      [process.env.VUE_APP_GRAPH_API]: {
        target: `http://localhost:9999`,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_GRAPH_API]: ''
        }
      },
    },

    disableHostCheck: true,
    headers: {
      'Access-Control-Allow-Origin': '*' // 允许跨域
    }
  },

  css: {
    loaderOptions: {
      sass: {
        additionalData: `@import "@/assets/styles/util.scss";`, // 全局配置 util.scss
        sassOptions: {outputStyle: "expanded"},
      }
    }
  },
  configureWebpack: {
    name: name,

    resolve: {
      alias: {
        '@': resolve('src')
      }
    },
    plugins: [
      // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
      new CompressionWebpackPlugin({
        cache: false,                   // 不启用文件缓存
        test: /\.(js|css|html)?$/i,     // 压缩文件格式
        filename: '[path].gz[query]',   // 压缩后的文件名
        algorithm: 'gzip',              // 使用gzip压缩
        minRatio: 0.8                   // 压缩率小于1才会压缩
      }),
      new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        openAnalyzer: false,
        reportFilename: 'bundle-report.html'
      })
    ],
    output: {
      library: `${packageName}`,
      libraryTarget: 'umd',
      jsonpFunction: `wabpackJsonp_${packageName}`
    },
  },

  chainWebpack(config) {
    config.plugins.delete('preload') // TODO: need test
    config.plugins.delete('prefetch') // TODO: need test

    // set svg-sprite-loader
    config.module
      .rule('svg')
      .exclude.add(resolve('src/assets/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/assets/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()

    config.when(process.env.NODE_ENV !== 'development', config => {
      config
        .plugin('ScriptExtHtmlWebpackPlugin')
        .after('html')
        .use('script-ext-html-webpack-plugin', [{
          // `runtime` must same as runtimeChunk name. default is `runtime`
          inline: /runtime\..*\.js$/
        }])
        .end()

      config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          vueBase: {
            name: 'chunk-vueBase',
            test: /[\\/]node_modules[\\/](vue|vue-router|vuex)/,
            priority: 40,
            chunks: 'initial'
          },
          elementUI: {
            name: 'chunk-elementUI',
            test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
            priority: 30,
            chunks: 'initial'
          },
          echarts: {
            name: 'chunk-echarts',
            test: /[\\/]node_modules[\\/](echarts|echarts-gl|zrender)/,
            priority: 25,
            chunks: 'async'
          },
          three: {
            name: 'chunk-three',
            test: /[\\/]node_modules[\\/]three/,
            priority: 20,
            chunks: 'async'
          },
          moment: {
            name: 'chunk-moment',
            test: /[\\/]node_modules[\\/]moment/,
            priority: 15,
            chunks: 'async'
          },
          vendors: {
            test: /[\\/]node_modules[\\/]/,
            name(module) {
              const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]
              return `npm.${packageName.replace('@', '')}`
            },
            priority: 10,
            chunks: 'all'
          },
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'),
            minChunks: 2,
            priority: 5,
            reuseExistingChunk: true
          }
        }
      })
      config.optimization.runtimeChunk('single')
    })
  }
}

2.5.2 nginx配置文件

worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    tcp_nopush      on;
    keepalive_timeout  65;
    server_tokens   off;

    # Gzip 全局配置
    gzip  on;
    gzip_min_length  1k;
    gzip_comp_level  6;  # 1-9,6 是性能与压缩率的平衡点
    gzip_types  text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary  on;
    gzip_disable  "MSIE [1-6]\.";  # 禁用旧版 IE 的压缩
    gzip_proxied  any;  # 允许代理请求的压缩

    server {
        listen       80;
        server_name  localhost;
        root        /usr/share/nginx/html;
        index       index.html;

        # 静态资源(JS/CSS/图片)配置
        location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|svg)$ {
            expires     30d;
            add_header  Cache-Control "public, no-transform";
            access_log  off;

            # 优先使用预压缩文件(需提前生成 .gz 文件)
            gzip_static  on;
            try_files $uri.gz $uri  =404;

            # 动态压缩(如果预压缩文件不存在)
            gzip  on;
            add_header  Vary Accept-Encoding;
        }

        # 前端项目路由:/ecap/
        location /ecap/ {
            try_files $uri $uri/ /ecap/index.html;
        }

        # 前端项目路由:/electric/
        location /electric/ {
            try_files $uri $uri/ /electric/index.html;
        }

        # 接口代理:/test-api/
        location /test-api/ {
            proxy_pass          http://169.254.240.105:8081/;
            proxy_set_header    Host $host;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
        }

        # 接口代理:/test-graph/
        location /test-graph/ {
            proxy_pass          http://169.254.240.105:9999/;
            proxy_set_header    Host $host;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
        }

        # 404 处理
        error_page  404 /404.html;
        location = /404.html {
            internal;
        }

        # 50x 错误处理
        error_page  500 502 503 504 /50x.html;
        location = /50x.html {
            internal;
        }
    }
}


标题:vue项目chunk.js太大导致页面加载超时
作者:wenyl
地址:http://www.wenyoulong.com/articles/2025/05/08/1746696527259.html