整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

前端性能优化(四)-网页加载更快的N种方式

站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户。除此之外,前端优化得好,还可以为企业节约成本。那么我们应该如何对我们前端的页面进行性能优化呢?

前端性能优化可以分为三个方面:接口访问优化、静态资源优化和页面渲染速度优化。

一、接口访问优化

1.1、减少http请求,合理设置 HTTP缓存

http协议是无状态的应用层协议,每次发送http请求时,都需要建立连接、通信、断开连接,在服务器端每个http都需要开启独立的线程去处理。所以尽量减少http请求,尽可能地提高访问性能。

减少http请求的方法:

  1. 合并 js、css、图片等文件,合并成一个文件,浏览器就只需请求一次就可以了。图片合并要适当,不能想着优化呢,盲目地都合并成一张图片。
  2. 借用浏览器缓存。恰当的缓存设置可以大大减少http请求。不懂浏览器缓存的可参考《浏览器九大缓存方法》。
  3. 接口合并。前端交互,经常需要请求多个并行或串行接口,此时可以通过接口合并方式,提高接口访问速度。
  4. 能使用css的尽量不使用js,能使用js的尽量不用三方插件,避免三方插件大量的库。

1.2、减少cookie传输

cookie 存在于 http 头,在客户端与服务器之间交换,尽可能地控制 cookie 的大小,cookie越小,响应速度越快,减少 cookie 传输,响应速度更快。

1.3、使用CDN提供静态文件

使用 CDN 可以更快地在全球范围内获取到你的静态文件,加快网页加载。

1.4、启用 GZIP 压缩

http 协议上 GZIP 编码,是一种用来改进 web 应用程序的。开启 GZIP 后,服务器会把网页内容压缩后传输,一般能压缩到原大小40%,这样网页传输速度就更快了。GZIP 有两大好处:一是减少存储空间,二是通过网络传输文件时,可以减少传输时间。

1.5、分域存放资源

HTTP 客户端一般对同一个服务器的并发连接个数都是有限制的,通常最大并行连接为四了,剩下的会进入等待队列,等前边的执行完毕,等待的才会执行。所以利用多域名主机存放资源,增加并行连接量,缩短资源加载时间。

1.6、减少页面重定向

开启 https 可以有效防范攻击,保证用户始终访问到网站的加密连接,保护数据安全,同时省去 301/302 跳转的时间,大大提升网站的安全系数和用户体验。

如果在网站设置当用户访问域名的时候强制 https 进行 301 或者 302 跳转,但是这个过程中,用到 HTTP 因此容易发生劫持,受到第三方的攻击。所以尽可能使用https安全。

1.7、避免使用iframe

iframe 相当于本页面又嵌套了一个页面,消耗性能,还要加载嵌套页面的资源,所以更消耗时间。

1.8、借用浏览器缓存

ajax 请求到的数据,可以缓存到浏览器,下次使用的时候无需再次获取,直接取缓存数据就可以。这个会根据具体的项目来做,比如常用的角色类型就会缓存,获取到的普通数据为了保证实时性,不能使用缓存。

二、静态资源优化

2.1、压缩 html、css、js 等文件

删除不必要的空格、注释和中行,减少文件大小,显著减少用户下载时间,加快网页加载速度。可以直接使用压缩工具,可以自动删除所有不必要内容。

2.2、在 js 之前引用 css

这是一个小细节,js 执行的时候会进入阻塞,如果放入 js 之后加载,会等待 js 执行完成之后才能加载 css,渲染页面,此时就会出现布局错乱。所以 css 文件需要非阻塞引入,以防DOM 花费更多时间才能渲染。

2.3、非阻塞 js

js 会阻止 html 文档的正常解析,当解析器到达 script 标记时,它会停止解析并执行脚本。所以我们经常把 script 引入的 js,放到 html 中最底下。如果需要让脚本位于页面顶部,建议添加非阻塞属性。经常使用 defer 和 async 来异步加载js文件。

<!--  使用defer  -->
<script defer src="foo.js" ></script>
<!--   使用async  -->
<script async src="foo.js"></script>

2.4、图片压缩

最常见的就是 css 雪碧,就是将很多很多的小图标放在一张图片上,就称为雪碧图。雪碧图最大优点就是可以减少http请求,除此也能压缩图片文件大小。使用的时候,通过设置 background-position ,移动图片的位置。除此之外,网站用到的大图,也需要在保证图片质量前提下优化到最小。

2.5、矢量图替代位图

