前端优化常用技术

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m_review/article/details/83546384

前端优化常用技术

从建立 http 连接开始,到页面展示在浏览器中,一共经过了加载,执行,渲染,重构等几个阶段。

加载

PV(访问量):即 Page View,页面刷新一次算一次。也叫并发量。

UV(独立访客):即 Unique Visitor,一个客户端(电脑,手机)为一个访客

比如:百万级的 PV,并发量过大怎么办 ?

由此,前端所有的优化都是基于这个点和单线程而延伸出来的。所以,前端的资源加载优化有两个方向:

  1. 开源

增加域名,简单来说就是 cdn。

  1. 节流

资源压缩,去除空格,gzip 等。

一旦开发中引入的 UI 库或第三方插件多了,总文件体量也不在少数;就有了:按需加载、延时加载的用武之地。

比如在 webpack 打包的时候从 template 的 html 中单独加入某个 css 或 js;

另外,图片也需要做很多相应的处理:

  • css 实现效果(按钮、阴影等)
  • 压缩尺寸和 size
  • sprite 合并
  • svg、toff 字体图
  • canvas 绘制大图(地图相关)

阻塞性优化

js 页面加载之后是否要立即执行?立即执行是否会影响页面渲染?

过去浏览器在加载和执行 js 文件时是阻塞状态,就是按照栈原理一个一个来;所以,原来要求把 js 文件放到 html 代码底部前。

现代浏览器某种程度上解决了并行加载的问题,也可以进行预加载,但是执行之后会否对页面造成重排?

所以要灵活应用 dns-prefetch、preload 和 defer|async。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Demo</title>
<link rel="dns-prefetch" href="//cdn.com/">
<link rel="preload" href="//js.cdn.com/currentPage-part1.js" as="script">
<link rel="preload" href="//js.cdn.com/currentPage-part2.js" as="script">
<link rel="preload" href="//js.cdn.com/currentPage-part3.js" as="script">
<link rel="prefetch" href="//js.cdn.com/prefetch.js">
</head>
<body>
<!-- html code -->

<script type="text/javascript" src="//js.cdn.com/currentPage-part1.js" defer></script>
<script type="text/javascript" src="//js.cdn.com/currentPage-part2.js" defer></script>
<script type="text/javascript" src="//js.cdn.com/currentPage-part3.js" defer></script>
</body>
</html>

## 执行优化

作用域优化

  1. 变量层级不要嵌套太深。
(function(w, d) {})(window, document);

// 目的就是如此,再比如说的缓存某个变量或对象

function check() {
  var d = document,
    t = document.getElementById("t"),
    l = t.children;
  for (let i = 0; i < l; i++) {
    //code
  }
}

循环优化

  1. 简化终止条件
for (var i = 0; i < values.length; i++) {
  process(values[i]);
}

for (var i = 0, len = values.length; i < len; i++) {
  process(values[i]);
}
  1. 展开循环
// 针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。

function process(v) {
  alert(v);
}

var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
var iterations = Math.floor(values.length / 8); // 商
var leftover = values.length % 8; // 余数
var i = 0;

// 一共循环的次数 = 余数的次数 + 商*除数(8)的次数

if (leftover > 0) {
  do {
    process(values[i++]);
  } while (--leftover > 0);
}

do {
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
  process(values[i++]);
} while (--iterations > 0);

避免双重解释

eval("console.log('hello world');"); // 避免
var sayHi = new Function("console.log('hello world');"); // 避免
setTimeout("console.log('hello world');", 100); // 避免

/**
 * 以上代码是包含在字符串中的,即在JS代码运行的同时必须新启运一个解析器来解析新的代码。
 * 实例化一个新的解析器有不容忽视的开销,故这种代码要比直接解析要慢。
 * 正确的应该这么做:
 */
console.log("hello world");
var sayHi = function() {
  console.log("hello world");
};
setTimeout(function() {
  console.log("hello world");
}, 100);

最小化语句数

  1. 多个变量声明
// 避免
var i = 1;
var j = "hello";
var arr = [1, 2, 3];
var now = new Date();

// 提倡
var i = 1,
  j = "hello",
  arr = [1, 2, 3],
  now = new Date();
  1. 插入迭代值
// 避免
var name = values[i];
i++;

// 提倡
var name = values[i++];
  1. 使用数组和对象字面量,避免使用构造函数 Array(),Object()
// 避免
var a = new Array();
a[0] = 1;
a[1] = "hello";
a[2] = 45;

var o = new Obejct();
o.name = "bill";
o.age = 13;

// 提倡
var a = [1, "hello", 45];
var o = {
  name: "bill",
  age: 13
};

性能的其它注意事项

  1. 原生方法更快

用诸如 C/C++之类的编译型语言写出来的,要比 JS 的快多了。

  1. switch 语句较快

