web性能优化原理与方案

深入理解http请求的过程是前端性能优化的核心

从请求过程中可以发现一些优化点

  • dns是否可以通过缓存减少dns查询时间?dns缓存可以减少dns解析时间
  • 网络请求的过程走最近的网络环境?cdn以空间换时间,减少请求时间
  • 相同的静态资源是否可以缓存?下次不再请求,减少请求时间,减轻服务端压力
    能否减少请求http请求大小?静态文件压缩, gzip
  • 减少http请求css\js\img合并
  • 服务端渲染加快首屏渲染时间

资源的合并与压缩

关键点:减少http请求数量 减少请求资源大小

  1. html压缩
    原理:去掉空格,制表符,换行符,HTML注释等。
    方法:
    使用在线压缩网站进行压缩
    利用nodejs提供的 html-minifier
    后端模板引擎渲染压缩
  2. css压缩
    原理: 无效代码删除,css语义合并
    方法:
    使用在线压缩网站进行压缩
    使用html-minifier对html中的css进行压缩
    使用clean-css对css进行压缩
  3. js的压缩与混乱
    原理:无效字符的删除,剔除注释,代码语义的缩减和优化,代码保护
    方法:
    使用在线压缩网站进行压缩
    使用html-minifier对html中的js进行压缩
    使用uglifyjs2对js进行压缩
  4. 文件合并
    原理:例如将a.js b.js c.js合并成a-b-c.js
    在这里插入图片描述
    优点:
    减少了n-1个网络请求, 丢包问题影响变小
    缺点:
    单文件变大导致首屏渲染变慢,修改局部导致整体缓存失效
    解决:
    公共库合并,不同页面的合并,随机应变
  5. 开启gzip
    增加请求头 Accept-Encoding:gzip,deflate
  6. 使用fis进行资源的压缩与合并
  • 文件目录
    在这里插入图片描述
  • 安装依赖
    cnpm i --save-dev fis3 fis-optimizer-html-minifier fis-parser-babel-5.x fis-parser-node-sass fis3-hook-relative html-minifier
  • 配置fis-conf.js
//让所有文件,都使用相对路径。
fis.hook('relative');
fis.match('**', {
  relative: true
});

//不压缩layout目录下的文件
fis.match('layout/**', {
  release: false
});

//发布时,忽略项目中的这些文件
fis.set('project.ignore', ['.git/**', 'package.json','node_modules/**','fis-conf.js', '*.bat']);

//合并所有css文件
fis.match('css/**.scss', {
  rExt: '.css',
  parser: fis.plugin('node-sass'),
  packTo: 'css/app.min.css'
});

//合并所有js文件
fis.match('js/**.js', {
  // fis-optimizer-uglify-js 插件进行压缩,已内置
  optimizer: fis.plugin('uglify-js', {
    mangle: {},
    compress: {
      drop_console: true
    }
  }),
  packTo: 'js/app.min.js'
});

//压缩html文件
fis.match('*.html',{
  optimizer:fis.plugin('html-minifier')
});

//压缩合并css文件
fis.match('*.css', {
  // fis-optimizer-clean-css 插件进行压缩,已内置
  optimizer: fis.plugin('clean-css')
});

//压缩整合图片
fis.match('*.{png,jpg}', {
  // fis-optimizer-png-compressor 插件进行压缩,已内置
  optimizer: fis.plugin('png-compressor')
});

//将es6转es5
fis.match('*.js',{
  rExt:'.js',
  parser:fis.plugin('babel-5.x',{
    blacklist:['regenerator'],
    stage:3
  }),
  isMod:true,
  useHash:true,
  isJsLike:true
});

//压缩js文件
fis.match('*.js', {
  // fis-optimizer-uglify-js 插件进行压缩,已内置
  optimizer: fis.plugin('uglify-js')
});

fis.media('debug').match('*.{js,css,png}', {
  useHash: false,
  useSprite: false,
  optimizer: null
});
  • 命令行运行fis3 release -d output

图片资源的优化

  1. 不同格式图片应用场景
  • jpg有损压缩,压缩率高,不支持透明 不透明用这个
  • png支持透明,浏览器兼容好透明用这个
    png8 —— 256色 + 支持透明
    png24 —— 2^24色 + 不支持透明
    png32 —— 2^24色 + 支持透明
  • webp压缩程度更好,在ios webview有兼容性问题 安卓全用这个
    WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都非常优秀、稳定和统一。
  • svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景,小图标,字体图标用这个
  1. 图片压缩
  • 在线图片压缩网站
  • 在列表页使用缩略图,详情页使用中型图,放大才使用原图。
    通过服务端解析文件名,网站访问不同的文件后缀实现, 如:
    原图 aaa.jpg
    中型图 aaa.jpg_500x500Q50S50.jpg
    缩略图 aaa.jpg_100x100Q50S50.jpg
  1. image inline
    小于10KB的图片转成base64直接内嵌到html中,减少请求资源。
    进一步优化:将base64图片数据放到localstorage中,从缓存读取,减少html文件大小
  2. css sprite
    使用ps将小图标放到一个png文件中合成雪碧图
    通过在线获取雪碧图css代码,直接获取某个图标的css样式代码
  3. 字体图标
    比如我们在www.iconfont.cn上收藏一些图标=》添加项目=》下载字体文件=》导出=》导入项目并使用
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    使用方法在demo_index.html中