矢量图(SVG)往往比图像小很多,缩放的时候不失真,这些图像还可以通过 css 进行动画和修改,比位图方便控制。可以的话,尽量用矢量图多点。

2.6、js代码相关优化

  1. 尽量减少使用闭包,因为闭包所在的上下文不会被释放。
  2. js避免嵌套循环和死循环,一旦遇到死循环,浏览器会卡死。
  3. 在js封装过程中,尽量做到低耦合高内聚。减少页面的冗余代码。
  4. 尽量减少递归,避免死递归。
  5. 尽量使用window.requestAnimationFrame替代传统的定时器。

三、页面渲染速度

3.1、懒加载

素材类的网站,页面一屏展示很多图片,而且图片还不能失真,图片加载太多,网页加载慢得很,所以就引用懒加载,只加载可视区的图片,避免加载可以能不需要或不必要的图像。改善页面的响应时间。

3.2、避免响应式布局

响应式网站虽然能够兼容所有终端设备,但是会出现隐藏部分无用内容,浪费带宽,加载时间还长,页面的渲染时间也长。想更多了解响应式布局,请点击《前端响应式布局为什么是个坑?》。

3.3、设置大小,避免重绘

遇到 img 标签,会立马发送一个 http 请求,下载图片,页面继续向下渲染,等图片加载成功了,发现图片的宽高大小发生变化,影响后边排版,所以页面会重新再绘制一次这部分。所以尽可能设置图片的大小。

3.4、减少DOM元素

解析 html 内容,将标签转化为DOM节点,之后再解析其他文件,DOM元素越少,也就是标签越少,文件转化得越快,加载速度也就快了。

3.5、减少 Flash 的使用

flash 文件比较大,加载起来耗时。除此,flash 插件还需要运行才能运行,最主要有些浏览器flash插件马上要下线了,建议尽量不用 flash。

3.6、文件顺序

css文件放在最顶部,优先渲染。js放在最底部,避免阻塞。

让网页如何加载更快,有好多的细节,还是要好好提升自己的技能~~~~~~~~~


点个关注!更多分享内容。

做过前端开发都知道前端的工作内容是很多的,对于HTML、CSS、Javascript、Image、Flash等各种内容的使用。为了更好提升应用的性能,我们需要对各种资源内容进行不同方面的优化。

对用户而言,优化可以让应用的响应速度加快,加载更加迅速,可以带来更好的使用体验。

对于服务商而言,前端优化能够减少页面请求数量,宽带所占带宽,有效的节省资源。

前端优化的内容很多,按照粒度等级划分可以大致分为两类:页面优化级别和代码级别优化。

页面优化主要针对页面加载环节,包括:HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等内容。代码优化包括:Javascript中的DOM操作优化、CSS选择符优化、图片优化以及HTML结构优化等内容。

代码级别优化则更关注数据请求,很重要的一条就是减少HTTP请求的数量。一个完整的HTTP请求需要经过路由查找,TCP握手,发送请求,服务器响应和浏览器接收等一些列过程。对于小文件,实际下载文件的时间对于整个请求的时间占比很低,因此需要将小文件合并为大文件来传输。

页面级别:提升页面加载速度

加载优化是为了解决页面内容加载速度受限于网络带宽,过于耗时的问题,主要手段有:

项目打包优化

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。通常我们使用Webpack将多种静态资源js、css、less 转换成一个静态文件,减少了页面的请求。

核心概念有:

Output:告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。

Module:Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。

Chunk:一个 Chunk 由多个模块组合而成,用于代码合并与分割。

Loader:loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

Plugin:被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。

雪碧图(CSS Sprite)

CSS雪碧 即CSS Sprite,也有人叫它CSS精灵,是一种CSS图像合并技术,该方法是将小图标和背景图像合并到一张图片上,然后利用css的背景定位来显示需要显示的图片部分。

雪碧图实现的基本原理是把我们从网上用到图片整合在同一张图片中,从而可以减少网站HTTP的请求数量。这一张图片使用CSS background和background-position属性渲染,

这意味着我们的标签变得更加复杂,图片是在CSS中定义,而非<img>标签。

使用雪碧图有两个明显的优点:

  1. 降低网页图片内容对服务器的请求次数

雪碧图可以合并大多数的背景图片和小图标,方便我们在任何位置使用。不同位置的请求只会调用同一个图片,大大减少页面对服务器的请求次数,降低服务器的压力;这样也可以提高页面的加载速度,节约服务器的流量。

  1. 提升页面加载速度

