最近在做项目的首屏优化工作, 提起首屏优化会有很多方式,但是对于如何减小静态资源的加载体积少之又少, 这次记录下在项目中优化的过程.
背景
我们的项目是C端项目, 页面内容是强依赖后端接口数据, 后端接口不返回数据页面不会展示任何内容,(体验的事情,暂时先不谈😭)
页面组件结构
首页模版中用了一些组件,有第三方的,有自己写的
简单介绍下各个组件
- XxxGuider:一个自定义的组件,展示一些业务相关的引导
- loading:整个页面的loading
- cb-ui-render:一个历史悠久的组件(也是后面重点优化的对象), 源码已经没人维护, (我有一次翻到源码,做了一次构建,构建出来的产物功能已经不全了😭), 页面的展示就是依靠它
- popup, error, lottery,Risk-Control-Part: 自定义的功能性组件
cb-ui-render
关于cb-ui-render的组件要多说一些
在工程中的某个位置, 代码长这样,cb-ui-render是这一坨代码导出其中的一个组件
在项目中这样使用
javascript
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
的数据结构长这样
uiData
是基于接口返回的数据生成的 整体是一个对象结构, 最外层是页面级的数据,
elements
是页面中各个组件的数据, 其中的type
属性是组件名称, props
属性是组件用到的数据, on
属性是事件
这样的数据结构传入到 uiData
后, 会根据这样的数据结构生成一个vue的组件对象, 该组件对象的模板(template
)属性字符串中用到的组件标签就是 uiData
中的各个 type
关于cb-ui-render
组件内部逻辑比较复杂,这里不在赘述
看下代码执行截图吧, 这里注册 CbUiRender
, xs
是对应的组件对象 经过一系列的执行之后,这里生成一个组件对象, 看下
template
属性
这里箭头指的就是 uiData
中的 type
项目中自定义组件
在这样的前提下,我们想要用 cb-ui-render
渲染,就要提前把对应的组件通过Vue.component
注册(这里是为了不用的业务应用做了分开的注册)
然后在首页导入并执行 Vue.use
webpack 分包
由于这个 '@phoenix'
和自定义的 import { bocComponent } from '@/component/uiGadget/components/bocComponent.js'
会比较大,如果不用webpack
做分包处理会达到默认的vendor
中
所以在webpack
中这样配置下
这些包就被分到独立的 vendor
中,在浏览器中间加载看看
要等到这些vendor
加载完才会调用接口,获取数据渲染页面,严重影响页面渲染
开始优化
先看之前的 app.vue
cb-ui-render
这个组件依赖接口数据, 可以不在最开始写在模版中, 可以先写一个挂载的dom节点, 利用Vue组件实例挂载的方式等到接口获取到数据再挂载到dom上
具体代码如下:
模版中加一个 id
为 render_container
的 div
原本注册组件的逻辑由
javascript
// 引入渲染器组件
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的请求
同时利用
Promise.all
和接口做并发请求, 这样在获取到数据,获取到两个js资源后,再注册组件,我给封装到一个函数中
处理组件挂载的逻辑,上图中的 this.mount
这里可以用Vue.extend,也可以用new Vue(), 用Vue.extend扩展出一个构造函数,在用这个构造函数生成一个Vue实例,然后挂载到dom中
bocComponent.perf.js
文件改造如下
(修改前) 这样写有个问题: 网络加载的资源中是包含所有组件的js, 体积庞大
(修改后)
index.perf.js
文件改造如下
(修改前)
(修改后)
目标实现接口中返回哪个组件就加载哪个组件, 即所谓的按需加载. 通过 import
函数在配合 /* webpackChunkName: 'xxx'*/
(这样改了之后,要把 webpack 原来 optimization
的分包逻辑去掉)
即可实现组件的按需加载,同时还能减小首屏加载js资源的个数和体积. 部署到线上再看资源的加载情况,
由修改之前的
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
三个组件, 在网络请求中也只加载这三个组件
各个bundle体积也明显减小
总结
利用 import
函数,灵活调整导入组件位置, 同时利用webpack的分包机制, 实现组件的按需导入,减小原来bundle的体积, 加快页面的加载速度