关于h5首页白屏时间太久问题优化思路

703 阅读4分钟

问题发生背景

vue项目首屏加载缓慢,f12一看有的js文件很大,有1.3M旁边,还有个chunk文件也有1.2M,但是目前不知道里面打包了什么东西,导致入口文件加载缓慢,导致发接口的时机延长,所以看见首页的东西就很晚了。

b15e1f255e75e833d60fefd776b55fa.png 所以原因是:首屏加载的js缓慢,导致页面空白时间太久。

优化思路

先把打包报告打出来看一下,打包了哪些东西?

1. 安装个webpack打包工具插件,查看打包报告

296b523711cc36032b25f8b9cbc7f35.jpg 打包报告:

10fc241dda575c31ff94c1a03a1275d.jpg

6c68bae16789876124dfa8994701d77.jpg

可以看到有两个js文件很大,9300那个大部分是图片打包到了js里面,在代码里面看一下这个文件:

b3e5af0326bcae7c4b6b7f6c4cb4ca7.jpg

可以看到webpack打包,把某些文件图片转换成了base64图片格式,图片又很多,导致整个的js文件很大。

按照这个思路试想一下:能不能不让图片转换成base64格式

查了一下文档发现,webpack会默认把某些较小的图片文件打包成base64,为了加快访问图片,不用请求资源,原本是一件好事,没想到我们这个项目图片太多了,导致用这个配置之后,反而加慢了整体的进程。

2. 设置图片限制,不打包成base64格式。

const { defineConfig } = require("@vue/cli-service");
const BundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = defineConfig({
  publicPath: process.env.VUE_APP_CURENVs,
  outputDir: "h5_integral_mall", // 打包名称
  lintOnSave: process.env.NODE_ENV !== "production",
  transpileDependencies: true,
  productionSourceMap: false,
  css: {
    loaderOptions: {
      scss: {
        additionalData: `@import "~@/assets/css/app.scss";`,
      },
    },
  },
  // 该配置表示,图片大小最大为1KB,小于1KB的才会转换为base64
  chainWebpack: (config) => {
    config.module
      .rule("images")
      .test(/\.(png|svg|jpg|jpeg|gif)$/)
      .set("parser", {
        dataUrlCondition: {
          maxSize: 1,
        },
      });
  }
});

上面优化配置项要注意的是:不同的webpack版本对应不同的写法,需要对照webpack官网尝试。当时试了好久不成功,是因为webpack版本不同,写法也没生效。

打包后试一下:

image.png

可以看到文件已经变小了。但是打包完后整体的包的img文件夹大了,是正常的,因为图片都被放出来了。

3. chunk文件过大,拆包处理。

chainWebpack: (config) => {
    config.module
      .rule("images")
      .test(/\.(png|svg|jpg|jpeg|gif)$/)
      .set("parser", {
        dataUrlCondition: {
          maxSize: 1,
        },
      });
      // 开发环境不拆包
      // 分模块和优先级进行拆分
      if (process.env.NODE_ENV !== "development") {
      config.optimization.splitChunks({
        chunks: "all",
        automaticNameDelimiter: "-",
        cacheGroups: {
          vant: {
            name: "chunk-vant",
            priority: 32,
            test: /[\\/]node_modules[\\/]_?vant(.*)/,
          },
          mathjs: {
            name: "chunk-mathjs",
            priority: 35,
            test: /[\\/]node_modules[\\/]_?mathjs(.*)/,
          },
          vue: {
            name: "chunk-vuejs",
            priority: 30,
            test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
          },
          // src/components组件从chunk里拆分出来
          components: {
            name: "chunk-components",
            test: resolve("src/components"),
            minChunks: 2,
            priority: 5,
            reuseExistingChunk: true,
          },
        },
      });
      config.optimization.runtimeChunk("single");
    }
  }

分模块和优先级进行拆分。

打包后如下:

5de76dbe2fc0016fed1d1cf87ea37a0.jpg

41555abda8d6ed8f24a2361c858e346.jpg

可以看出从之前的一个chunk文件拆分成了好几个chunk文件。整个项目的文件大小也从6.39M减小到4.66M。

4. moment插件处理

从分析报告可以看出:moment文件加有一个local文件,查了一下文档,这个文件主要是用于各个地区的时间处理,由于项目中没使用到,主要是在中国时区,所以可以考虑去除该模块。

c30d167c6fcbff1acec4390933b918d.jpg

 configureWebpack: (config) => {
    if (process.env.NODE_ENV !== "development") {
      config.plugins.push(
        new BundleAnalyzer({
          // 设置混淆选项
          openAnalyzer: true,
          analyzerPort: 1000,
        })
      );
      // 去除moment文件夹中的local文件。
      config.plugins.push(
        new webpack.IgnorePlugin({
          resourceRegExp: /^\.\/locale$/,
          contextRegExp: /moment$/,
        })
        /* 压缩loader */
      );

    }
  },

打包后如下:

51eb1720495ec7c6669571fa0980e97.jpg

