如何快速开发一个自定义的视频播放器(5)——代码优化

代码优化

无论是前端代码,还是后端代码,优化的目的主要就两方面,一是健壮性,二是可读性。前者主要是让我们的代码性能安全性等等尽量不那么拉,后者主要让我们的代码简单易读,便于理解,方便团队合作和代码维护。本篇主要基于这两块对我们的代码进行进一步的优化,涉及一些前端编码或者后端编码常用的手段。

sfc的一些优点

个人是比较喜欢vue的sfc模式的,感觉它还是比较符合前端新手的学习路径,适合刚接触前端的同学们进行学习。因为按正常的前端学习路径,一般是先学html然后css再才是javascript,然后自己可以根据html和css来写一些静态网页,然后学javascript让网页动起来,最后再学ajax和服务端进行联调,让页面能够动起来。最后到实际开发中,学一点切图的小技巧,再快速上手vue,基本一个可用的牛马就培养好了。我想这也是vue的培训行业比较热,使用的小成本企业比较多的根本原因。

sfc,基本就是html+css+js的模式,而且script的模块构造的数据类型和生命周期也是比较偏面相对象,符合正常人的思维习惯。所以如果有一点面相对象思维,入手vue不要太容易。

引用

js的数据类别大体分为两类:常量和引用。我们在使用引用的时候是需要注意一些事情的。比如我们的代码:

javascript 复制代码
 TouchMove(e) {
      this.video.target.pause()
      var pageX = e.touches[0].pageX
      var idistance = pageX - this.video.istartX
      var moveTime =
        (idistance / this.$refs.progress.offsetWidth) * this.video.duration
      // set left
      var left = this.video.istartX + idistance - this.video.istartOffsetX
      const maxx =
        this.$refs.progress.offsetLeft +
        this.$refs.progress.offsetWidth -
        this.$refs.indicator.offsetWidth / 2
      const minx =
        this.$refs.progress.offsetLeft - this.$refs.indicator.offsetWidth / 2
      if (left <= maxx && left >= minx) {
        var currentTime = moveTime + this.video.currentTime
        this.video.offsetCurrentTime = currentTime
        this.video.progress = (currentTime * 100) / this.video.duration
        this.$refs.progressCurrentLine.style.width = `${this.video.progress}%`
        var transformx =
          (currentTime * this.$refs.progress.offsetWidth) /
            this.video.duration -
          this.$refs.indicator.offsetWidth / 2
        this.$refs.indicator.style.transform = `translateY(-50%) translateX(${transformx}px)`
      }
    }

这个函数是在我们拖动的时候调用的,实际上在拖动的过程中1s可能会调用5-6次,可以看到我们在代码中直接调用了许多ref对应的属性,让代码看起来很乱很复杂,而这些属性比如this.refs.progress.offsetLeft和this.refs.indicator.offsetWidth在后续的计算中多次出现。特别的在js引擎中我们访问多层级的对象属性实际上是一个开销比较大的动作,了解原型链的朋友们可能知道javaScript引擎读取多层级链式对象属性的过程被称为属性查找或属性访问。 比如我们访问a.b.c.d,以下是这个过程的简单概述:

识别对象:首先,JavaScript引擎会确定你正在尝试访问的对象。在表达式a.b.c.d中,a就是这个对象。
查找属性:接着,JavaScript引擎会在对象的自身属性中查找你正在访问的属性。在a.b.c.d中,引擎首先会查找a的b属性。
原型链查找:如果在对象自身的属性中找不到指定的属性,JavaScript引擎会沿着对象的原型链向上查找。原型链是一种链接对象和其原型的机制,它允许对象共享其原型的属性和方法。如果b不是a的自身属性,但是b是a的原型或原型链上的一个属性,那么b就会被找到。
递归查找:如果找到了属性,并且这个属性的值是一个对象,那么JavaScript引擎会递归地进行属性查找。在a.b.c.d中,如果b是一个对象,那么引擎会在b中查找c,然后在c中查找d。
返回结果:如果在对象自身或其原型链上找到了指定的属性,那么这个属性的值就会被返回。如果在整个查找过程中都没有找到指定的属性,那么返回值就会是undefined。

在这个过程中,每一次属性查找都可能涉及到对象自身属性的查找和原型链的遍历,所以多层级链式对象属性的查找可能会比单层级的属性查找要慢。 因此针对于这一点,我们可以把上诉代码优化为:

javascript 复制代码
// 首先我们定义变量
// 进度条宽度
let progressWidth = 0
// 进度条左偏移
let progressLeft = 0
// 指示器宽度
let indicatorWidth = 0

// 初始化我们的常量
progressLeft = this.$refs.progress.offsetLeft
progressWidth = this.$refs.progress.offsetWidth
indicatorWidth = this.$refs.indicator.offsetWidth

