这个项目从开始去看项目用到的技术到结束林林总总大概花了25个工作日,跟我之前做的项目相比,算是比较复杂的项目了。 趁着前端没什么问题,其他技术在改bug的时间,写下此篇文章来总结一下。
项目介绍
24h 导播台是一个类似于官方电视台(直播间)的东西,电视台里播放的内容会按照运营的设置而改变,这个系统的使用者---运营就像是电视台中的导播。在导播台中运营可以自己编排待播主播,并且可以设置他们的上播时间以及次序,或者选择要不要给直播间加个特效。在YY直播中已经有了此功能,见图1。在 虎牙里面也有此项功能,但是用的频次不高,之前都是靠运营同学手动切换某个直播间的直播流,风险较大且人力成本高(需要蹲守值班),这个系统就是让运营同学解放双手的。
先大致浏览一下这个系统的全貌,见图2。系统功能大概分为了上播队列,预览画面,直播画面,输入画面,模板设置和权限管理等功能。
项目细节
-
技术选型
从上面的截图上我们可以了解到,这个系统中数据状态很多,很明显不适合使用jQuery,适合使用“现代”一些的框架。加上之前有过Vue.js的使用经验以及后台系统没有对浏览器的兼容要求,就选择了Vue做为主框架进行开发,使用了官方的脚手架vue-cli,用了一直想试试但是没试过的ES6语法,以及vue官方指定的ajax库---axios。
-
项目结构
一图顶前言,直接画了个图。见图三。可能api center 需要解释一下,api center里面封装了与接口通信的方法,与组件进行了分离,当组件需要调用时才引入。
-
项目难点分析
回过头看,整个项目的难点主要在以下四个方面。
- 逻辑复杂(例:主要是上播队列和输入画面这部分,很复杂,很多边界情况)
- 数据同步(例: 输入画面和上播队列之间同步状态,以及后台接口坑爹,有很多应由后台处理的逻辑没处理)
- 交互复杂 (例:上播队列和输入画面的操作)
下面举几个例子,仔细讲讲:
实时的上播队列
功能点分析
- 实时的,会局部刷新数据
- 在页面上进行操作,只有“添加主播”和“切下一个”是直接生效,进行其他操作后必须“发布”才能生效
- 可以对队列进行“上移,下移,移除,设置时长”操作,进行操作后,页面上显示改变,并且上播时间和开播时长均由前端计算
- 数据同步,由于需求是可以在队列中加入相同的主播,后台是把每条记录当成不同的来处理,所以每个主播的上播时长以及品类和输入流监控是不同的,需要前端做同步逻辑,以及控制“加入监控”的入口,只让相同主播中的第一个主播才会有“加入监控”的按钮
实现思路
1 . 维护两套数据,一套是用户的配置数据,另一套是处理过的接口数据,定时刷新,经过比对后,选择局部刷新配置数据或者直接替代配置数据
2 . 勾选主播后,进行“上移,下移,移除,设置时长”操作,操作数组,计算第一个主播的时间,然后再遍历计算其他主播的上播时间
3 . 比对配置数据和接口数据,当只有‘输入流监控,直播状态,品类’其中一项或者多项发生改变时,接口数据不会直接替代配置数据,而是会局部刷新这几项数据
4 . 处理数据同步。 在接口数据中,相同主播的‘inputStream’字段的数据是不同的,我们需要处理,需要遍历两次数据,然后进行替代。并且如果队列中有相同的主播且未加入监控,则只让相同主播中的第一个显示“加入监控入口“。
-
项目优化
重构和优化都是持续进行的,在项目开发过程中一直都要进行的,而不是非要等要项目结束之后再进行
- 减少http请求数
- 页面上有很多弹窗,有许多请求会在弹窗中发起。看下面的伪代码
父组件 A
子组件 diaog
dialog.created = ()=>{
// 发起请求
}
上面代码的关键点就是当组件真正的被渲染之后才会发起请求,而不要使用v-show。
- 同样是上面的例子,页面上有弹窗,弹窗显示后去请求数据。但对于实时性要求不高的数据,我们要做处理,不能每次弹窗显示就去请求数据。伪代码如下:
dialog组件
{{data}} // 请求的数据
上面的思路就是监听show的值,当弹窗显示后,先判断数据有没有存在,没有的话再去请求。
- 代码优化
代码优化方面比较多,我重点讲下几个原则。- 函数的长度尽量不要超过20行,超过20行之后可读性会大大降低
- 单一职责
- 抽象复用, 重复三次以上就有必要抽取出来
- 函数的参数最好不要超过三个
-
ES6在项目中的使用
- 模块化
- 解构赋值
解构对象,合并属性值,复制对象,替代Object.assign - 默认参数
- 箭头函数
使用箭头函数,减少了代码量,尤其是使用数组的一些方法的时候 - let const
- 数组,字符串的新方法
find,some,every,includes等方法 - promise 与 async,await(ES7)
- 模板字符串
用来拼接接口字段和模板
-
async,await 同步的写法进行异步操作
在以往的项目中,进行异步操作的方式大都是写在回调函数里面,比如说是jq的fadeIn,show等函数以及ajax请求方法,这样能够处理异步操作。但是当遇见 A->B->C->D(A,B,C,D函数均包含了异步操作)这种较为复杂的情况,就很容易陷入‘回调地狱’。除此之外,如果A,B,C,D为不同模块的方法,那处理起来就更烦了,大大的降低了代码的可读性。
在新的ES7规范中,就推出了async,await特性,让我们用同步的写法处理异步的操作。对于上面的情况,用async,await的写法如下:
1. async function test(){
await A();
await B();
await C();
await D();
}
2. async function A(){
let res = await getData();
return res.data;
}
async function B(){
let data = await A();
}
异步操作是很容易掉坑里的,当多此进行异步操作的时候,很容易忽略了这个问题,产生了bug.
-
跨组件数据共享与数据通信
因为技术选型的失误,加上看了vuex之后一头雾水,没有使用vue这一数据状态管理模式,导致后来在数据共享和数据同步这方面,写了很多冗余代码。
- 跨组件数据共享
还是以系统中的功能为例。页面中有系统配置的功能,每个人的操作只针对自己的系统配置。在其他地方可以获取‘默认的上播时长’,通过sessonStorage来共享数据。
当需要获取默认上播时长,先看sessionStorage里有没有这个字段,当没有的话再去请求接口数据;
当通过全局设置进行设置的时候,保存之后就会重新写入sessionStorage
-
跨组件数据通信
跨组件通信的类别主要有父子组件通信,子父组件通信,其他组件间通信。前两种就不介绍了,主要是通过prop 向下传递,事件向上传递来通信。在本系统中,使用了** event bus ** 作为事件总线
代码如下:
// eventBus.vue
export default new Vue();
//index.vue
import eventBus from 'eventBus.vue';
eventBus.$on('eventName');
eventBus.$emit('eventName');
通过eventBus可以传递数据,但是有很多问题,在此列举以下,以后使用此方案的时候要慎重考虑下:
- 不同组件间相互调用,可读性差
- 使用eventBus只能传递数据,例如字符串,数字,对象等,但是不能传递方法(function)
- 当使用eventBus触发事件,在事件中进行异步请求,异步请求成功后再告诉事件触发的地方,只能再去触发一个requestSuceess的事件
经验与教训
- 技术选型要慎重。技术选型要听取经验丰富的人的意见,项目中没有使用vuex是很严重的一个失误,在本项目中没有使用vuex主要是因为vuex的学习成本稍微有点高,概念不太清楚所以没使用。
- 新技术的学习。 学习新技术能够开拓视野,提高你技术的广度,提前做好技术储备,比等项目来到手里了再去学习要好的多。本项目中对我来说的新技术有axios,ES6。
- 把控好需求再进行开发。这个是这个项目组没做好的地方之一。由于项目比较复杂以及大家都对导播的这项业务不熟悉,所以导致后期开发的时候很被动。有很多需求是开发过程中才明确的,以及开发过程中反复修改,以及新增了很多需求。其中还包括产品没有和技术同步需求,前后端都开发了,但是音视频那边没有开发功能的需求。
还有就是第一次的需求评审会所有技术都去了,唯独没有叫上前端..... - 改善测试流程。以前做接口比较少的项目没发现,做这个接口很多的项目才感受到前端快成了测试接口的人了。必须在界面上进行了某项操作之后才能去请求接口,这个是很多余且繁琐的操作,很需要改进。
- 学会拒绝。有很多本应该在后端完成的逻辑,但是后端反馈说在后端修改比较麻烦,不做。但是基于用户体验的考虑把这些逻辑在前端处理了,,,后来就越来越麻烦了。
- 新建分支开发。 做长期维护的项目这种技能还是要掌握的