如果有一系列的复杂的 if-else 语句,可以转换成单个的 switch 语句会更快,还可以通过 case 语句按照最可能的到最不可能的顺序进行组织,进一步优化代码。

数据存储

通过改变数据存储的位置来获取最佳的读写性能。

  • 字面量 字面量就代表自身,不存储在特定位置。JS 字面量有:字符串、数字、布尔、对象、数组、函数、正则表达式和特殊的 null、undefined
  • 本地变量 使用 var 定义的数据存储单元
  • 数组对象 存储在 JS 对象内部
  • 对象成员 存储在 JS 对象内部

尽量使用字面量和局部变量,减少数组项和对象成员的使用。

作用域

生效的范围(域),哪些变量可以被函数访问,this 的赋值,上下文(context)的转换。

function fn(a, b) {
  return (res = a * b);
}

当 fn 被创建时,它的作用域链(内部属性[[Scope]])中插入了一个对象变量, 这个全局对象代表着在全局范围内定义的所有变量。

全局对象包括:window、navigator、document 等。

fn 执行的时候就会用到作用域,并创建执行环境也叫执行上下文。它定义了一个函数执行时的环境,即便是同一个函数,每次执行都创建新的环境,函数执行完毕,环境就销毁。

每个环境都要根据作用域作用域链解析参数,变量。

闭包的是根据 JS 允许函数访问局部作用域之外的数据,虽然会带来性能问题,因为执行环境虽然销毁,但激活的对象依然存在,所以可以缓存变量,从而不用全局对象。

原型和原型链

function fun(name, age) {
  this.name = name + "";
  this.age = age;
}
fun.prototype.getName = function() {
  return this.name;
};

var fn = new fun();

fn instanceof fun; // true
fn instanceof Object; // true

fn.__proto__ = fun.prototype;

/** fun的原型方法
  __proto__ = null
  hasOwnProperty = (function)
  isPrototypeOf = (function)
  propertyIsEnumerable = (function)
  toLocaleString = (function)
  toString = (function)
  valueOf = (function)
*/

重排 reflow 和重绘 repaint

会导致重排重绘的情况:

  • 添加或删除可见元素
  • 元素的位置发生改变
  • 元素的尺寸发生改变
  • 容器内容发生变化导致元素的宽高发生改变
  • 浏览器窗口初始化和尺寸改变

避免或减少发生重排和重绘的方法:

  • 尽可能少的访问某些变量
// offsetTop、offsetLeft、offsetWidth、offsetHeight
// scrollTop、scrollLeft、scrollWidth、scrollHeight
// clientTop、clientLeft、clientWidth、clientHeight

function scroller() {
  var H = document.body.offsetHeight || scrollHeight;
  return function() {
    var rgs = arguments,
      ct = this;
    // you core
  };
}
  • 字符串或数组.join(’’) innerHTML 方式
  • createElement 最后 appendChild
  • document.createDocumentFragment,cloneNode 需要改变的节点到缓存节点中,改完替换
function move2RB() {
  var dom = document.getElementById("id"),
    curent = dom.style.top;
  while (curent < 500) {
    curent++;
    dom.style.cssText = "left:" + curent + "px; top:" + curent + "px";
    // 不要写成每次都去获取,left=dom.style.left再加1,甚至是dom.style.left = (pareSint(dom.style.left,10)+1)+'px'这种写法,直接改变className也是可以的。
  }
}

总的来说:少访问 DOM,在 js 里处理计算完了再一次性修改,善用缓存和原生 API;用现在的三大框架(angular、react、vue)即可不用操心这些

算法和流程控制

循环

  • 倒序循环 for(var i=10;i>0;i–){}
  • 后置循环 do{}while(i++<10)
  • for-in

条件判断

  • switch 代替 if-else

  • 三目运算

  • 判断可能性从大到小

  • 将字符串、变量、方法存到数组或对象中

function getweek() {
  var w = ["日", "一", "二", "三", "四", "五", "六"],
    now = new Date(),
    d = now.getDay();
  return "星期" + w[d];
}

递归

// 阶乘
function facttail(n) {
  if (n == 0) {
    return 1;
  } else {
    return n * facttail(n - 1);
  }
}
console.log(facttail(5, 1));

// 幂次方
function fn(s, n) {
  if (n == 0) {
    return 1;
  } else {
    return s * fn(s, n - 1);
  }
}
console.log(fn(2, 3));

利用闭包缓存数据

某个方法内部可以存储计算过的数据或变量:

function memfacttail(n) {
  if (!memfacttail.cache) {
    memfacttail.cache = {
      "0": 1,
      "1": 1
    };
  }
  if (!memfacttail.cache.hasOwnProperty(n)) {
    memfacttail.cache.n = n * memfacttail(n - 1);
  }
  return memfacttail.cache.n;
}
console.log(memfacttail(4)); // 4*3*2*1 = 24;

猜你喜欢

转载自blog.csdn.net/m_review/article/details/83546384