vue 首屏加载性能优化方案(三)加载打包篇超详细版

460 阅读5分钟

前言解读

前面两篇说了针对依赖文件的优化来提升速度

这里讲一下如何从加载方式,渲染体验方面进一步提升用户体验效果

1、路由懒加载

路由懒加载,原理其实就是将原来 顶层直接import引入的方式

改为利用函数被调用时才执行的特性

结合 es11 的新特性 动态加载import(), 允许在局部作用域引用其他文件模块

{
  path: '/test',
  component: () => import('../views/Test.vue'),
}

或者使用 CommonJS 模块 的 require() 引入

component: () => require('../views/Test.vue')

image-20231016152945183.png

以test路由为例,在 test 路由文件 输出日志,使用懒加载后,只有当跳转到这个路由时才会显示日志输出,否则应用一开始就将加载所有路由页面并显示日志输出

image-20231024154436502.png

image-20231024154520888.png

打包后也会将设置了懒加载的路由文件分离出来,生成异步 chunk 文件

当跳转到该路由页面时才会加载该文件,而不是一次性加载全部

2、体验效果优化

vue 是单页面应用,初始化完成就会将dom插入,将实例挂载到

中,

那么可以在 vue 生成的 dom 插入之前,加入loading过渡效果,等vue插入后自动覆盖了原先的过渡效果元素,让用户视觉体验更好

在public/index.html 入口文件

中 插入过渡效果的元素

<body>
    <div id="app">
      <div class="loading-container">
        <div class="loading-spin"></div>
        <span class="loading-text">努力加载中。。。</span>
      </div>
    </div>
</body>

引入样式

<link rel="stylesheet" href="/loading/index.css" />
.loading-container{
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
.loading-container .loading-spin{
  border: 4px solid #f6f6f6; /* 设置边框样式 */
  border-top: 4px solid #409EFF; /* 设置顶部边框样式为蓝色 */
  border-radius: 50%; /* 设置边框圆角 */
  width: 60px; /* 设置 loader 宽度 */
  height: 60px; /* 设置 loader 高度 */
  animation: spin 0.5s linear infinite; /* 添加旋转动画 */
}
.loading-container .loading-text{
  margin-top: 10px;
  font-size: 16px;
  color: #303133;
}

@keyframes spin { /* 定义旋转动画 */
  0% { transform: rotate(0deg); } /* 起始状态:不旋转 */
  100% { transform: rotate(360deg); } /* 终止状态:360度旋转 */
}

这样在vue 实例挂载前就可以展示出一个加载效果,从视觉上提升体验,平滑进入应用的时间

效果

GIF.gif

3、按需异步加载组件

Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

异步组件

全局注册使用

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

局部注册使用

components: {
    'my-component': () => import('./my-async-component')
}

只在需要使用的时候才会加载这个组件

4、vue-cli 去除生产环境sourceMap

source map:源映射文件,帮助开发者在浏览器的开发者工具中,将错误和日志定位到原始代码的具体位置

默认情况下,Vue CLI 会在开发环境中生成 source map 文件,以便开发者可以方便地调试代码。但是,在生产环境中,生成 source map 文件可能会暴露一些敏感信息,因此通常需要将其关闭。

要关闭 productionSourceMap,可以在 Vue CLI 的配置文件vue.config.js中设置 productionSourceMap: false

module.exports = {
  productionSourceMap: false,
};

image-20231023120101427.png

打包后不会生成map映射文件,大大减少打包后的体积

image-20231023151959175.png

5、SplitChunksPlugin 拆包

在不做任何优化情况下,chunk-vendors.jsapp.js的体积都太大了,特别是初始第三方包竟有 955kb。非常不利于首屏加载的响应速度

image-20231025150757112.png

在 vue-cli 脚手架的vue.config.js 通过 configureWebpack 配置项覆盖掉 SplitChunksPlugin cacheGroups 项默认值的配置

将公共模块代码文件和一些依赖包拆分出来,有利于提升首屏时加载的速度

拆分代码并没有标准的方案,要结合项目的实际情况去找到最合适的拆包策略

自定义缓存组,从第三方依赖库抽离 element 、vue相关依赖 和公共代码文件

这里为测试demo项目,实际没有多少逻辑代码,所以将拆包限制改为最小,先将效果展示出来

module.exports = {
    // webpack额外配置
  configureWebpack: {
    optimization: {
      splitChunks: {
        chunks: "all", // 选择哪些 chunks 进行分割,可选值有:async异步,initial同步和 all所有
        minSize: 1, // 允许新拆出 chunk 的最小体积
        maxSize: 0, // 旨在与 HTTP/2 和长期缓存一起使用。它增加了请求数量以实现更好的缓存。它还可以用于减小文件大小,以加快二次构建速度。
        minChunks: 1, // 拆分前被 chunk 公用的最小次数
        maxAsyncRequests: 5, // 每个异步加载模块最多能被拆分的数量
        maxInitialRequests: 3, // 每个入口和它的同步依赖最多能被拆分的数量
        enforceSizeThreshold: 0, // 强制执行拆分的体积阈值并忽略其他限制,默认50kb
        cacheGroups: {
          // 缓存组
          vendors: {
            // 第三方库
            name: `chunk-vendors`,
            test: /[\\/]node_modules[\\/]/,
            priority: 10, // 缓存组权重,数字越大优先级越高
          },
          vueVendor: {
            // vue 依赖拆包
            test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
            name: 'chunk-vue-vendors',
            priority: 20, // 缓存组权重,数字越大优先级越高
          },
          element: {
            // elementUI 单独拆包
            name: "chunk-element",
            test: /[\\/]node_modules[\\/]element-ui[\\/]/,
            priority: 20, // 缓存组权重,数字越大优先级越高
          },
          common: {
            // 公共模块包
            name: `chunk-common`,
            minChunks: 1, // common 组的模块必须至少被 1 个 chunk 共用 (本次分割前)
            priority: 0, // 缓存组权重,数字越大优先级越高
            reuseExistingChunk: true, // 复用已被拆出的依赖模块,而不是继续包含在该组一起生成
          },
        },
      },
    },
  }
}

打包后的效果如下

image-20231025162012171.png

6、依赖按需引入

7、缓存优化