js、dome 优化汇总

123 阅读5分钟

dom操作

dom文档碎片优化节点添加

// 添加节点
for (var i = 0; i < 10; i++) {
  var oP = document.createElement('p')
  oP.innerHTML = i
  document.body.appendChild(oP)
}
// createDocumentFragment 优化
const fragEle = document.createDocumentFragment()
for (var i = 0; i < 10; i++) {
  var oP = document.createElement('p')
  oP.innerHTML = i
  fragEle.appendChild(oP)
}
document.body.appendChild(fragEle)


//克隆优化节点操作
for (var i = 0; i < 3; i++) {
  var oP = document.createElement('p')
  oP.innerHTML = i
  document.body.appendChild(oP)
}
// 优化
var oldP = document.getElementById('box1')
for (var i = 0; i < 3; i++) {
  var newP = oldP.cloneNode(false)
  newP.innerHTML = i
  document.body.appendChild(newP)
}

动态加载js

function loadJS(files, done) {
  const head = document.getElementsByTagName('head')[0];
  Promise.all(files.map(file => {
    return new Promise((resolve) => {
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.async = true;
      script.src = file;
      script.addEventListener('load', resolve,false);
      head.appendChild(script);
    });
  })).then(done);
}
loadJS(['./arithmetic.js', './binaryTree.js'],()=>{
  console.log("all load");
});

获取元素尺寸位置

const box = document.getElementById('box');
const {x,y,width,height} = box.getBoundingClientRect();//top,right,bottom,left

dom节点四大监听

1 异步监测元素可见 IntersectionObserver 懒加载demo

const images = document.querySelectorAll('img');
const observer = new IntersectionObserver((entries,observer)=>{
  entries.forEach(entry=>{
    //如果元素可见
    if(entry.isIntersecting){
      //当前图片
      const img = entry.target;
      //获取图片地址
      const src = img.dataset.src;
      if(src){
        //设置图片地址,触发图片加载
        img.src = src;
        //加载完成,不再观察图片
        observer.unobserve(img);
      }
    }
  });
},{threshold:0.5});//设置0.5,当图片进入视50%区域,开始加载
//开始观察所有图片
images.forEach(image=>{
  observer.observe(image);
});

2 观察dom,特定属性变动

var insertedNodes = [];
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // 遍历所有添加的节点
    for (var i = 0; i < mutation.addedNodes.length; i++) {
      var node = mutation.addedNodes[i];
      // 判断节点类型
      if (node.nodeType === 1) {
        // 执行你的操作
        insertedNodes.push(node);
      }
    }
  });
});
// 配置观察选项:
var config = { attributes: true, childList: true, subtree: true };
observer.observe(document.body, config);//开始观察body元素

3 观察元素大小变化

const resizeObserver = new ResizeObserver((entries)=>{
  entries.forEach(entry=>{
    //entry.contentRect 元素大小信息
  });
});
resizeObserver.observe(document.getElementById('box'));//开始观察box元素

4 浏览器各项指标监测

const ovserver = new PerformanceObserver((list)=>{
  list.getEntries().forEach(entry=>{
    console.log(entry);
    if(entry.initiatorType === 'img'){
      console.log('图片加载时间',entry.duration);
    }
    if(entry.name === 'first-contentful-paint'){
      console.log('首次渲染时间',entry.startTime);
    }
  });
});
ovserver.observe({entryTypes:['resource','paint']});//开始观察,resource图片加载,paint页面渲染

平滑滚动到元素视图中

const scrollToElement = (element)=>{
  element.scrollIntoView({behavior: 'smooth',block: 'center'});
}
scrollToElement(document.getElementById('box'));

点击元素外事件

const outsideClick = (element,callback)=>{
  const outside = (e)=>{
    if(!element.contains(e.target)){
      callback();
      document.removeEventListener('click',outside);
    }
  }
  document.addEventListener('click',outside);
}

获取选定的文本

const getSelectedText = ()=>{
  return window.getSelection().toString();
}
getSelectedText();//hello world

