| 导语 直播页面是一个功能丰富且复杂的页面,整个页面几乎全部由若干个功能组件构成,在这样一个背景下,如何通过前期的合理设计来接入这些功能组件,同时提高页面的扩展性和可维护性。
一、背景
开播了鹅是手Q今年上线的一个直播带货平台。
图片来源:直播截图
在实现上,我们采用的是客户端+h5模式,底层的视频流属于客户端逻辑,顶层的各种挂件属于h5逻辑。去掉视频流之后,也就是右边这张图,就是一个纯h5页面。
二、功能模块划分
在h5页面里面,总的来说可以分成两大块,一块是各种类型的挂件,比如礼物动画,商品橱窗,另一块则是一些公共基础逻辑,比如消息轮询,挂件接入等。
挂件这里其实跟我们普通开发功能组件没有多大的区别,这里就不展开描述了。而如何把各种类型的挂件组织到一个h5页面上,就是我们的挂件体系需要考虑的。
三、目标
我们在设计这套体系的时候,设置了几个目标。
四、解决方案
大致思路是一个挂件即一个组件,所有挂件以vue组件的形式开发。这套方案跟我们平时开发的思路是差不多的。但是会有以下几个问题:
基于以上几点考虑,我们就舍弃了这个方案。然后就有了方案二。
这套方案有以下几个特点。
import { util } from '@tencent/qlib';
// 插件体系模块,与直播插件独立
import { BasePlugin, Subscriber, LAYOUT_TYPE, LAYOUT } from'@packages/basePlugin';// 插件自身的逻辑组件
import audienceInfo from './src/AudienceInfo.vue';
import storeConfig from './store';
class AudienceInfo extends BasePlugin {
init(): void {
const MyComponent = this.RootVue.extend(audienceInfo);
// 注册状态管理器
this.rootStore.registerModule(storeConfig.name, storeConfig);
const componentEl = new MyComponent({
store: this.rootStore,
data: {
uid: 111,
},
}).$mount();
// layer由插件体系分配,把生成的DOM插入到layer里面即可完成渲染,这里不限制语言框架,只要提供html即可
this.layer.appendChild(componentEl.$el);
}
getLayout(): LAYOUT {
return {
width: '100w',
offsetBottom: '0',
zIndex: 3,
layoutType: LAYOUT_TYPE.LEFTBOTTOM,
};
}
subscribeMsg(): Array {
return [];
}
dismiss(): void {
// 销毁内容
this.rootStore.unregisterModule(storeConfig.name);
}
}
export default AudienceInfo;
然后再来分析下插件布局,目前把插件分为三类:可交互的UI组件,弹窗类型组件,纯展示不可交互组件。首先我们做了一个分层处理,把弹窗类型组件放在了最顶层,可交互的UI组件放在第二层,纯展示的放在最底层,如图:
确认好层次之后,再确认每一层的布局。目标是每一层的布局模式都保持一致。
以第二层为例,这里面的组件虽然在页面的任何一个地方,比较杂乱。但仔细观察,还是有一定的规律的。第二层所有的组件都是从某个角出发,向不同的两个方向排列的。如下图所示,比如分享插件,在左下角向右排列,消息组件,在左下角向上排列。
图片来源:直播截图
找到规律之后,我们就可以去布局了。
我们分析了主流的布局方案:
布局类型 | 特点 |
---|---|
block | 需要组件管理自己的位置信息,一旦组件膨胀将难以维护 |
flex | 侧重一维布局,难以处理复杂的层级关系,不方便精准定位 |
grid | 写法复杂,不方便精准定位,还有兼容性问题(iOS10) |
绝对定位 | 精准定位,但需要大量计算 |
发现都有优缺点。最后我们选择了第四种方案,绝对定位布局。
这个方案需要解决的最大问题就是位置计算,如果把这个计算交给插件做的话,每个插件接入进来都需要去计算当前已有的插件的位置,势必会把插件开发变得更复杂。所以我们把这套计算交给了插件体系去做。
插件体系在这八个方位里面分别用一个变量来存储当前方位已经占据的空间,当下一个插件接入进来的时候,根据对应的方位里面的数据来确定位置,同时把当前插件的位置信息更新到对应的变量里面。
对于插件开发者来说,只需要声明具体的方位,以及自身需要的宽高(上面代码里面的getLayout方法),插件体系就可以给插件分配好具体的位置了(上面代码里面的this.layer)。
五、目标
这套插件体系已经在直播这边平稳的使用了快半年,也从刚开始的几个插件到现在的30多个插件,到目前位置,不管收到什么样类型的需求挂件,这套体系基本都能够承接住。也从侧面看出来这套体系是基本合格的。而这篇文章的目的,还是希望在遇到类似场景的需求的时候,能给大家一些参考。