优化

262 阅读10分钟

优化:

防抖和节流的相同点:

防抖和节流都是为了解决短时间内大量触发某函数而导致的**性能问题,**比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象

防抖:

只会执行最后一次

应用场景:

(1) 用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次的查询ajax请求,这样可以有效减少请求次数,节约请求资源;

(2) window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖让其只触发一次;)

(3)顾名思义,防止用户手抖,或者用户疯狂点击,只会执行最后一次

优化前代码:(如果用户不断点击,这样事件触发就不断延后,但这样的做法始终还是有他的不合理性)

function debounce(fn,wait){
    var timer = null;
    return function(){
        if(timer !== null){
            clearTimeout(timer);
        }
        timer = setTimeout(fn,wait);
    }
}

function handle(){
    console.log(Math.random());
}

window.addEventListener("resize",debounce(handle,1000));

优化后代码:(这样的话事件触发就不会延后了,并且也起到了防抖的效果。

需要防抖的场景还有很多,比如resize,onscroll,click,input等。

function debounce(fn,wait){
 2     var timer = null;
 3     return function(){
 4         if(timer === null){
 5             timer = setTimeout(()=>{
 6                 fn()
 7                 clearTimeout(timer)
 8                 timer = null
 9             },wait)
10         }
11     }    
12 }
13     
14 function handle(){
15     console.log(Math.random());
16 }
17     
18 window.addEventListener("click",debounce(handle,1000))

节流:

不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

应用场景:

(1)鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;

(2)在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据;

(3)监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断;

function throttle(fn,delay){
 2     let valid = true
 3     return function() {
 4        if(!valid){
 5            //休息时间 暂不接客
 6            return false 
 7        }
 8        // 工作时间,执行函数并且在间隔期内把状态位设为无效
 9         valid = false
10         setTimeout(() => {
11             fn()
12             valid = true;
13         }, delay)
14     }
15 }
16 /* 请注意,节流函数并不止上面这种实现方案,
17    例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
18    也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
19     */
20 
21 function showTop  () {
22     var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
23   console.log('滚动条位置:' + scrollTop);
24 }
25 window.onscroll = throttle(showTop,1000) 

图片懒加载原理:

说明:

懒加载”说白了就是延迟加载,比如我们加载一个页面,这个页面很长很长,长到我们的浏览器可视区域装不下,那么懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。

目的:

1.全部加载的话会影响用户体验,如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了

2.浪费用户的流量,有些用户并不想全部看完,全部加载会耗费大量流量。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <style>
    img {
      display: block;
      margin-bottom: 50px;
      width: 400px;
      height: 400px;
    }
  </style>
</head>
<body>
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <img src="Go.png" data-src="./lifecycle.jpeg" alt="">
  <script>
    let num = document.getElementsByTagName('img').length;
    let img = document.getElementsByTagName("img");
    let n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历

    lazyload(); //页面载入完毕加载可是区域内的图片

    window.onscroll = lazyload;

    function lazyload() { //监听页面滚动事件
      let seeHeight = document.documentElement.clientHeight; //可见区域高度
      let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
      for (let i = n; i < num; i++) {
        if (img[i].offsetTop < seeHeight + scrollTop) {
          if (img[i].getAttribute("src") == "Go.png") {
            img[i].src = img[i].getAttribute("data-src");
          }
          n = i + 1;
        }
      }
    }
  </script>

</body>

</html>

减少HTTP请求,合理设置HTTP缓存:

一个完整的请求都需要经过 DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个 “漫长” 而复杂的过程。时间成本就是用户需要看到或者 “感受” 到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。另外,由于浏览器进行并发请求的请求数是有上限的,因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。

资源合并与压缩:

尽可能的将外部的脚本、样式进行合并,多个合为一个。另外, CSS、 Javascript、Image 都可以用相应的工具进行压缩,压缩后往往能省下不少空间。

CSS Sprites:

合并 CSS图片,减少请求数的又一个好办法。

利用CDN加速:

本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据。CDN缓存的一般是静态资源,如图片、文件、CSS、script脚本、静态网页等,但是这些文件访问频度很高,将其缓存在CDN可极大改善网页的打开速度。

先引入CSS再引入JS:

  • js代码的下载是阻塞下载,不可以和其他代码并行下载和解析;CSS的加载不会阻塞DOM树的解析
  • 页面加载时,是按照从上到下,从左到右的顺序加载的。如果将js放在前面,会立即执行,阻塞后续的资源下载和执行。如果外部脚本加载时间过长,会造成网页长时间失去响应,浏览器就会呈现“假死”状态(阻塞效应)
  • 页面需要等到head中的js和css加载完成之后才开始绘制,当js放在body最后时,不需要等待,可以避免资源阻塞,同时使静态页面迅速显示
  • 部分js的执行依赖于前面的样式。
  • js一般是处理功能,所以不需要提前加载。先给用户观感,在给用户上手体验。

浏览器重绘(Repaint)和回流(Reflow):

回流必将引起重绘,重绘不一定会引起回流

重绘(Repaint):

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

回流(Reflow):

当 Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。

会导致回流的操作:

* 页面首次渲染
* 浏览器窗口大小发生改变
* 元素尺寸或位置发生改变元素内容变化(文字数量或图片大小等等)
* 元素字体大小变化
* 添加或者删除可见的 DOM 元素
* 激活 CSS 伪类(例如:hover)
* 查询某些属性或调用某些方法
* 一些常用且会导致回流的属性和方法
clientWidth、clientHeight、clientTop、clientLeftoffsetWidth、offsetHeight、offsetTop、offsetLeftscrollWidth、scrollHeight、scrollTop、scrollLeftscrollIntoView()、scrollIntoViewIfNeeded()、getComputedStyle()、
getBoundingClientRect()、scrollTo()

JS性能优化(事件委托):

减少dom操作,在js中,添加到页面上的事件处理程序数量会直接关系到页面整体运行运行性能。导致这一问题的原因是多方面的。首先函数都是对象,都会占用内存;内存中对象越多,性能就越差。

//html代码
<ul id="myLinks">
    <li id="goSomeWhere">Go somewhere</li>
    <li id="sayHi">Say hi</hi>
</ul>

//分别加上事件处理-JS代码
let item1 = document.getElementById("goSomeWhere");
let item2 = document.getElementById("sayHi");

EventUtil.addHandler(item1, "click", function(event){
  console.log("goSomeWhere")  
}

EventUtil.addHandler(item2, "click", function(event){
  console.log("sayHi");  
}

//改善点即将click事件结合在一起
let list = document.getElementById("myLinks")

EventUtil.addHandler(list, "click", function(event){
  event = EventUtil.getEvent(event);
  let target = EventUtil.getTarget(event);

  switch(target.id){
    case "goSomeWhere":
      console.log("goSomeWhere");
      break;
   case "sayHi":
      console.log("sayHi");    
      break;            
  }     
}

JS性能优化(作用域链):

访问当前作用域之外的变量时间会增加,所以 访问全局变量总是比访问局部变量要慢,因为需要遍历作用域链。只要能减少花费在作用域链上的时间,就能增加脚本的整体性能。

浅谈SPA, SEO, SSR:

  • SPA全称 single page application:

即单页面应用,在传统的网站中,不同的页面之间的切换都是直接从服务器加载一整个新的页面,而在SPA这个模型中,是通过动态地重写页面的部分与用户交互,而避免了过多的数据交换,响应速度自然相对更高,常见的SPA框架有react vue angluar。一般是将框架及网站页面代码发送到浏览器,然后在浏览器中生成和操作DOM(这里也是第一次访问SPA网站在同等带宽及网络延迟下比传统的在后端生成HTML发送到浏览器要更慢的主要原因)

优点:

页面之间的切换非常快,一定程度上减少了后端服务器的压力(不用管页面逻辑和渲染),后端程序只需要提供API,完全不用管客户端到底是Web界面还是手机等。

缺点:

首屏打开速度很慢,因为用户首次加载需要先下载SPA框架及应用程序的代码,然后再渲染页面。不利于SEO

  • 搜索引擎优化SEO(Search Engine Optimization):

seo是通过总结搜索引擎的排名规律,对网站进行合理的优化是你的网站在搜索引擎的排名提高,从而利用搜索引擎给自己带来客户。优化:

(1)当前页面的title上出现这个关键字;

(2)当前页面的keywords和description中出现这个关键字;

(3)当前页面的内容里多次出现这个关键词,并且在第一次出现的时候,加粗;

(4)其他页面的锚文本里,出现这个关键词。

  • SSR Server-Side Rendering(服务器端渲染):

例子juejin.cn/post/684490…

常用的SSR框架有:React 的 Next,Vue.js 的 Nuxt 。因为SPA不利于SEO,因为搜索引擎对于异步数据的支持也还不足,如果你的应用程序初始展示 loading 菊花图,然后通过 Ajax 获取内容,抓取工具并不会等待异步完成后再行抓取页面内容。所以有了SSR:将SPA应用打包到服务器上,在服务器上渲染出HTML,发送到浏览器

优点:

更快的响应时间,不用等待所有的JS都下载完成,浏览器便能显示比较完整的页面了。我们可以将SEO的关键信息直接在后台就渲染成HTML,而保证搜索引擎的爬虫都能爬取到关键数据

缺点:

相对于仅仅需要提供静态文件的服务器,SSR中使用的渲染程序自然会占用更多的CPU和内存资源; 一些常用的浏览器API可能无法正常使用,比如windowdocmentalert等,如果使用的话需要对运行的环境加以判断; 开发调试会有一些麻烦,因为涉及了浏览器及服务器,对于SPA的一些组件的生命周期的管理会变得复杂; 可能会由于某些因素导致服务器端渲染的结果与浏览器端的结果不一致。