url处理 常用功能

//获取base URL
const baseUrl = (url)=>url.replace(/[?#].*$/, '');
console.log(baseUrl('https://www.baidu.com/s?wd=hello'));//https://www.baidu.com/s
//绝对路径
const absoluteUrl = (url)=>/^[a-z][a-z0-9+.-]*:/.test(url);
absoluteUrl('https://www.baidu.com/s?wd=hello');//true
absoluteUrl('hello.html');//false
//url参数
const getURLParamet = (url)=>
  (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
    (a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a),
    {}
  );
getURLParamet('https://www.baidu.com/s?wd=hello');//{wd: 'hello'}
//获取元素所有祖先
const getAncestors = (el)=>{
  const parents = [];
  while(el){
    parents.unshift(el);
    ancestor = el.parentNode;
  }
  return parents;
}

sse (它允许服务器向客户端推送数据)

if ('EventSource' in window) {
  const source = new EventSource('http://localhost:8080/sse');
  source.addEventListener('message', function(event) {
    console.log(event.data); // 在控制台输出服务器发送的消息
  });
  source.addEventListener('open', function(event) {
    console.log('SSE 连接已经打开!'); // 在控制台输出连接已经打开的消息
  });
  source.addEventListener('close',function(event){
    console.log('SSE 连接已经关闭。'); // 在控制台输出连接已经关闭的消息
    source.close();
    source.removeEventListener('message');
    source.removeEventListener('open');
    source.removeEventListener('close');
    source.removeEventListener('error');
  });
  source.addEventListener('error', function(event) {
    if (event.eventPhase === EventSource.CLOSED) {
      console.log('SSE 连接已经关闭。'); // 在控制台输出连接已经关闭的消息
    } else {
      console.log('发生了未知错误。'); // 在控制台输出发生了未知错误的消息
    }
  });
}

防抖、节流

//防抖
function debounce(fn, delay) {
  let timer = null;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

//节流
function throttle(fn, delay) {
  let canRun = true;
  return function() {
    if (!canRun) return;
    canRun = false;
    setTimeout(() => {
      fn.apply(this, arguments);
      canRun = true;
    }, delay);
  };
}

//使用示例
window.addEventListener('resize', debounce(function() {
  console.log('Resize event handler');
}, 200));

window.addEventListener('scroll', throttle(function() {
  console.log('Scroll event handler');
}, 200));

js 简化技巧

For循环优化

var arrList = new Array(1, 2, 3, 4, 5);
for (var i = 0; i < arrList.length; i++) {
  console.log(i)
}
// 优化后的
for (var i = 0; len = arrList.length; i < len; i++) {
  console.log(i)
}
// 性能最好
arrList.forEach(function(item) {
	console.log(item)
})
// 其次
for (var i = arrList.length; i; i--) {
	console.log(arrList[i])
}

去除数组假值

let arr = [0, '', false, null, undefined, NaN,12,-25,0.5];
let filterArr = arr.filter(Boolean);

数组查找某个值是否存在

let arr = [1, 2, 3, 4, 5];
if (~arr.indexOf(4)) {
  console.log('存在');
}
if (arr.includes(4)) {
  console.log('存在');
}

空值合并运算

let data;
let userName = data ?? 'admin';
let myName = null;
myName ??= 'admin';
console.log(myName,userName);
//逻辑或运算符
myName ||= 'leo';

短路求值 false 0 '' null undefined NaN

let valueDL = data || 'new';
let valueDL1 = data && 'new'; //new || null

多值匹配,简化 if (a === 1 || a === 2 || a === 3)

let value = 'two';
if([1,'one',2,'two',3,'three'].includes(value)){
  console.log('value is either 1,2 or 3');
}

查看对象是否有值

Object.keys(obj).length === 0;

日期是否有效

const isDateValid = (...val)=>!Number.isNaN(new Date(...val).valueOf());
isDateValid(2019,1,1);
isDateValid('december 17,1995 03:24:00');

函数强制参数

//传统
function foo(bar){
  if(bar === undefined){
    throw new Error('bar is required');
  }
  return bar;
}
//简写
let mandatory = () => {
  throw new Error('bar is required');
}
let foo = (bar = mandatory()) => {
  return bar;
};

扁平化数组

let arrFlat = [1, 2, [3], [4, 5]];
let arrFlat2 = arrFlat.flat();
let arrFlat3 = arrFlat.flatMap(x => x);
let arrFlat1 = arrFlat.reduce((acc, val) => acc.concat(val), []);

数组of,from,at

const arr1 = Array.of(1,2,3);//[1,2,3]
const arr2 = Array.from('123');//['1','2','3']
const doubled = Array.from([1,2,3],x=>x*2);//[2,4,6]
const arr3 = [1,2,3];
arr3.at(2);//3

promise.all实现并发请求

function fetchData(urls) {
  const promises = urls.map(url => fetch(url));
  return Promise.all(promises).then(res => {
      return res.json();
  });
}

reduce 常用技巧

//求和
let redTotal = [0, 1, 2, 3].reduce((acc, val) => acc + val, 0);
//扁平数组
let redFlat = [1, 2, [3, 4]].reduce((acc, val) => acc.concat(val), []);
//代替filter().map()
let numbers = [1, 2, 3, 4, 5];
let odds = numbers.filter(v => v % 2).map(v => v * 2);
let odds2 = numbers.reduce((acc, val) => {
  if(val % 2) acc.push(val * 2);
  return acc;
}, []);
//统计元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let countedNames = names.reduce(
  (acc, name) => {
    acc[name] = (acc[name] || 0) + 1;
    return acc;
  }
  , {});
//串行执行异步函数
const functions = [
  async function() { return 1; },
  async function() { return 2; },
  async function() { return 3; },
];
const results = await functions.reduce((promise, fn)=>{
  return promise.then(fn);
},Promise.resolve());
console.log(results);//3
//反转字串
let str = 'hello';
let revStr = str.split('').reduce((acc, val) => val + acc, '');
let revStr2 = str.split('').reverse().join('');
console.log(revStr);//olleh
//数组去重
let arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1];
let uniqArr = arr.reduce(
  (acc, val) => {
    return acc.includes(val) ? acc : acc.push(val);
  },
  []
);
let uniqArr2 = [...new Set(arr)];
//验证括号合法
[...'(())()(()())'].reduce((a,i)=>i==='('?a+1:a-1,0);

数组转换

let arr = [1, 2, 3, 4, 5, 2, 4, 6, 7, 8, 9, 10];
let set = new Set(arr);
let arr2 = [...set];
let arr3 = Array.from(set);

优化查找

let arr4 = [1, 2, 3, 4, 5, 2, 4, 6, 7, 8, 9, 10];
let dataSet = new Set(arr4);
if(dataSet.has(2)){
  console.log('2 is in the array');
}

js 取整

Math.floor(Math.random()*50);
~~(Math.random()*50);

高效数组合并

//小数组
const arr1 = [1,2,3,4,5];
const arr2 = [6,7,8,9,10];
console.log(list1.concat(list2));
//大数组
console.log(arr1.push.apply(arr1,arr2));
const arr3 = [...arr1,...arr2];

map转换 map转对象,map转数组

let map = new Map().set('a', 1).set('b', 2).set('c', 3);
let obj = Object.fromEntries(map)
let array = Array.from(map);
let myMay = new Map(Object.entries(obj));

单例

class SingleClass{
  constructor(){
    console.log("SingleClass");
  }
  static getInstance(){
    if(!this.instance){
      this.instance = new SingleClass();
    }
    return this.instance;
  }
}
const s1 = SingleClass.getInstance();
const s2 = SingleClass.getInstance();
console.log(s1 === s2);//true

RGB转16进制

const rgbToHex = (r,g,b)=>"#"+((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1);
rgbToHex(255,255,255);

缓存函数结果

function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}