雪碧图拼接的图片尺寸明显小于所有图片拼合之前的打小。

从这两方面可以明显对前端请求速度进行优化。

在HTTP2之后,已经不需要考虑减少请求数,故雪碧图现在在前端页面优化性能的意义已经不大。现在更加推荐使用字体图标,文件很小并且是矢量图标

CDN加速

CDN的全称是Content Delivery Network,即内容分发网络。其目的是通过在现有的Internet中增加一层新的CACHE(缓存)层,将网站的内容发布到最接近用户的网络”边缘“的节点,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等原因,提高用户访问网站的响应速度。

Cache层技术可以用来消除峰值数据访问造成的节点设备阻塞。Cache服务器具有缓存功能,绝大部分的网页对象的重复访问不需要从原始网站重新传送文件,只需要通过简单认证将副本发送即可。缓存服务器的位置通常不输在用户端附近,所以可以获得局域网的响应速度,有效减少广域宽带消耗。

对于提升响应速、节约带宽、有效减轻源服务器的负载十分有效。

总结来说CDN对网络的优化作用主要体现在如下几个方面: 

  • 解决服务器端的“第一公里”问题  
  • 缓解甚至消除了不同运营商之间互联的瓶颈造成的影响  
  • 减轻了各省的出口带宽压力  
  • 缓解了骨干网的压力  
  • 优化了网上热点内容的分布

gzip压缩

Gzip是GNUzip的缩写,是一个GNU自由软件的文件压缩程序,在使用中基本可以压缩50%的文本文件大小。在说Gzip之前,我们先介绍一个概念,HTTP 压缩。HTTP 压缩是一种内置到网页和网页客户端中以改进传输速度和带宽利用率的方式。在使用 HTTP 压缩的情况下,HTTP 数据在从服务器发送前就已压缩:兼容的浏览器将在下载所需的格式前宣告支持何种方法给服务器;不支持压缩方法的浏览器将下载未经压缩的数据。

HTTP 压缩就是以缩小体积为目的,对 HTTP 内容进行重新编码的过程。

Gzip就是HTTP压缩的经典例题。

减少文件大小会带来两个明显的好处:

  1. 减少存储空间
  2. 通过网络传输时可以减少传输时间

Gzip 压缩背后的原理,是在一个文本文件中找出一些重复出现的字符串、临时替换它们,从而使整个文件变小。也正是因为这个原理,文件中代码的重复率越高,Gzip压缩的效率就越高,使用 Gzip 的收益也就越大。反之亦然。

代码级别:减少数据请求次数

前面我们列举了在页面初始加载时的优化方法,然而在某些场景下这还不够,因为经常会出现页面展示和使用时,频繁请求服务来更新信息的场景。

例如在开发类Excel在线协同系统时,因为单元格业务相互独立,全屏刷新无法满足需求。我们只能定时从服务器获取每个单元格的值,检测到变化后展示在页面上。而每个单元格分别调用api获取内容,就会产生大量网络请求。大量的请求一方面拖累了加载速度,页面也会发生卡顿。

在这种场景下,WebSocket是一个很好的选择,通过长链接的方式保持与服务器的同步,服务端主动推送更新到客户端,减少了网络的开销。但是WebSocket也有自身的缺点,开发成本高,无论是客户端还是服务端都需要考虑断开重连、频繁推送、资源占用等问题。所以,我们还需要通过优化,尽量减少请求频率。

优化思路

如何减少数据请求数量?我们可以通过请求队列的方式,对逻辑进行优化。

(通过请求队列优化Web请求)

经过优化,类Excel在线协同系统获取数据的逻辑变成了如下的样子:

  • 当单元格发送请求时,请求先添加ID,并通过ID缓存callback方法,然后进入请求队列,队列管理器定时或者根据队列中请求数量多少像服务端发送请求包。
  • 服务端接收到请求包后批量处理,处理后封装新的返回包
  • 前端接受到返回包后根据请求的唯一ID,调用对应的callback方法执行,完成单元格的请求

使用此方法进行优化,优点是显而易见的:

  • 实现简单,代码改动小,原本的ajax请求改为队列调用即可,请求后的callbak无需修改。服务端添加一个新接口拆分请求即可。
  • 根据实际场景设置请求频率或者一次请求中数据的数量,兼顾更新频率和相应次数。

应用实例

