【Effective Web】页面优化

页面优化

页面渲染流程

JavaScript 》 Style 》 Layout 》 Paint 》 Composite

首先js做了一些逻辑,触发了样式变化,style计算好这些变化后,把影响的dom元素进行重新布局(layout),再画到画布中(Paint),最后把这个画布刷新到屏幕上(Composite),形成一帧。

屏幕的分辨率一般是60Hz,也就是一秒要60帧,1000ms/60约等于16.67ms,加上浏览器内核自身运算也要消耗些时间,一帧差不多就10ms,渲染的过程中上面哪一步耗时长了,这一帧耗时变多,帧率就下降,页面看起来就卡。

上面的步骤不一定都会执行,页面优化可以从这些方面考虑,比如:

  • js只做计算,没有增加删除dom或改变css,后面几步就不会执行。
  • style只改了color/background等不需要重新layout的不用执行layout
  • style改了transform属性,在edge不需要layout和Paint

devtool调试

edge浏览器有个devtool工具,点击performance页签,记录一段时间的浏览器操作,关闭记录按钮就会生成这次操作的详细信息

这种有个小红方块的是渲染时间比较长的帧
在这里插入图片描述

取其中一个帧,可以看到是js执行时间比较长
在这里插入图片描述

拆分代码块

既然一个js 代码块执行太久导致卡顿,那能不能把代码拆分?
我们把代码拆分成几个单元task,每个单元控制执行不超过10ms,建立一个Task类来管理这些task,js每次渲染帧时会调用一个函数requestAnimationFrame,接受一个函数,建立一个任务队列。

// Task定义
  class Task {
    constructor() {
      this.tasks = [];
    }
    addTask(task) {
      this.tasks.push(task);
    }

    draw() {
      let that = this; // 这里用that来代替是避免this指向问题
      window.requestAnimationFrame(function () {
        let tasks = that.tasks;
        if (tasks.length) {
          let task = tasks.shift();
          task();
        }
        window.requestAnimationFrame(function () {
          that.draw.call(that);
        });
      });
    }
  }

使用的时候先创建Task实例,调draw初始化,有任务时addTask添加到队尾,执行任务时调shift执行任务

  // 用单例模式控制mapTask只有一个
  let mapTask = {
    get: function () {
      if (!atask) {
        atask = new Task();
        atask.draw();
      }
      return atask;
    },
    add: function (task) {
      mapTask.get().addTask(task);
    },
  };

这里举个例子,模拟执行时间长的函数

  let fullTask = () => {
    for (let i = 0; i < 5000; i++) {
      console.log("i :>> ", i);
    }
  };

执行时间78.3ms
在这里插入图片描述

把这个函数拆分成很多给子任务去执行

  let subTask = () => {
    let k = 0;
    let sub1 = () => {
      for (let j = 0; j < 100; j++) {
        k++;
        console.log("k :>> ", k);
      }
    };

    for (let i = 0; i < 50; i++) {
      mapTask.add(sub1);
    }
  };

在这里插入图片描述
每个sub1函数不超过10ms,这样大大减少函数执行时长。
在这里插入图片描述

补一个全局的对比
拆分前的:
在这里插入图片描述

拆分后的:
在这里插入图片描述

全部的代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button onclick="fullTask()">fullTask</button>
    <button onclick="subTask()">subTask</button>
  </body>
</html>

<script>
  let fullTask = () => {
    for (let i = 0; i < 5000; i++) {
      console.log("i :>> ", i);
    }
  };

  class Task {
    constructor() {
      this.tasks = [];
    }
    addTask(task) {
      this.tasks.push(task);
    }

    draw() {
      let that = this; // 这里用that来代替是避免this指向问题
      window.requestAnimationFrame(function () {
        let tasks = that.tasks;
        if (tasks.length) {
          let task = tasks.shift();
          task();
        }
        window.requestAnimationFrame(function () {
          that.draw.call(that);
        });
      });
    }
  }

  let atask = null;

  // 用单例模式控制mapTask只有一个
  let mapTask = {
    get: function () {
      if (!atask) {
        atask = new Task();
        atask.draw();
      }
      return atask;
    },
    add: function (task) {
      mapTask.get().addTask(task);
    },
  };

  let subTask = () => {
    let k = 0;
    let sub1 = () => {
      for (let j = 0; j < 100; j++) {
        k++;
        console.log("k :>> ", k);
      }
    };

    for (let i = 0; i < 50; i++) {
      mapTask.add(sub1);
    }
  };
