美文网首页
性能优化

性能优化

作者: 泡杯感冒灵 | 来源:发表于2020-07-23 16:37 被阅读0次

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

    性能优化原则

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

    从何入手

    让加载更快
    • 减少资源体积,压缩代码
      以webpack为例,当我们把webpack的mode设置为production模式时,打包出来的代码就是经过压缩的,体积也会减少很多

    • 减少访问次数,合并代码,SSR服务端渲染,缓存
      还是以webpack为例,我们写代码的时候,可能会有很多JS,但是经过打包后,js文件被整合到一个里边,大大减少了请求次数;当然雪碧图,也是减少访问次数的一种方式。


      image.png

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

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

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

    让渲染更快
    • css放在head里边,JS放在body最下边
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载 (图片懒加载,上滑加载更多)
      懒加载原理:页面有很多img图片,正常情况下会发起很多次请求,耗费性能。这个时候,可以给img的src设置同一个预览图片,然后再给img设置一个自定义属性,比如叫data-realsrc,把真正要显示的图片地址赋值给这个属性,然后监听img元素出现在视口的时候,就把data-realsrc的图片链接赋值给src属性。就实现了懒加载
    • 对DOM查询进行缓存(DOM操作是比较耗费性能的)
    • 频繁的DOM操作,合并到一起插入DOM结构
    • 节流(throttle),防抖(debounce)
    防抖(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对象。

    相关文章

      网友评论

          本文标题:性能优化

          本文链接:https://www.haomeiwen.com/subject/rxaclktx.html