Эта статья в основном изоптимизация производительностиМы обсудим идеи оптимизации и практические методы JavaScript в процессе загрузки и выполнения с точки зрения JavaScript.Это подробное описание.В статье, где речь идет о принципе, необходимо сказать еще несколько слов. Я также надеюсь, что читатели будут терпеливы и внимательно поймут.Пожалуйста, поверьте, Ваше терпение обязательно окупится.
источник
С усилением внимания к пользовательскому опыту влияние производительности внешнего интерфейса на пользовательский опыт привлекло большое внимание, но из-за относительно сложных причин проблем с производительностью нам сложно всесторонне решить это с одного или нескольких аспектов. Причина для этой статьи в том, что я хочу использовать эту статью в качестве отправной точки, чтобы использовать серию статей для глубокого изучения и сортировки всех аспектов производительности Javascript, чтобы заполнить и закрепить мою структуру знаний.
Структура каталогов
Общие идеи написания этой статьи включают, но не ограничиваются:
-
Функции блокировки JavaScript, о которых нужно сказать
-
Размещайте сценарии разумно, чтобы оптимизировать загрузку, и размещайте сценарии js в
<body>
до закрытия этикетки. -
Сократите количество HTTP-запросов, сожмите и упростите код сценария.
-
Загружать скрипты JavaScript без блокировки:
-
использовать
<script>
Атрибут отсрочки тега. -
Используйте атрибут HTML5.
-
динамически созданный
<script>
Элемент загружает JavaScript. -
Загрузите JavaScript с помощью объектов XHR.
-
Функция блокировки JavaScript, о которой я должен сказать
Все фронтенд-разработчики должны знать, что JavaScript работает в одном потоке, то есть когда JavaScript запускает блок кода, другие действия на странице (обновления пользовательского интерфейса или загрузка и выполнение других скриптов и т. д.) выполняются в тот же период времени. Приостановленное состояние не может быть обработано одновременно, поэтому при выполнении js-скрипта этот код повлияет на другие операции. Это особенность самого JavaScript, и мы не можем ее изменить.
Мы называем эту особенность JavaScriptхарактеристики блокировки, из-за этой функции блокировки оптимизация производительности внешнего интерфейса, особенно оптимизация производительности JavaScript, становится относительно сложной.
Зачем блокировать?
Может быть, вы также спросите, поскольку функция блокировки JavaScript вызовет столько проблем, почему язык JavaScript не может использовать многопоточность, как Java и другие языки, разве это не нормально?
Полностью понять однопоточный дизайн JavaScript несложно.Обобщим его так: цель JavaScript изначально была разработана для улучшения взаимодействия с пользователем веб-страниц на стороне браузера и для решения некоторых простых задач, таких как проверка формы в браузере. некоторые страницы. Поэтому в то время JavaScript делал очень мало, а кода было не слишком много, что также устанавливало сильную корреляцию между JavaScript и операциями интерфейса.
Поскольку JavaScript тесно связан с интерфейсными операциями, мы могли бы понять его и так: представьте, если на странице есть два скрипта js, которые будут изменять содержимое элемента dom, если JavaScript использует многопоточный метод обработки, затем последний элемент страницы Отображаемый контент является результатом того, что операция сценария js неопределенна, потому что два js загружаются через разные потоки, и мы не можем предсказать, кто обработает их первым Это результат, который нам не нужен, и это тип данных интерфейса Операции обновления изобилуют в JavaScript. Поэтому нам нетрудно понять причины дизайна однопоточного JavaScript:JavaScript использует один поток, чтобы избежать непредсказуемых повторных изменений содержимого страницы во время выполнения..
Чтобы узнать больше о тайнах JavaScript из «жизненного опыта», вы можете посмотреть книгу Учителя Жуана Ифэна.Рождение Javascript
Оптимизировано от загрузки: разумное размещение скриптов
Из-за блокирующей природы JavaScript, когда появляется каждый
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>js引用的位置性能优化</title>
<script type="text/javascript" src="index-1.js"></script>
<script type="text/javascript" src="index-2.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
Приведенный выше код представляет собой простой html-интерфейс, в котором загружаются два файла сценария js и один файл стиля css.Из-за проблемы блокировки js, когда он загружается в index-1.js, содержимое за ним будет приостановлено и ждет. , пока index-1.js не будет загружен и выполнен, будет выполнен второй файл скрипта index-2.js. В это время страница снова будет приостановлена в ожидании завершения загрузки и выполнения скрипта, и и так далее, так что пользователь открывает интерфейс, содержимое интерфейса будет значительно задержано, и мы увидим пустую страницу, которая мелькает.Этот опыт явно нехороший, поэтомуМы должны сделать все возможное, чтобы содержимое и стили отображались первыми, и поместить файл js в конец для оптимизации взаимодействия с пользователем..
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>js引用的位置性能优化</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="index-1.js"></script>
<script type="text/javascript" src="index-2.js"></script>
</body>
</html>
Оптимизировать от количества запросов: уменьшить количество запросов
Одна вещь, которую мы должны знать: в процессе загрузки страницы больше всего времени занимает не загрузка и выполнение самого js, а наоборот, каждый раз, когда вы переходите к бэкенду для получения ресурсов, это больше всего времени. для установления связи между клиентом и серверной частью. То есть знаменитое трехстороннее рукопожатие Http. Конечно, запрос HTTP не является темой нашего обсуждения в этот раз. Если вы хотите узнать о нем больше, есть много соответствующие статьи в Интернете.
Поэтому сокращение HTTP-запросов — это элемент, на котором мы фокусируемся на оптимизации.На самом деле, во многих случаях, когда файлы js-скриптов загружаются на страницу, их эффект оптимизации очень значителен. Чтобы уменьшить HTTP-запросы, вы должны упомянуть оптимизированное сжатие файлов.
Сокращение и сжатие файлов
Для сокращения запросов на доступ неизбежно будет использоваться **минифукция и сжатие** js.Следует отметить, что файл сокращения на самом деле не сложен, но неуместное использование также может привести к ошибкам или коду.Недопустимая проблема, поэтому в действительности использования, лучше всего проанализировать js перед сжатием, чтобы помочь нам избежать ненужных проблем (таких как проблемы с транскодированием юникода, такие как китайский язык в файле).
Существует три широко используемых аналитических инструмента сжатия: YUI Compressor, Closure Compiler, UglifyJs.
YUI Compressor: Появление YUI Compressor когда-то считалось самым популярным инструментом сжатия на основе парсера, он будет удалять комментарии и лишние пробелы в коде и заменять локальные переменные одним или двумя символами, чтобы сохранить больше слов Festival. Но по умолчанию будут отключены подстановки, которые могут вызвать ошибки, такие как with или eval();
Closure Complier: Closure Compiler также является инструментом сжатия на основе парсера, который пытается сделать ваш код как можно меньше. Он удалит комментарии и лишние пробелы и выполнит подстановку переменных, а также проанализирует ваш код для соответствующей оптимизации.Например, он удалит переменные, которые вы определяете, но не используете, а также сделает переменные, которые используются только один раз, встроенными функциями.
UglifyJs: UglifyJs считается первым инструментом минификации на основе node.js, он удаляет комментарии и лишние пробелы, заменяет имена переменных, объединяет выражения var, а также выполняет некоторые другие оптимизации.
У каждого инструмента есть свои преимущества. Например, сжатый код YUI точен, а сжатый код Closure будет меньше. UglifyJs не полагается на Java, а основан на JavaScript. По сравнению с Closure меньше ошибок. один лучше использовать?Я не думаю, что есть определенный ответ.Разработчики должны выбирать в соответствии с реальной ситуацией своих собственных проектов.
Оптимизирован из метода загрузки: неблокирующая загрузка скрипта
С точки зрения оптимизации производительности JavaScript уменьшение размера файла скрипта и ограничение количества HTTP-запросов — это только первый шаг к быстрому отклику интерфейса.Сегодняшние веб-приложения богаты функциями, а скриптов js становится все больше. одного размера исходного кода и сокращения количества раз недостаточно, это всегда выполнимо, даже если это HTTP-запрос, но файл слишком велик, и интерфейс будет заблокирован на долгое время, что явно не к добру.Поэтому и появилась технология неблокирующей загрузки.
Проще говоря,То есть js-код загружается после загрузки страницы, то есть скрипт загружается после срабатывания события загрузки оконного объекта.. Для этого обычно используются следующие методы:
Отложенная загрузка скрипта (defer)
Позже HTML4 определяет расширенный атрибут для тега
<script type="text/javascript" src="index-1.js" defer></script>
Теги
Ленивая загрузка скрипта (асинхронно)
Атрибут async также представлен в спецификации HTML5, который используется для асинхронной загрузки скриптов. Его общая функция такая же, как и у defer. Оба используются для параллельной загрузки. В процессе загрузки не будет блокировки, ноРазница заключается во времени их выполнения: Async должен автоматически выполнять код после загрузки, а defer должен дождаться загрузки страницы перед выполнением..
Оптимизируйте метод загрузки: динамически добавляйте элементы скрипта
Преимущество добавления кода динамическим способом в том, что независимо от того, когда начнется загрузка скрипта, процесс его загрузки и выполнения не повлияет на другие процессы на странице, мы даже можем добавить его прямо в тег head вместе с заголовком. части будут затронуты.
Поэтому как разработчик вы наверняка видели такие блоки кода:
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'file.js';
document.getElementsByTagName('head')[0].appendChild(script);
Этот метод представляет собой способ динамического создания сценариев, что мы теперь называем динамическим созданием сценариев. После загрузки файла таким образом код выполняется автоматически. Но в современных браузерах этот скрипт будет ждать загрузки всех динамических узлов перед выполнением. В этом случае, чтобы обеспечить успешный вызов интерфейса или метода другого кода, содержащегося в текущем коде, подготовка этого кода должна быть завершена до загрузки другого кода. Конкретные идеи операции для решения:
Современные браузеры получат событие загрузки после загрузки содержимого тега скрипта, и мы сможем загрузить и запустить код, который хотим выполнить, после события загрузки.В IE он будет получать события загрузки и завершения.Теоретически это происходит только после загруженное завершается завершенным, но практика подсказывает нам, что у них, похоже, нет последовательности, и даже иногда получается только одно из событий, мы можем отдельно инкапсулировать специальную функцию, чтобы отразить практичность этой функции, поэтому единый способ записи:
function LoadScript(url, callback) {
var 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;
// todo 执行要执行的代码
callback()
}
}
} else {
script.onload = function () {
callback();
}
}
script.src = 'file.js';
document.getElementsByTagName('head')[0].appendChild(script);
}
Функция LoadScript получает два параметра: путь к загружаемому скрипту и функцию обратного вызова, которая должна быть выполнена после успешной загрузки.Сама функция LoadScript имеет функцию обнаружения функции.По результатам обнаружения (IE и другие браузеры ), он определяет процесс мониторинга во время обработки скрипта, какое событие.
Фактически функция LoadScript() здесь является прототипом того, что мы называем LazyLoad.js (отложенная загрузка).
С помощью этого метода мы можем реализовать простой многофайловый загрузочный блок кода в фиксированном порядке:
LoadScript('file-1.js', function(){
LoadScript('file-2.js', function(){
LoadScript('file-3.js', function(){
console.log('loaded all')
})
})
})
Когда приведенный выше код выполняется, файл-1.js будет загружен первым, а затем после загрузки будет загружен файл-2.js и так далее. Конечно, такой способ написания определенно под вопросом (многократное вложенное написание обратного вызова — это просто ад), но идея динамического добавления скрипта и проблем, на которые нужно обращать внимание и которых нужно избегать в процессе загрузки, все проясняется. и решается в функции LoadScript.
Конечно, если файлов слишком много и требуется порядок загрузки, лучшим решением будет предложить их объединить и загрузить вместе в правильном порядке, что является лучшим способом во всех аспектах.
Оптимизировано из метода загрузки: внедрение скрипта XMLHttpRequest
Получение скрипта через объект XMLHttpRequest и внедрение его на страницу — это еще один способ добиться неблокирующей загрузки. Я не думаю, что это сложно понять. На самом деле это та же идея, что и способ динамического добавления скриптов. посмотрите на конкретный код:
var xhr = new XMLHttpRequest();
xhr.open('get', 'file-1.js', true);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
// 如果从后台或者缓存中拿到数据,则添加到script中并加载执行。
var script = document.createElement('script');
script.type = 'text/javascript';
script.text = xhr.responseText;
// 将创建的script添加到文档页面
document.body.appendChild(script);
}
}
}
Полученные таким образом данные имеют два преимущества: во-первых, мы можем контролировать, должен ли скрипт выполняться немедленно, потому что мы знаем, что вновь созданный тег скрипта будет выполняться немедленно, как только он будет добавлен в интерфейс документа. интерфейс, то есть перед appendChild() мы можем реализовать требования в соответствии с нашей фактической бизнес-логикой.Когда мы хотим, чтобы он выполнялся, мы можем appendChild(). Во-вторых: хорошая совместимость, поддерживается всеми основными браузерами, не нужно уподобляться способу динамического добавления скриптов, код определения фичи пишем сами;
Однако, поскольку используются объекты XHR, недостатком является наличие «доменных» ограничений на получение таких ресурсов. Ресурсы должны находиться в одном домене и не могут работать между доменами.
окончательное резюме
В статье в основном исследуются и обсуждаются решения для оптимизации внешнего интерфейса в процессе загрузки и выполнения JavaScript, а также подробно перечисляются преимущества и недостатки каждого решения.Конечно, оптимизация производительности внешнего интерфейса относительно сложна. долгий путь, чтобы понять причины этого!
Основные идеи этой статьи:
-
Функции блокировки JavaScript, о которых нужно сказать
-
Размещайте сценарии разумно, чтобы оптимизировать загрузку, и размещайте сценарии js в
<body>
до закрытия этикетки. -
Сокращайте HTTP-запросы, сжимайте и упрощайте код скрипта.
-
Загружать скрипты JavaScript без блокировки:
-
использовать
<script>
Атрибут отсрочки тега. -
Используйте асинхронный атрибут HTML5.
-
динамически созданный
<script>
Элемент загружает JavaScript. -
Загрузите JavaScript с помощью объектов XHR.
-
Наконец, по причинам личного характера, если в тексте есть какие-либо неполноты или упущения, я призываю всех читателей критиковать и исправлять меня, я буду всем вам благодарен! .
Благодаря этой эпохе мы можем стоять на плечах гигантов и подглядывать за великолепием мира программирования.Я хотел бы путешествовать по горам и рекам мира программирования с чистым сердцем! Пусть каждый коллега, идущий по миру программирования, живет так, как ему хочется, давай!