0
点赞
收藏
分享

微信扫一扫

前端优化常见方式

晚熟的猫 2022-01-21 阅读 50
  1. 减少http请求
    避免小资源多次请求,可以将多个资源合并为一个资源,例如js, 因为发送网络请求很耗费时间
  2. css放在头部,js放在body底部
    有利于更快的渲染出页面,css会阻塞js的执行,因为只有cssom创建后才会执行js,而js会阻塞整个渲染进程
    不过js放在头部也可以,但是要想不阻塞渲染的话可以加上defer或者async属性
  3. 使用字体图标代替图片图标
    字体图标较图片图标来说会小很多,节省传输开销
  4. 减少重绘重排
    避免使用js频繁操作样式, 可以使用class替换的方式
    多次对dom操作可以借助documentFragment或者操作display:none的元素, 操作完成后一并添加到或显示
    使用transform和opacity实现动画可减少重绘重排, 二者是有和合成器单独处理的属性。
  5. 在判断语句情况很多时, 避免使用if-else,可以使用switch-case或策略者模式,易于阅读维护
  6. 避免使用定时器设置动画
    定时器设置动画由于需要指定时间,并且对于时间的把控不当会造成页面动画卡顿。
    可以使用requestAnimationFrame来设置动画,前面也已经介绍过这个针对于帧来执行逻辑的api
  7. 对于静态资源,可以放在cdn上
    cdn是一组分布在各地区的服务器,因此可以根据哪个服务器与用户最近使用哪个服务器为用户服务
  8. 可以使用web workers
    对于耗时很长的逻辑,可以使用web workers开启额外的线程去执行,其不会阻塞主线程js代码的执行。
    注意:
    • 有同源限制
    • 无window对象,其全局对象或者this就是它自己
    • 无法访问dom节点
    • 与主线程之间数据的传递是通过拷贝传递的
      简单使用
// 主线程
var worker = new Worker('worker.js') // work线程的URI
worker.postMessage([10, 24])
worker.onmessage = function(e) {
    console.log(e.data)
}