</script>

减少渲染堵塞

避免head标签js堵塞

所有放在head标签的css和js文件都会堵塞渲染,应避免在head标签价加载css和js文件。
可以在js文件加上defer,具有 defer 特性的脚本不会阻塞页面。具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行。
加上async可以实现异步加载,async和defer的区别主要在加载后的处理,下面举一个例子说明:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>defer</title>
  </head>
  <body></body>
</html>

<script src="test1.js" onload="test1()" defer></script>
<script src="test2.js" onload="test2()" defer></script>

<script src="test1.js" onload="test1()" async></script>
<script src="test2.js" onload="test2()" async></script>
// test1.js
var a;
function test1() {
  a = "一个超长的字符串"; // 大概有几m大小,这里省略
  alert(a);
}

// test2.js
var b;
function test2() {
  b = "示例文字";
  alert(b);
}

这里先后引入两个js文件,分别以defer和async的方式导入,然后在onload回调方法,在页面运行:
defer方式:等所有js文件加载完成后执行,按引入的顺序执行,先执行test1(),再执行test2()
在这里插入图片描述

async方式:同时加载js文件,不同的是先加载完的先执行,test2.js更快一些,先执行test2()
在这里插入图片描述

defer特点是按顺序执行,不阻塞html,async的特点是先到先执行,可能阻塞也可能不阻塞html,可能会导致乱序。

优化图片

响应式图片

img的srcset属性可以做响应式图片,浏览器根据屏幕大小,设备像素比dpr自动加载合适的图片

    <img
      srcset="Andreas-Kremer-800x1200.jpeg 1x, Karen-Pape-1800x1200.webp 2x"
      alt=""
      src="Andreas-Kremer-800x1200.jpeg"
    />

压缩和缓存

gzip压缩

nginx的一个配置,可以压缩css文件和js文件

server {
	gzip on;
	gzip_types text/plain application/javascript text/css
}

使用etag

ETag HTTP 响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web 服务器不需要发送完整的响应。而如果内容发生了变化,使用 ETag 有助于防止资源的同时更新相互覆盖(“空中碰撞”)。

etag就是对文件的一个校验和,第一次访问时,响应头里面返回这个文件的etag,浏览器第二次请求时把etag带上,Nginx根据这个etag和请求的文件的etag做对比,如果相同就返回304,说明无需再次传输请求的内容,也就是说可以使用缓存的内容

在nginx.conf中:

etag on;

升级到HTTP/2

HTTP/2的优点是一个域只需要建立一次TCP连接,使用多路复用,传输多个资源,不用进行资源排队,像chrome使用HTTP/1.1 只能一次传输6个资源,而且HTTP/2兼容HTTP1.1,如果遇到0不支持HTTP/2的浏览器,会自动转为HTTP/1.1
HTTP/2需要nginx1.10.0和openssl1.0.2以上版本,在nginx.conf中:

listen 443 ssl http2;

增加用户体验

加Loading

  1. 在需要加载很长时间的地方增加加载中的效果,缓解用户等待的焦急心情。
  2. 在按钮上加个loading图标,表示正在执行,也可以避免频繁点击。

增加进度条

  1. 在下发比较久的AJAX请求时,可以增加进度条,但是这个进度条是假的,因为普通的AJAX请求只有0%和100%,可以先load到进度条的60%-80%的一个随机位置,等到请求完成后再直接到100%。
  2. 上传文件的进度条,上传文件可以返回上传的进度,这个进度是真的,可以做一个真的进度条。

按钮按下

按钮在点击的时候,要给人一个被按下去的感觉,只要改动两个属性就行。

  button {
    background-color: #249dff;
  }
  button:active {
    background-color: #3491df;
    padding-top: 2px;
  }