// 优化后的代码
TouchMove(e) {
  this.video.target.pause()
  var pageX = e.touches[0].pageX
  var idistance = pageX - this.video.istartX
  var moveTime = (idistance / progressWidth) * this.video.duration
  // set left
  var left = this.video.istartX + idistance - this.video.istartOffsetX
  const maxx = progressLeft + progressWidth - indicatorWidth / 2
  const minx = progressLeft - indicatorWidth / 2
  if (left <= maxx && left >= minx) {
    var currentTime = moveTime + this.video.currentTime
    this.video.offsetCurrentTime = currentTime
    this.video.progress = (currentTime * 100) / this.video.duration
    this.$refs.progressCurrentLine.style.width = `${this.video.progress}%`
    var transformx =
      (currentTime * progressWidth) / this.video.duration -
      indicatorWidth / 2
    this.$refs.indicator.style.transform = `translateY(-50%) translateX(${transformx}px)`
  }
}   

可以看到我们的简洁了许多,同时在性能方面也要比之前有许多提升。

重复调用

如果站在用户的角度上,当他们遇到一个提交按钮时,在填写完需要的提交信息后,肯定是点点点,而不是只点一下。这个时候如果没有做到防抖,那么我们可能经常发现重复提交的信息。防抖其实就是防止这种疯狂的事情。具体的概念,这里就不深入讲。我们的TouchMove方法里面也有类似的场景,比如第一行代码:

javascript 复制代码
  this.video.target.pause()

实际上在拖动的时候,它被执行了n遍,本意上我们也是为了防止一些异常情况导致拖动的时候video忽然就播放了。但是我们是可以拿到video的播放状态的。因此这里我们可以做如下优化:

javascript 复制代码
  if (!this.video.target.paused) {
    this.video.target.pause()
  }

这其实也是一种防抖效果,不过没有使用到timer

抽象方法

在coding的时候,一开始遇到进入心流状态时,我们代码往往写的飞起,也常有一些自己cv自己的时刻,这种感觉很好。但是如果有抽象方法的意愿,这时候应该在心里埋下这样一个暗示,等项目搞完有时间要把这些内容优化一下。遇到重复的逻辑,一般要把它抽象为一个方法,这个不管是前端开发还是服务端开发都是不可缺少的编码习惯。比如我们的代码中关于进度计算的逻辑在ontimeupdate和TouchMove中就有重复的逻辑,我们就可以抽象出一个计算进度的方法,只需传入currentTime:

javascript 复制代码
    SetProgress(currentTime) {
      this.video.progress = (currentTime * 100) / this.video.duration
      this.$refs.progressCurrentLine.style.width = `${this.video.progress}%`
      var transformx =
        (currentTime * progressWidth) / this.video.duration - indicatorWidth / 2
      this.$refs.indicator.style.transform = `translateY(-50%) translateX(${transformx}px)`
    },

那么我们的代码又可优化为:

javascript 复制代码
   video.ontimeupdate = () => {
        var ctime = +video.currentTime.toFixed(2)
        this.video.currentTime = ctime
        this.video.currentTimeString = this.GetTime(ctime)
        // 更新当前进度
        this.SetProgress(ctime)
        if (video.currentTime >= this.video.duration) {
          this.video.status = 'stop'
        }
      }
      
    TouchMove(e) {
      if (!this.video.target.paused) {
        this.video.target.pause()
      }
      var pageX = e.touches[0].pageX
      var idistance = pageX - this.video.istartX
      var moveTime = (idistance / progressWidth) * this.video.duration
      // set left
      var left = this.video.istartX + idistance - this.video.istartOffsetX
      const maxx = progressLeft + progressWidth - indicatorWidth / 2
      const minx = progressLeft - indicatorWidth / 2
      if (left <= maxx && left >= minx) {
        var currentTime = moveTime + this.video.currentTime
        this.video.offsetCurrentTime = currentTime
        this.SetProgress(currentTime)
      }
    },

同理,关于全屏事件在微信端的处理逻辑同样可以进行这样的优化,这里就不再赘述。

总结

代码优化实际上有很多方式,读读一些经典的书,一些经典的源码,做一些经典的算法题,我们所有的优化方式都是从学习的过程中获取,优雅的代码生产方式才是一个coder能够窥见的宝藏。当我们的demo运行没问题同时我们的代码也都ok之后,我们需要进一步考虑我们代码的易用性,而易用的最简单的方式就是组件化。那么后续的几篇都会讲到如何把我们的代码封装为易用且多端一致的组件。下一篇,简单的vue组件。

相关推荐
夏花里的尘埃2 小时前
vue3实现echarts——小demo
前端·vue.js·echarts
努力学习的木子3 小时前
uniapp如何隐藏默认的页面头部导航栏,uniapp开发小程序如何隐藏默认的页面头部导航栏
前端·小程序·uni-app
java小郭5 小时前
html的浮动作用详解
前端·html
水星记_6 小时前
echarts-wordcloud:打造个性化词云库
前端·vue
强迫老板HelloWord6 小时前
前端JS特效第22波:jQuery滑动手风琴内容切换特效
前端·javascript·jquery
luanluan88887 小时前
维护el-table列,循环生成el-table
javascript·vue.js·ecmascript·element plus
续亮~7 小时前
9、程序化创意
前端·javascript·人工智能
RainbowFish8 小时前
「Vue学习之路」—— vue的常用指令
前端·vue.js
Wang's Blog8 小时前
Webpack: 三种Chunk产物的打包逻辑
前端·webpack·node.js
pan_junbiao8 小时前
HTML5使用<blockquote>标签:段落缩进
前端·html·html5