// Worker 线程, worker.js
onmessage = function (e) {
    if (e.data.length > 1) {
        postMessage(e.data[1] - e.data[0])
    }
}
// 停止worker线程可以在主线程调用实例的.close方法
worker.terminate()
// 或者worker调用自身的close()
this.close()
  1. 事件相关

    1. 事件委托
      给多个子元素绑定同一个事件不如直接给父元素绑定事件, 借助事件委托的方式来节省绑定事件的内存开销

    2. 防抖
      频繁触发事件时,例如表单输入检查,只有当停止或暂停输入后一小段时间后才会执行检查,而不去一直输入一直检查
      注意需要绑定一下this

      function debounce(callback, delay) {
        let timer = null
        return function(){
        // 这里需要清除上一次的定时器,一直触发一直清除, 知道停止触发时才绑定定时器执行
          clearTimeout(timer)
          let context = this
          timer = setTimeOut(()=> {
            callback.call(context)
          })
        }
      }
      
    3. 节流
      防抖只针对频繁触发且会暂停会停止的操作, 如果对于一些一直触发并且在不停止时就需要回应的情况下, 比如判断鼠标滚动距离来实现懒加载时, 对鼠标滚动可以使用节流
      实现方式
      定时器
      对定时器定时, 触发时没到时间即定时器还存在就结束, 否则就执行逻辑并且清除定时器供下次重新计时。

      	/*
      	  如果定时器还存在就退出不执行, 不存在的话就绑定一个定时器
      	  绑定的函数会在延迟时间到达后执行, 执行后需要将timer = null
      	  注意不是清除定时器, 而且设置为null, 因为timer保存的定时器的id值
      	  清除了定时器但是id值还是在的
      	*/
      	function throttle(callback){
      	  let timer = null
      	  return function(){
      	    if(timer) return
      	    let context = this
      	    timer = setTimeout(() => {
      	      callback.call(context)
      	      timer = null
      	    })
      	  }
      	}
      	```
      	
      

    时间戳
    时间戳的方式不需要使用定时器, 判断当前时间与上一次执行的时间的差值是否 > 基准值, 若大于则执行并设置基准值为当前时间, 否则结束。

    function throttle(callback, delay){
      let pre = 0
      return function(){
        let now = Date.now()
        if(now - pre > delay) {
          callback.call(this)
          pre = now
        }
      }
    }
    			
    			
    
  2. 图片懒加载
    首先给图片一个资源占位属性,等到图片出现在视口上才开始加载,否则所有图片同时加载会消耗大量网络资源。
    实现懒加载主要两种方式,一个是借助元素的样式属性判断是否到达视口,另一种是使用js封装的api。
    clientHeight,offsetTop,scrollTop实现
    原理:
    clientHeight是容器视口的高度,scrollTop是滚动条高度,两者的和是元素若没有在滚动条里实际到容器视口顶部的距离,而offectTop就是元素到父节点的偏移距离,如果clientHeight + scrollTop ≤ offectTop则说明元素出现在了视口,即可以显示了。
    注意: 借助一个n来保存当前加载的图片的位置, 这样就不用每次都从头循环遍历了

    	<style>
    	  #div{
    	    border: 2px solid black;
    	    height: 500px;
    	    overflow: auto;
    	  }
    	  .div {
    	    margin-bottom: 5px;
    	    background-color: aquamarine;
    	    width: 200px;
    	    height: 300px;
    	  }
    	</style>
    	<body>
    	  <ul id="list">
    	    <li><img data-src="./images/iu.jpg"  src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu1.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu2.png" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu3.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu4.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu5.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu6.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu7.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu8.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu9.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/iu10.jpg" src='./images/lazy.png' alt=""></li>
    	    <li><img data-src="./images/zzf_01.jpg" src='./images/lazy.png' alt=""></li>
    	  </ul>
    	</body>
    
    	
    	let list = document.getElementById('list')
    	let imgs = document.getElementsByTagName('img')
    	let len = imgs.length
    	let n = 0
    	function lazyLoad(){
    	  let clientHeight = list.clientHeight
    	  let scrollTop = list.scrollTop
    	  for(let i = n; i < len; i++) {
    	    let tempSrc = imgs[i].getAttrbute('data-src')
    	    if(imgs[i].offset <= clientHeight + scrollTop ) {
    	      imgs[i].src = tempSrc
    	      n = i // 缓存当前加载到哪里了
    	    }
    	  }
    	}
    	// 使用防抖优化
    	list.addEventListenr('scroll', throttle(lazyLoad, 500))
    	
    

上述可以正常实现, 经过n = i优化后也不至于执行重复之前已经加载的了, 但是对于往返滚动, 还是会执行for循环进行判断
可以使用js的api的IntersectionObserver, 传入元素的话, 其可以观察是否在视口中显示或者移出时会触发相应的事件

/*
  首先创建一个观察对象, 其回调中接收的参数就是被观察的对象,可以是多个,给每个绑定事件
  被观察对象的intersectionRation是一个值, 当出现在视口时, 其>0, 反之
  可以设置当已经触发加载后, 就移除被观察对象里
*/
let obser = new IntersectionObserver((imgs) => {
  for(let i = 0; i < imgs.length; i++) {
    let img = imgs[i]
    if(img.inersectionRatio > 0) {
      img.src = img.getAttrbute('data-src')
      obser.unobserve(img)
    }
  }
})

obser.observe(Array.from(imgs))
  1. webpack相关
    使用缓存
  • 在资源未发生改变时, 直接走缓存, 并用contentHash值拼接文件名, 防止内容改变了也走缓存。

  • babel处理时, 很多兼容性处理的代码并不需要重新构建, 可以开启babel的缓存cacheDirectory: true
    代码压缩

    css压缩使用OptimizeCssAssetsWebpackPlugin 插件

    html压缩使用html-webpack-plugin

    js在生产环境下自动压缩

举报

相关推荐

0 条评论