Webpack 捆绑机制

Posted

技术标签:

【中文标题】Webpack 捆绑机制【英文标题】:Webpack bundling mechanism 【发布时间】:2021-07-15 05:45:25 【问题描述】:

我正在与您联系以请求支持以优化我们的前端应用程序在客户端的加载时间。请在下面找到有关该情况的更多详细信息。

上下文

我们的前端应用程序由 Heroku 托管(我们使用 2 个标准 1x dynos)。网络测功机负责两件事:

提供使用 Vue.js 构建的单页应用程序 (SPA) 的静态资产 充当代理与后端通信。

以下是我们在前端应用程序中使用的技术列表:

我们将 Vue.js 与 Vue CLI 3 结合使用。 我们使用 Webpack 进行捆绑。 我们使用 Node-Express.js 来托管静态资产。 我们使用 Raygun 作为真实用户监控。 我们使用 Fastly 作为 CDN 来提供静态资产(js、css 和图像)。 我们使用 Service Worker 从浏览器缓存中提供资产。

问题 我们的主要问题是客户端的加载时间:我们的真实用户监控工具 Raygun 报告的平均加载时间约为 10.4 秒。

Screenshot 1

Screenshot 2

问题

我们的主要问题是客户端的加载时间:我们的真实用户监控工具 Raygun 报告的平均加载时间约为 10.4 秒。 我们希望改进从入口点 main.js 加载的页面的加载时间(我们有 2 个入口点 main.js 和 signup.js) 我们认为问题与我们的 webpack 分块策略的优化有关。 Webpack 分块在 vue.config.js 文件中设置。

约束

我们使用 Service Worker 来从浏览器缓存中加载前端资产。我们注意到,如果块数超过 100 个块,则 Service Worker 注册需要 2-3 秒以上。

问题

为了缩短加载时间,我们可以做哪些优化? (请在 vue.config.js 文件下方找到)

参考

请在下面找到我们的 vue.config.js 文件源代码:
const CompressionPlugin = require('compression-webpack-plugin');
const htmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const webpack = require('webpack');
const Dotenv = require('dotenv-webpack');

module.exports = 
    productionSourceMap: false,
    css: 
      loaderOptions: 
        sass: 
          prependData: `
          @import "@/assets/scss/generics/variables.scss";
          @import "@/assets/scss/generics/spacing.scss";
          @import "@/assets/scss/generics/sizing.scss";
          `
        ,
      ,
    ,
    devServer: 
      proxy: 
        '^/auth': 
            target: process.env.JB_BACKEND_URL,
            changeOrigin: true,
            pathRewrite: '^/auth' : '/api/v1.0/get-token/'
        ,
        '^/api': 
            target: process.env.JB_BACKEND_URL,
            changeOrigin: true
        ,
      
    ,
    chainWebpack: config => 
      // delete default entry point 'app'
      config.entryPoints.delete("app").end();
      //delete default 'html' plugin - in case you don't want default index.html file
      //delete 'prefetch' and 'preload' plugins which are dependent on 'html' plugin
      config.plugins
        .delete("html")
        .delete("prefetch")
        .delete("preload");

      config.optimization.splitChunks(false);

      config.plugin('CompressionPlugin').use(CompressionPlugin);
    ,
    pluginOptions: 
      webpackBundleAnalyzer: 
        openAnalyzer: false
      
    ,
    configureWebpack:
      entry: 
        index: "./src/main.js",
        signup: "./src/signup.js"
      ,
      output: 
        filename: "[name].bundle.[hash].js"
      ,
      plugins: [
        new HtmlWebpackPlugin(
          template: "public/index.html",
          inject: true,
          filename: "index.html",
          chunks: ["index"]
        ),
        new HtmlWebpackPlugin(
          template: "public/signup.html",
          inject: true,
          filename: "signup.html",
          chunks: ["signup"]
        ),
        new WorkboxPlugin.GenerateSW(
          // these options encourage the ServiceWorkers to get in there fast
          // and not allow any straggling "old" SWs to hang around
          clientsClaim: true,
          skipWaiting: false, // Since we load chunks with hash in it.
          // Do not cache images on service worker registration
          exclude: [/\.(?:png|gif|ttf|jpg|jpeg|svg|gz)$/],
          runtimeCaching: [
              // Some code goes here (Hidden)
          ]
        ),
        new webpack.optimize.LimitChunkCountPlugin(
          maxChunks: 30,
        ),
        // Ignore all locale files of moment.js
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
        // Import 3rd party environment variables
        new Dotenv(
          path: './.env', // load '.env'
          safe: false, // load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.
          allowEmptyValues: true, // allow empty variables (e.g. `FOO=`) (treat it as empty string, rather than missing)
          systemvars: true, // load all the predefined 'process.env' variables which will trump anything local per dotenv specs.
          silent: true // hide any errors
        ),
      ],
      optimization: 
        splitChunks: 
          chunks: 'all'
        ,
      ,
    ,
    publicPath: process.env.STATIC_PATH || "/"
  

如果您有任何问题,请告诉我。

【问题讨论】:

【参考方案1】:

您可能想尝试类似的方法:

module.exports =

  configureWebpack: (config) =>
  
    config.optimization = 
      runtimeChunk: 'single',
      moduleIds: 'hashed',
      splitChunks:
      
        automaticNameDelimiter: '_',
        chunks: 'all',
        maxInitialRequests: 5,
        minSize: 5000,
        //maxSize: 250000,
        cacheGroups:
        
          default:
          
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true,
            ,
          vendor:
          
            reuseExistingChunk: true,
            test: /[\\/]node_modules[\\/]/,
            name(module)
            
              // get the name. E.g. node_modules/packageName/not/this/part.js
              // or node_modules/packageName
              const packageName = module.context.match(/[\\/]node_modules[\\/]([^\\/]+)([\\/]|$)/)[1];

              // npm package names are URL-safe, but some servers don't like @ symbols
              return `vendor_$packageName.replace('@', '')`;
            ,
          ,
        ,
      ,
    ;
  

【讨论】:

以上是关于Webpack 捆绑机制的主要内容,如果未能解决你的问题,请参考以下文章

Webpack:如何使用动态捆绑组合两个完全独立的捆绑包

Webpack将两个应用程序捆绑在一个捆绑包中,Angular需要Zone.js

webpack 不再缩小捆绑包

捆绑问题(angular2 + webpack 捆绑)

webpack 4:找不到捆绑js

是否可以使用webpack单独导入捆绑的webpack和库?