下面代码是GETNUMBERFROMSERVER的实现,该函数负责调用服务器的getData接口,传递参数,获取显示内容并展示在单元格。为了确保异步更新单元格的用户体验,这个函数源自SpreadJS的异步函数。

  1. var GetNumberFromServer = function () {
  2. };
  3. GetNumberFromServer.prototype = new GC.Spread.CalcEngine.Functions.AsyncFunction("GETNUMBERFROMSERVER", 1, 2);
  4. GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) {
  5. fetch("/spread/getData?data="+arg1)
  6. .then(function(response) {
  7. return response.text();
  8. })
  9. .then(function(text) {
  10. context.setAsyncResult(text);
  11. });
  12. };
  13. GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETNUMBERFROMSERVER", new GetNumberFromServer());

为了减少请求,我们首先需要使用一个缓存对象存放请求数据,定时调用接口处理。

  1. let callStack = {}; //收集请求数据
  2. let callingStack = {}; //缓存正在请求中的数据信息
  3. let callStackCount = 0; //请求数量,当作请求ID,用于区分请求内容
  4. let timingId = 0; //用于判断当前是否有定时器等待请求中

然后,我们定义新的队列化请求方法,代替在函数中直接调用API接口。

  1. // data 请求数据
  2. // context 异步函数context, 网络请求结束后回调时使用
  3. // callback 回调函数
  4. function stackCall(data, context, callback){
  5. let id = callStackCount++;
  6. callStack[id] = {};
  7. callStack[id].data = data;
  8. callStack[id].context = context;
  9. callStack[id].callback = callback;
  10. if(timingId === 0){ // 同时只有一个定时器
  11. timingId = setTimeout(function(){
  12. callingStack = callStack;
  13. callStack = {};
  14. let newData = "" //合并请求数据,根据实际业务情况整理
  15. for(let cId in callingStack){
  16. newData += (cId + "," + callingStack[cId].data + ";");
  17. }
  18. // 发送请求,这里模拟数据,发送什么返回什么
  19. fetch("/spread/getData?data=" + newData)
  20. .then(function(response) {
  21. return response.text();
  22. })
  23. .then(function(text) {
  24. let resData = newData.split(";");
  25. let spread = designer.getWorkbook();
  26. spread.suspendPaint(); //暂定页面绘制
  27. //解析返回的数据
  28. for(let resId in resData){
  29. if(resData[resId]){
  30. let ress = resData[resId].split(",");
  31. // 根据Id,获取函数的context,调用callback回调
  32. callingStack[ress[0]].callback.call(null, callingStack[ress[0]].context, ress[1])
  33. }
  34. }
  35. spread.resumePaint(); //重启统一绘制
  36. timingId = 0;
  37. });
  38. }, 1000)
  39. }
  40. }

最后更新异步函数的实现方式,在函数中调用stackCall堆栈函数,批量调用成功后执行callback回调中的setAsyncResult方法,最终实现业务逻辑。

  1. GetNumberFromServer.prototype.evaluate = function (context, arg1, arg2) {
  2. stackCall(arg1, context, function(context, text){
  3. context.setAsyncResult(text);
  4. })
  5. };

经过这次优化,当页面有大量异步请求时,这些请求会放到队列中,定时统一处理,一次刷新。

此外,我们还可以使用SpreadJS的doNotRecalculateAfterLoad导入选项,在首次加载时不计算,改用json中原始值;以及calcOnDemand开启按需计算。进一步优化页面初始化的速度和体验。

  1. json.calcOnDemand = true;
  2. spread.fromJSON(json, { doNotRecalculateAfterLoad: true });

总结

本文分类介绍了几种前端性能优化的方法。这些最佳实践覆盖了页面加载和数据请求环节。在文章的后半部分,我们通过类Excel在线协同编辑的实例,详细介绍了“数据请求队列化”的实现,希望对您的前端开发有帮助。

边学习javascript一边跟大家分享成果,喜欢就关注我吧,大家一起学习!

今天分享当页面加载完成后自动执行函数我们写的函数

1错误用法

当window.onload的方法使用两次的时候,js引擎只会执行最后一个window.onload

结果如下

下面分享正确的方法

正确方法1

结果如下

正确方法2

结果如下

方法解读:先把现有的window.onload事件用一个变量(oldonload)存起来,如果在window.onload中没有绑定事件则把fun函数添加给他;如果window.onload已经有绑定函数了,则把fun追加到后面执行。

javascript学习结果分享给大家,如有错误欢迎指正,大家喜欢欢迎转发收藏,欢迎大家一起在评论区交流