css、js的加载与执行

  1. 加载规则
    在这里插入图片描述
  • 浏览器采用自上而下的方式解析,在遇到link,style,script时都会阻塞浏览器的解析,直到外部资源加载并解析或执行完毕后才会继续向下解析html
  • html预加载器会扫描代码,并行加载所有外部资源,但受浏览器同一域名下最大并行数量限制,优化:向不同的cdn服务器请求
  • 外部样式不会阻塞后续外部脚本的加载,但外部样式会阻塞后续脚本执行,直到外部样式加载并解析完毕。(因为script脚本执行过程中可能会修改html界面;DOM节点的CSS样式会影响js的执行结果)
  • 所有js按引入顺序执行,而不是加载完成的顺序;执行某个js脚本时,会阻塞html解析和后续js执行
  1. js引入方式 script defer async
    在这里插入图片描述
    实例:
//domhandle.js
document.getElementById('app').innerHTML="修改成功";
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--普通方式,一定修改不成功-->
<script src="domhandle.js"></script>
<!--defer, 一定修改成功-->
<script src="domhandle.js" defer></script>
<!--async, 有可能修改成功-->
<script src="domhandle.js" async></script>
<div id="app">test</div>
</body>
</html>

懒加载和预加载

  1. 懒加载
    并发加载的资源过多,会阻塞js的加载,影响网站的正常使用
    页面过长但用户不一定会看下面的资源,减少无效资源的加载
    图片进入可视区域之后才请求图片资源
  2. 预加载
    图片等静态资源在使用之前提前请求,使用时从缓存中加载速度更快,提升用户体验
  3. 实现
  • body中直接引入(预加载)
<body>
	<img src="xxx" style="display:none">
</body>
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .imgs .img-item{
            display:block;
            width:auto;
            height:700px;
        }
    </style>
</head>
<body>
<div class="imgs">
    <img src="" class="img-item" lazyload="true" data-original="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2862526269,2797435169&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3771202784,101134037&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3498323135,1472840100&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1082729974,3416555379&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=242247130,457677083&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2386982545,3518140799&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2953216290,3722654732&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=295297435,3968482153&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=113998444,2470350779&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1530410351,3540869741&fm=26&gp=0.jpg">
    <img src="" class="img-item" lazyload="true" data-original="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1998755803,3998950952&fm=26&gp=0.jpg">
</div>

<script src="lazyload.js"></script>
</body>
</html>
//lazyload.js
let viewHeight=document.documentElement.clientHeight;//可视区域高度

function lazyload(){
  let eles=document.querySelectorAll('img[data-original][lazyload]');
  Array.prototype.forEach.call(eles,(item,index)=>{
    if(item.dataset.original===""){
      return ;
    }
    let rect=item.getBoundingClientRect();
    if(rect.bottom>=0 && rect.top < viewHeight){
      let img=new Image();
      img.src=item.dataset.original;
      img.onload=()=>item.src=img.src;
      item.removeAttribute('data-original');
      item.removeAttribute('lazyload');
    }
  })
}

lazyload();

document.addEventListener('scroll',lazyload);

重绘和回流

  1. 回流 reflow
    当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)
  • 盒子模型相关属性会触发重布局
  • 定位属性及浮动也会触发重布局
  • 改变节点内部文字结构也会触发重布局
  • 添加或删除DOM节点
  • 浏览器窗口尺寸改变
width height padding margin display border-width
border min-height top bottom left right
position float clear text-align overflow-y font-weight
overflow font-family line-height vertical-align white-space font-size
  1. 重绘 repaint
    当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
color border-style border-radius visibility text-decoration background
background-image background-position background-repeat background-size outline-color outline
outline-style outline-width box-shadow
  1. 回流必将引起重绘,而重绘不一定会引起回流

  2. 新建DOM的过程

  • 获取DOM后分割为多个图层
  • 对每个图层的节点计算样式结果(Recalculate style–样式重计算)
  • 为每个节点生成图形和位置(Layout–回流和重布局)
  • 将每个节点绘制填充到图层位图中(Paint Setup和Paint–重绘)
  • 图层作为纹理上传至GPU
  • 符合多个图层到页面上生成最终屏幕图像(Composite Layers–图层重组)
    在这里插入图片描述
  1. Chrome创建图层的条件
  • 3D或透视变换(perspective transform)CSS属性
  • 使用加速视频解码的节点
  • 拥有3D(WebGL)上下文或加速的2D上下文的节点
  • 混合插件(如Flash)
  • 对自己的opacity做CSS动画或使用一个动画webkit变换的元素
  • 拥有加速CSS过滤器的元素
  • 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
  • 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
  1. 实战优化点
  • 用translate替代top改变 top会触发reflow但translate不会
  • 用opacity替代visibilityvisibility会repaint, 但opacity不会, 因为opacity是Composite Layers阶段变化
  • 不要一条一条地修改 DOM 的样式(虽然浏览器有缓冲机制)
var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
ele.style.padding = '5px';

看起来元素的样式改变了三次,也就是三次重排和重绘,但是大多数浏览器会通过队列化修改并批量执行来优化重排过程,一次完成!但某些操作(获取布局信息的操作)会强制刷新队列并要求计划任务立即执行。例如:

offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (currentStyle in IE)

  1. 预先定义好 class,然后修改 DOM 的 className
  2. 使用cssText 合起来写只有一次reflow: div.style.cssText ="width:100px;height:100px;"
  3. 把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow),然后你修改100次,然后再把它显示出来
  4. 使用document.createDocumentFragment创建文档片段修改,再添加到DOM树
  • 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量,会回流强制执行缓冲区域以获取最新值
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
  • 动画实现的速度的选择 ,使用absolute或fixed定位页面上的动画元素,将其脱离文档流
  • 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。
  • 对于动画新建图层will-change:transform
  • 启用 GPU 硬件加速(3D) transform:translateZ(0) transform:translate3d(0,0,0);

缓存

深入理解浏览器的缓存机制

发布了38 篇原创文章 · 获赞 66 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/lyt_angularjs/article/details/100058428