оптимизация производительности JavaScript

внешний интерфейс JavaScript браузер React.js

Эта статья в основном написана после того, как я прочитал «Высокопроизводительный Javascript», я хочу записать некоторые полезные решения по оптимизации и поделиться с вами своим собственным опытом.,

Загрузка и выполнение Javascript

Как мы все знаем, когда браузер анализирует дерево DOM, когда анализируется тег скрипта, он блокирует все остальные задачи и будет продолжать выполняться до тех пор, пока файл js не будет загружен, проанализирован и выполнен. Поэтому в это время браузер будет заблокирован здесь. Если тег script помещен в голову, пользователь может видеть только пустую страницу до загрузки и выполнения js-файла. Такой пользовательский опыт должен быть особенно плохим. Общие методы для этого следующие:

  • Поместите все теги script в нижнюю часть тела, чтобы убедиться, что файл js загружается и выполняется последним, а страница может быть отображена пользователю первой. Тем не менее, вы должны сначала понять, зависит ли первый экранный рендеринг страницы от каких-то ваших js-файлов, и если да, то вам нужно поставить эту часть js-файлов на голову.
  • Используйте отсрочку, например, следующую запись. При использовании defer, хотя браузер и загрузит соответствующий js-файл при разборе тега, он не выполнит его сразу, а выполнит эти js-файлы после разбора DOM (до DomContentLoader).document. Таким образом, нет блокировки для браузера.
<script src="test.js" type="text/javascript" defer></script>
  • Динамическая загрузка js-файлов.Таким образом, вы можете загрузить требуемый код после загрузки страницы, а также вы можете использовать этот метод для реализации ленивой загрузки/загрузки js-файлов по требованию.Например, сейчас чаще встречается комбинация webpack vue-router/react-router реализует загрузку по запросу, и соответствующий код загружается только при доступе к определенному маршруту. Конкретный метод заключается в следующем:

1. Динамически вставляйте теги сценариев для загрузки сценариев, например, с помощью следующего кода.

  function loadScript(url, callback) {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    // 处理IE
    if (script.readyState) {
      script.onreadystatechange = function () {
        if (script.readyState === 'loaded' || script.readyState === 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      }
    } else {
      // 处理其他浏览器的情况
      script.onload = function () {
        callback();
      }
    }
    script.src = url;
    document.body.append(script);
  }

  // 动态加载js
  loadScript('file.js', function () {
    console.log('加载完成');
  })

2. Загрузите файл js через xhr, но таким образом вы можете столкнуться с междоменными проблемами. Примеры следующие:

  const xhr = new XMLHttpRequest();
  xhr.open('get', 'file.js');
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.text = xhr.responseText;
        document.body.append(script);
      }
    }
  }

3. Объедините несколько файлов js в один и сожмите их. Причина: В настоящее время большинство браузеров уже поддерживают параллельную загрузку js-файлов, но все еще существует определенное количество одновременных загрузок (в зависимости от браузера, некоторые браузеры могут загружать только 4), и для каждого js-файла необходимо создать дополнительное HTTP-соединение, загрузка четырех файлов размером 25 КБ занимает больше времени, чем загрузка одного файла размером 100 КБ. Поэтому нам лучше всего объединить несколько файлов js в один и сжать код.

область JavaScript

Когда функция выполняется, создается контекст выполнения, который определяет среду, в которой выполняется функция. Когда функция завершает выполнение, контекст выполнения уничтожается. Поэтому многократный вызов одной и той же функции приводит к созданию нескольких контекстов выполнения. Каждый контекст выполнения имеет свою собственную цепочку областей видимости. Я считаю, что каждый должен был знать об области видимости, для функции первой областью видимости являются переменные внутри этой функции. В процессе выполнения функции каждый раз, когда встречается переменная, цепочка областей видимости функции будет искать первую совпадающую переменную, сначала будут искать переменные внутри функции, а затем цепочка областей видимости будет искать слой слой. следовательно,Если мы хотим получить доступ к самой внешней переменной (глобальной переменной), это приведет к относительно большой потере производительности по сравнению с прямым доступом к внутренней переменной.. Поэтому мы можемХраните часто используемые ссылки на глобальные переменные в локальной переменной.

const a = 5;
function outter () {
  const a = 2;
  function inner () {
    const b = 2;
    console.log(b); // 2
    console.log(a); // 2
  }
  inner();
}

объект прочитан

