性能优化

2020-07-23  本文已影响0人  泡杯感冒灵

这是一个综合性问题,并没有标准的答案,只能尽量全面的总结

性能优化原则

  1. 所使用内存,缓存或其他办法
  2. 减少CPU计算量,减少网络加载耗时
  3. 适用于所有编程的性能优化,空间换时间

从何入手

让加载更快

缓存也可以减少访问次数,比如本来要请求10次,但是其中4个可以从缓存获取,那就请求6次就可以了,当然,前提是要命中缓存。

image.png
image.png
当我们用webpack打包时,输出文件名配置了contenthash,这个时候会根据文件内容生成hash值,拼接到文件名里。之所以这样配置,就是为了缓存:
  1. 静态资源加hash后缀,根据文件内容计算hash
  2. 文件内容不变,则hash不变,hash不变则url不变
  3. url和文件不变,则会自动触发http缓存机制,返回304

SSR服务端渲染:将网页和数据一起加载一起渲染
非SSR前后端分离 先加载网页,网页的ajax再去请求数据,服务器返回数据后,再渲染数据
简单的说,就是把原服务器上的数据,赋值到其他服务器上,用户访问时,哪那台服务器离用户近,就访问哪台服务器上的数据,工作中我们经常把静态文件通过CDN引入,比如js,css,图片等
JSP,ASP,PHP都是SSR,现在的vue react SSR

让渲染更快
防抖(debounce)

概念:不管触发频率多大,都在停止触发后的给定时间触发(涉及到定时器的使用)
场景:监听一个输入框,输入内容变化后,触发keyup事件,如果输入速度很快,会频繁触发keyup事件。这不是我们想要的。
应用:我们的目标是,最后一次输入后500毫秒内,没有再次输入,也就是没有再次触发keyup事件,才去进行一些操作,比如发请求,具体实现如下:

<input type="text" id="input1">
<script>
const input1 = document.getElementById('input1')
let timer = null;
input1.addEventListener('keyup', function () {
  if (timer) {
    clearTimeout(timer)
  }
  timer = setTimeout(() => {
    console.log(input1.value)
    // 如果过了500毫秒,没有再触发keyup,则计时器内的内容会被执行,执行完之后,计时器就可以取消了
    timer = null
  },500)
})
</script>

// 当然,这只是一个input,如果有很多input,每个都像上边那样写很多代码,是很浪费的,我们可以封装一下debounce
// 防抖封装
function debounce(fn, delay = 500) {
  // timer 是在闭包中的,也是在debounce作用域中
  // timer 是不对外暴露的,不能被别人修改
  let timer = null 
  return function () {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(function () {
      fn.apply(this, arguments)
      timer = null
    },delay)
  }
}

// debounce的使用
input1.addEventListener('keyup', debounce(function () {
  console.log(input1.value)
},600))

节流(throttle)

概念:不管触发频率多大,都以恒定频率触发(涉及到定时器的使用)
场景:当拖拽一个元素时,要随时拿到该元素的位置。直接用drag事件,则会频繁触发,很容易导致卡顿。
应用:使用节流,无论拖拽速度多块,都每隔100ms触发一次

const div1 = document.getElementById('div1')
let timer = null

div1.addEventListener('drag', function (e) {
  if (timer) {
    return 
  }
  timer = setTimeout(function () {
    console.log(e.offsetX, e.offsetY)
    timer = null
  },100)
})  

// 节流函数工具化
function throttle(fn, delay = 100) {
  let timer = null
  return function () {
    if (timer) {
      return 
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
      timer = null
    },delay)
  }
}
div1.addEventListener('drag', throttle(function (e) {
  console.log(e.offsetX, e.offsetY)
},200))
无论是防抖还是节流,我们封装的时候,都用了fn.apply(this,arguments),原因如下

那节流为例,当我们调用throttle工具函数的时候,我们给throttle函数传入了一个函数作为第一个参数。而这个函数里还有一个e作为参数,这个e是event事件对象。我们通过事件对象去获取offsetX和offsetY。


image.png

注意正常情况下,当我们触发了事件,然后事件对象会被作为参数传给事件处理程序,这个时候的function里的e是直接被传进去的。因为可以直接用e


image.png

而当我们使用throttle的时候,我们给throttle传了一个function(e){xxx}函数作为fn,这个函数里是接收不到e对象的,因为e这个时候会传给throttle的返回函数。实际情况也是这样,我们打印arguments里可以看到 dragEvent


image.png
然后,我们只有通过fn.apply把reutrn 函数的argument换给fn,才可以在fn里获取到e对象。
上一篇 下一篇

猜你喜欢

热点阅读