记住用户使用习惯

  1. 记住位置,在用户拖动页面后,记录当前位置,然后在用户刷新页面后自动跳到刚才的位置,而不是让用户再重新拖动页面。
  2. 记住输入,用户填写表单后,下次使用的时候可以给他自动填充表单,减少操作。

总结

本文从避免页面卡顿,怎么加快页面打开速度,增强用户体验三方面入手,说明如何对页面进行优化。当然不止以上这些方式,这里只做抛砖引玉。更重要的是要站在使用者的角度去思考问题,这样才能做出用户满意的页面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/505578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法】二叉树遍历、判断和 diff 算法

遍历 深度优先遍历 function Node(value) {this.value valuethis.left nullthis.right null }let a new Node(a) let b new Node(b) let c new Node(c) let d new Node(d) let e new Node(e) let f new Node(f) let g new Node(g) a.left c a.right b c.l…

如何提升公众号搜索量?分享内部运营的5步优化技术!

最近一直有自媒体同行朋友在写关于公众号的内容&#xff0c;很多都说公众号现在没得玩了。其实&#xff0c;在运营自媒体上面&#xff0c;思维不通&#xff0c;技术不到位&#xff0c;哪个平台都不适合你玩。 想要在自媒体上面运营变现&#xff0c;一定不要先点击广告变现&…

【二分查找】查找数列中数第一次出现的编号

一道巩固二分查找知识的题&#xff0c;非常简单&#xff0c;一起做一下吧 题目&#xff1a; 答案&#xff1a; #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N1000010;int n,m; int q[N];bool isBlue(int num…

7种2024年算法优化BP,实现回归,单/多变量输入,单/多步预测功能,机器学习预测全家桶再更新!...

截止到本期MATLAB机器学习预测全家桶&#xff0c;一共发了19篇关于机器学习预测代码的文章。算上这一篇&#xff0c;一共20篇&#xff01;参考文章如下&#xff1a; 1.五花八门的机器学习预测&#xff1f;一篇搞定不行吗&#xff1f; 2.机器学习预测全家桶&#xff0c;多步预测…