В javascript он в основном делится на четыре типа: литералы, локальные переменные, элементы массива и объекты. Доступ к литералам и локальным переменным является самым быстрым, а доступ к элементам массива и членам объекта — относительно медленным. При доступе к элементам объекта он ищется в цепочке прототипов, как и в цепочке областей видимости. Следовательно, если искомый элемент находится слишком глубоко в цепочке прототипов, скорость доступа будет ниже. следовательно,Мы должны максимально сократить количество поисковых запросов и глубину вложенности членов объекта.. Например следующий код

  // 进行两次对象成员查找
  function hasEitherClass(element, className1, className2) {
    return element.className === className1 || element.className === className2;
  }
  // 优化,如果该变量不会改变,则可以使用局部变量保存查找的内容
  function hasEitherClass(element, className1, className2) {
    const currentClassName = element.className;
    return currentClassName === className1 || currentClassName === className2;
  }

Оптимизация манипуляций с DOM

  • Сведите к минимуму количество манипуляций с DOM, как можно больше используйте javascript и как можно больше используйте локальные переменные для хранения узлов DOM. Например следующий код:
  // 优化前,在每次循环的时候,都要获取id为t的节点,并且设置它的innerHTML
  function innerHTMLLoop () {
    for (let count = 0; count < 15000; count++) {
      document.getElementById('t').innerHTML += 'a';
    }
  }
  // 优化后,
  function innerHTMLLoop () {
    const tNode = document.getElemenById('t');
    const insertHtml = '';
    for (let count = 0; count < 15000; count++) {
      insertHtml += 'a';
    }
    tNode.innerHtml += insertHtml;
  }
  • Максимально сократить перестановку и перерисовку. Перестановка и перекомпоновка могут быть очень дорогими. Поэтому, чтобы уменьшить количество случаев перестановки и перекомпоновки, мы можем сделать следующие оптимизации

1. Когда мы хотим изменить стиль Дома, мы должны максимально объединить все модификации и обработать их за один раз, чтобы уменьшить количество перестановок и рекомбинаций.

  // 优化前
  const el = document.getElementById('test');
  el.style.borderLeft = '1px';
  el.style.borderRight = '2px';
  el.style.padding = '5px';

  // 优化后,一次性修改样式,这样可以将三次重排减少到一次重排
  const el = document.getElementById('test');
  el.style.cssText += '; border-left: 1px ;border-right: 2px; padding: 5px;'

2. Когда мы хотим изменить узлы DOM в пакетном режиме, мы можем скрыть узлы DOM, затем выполнить ряд операций модификации, а затем сделать их видимыми, чтобы можно было выполнить не более двух перестановок. Конкретный метод заключается в следующем:

  // 未优化前
  const ele = document.getElementById('test');
  // 一系列dom修改操作

  // 优化方案一,将要修改的节点设置为不显示,之后对它进行修改,修改完成后再显示该节点,从而只需要两次重排
  const ele = document.getElementById('test');
  ele.style.display = 'none';
  // 一系列dom修改操作
  ele.style.display = 'block';

  // 优化方案二,首先创建一个文档片段(documentFragment),然后对该片段进行修改,之后将文档片段插入到文档中,只有最后将文档片段插入文档的时候会引起重排,因此只会触发一次重排。。
  const fragment = document.createDocumentFragment();
  const ele = document.getElementById('test');
  // 一系列dom修改操作
  ele.appendChild(fragment);

3.Использование делегирования событий: делегирование события заключается в перемещении события целевого узла на родительский узел для обработки.Из-за характеристик всплывающего окна браузера, когда целевой узел инициирует событие, родительский узел также инициирует событие. Следовательно, родительский узел отвечает за прослушивание и обработку события.

Так в чем его преимущества? Предположим, у вас есть список, в котором каждый элемент списка должен быть привязан к одному и тому же событию, и этот список может часто вставляться и удаляться. Обычным способом вы можете привязать только один обработчик событий к каждому элементу списка, и каждый раз, когда вставляется новый элемент списка, вам также необходимо зарегистрировать новый обработчик событий для нового элемента списка. В этом случае, если элемент списка очень большой, это приведет к особенно большому количеству обработчиков событий, что вызовет большие проблемы с производительностью. И через делегирование событий нам нужно только прослушивать это событие на родительском узле элемента списка и позволять ему обрабатывать его единообразно. Таким образом, для вновь добавленных элементов списка не требуется дополнительной обработки. И использование делегирования событий на самом деле очень просто:

function handleClick(target) {
  // 点击列表项的处理事件
}
function delegate (e) {
  // 判断目标对象是否为列表项
  if (e.target.nodeName === 'LI') {
    handleClick(e.target);
  }
}
const parent = document.getElementById('parent');
parent.addEventListener('click', delegate);

Адрес этой статьи находится по адресу ->Адрес моего блога, добро пожаловать, чтобы начать или подписаться