5. 开启CSS代码压缩

const { defineConfig } = require("@vue/cli-service");
const TerserPlugin = require("terser-webpack-plugin");
const BundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const webpack = require("webpack");
/* 开启css压缩 */
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = defineConfig({
  publicPath: process.env.VUE_APP_CURENVs,
  outputDir: "h5_integral_mall", // 打包名称
  lintOnSave: process.env.NODE_ENV !== "production",
  transpileDependencies: true,
  productionSourceMap: false,
  css: {
    loaderOptions: {
      scss: {
        additionalData: `@import "~@/assets/css/app.scss";`,
      },
    },
  },
  configureWebpack: (config) => {
    if (process.env.NODE_ENV !== "development") {
      config.plugins.push(
        new webpack.IgnorePlugin({
          resourceRegExp: /^\.\/locale$/,
          contextRegExp: /moment$/,
        })
      );
      config.optimization.minimizer = [
        /* 开启css压缩 */
        new CssMinimizerPlugin(),
      ];
    }
  },
  chainWebpack: (config) => {
    config.module
      .rule("images")
      .test(/\.(png|svg|jpg|jpeg|gif)$/)
      .set("parser", {
        dataUrlCondition: {
          maxSize: 1,
        },
      });
  },
  
});

6. CDN加载外部资源

vue.config.js中,配置哪些外部资源需要cdn加载:

/* 根据环境动态配置cdn资源 */
let externals = {};
let CDN = { css: [], js: [] };
/* 非开发环境 */
if (process.env.NODE_ENV !== "development") {
  externals = {
    /**
     * externals对象属性分析:
     * '包名':'在项目中引入的名字'**/

    lodash: "lodash",
    mathjs: "mathjs",
    vconsole: "vconsole",
  };
  CDN = {
    css: [],
    js: [
      "https://cdn.bootcdn.net/ajax/libs/mathjs/12.3.0/math.min.js",
      "https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js",
      "https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js",
    ],
  };
}

紧接上面的chainWebpack配置:

chainWebpack: (config) => {
    config.module
      .rule("images")
      .test(/\.(png|svg|jpg|jpeg|gif)$/)
      .set("parser", {
        dataUrlCondition: {
          maxSize: 1,
        },
      });
  },
   config.plugin('html').tap(args => {
        // 参数对象添加属性叫cdn,值就是上面CDN对象
        args[0].cdn = CDN// 配置CDN给插件
        return args
      })
  

接下来在html页面中,循环配置中的cdn资源:

ca4a71cdf19382ad5b317f597ce752b.jpg

    <!-- 导入js -->
    <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
      <script src="<%=js%>"></script>
    <% } %>

最后打包看一下:

259b653906aacf48f6aa50d2c1eff06.jpg

31f6c4a933c106bf2cc2a5af9489413.jpg

可以看到打包后,打包文件里已经没有我们在config.js里面配置的外部资源了。有效的减少了包的文件大小。从之前的6.38M减少到3.69M

在打包后的index.html可以看到,加载了我们配置的js包。

303892068220d877b2a6a5512840328.jpg

这个cdn加载有个注意的是::要找个靠谱的cdn网站,我之前配置了之后,cdn崩了一天,所以这种方案后面就没有使用。

7. 关于图片还有其他的思路,就是挺麻烦

这个思路适合在项目初期建设阶段,就是将项目中的图片文件放在public文件夹下,这样webpack就不会将图片打包。注意的就是,在代码中引用图片资源的写法就要注意,都要写成相对路径。

大部分图片有三种格式:

  • require方式引用图片
  • cssbackgrundImage设置背景图片
  • 还有就是在模板标签中使用图片

首先在vue.config.js里面配置一下环境变量,可以在css中写env中配置的环境变量。

module.exports = defineConfig({
  publicPath: process.env.VUE_APP_CURENVs,
  outputDir: "h5_xiaoyiyunxuan", // 打包名称
  lintOnSave: process.env.NODE_ENV !== "production",
  transpileDependencies: true,
  productionSourceMap: false,
  css: {
  // css增加环境变量,在css中就可以使用$env-host来替换项目的域名和端口
    loaderOptions: {
      scss: {
        additionalData: `$env-host:"${process.env.VUE_APP_HOST}";$env-curenvs:"${process.env.VUE_APP_CURENVs}";@import "~@/assets/css/app.scss";`,
      },
    },

  1. require方式引用图片格式代码改写:

75ea0dfdfc4a66a7ff09637d986e527.jpg

c03aeb7dc267b088e110f9d10d4332e.jpg

  1. cssbackgrundImage设置背景图片

ed22b4e68f203db37dc98df6e2d36ab.jpg

  1. 在模板标签中使用图片

2047969a6ff2165fc6a2fe96af132b3.jpg

总结

通过打包报告会有思路,去哪方面去优化,有问题解决问题,就是自己对webpack配置项还是不熟悉,很多配置还是需要去查,去搜。慢慢学习吧~