中文乱码 一文讲解 字符集和字符编码 不再困惑(有源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 这可能是应用级程序员最困惑的…

SSTI 服务器端模板注入(Server-Side Template Injection)

1.Web_python_template_injection {{}}是变量包裹标识符&#xff0c;里面存放的是一个变量&#xff0c;当你输入 http://61.147.171.105:55121/{{8*8}} 执行成功&#xff0c;说明存在模版注入。接下来&#xff0c;开始想办法编代码拿到服务器的控制台权限 。 首先&#xff0c…

Redis 命令行客户端

目 录 redis 客户端介绍 redis 客户端介绍 redis 是一个 客户端-服务器 结构的程序&#xff01;&#xff01;&#xff08;类似于 MySQL&#xff09; 因此 redis 客户端和服务器 可以在同一个主机上&#xff0c;也可以在不同主机上. Redis 的客户端也有很多种形态&#xff1a;…

2024 批量下载吾爱破解公众号文章内容/话题/图片/封面/视频/音频,导出文章pdf合集,excel数据包含阅读数留言数粉丝数

前几天看到吾爱破解论坛公众号文章吾爱破解精华集2023&#xff0c;于是把吾爱破解论坛公众号2022-2023年所有公众号文章也下载做成合集分享给大家&#xff0c;网盘地址https://pan.quark.cn/s/9c1b60b822a7 下载的excel文章数据包含文章日期&#xff0c;文章标题&#xff0c;文…

基于springboot实现图书个性化推荐系统项目【项目源码+论文说明】

基于springboot实现图书个性化推荐系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个图书个性化推荐系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论…

risc-v向量扩展strlen方法学习

riscv向量文档中给出了strlen的实现&#xff0c; 大概是这么一个思路&#xff0c; 加载向量: 使用向量加载指令&#xff08;如 vload&#xff09;从内存中加载一个向量长度的字符。比较向量与零: 使用向量比较指令&#xff08;如 vmask 或 vcmpeq&#xff09;来检查向量中的每…

Js之运算符与表达式

运算符&#xff1a;也叫操作符&#xff0c;是一种符号。通过运算符可以对一个或多个值进行运算&#xff0c;并获取运算结果。 表达式&#xff1a;由数字、运算符、变量的组合&#xff08;组成的式子&#xff09;。 表达式最终都会有一个运算结果&#xff0c;我们将这个结果称…

【电路笔记】-快速了解数字逻辑门

快速了解数字逻辑门 文章目录 快速了解数字逻辑门1、概述2、集成电路的分类3、摩尔定律4、数字逻辑状态5、数字逻辑噪声6、简单的基本数字逻辑门7、基本 TTL 逻辑门8、发射极耦合数字逻辑门9、集成电路的“74”子族10、基本 CMOS 数字逻辑门数字逻辑门是一种电子电路,它根据其…

C++从入门到精通——引用()

C的引用 前言一、C引用概念二、引用特性交换指针引用 三、常引用保证值不变权限的方法权限的放大权限的缩小权限的平移类型转换临时变量 四、引用的使用场景1. 做参数2. 做返回值 五、传值、传引用效率比较值和引用的作为返回值类型的性能比较 六、引用和指针的区别引用和指针的…

动态规划-最长回文子串

动态规划-最长回文子串 原题描述解答中心移动思想代码实现复杂度分析时间复杂度空间复杂度 动态规划思想代码实现复杂度分析时间复杂度空间复杂度 突然觉得很有必要将学过的内容记录下来&#xff0c;这样后续在需要用到的时候就可以避免从头进行学习&#xff0c;而去看自己之前…

调试技巧安全预编译头文件(C++基础)

调试 调试可以选择条件调试和操作调试&#xff1a; 条件调试来选择条件进入断点设置&#xff0c;操作调试来使达到断点条件后完成某些操作&#xff08;一般是output窗口输出&#xff09;。 在这里就只输出了小于6的条件。 安全 降低崩溃、内存泄露、非法访问等问题。 应该转…

GetSystemTimes:获取CPU占用率(WIN API)

原文链接&#xff1a;https://blog.csdn.net/qq_28742901/article/details/104960653 GetSystemTimes函数&#xff1a; BOOL WINAPI GetSystemTimes(__out_opt LPFILETIME lpIdleTime, // 空闲时间__out_opt LPFILETIME lpKernelTime, // 内核进程占用时间__out_opt LPFILETI…

【JavaWeb】Day29.SpringBootWeb请求响应——请求(二)

请求响应 4.数组集合参数 数组集合参数的使用场景&#xff1a;在HTML的表单中&#xff0c;有一个表单项是支持多选的(复选框)&#xff0c;可以提交选择的多个值。 4.1 数组 数组参数&#xff1a;请求参数名与形参数组名称相同且请求参数为多个&#xff0c;定义数组类型形参即…

C++取经之路(其一)——namespace(命名空间),cout,cin(输入输出流),缺省参数。

目录 目录&#xff1a; 前言&#xff1a; namespace(命名空间): 命名空间可以嵌套使用如&#xff1a; 相同的命名空间 cout cin输入输出 std命名空间的使用惯例&#xff1a; 缺省参数&#xff1a; 缺省类型&#xff1a; 前言&#xff1a; 最近开始学习C了&#xff0c;…

Web 前端性能优化之二:图像优化

1、图像优化 HTTP Archive上的数据显示&#xff0c;网站传输的数据中&#xff0c;60%的资源都是由各种图像文件组成的。 **图像资源优化的根本思想&#xff0c;可以归结为两个字&#xff1a;压缩。**无论是选取何种图像的文件格式&#xff0c;还是针对同一种格式压缩至更小的…

两种序列化的方式:fastjson 和 Jackson

public class TestMain {public static void main(String[] args) throws JsonProcessingException {//创建一个课表对象LearningLesson lesson new LearningLesson();lesson.setId(1L);lesson.setCourseId(2L);lesson.setStatus(LessonStatus.EXPIRED); //课程状态&#xff0…