import按需加载资源优化实践

387 阅读5分钟

最近在做项目的首屏优化工作, 提起首屏优化会有很多方式,但是对于如何减小静态资源的加载体积少之又少, 这次记录下在项目中优化的过程.

背景

我们的项目是C端项目, 页面内容是强依赖后端接口数据, 后端接口不返回数据页面不会展示任何内容,(体验的事情,暂时先不谈😭)

2025-03-05-15-32-55-image.png

页面组件结构

首页模版中用了一些组件,有第三方的,有自己写的

2025-03-05-15-53-46-image.png 简单介绍下各个组件

  • XxxGuider:一个自定义的组件,展示一些业务相关的引导
  • loading:整个页面的loading
  • cb-ui-render:一个历史悠久的组件(也是后面重点优化的对象), 源码已经没人维护, (我有一次翻到源码,做了一次构建,构建出来的产物功能已经不全了😭), 页面的展示就是依靠它
  • popup, error, lottery,Risk-Control-Part: 自定义的功能性组件

cb-ui-render

关于cb-ui-render的组件要多说一些

在工程中的某个位置, 代码长这样,cb-ui-render是这一坨代码导出其中的一个组件

2025-03-05-15-56-26-image.png 在项目中这样使用

import CbPlugIn from '@phoenix'
Vue.use(CbPlugIn)

然后在模版中才能试用,否则汇报组件未定义的警告. 写过Vue插件的朋友应该大致猜到了, 这个 CbPlugIn 是一个install函数或者带有install 方法的对象, install 中执行类似这样一行代码 Vue.component('CbUiRender', cb-ui-render组件)

cb-ui-render 接收 uiData 这样的 props 属性, 由 uiData 的数据结构渲染界面

uiData 的数据结构长这样 2025-03-05-16-30-08-image.png uiData 是基于接口返回的数据生成的 2025-03-05-17-36-38-image.png 整体是一个对象结构, 最外层是页面级的数据, elements 是页面中各个组件的数据, 其中的type 属性是组件名称, props 属性是组件用到的数据, on 属性是事件

这样的数据结构传入到 uiData 后, 会根据这样的数据结构生成一个vue的组件对象, 该组件对象的模板(template)属性字符串中用到的组件标签就是 uiData 中的各个 type 关于cb-ui-render 组件内部逻辑比较复杂,这里不在赘述

看下代码执行截图吧, 这里注册 CbUiRender , xs 是对应的组件对象 2025-03-05-16-56-20-image.png 经过一系列的执行之后,这里生成一个组件对象, 看下 template 属性 2025-03-05-16-58-42-image.png

2025-03-05-16-59-52-image.png

这里箭头指的就是 uiData 中的 type

项目中自定义组件

在这样的前提下,我们想要用 cb-ui-render 渲染,就要提前把对应的组件通过Vue.component 注册(这里是为了不用的业务应用做了分开的注册)

2025-03-05-23-16-53-image.png

2025-03-05-23-14-00-image.png

然后在首页导入并执行 Vue.use

2025-03-05-17-27-40-image.png

webpack 分包

由于这个 '@phoenix' 和自定义的 import { bocComponent } from '@/component/uiGadget/components/bocComponent.js' 会比较大,如果不用webpack做分包处理会达到默认的vendor

所以在webpack中这样配置下

2025-03-05-23-20-26-image.png

这些包就被分到独立的 vendor 中,在浏览器中间加载看看

2025-03-05-23-23-26-image.png

要等到这些vendor 加载完才会调用接口,获取数据渲染页面,严重影响页面渲染

开始优化

先看之前的 app.vue

2025-03-05-15-53-46-image.png cb-ui-render 这个组件依赖接口数据, 可以不在最开始写在模版中, 可以先写一个挂载的dom节点, 利用Vue组件实例挂载的方式等到接口获取到数据再挂载到dom上

具体代码如下:

模版中加一个 idrender_container 的 div

2025-03-07-15-14-21-image.png 原本注册组件的逻辑由

// 引入渲染器组件
import CbPlugIn from '@phoenix'
Vue.use(CbPlugIn)

// 注册自定义2c组件
import { bocComponent } from '@/component/uiGadget/components/bocComponent.js'
Vue.use(bocComponent)

改成用 import() 函数导入的方式, import 函数可以写在js的逻辑中, 在部署上线之后,每 import 一个js文件,就会发送一个js的请求

2025-03-07-15-19-05-image.png 同时利用 Promise.all 和接口做并发请求, 这样在获取到数据,获取到两个js资源后,再注册组件,我给封装到一个函数中

2025-03-07-15-29-10-image.png 处理组件挂载的逻辑,上图中的 this.mount

2025-03-07-15-29-57-image.png 这里可以用Vue.extend,也可以用new Vue(), 用Vue.extend扩展出一个构造函数,在用这个构造函数生成一个Vue实例,然后挂载到dom中

bocComponent.perf.js 文件改造如下

(修改前) 2025-03-05-23-16-53-image.png 这样写有个问题: 网络加载的资源中是包含所有组件的js, 体积庞大

(修改后)

2025-03-07-14-33-19-image.png

index.perf.js 文件改造如下

(修改前)

2025-03-05-23-14-00-image.png

(修改后)

2025-03-07-15-22-47-image.png

目标实现接口中返回哪个组件就加载哪个组件, 即所谓的按需加载. 通过 import 函数在配合 /* webpackChunkName: 'xxx'*/ (这样改了之后,要把 webpack 原来 optimization 的分包逻辑去掉)

2025-03-07-15-41-17-image.png 即可实现组件的按需加载,同时还能减小首屏加载js资源的个数和体积. 部署到线上再看资源的加载情况,

2025-03-07-14-46-19-image.png 由修改之前的 phoenixSrcVendor(100kb), cxui-components-vendor(228kb), cxui-boc-components-vendor(387kb), cxui-other-components-vendor(104kb) 4个 bundle 减小到现在的 @phoenix(100kb), bocComponent(779b), components(73.5kb)

同时页面中用到了 cxui-title-new ,cxui-banner-new, cxui-nav-menu-new三个组件, 在网络请求中也只加载这三个组件

2025-03-07-14-46-46-image.png 各个bundle体积也明显减小

总结

利用 import 函数,灵活调整导入组件位置, 同时利用webpack的分包机制, 实现组件的按需导入,减小原来bundle的体积, 加快页面的加载速度