Исходный текст опубликован на github.Если в Nuggets есть картинки, которые невозможно загрузить, вы также можете прочитать исходную версию напрямую.
Сегодня, когда в Интернете сотни цветов, скорость отклика веб-сайта является первым элементом пользовательского опыта, и его важность очевидна.Вот несколько важных условий для времени отклика:
Пользователи не заметят задержку менее 0,1 секунды при просмотре веб-страниц;
Задержка менее 1 секунды не прерывает мышление пользователя, но некоторые задержки могут быть замечены пользователем;
Если время задержки меньше 10 секунд, пользователь будет продолжать ждать ответа;
После того, как время задержки превысит 10 секунд, пользователь сдастся и начнет другие операции;
Поэтому все стали сосредотачиваться на оптимизации производительности, и многие производители начали делать некоторую оптимизацию производительности. Более известным является военный устав Yahoo, но с развитием браузеров и протоколов некоторые из них были постепенно устранены. Поэтому рекомендуется посмотреть на него с исторической точки зрения. Например, максимальное сокращение количества HTTP-запросов не работает в протоколе HTTP2, поскольку HTTP2 реализует мультиплексирование HTTP, сокращая количество HTTP-запросов и снижая производительность. Поэтому надо смотреть на конкретные принципы и средства оптимизации в сочетании с исторической средой, иначе это будет контрпродуктивно.
Yahoo Military Rules китайская версия:блог woohoo.cn on.com/limited to bro...
С быстрым развитием мобильного Интернета количество мобильных терминалов растет в геометрической прогрессии, и многие производители начали обращать внимание на опыт мобильных терминалов. Например, Алекс, инженер Google Chrome, предложил Progressive Web App (далее PWA) для повышения производительности мобильного Интернета. Ядром PWA является Service Worker, который позволяет программистам программно управлять сетевыми запросами вне основного потока JS.В сочетании с кешем самого браузера часто можно добиться очень хорошего пользовательского опыта. PWA предлагает множество нативных «функций», таких как автономная загрузка и ярлыки на рабочем столе, чтобы сделать мобильный Интернет более удобным. Кроме того, характеристики веб-стороны, такие как быстрая итерация, индексируемость (вы можете выполнять поиск в поисковых системах, но не в нативных приложениях) и т. д., заставляют больше людей вкладывать средства в PWA, которые обеспечивают лучший пользовательский опыт для веб-пользователей. Google придумал AMP раньше. В 2017 году станция Google dev в Шанхае продвигала PWA и AMP и отображала их визуально с помощью движущегося изображения (PWA и A переворачиваются, а затем W переворачивается вверх и вниз, чтобы получить AMP, и наоборот). AMP — это облегченная веб-презентация для мобильных телефонов, которая повышает производительность мобильных устройств за счет повторной реализации тяжеловесных элементов. Кроме того, используяasm.jsУпрощение компиляции кода в машинные инструкции также является частью оптимизации производительности. Если вы внимательно посмотрите на профиль выполнения приложения, то обнаружите, что время компиляции js-кода будет занимать много времени, особенно когда вы пишете много бесполезного js-кода или кода, который не нужно выполнять в первую очередь. время эффект еще больше. Код js в конечном итоге компилируется в двоичный файл для выполнения машиной, а js — это динамический язык, то есть там, где код js выполняется и компилируется, это большое отличие от статического языка, такого как java. В V8 внесены существенные оптимизации в эту часть, в общем-то, нам не о чем беспокоиться, в общем-то, это не станет узким местом в производительности, потому что эти времена не на порядок меньше времени сетевого ввода-вывода. Но в определенных ситуациях может пригодиться заблаговременная компиляция в код, который легче интерпретировать и выполнять.
Преждевременная оптимизация — корень всех зол
Когда я впервые соприкоснулся с фронтендом, то часто видел такие примеры оптимизации производительности:
// bad
for(var i = 0;i < data.length;i++){
// do something...
}
// good
for(var i = 0,len = data.length;i < len;i++){
// do something...
}
Причина в том, что приведенное выше будет вычислять data.length каждый раз. Персональная оптимизация абсурдна, не говоря уже о том, как она на самом деле работает. Даже если производительность ниже выше, чем выше, я думаю, что такая оптимизация производительности должна быть сделана компилятором, а не бизнесом верхнего уровня.Это потеряет его внятность.Всем очевидно, что вышеприведенное лучше.Легко чтобы понять, не так ли?
Преждевременная оптимизация заставит людей впасть в психологическое непонимание. Это психологическое недоразумение типично для молотка в руке и повсюду гвоздей.
Другой момент заключается в том, что если вы оптимизируете слишком рано, вы часто будете ослеплены. Оптимизация производительности должна соответствовать принципу ствола, то есть на производительность системы всегда влияет короткая сторона системы. Если вы оптимизируете слишком рано, у вас часто будет болеть голова и лечить ноги, и вы будете заняты безрезультатно.
классическая проблема
Давайте вспомним, какие шаги проходит браузер от загрузки URL до отображения страницы:
- Браузер вызывает loadUrl для анализа URL-адреса.
- Браузер вызывает модуль DNS и напрямую возвращает IP-адрес, если в браузере есть кеш DNS. В противном случае запросите DNS локального компьютера и просматривайте слой за слоем.
Наконец, IP-адрес возвращается, а затем сохраняется в кэше DNS и устанавливается время истечения срока действия.
Советы. В браузере Chrome введите chrome://dns/, чтобы просмотреть записи DNS, кэшированные Chrome.
-
Браузер вызывает сетевой модуль. Сетевой модуль устанавливает TCP-соединение с целевым IP-адресом и по пути выполняет трехэтапное рукопожатие.
-
Браузер отправляет http-запрос в следующем формате:
header
(空行)
body
-
Запрос поступает на целевую машину и устанавливает соединение с целевым веб-сервером через порт.
-
Веб-сервер получает поток запросов, анализирует поток запросов, а затем выполняет некоторую обработку столбцов, возможно, запрашивая базу данных и т. д.
Окончательный ответ возврата передается на внешний интерфейс.
- Браузер загружает документ (загрузка контента) и анализирует документ. Процесс разбора выглядит следующим образом:
Зная шаги, которые браузер предпринимает для загрузки веб-страницы, мы можем выбрать «относительно подходящую» стратегию по каждой из приведенных выше ссылок для оптимизации скорости загрузки. Например, на втором шаге выше будет выполняться поиск DNS, поэтому поиск DNS займет время.Если DNS заранее анализируется и кэшируется, эта часть потери производительности может быть уменьшена. Например, после установления TCP-соединения можно поддерживать длительное соединение.сериалпослать запрос. Друзья, знакомые с асинхронностью, должны знать, что потери серийника очень велики, а время его загрузки зависит от суммы времени загрузки ресурсов. принимаяпараллельноСпособ самый долгий по времени загрузки из всех. В настоящее время мы можем рассмотреть возможность сокращения HTTP-запросов или использования протокола, поддерживающего параллелизм (например, протокола HTT2). Если вы знакомы с принципом работы браузеров или внимательно наблюдаете за трансформацией графика загрузки сети, то обнаружите, что существует верхний предел одновременно загружаемых ресурсов (в зависимости от браузера), который является пределом браузера по максимальное количество подключений, установленных для одного доменного имени, поэтому рассмотрите возможность добавления нескольких доменов для оптимизации. Есть много похожих, о которых вам остается подумать. Но резюмировать можно только два момента, один — это оптимизация загрузки, то есть повышение скорости загрузки ресурсов. Второй — оптимизация рендеринга, то есть оптимизация этапа после получения ресурса до завершения парсинга.
После простого объяснения выше, я думаю, у всех есть общее представление о том, как браузер загружает HTML и начинает отображать страницу.Я объясню этот процесс более подробно позже. Существует термин, называемый критическим путем, который относится ко времени с момента получения браузером байтов HTML, CSS и JavaScript до их необходимой обработки для превращения их в визуализированные пиксели. В этом процессе есть несколько промежуточных шагов, и оптимизация производительности на самом деле заключается в понимании того, что происходит на этих этапах. Помните, что ресурсы на критическом пути включают HTML, CSS и JavaScript, которые не включают изображения. Хотя изображения очень распространены в наших приложениях, изображения не мешают взаимодействию с пользователем, поэтому критический путь не рассчитывается. сосредоточить внимание в подразделах ниже.
Чтобы дать всем более четкое понимание, я более подробно объясню CSSOM и DOM, а также процесс построения дерева рендеринга на седьмом шаге вышеописанных шагов загрузки веб-сайта браузером.
Браузер запрашивает файл HTML с сервера, и сервер отвечает браузеру потоком байтов. Браузер получает HTML-код и декодирует его в соответствии с указанным форматом кодирования. После завершения содержимое HTML будет проанализировано, HTML будет разделен на токены, а затем будут сгенерированы разные DOM в соответствии с разными токенами, и, наконец, дерево DOM будет сгенерировано в соответствии с иерархической структурой в HTML.
Следует отметить, что если вы встретите теги CSS и теги JavaScript (не асинхронные или отложенные сценарии js), рендеринг будет приостановлен, и рендеринг будет продолжаться до тех пор, пока ресурсы не будут загружены. Если файл CSS загружен (то же верно и для интровертных стилей), CSSOM будет сгенерирован после загрузки CSS. Процесс генерации CSSOM аналогичен: он также делит CSS на токены, а затем генерирует CSSOM в соответствии с разными токенами.CSOM используется для управления стилем DOM. Наконец, DOM и CSSOM синтезируются в дерево рендеринга.
CSS заблокирован ресурсы рендеринга. Это должно быть как можно скорее, как можно быстрее, чтобы загрузить клиента, чтобы сократить время для сначала.
Чтобы узнать точный размер и положение каждого объекта на веб-странице, браузер переходит от корневого узла дерева рендеринга, генерирует вычисляемый стиль (называемый вычисляемым стилем в chrome) в соответствии с блочной моделью и правилами расчета CSS. , и, наконец, вызывает поток рисования для DOM, отрисовываемого на странице. Поэтому оптимизация каждого из вышеперечисленных шагов очень важна. Теперь у нас есть четкое понимание того, что ключевой ресурс HTML — это отправная точка всего, и без HTML за ним нет никакого смысла. CSS следует загружать и анализировать как можно скорее, обычно мы помещаем CSS в голову для загрузки и выполнения в первую очередь, точно так же, как концепция оболочки приложения. Мы должны сначала представить пользователю наименьшее подмножество, а затем медленно отображать остальные, точно так же, как PJPEG (прогрессивный jpeg). Следующее изображение является примером прогрессивного рендеринга (изображение с сайта developer.google.com):
Мы не обсуждали JavaScript, который теоретически может как манипулировать CSS, так и напрямую изменять DOM. Браузер не знает конкретного содержимого JavaScript, поэтому по умолчанию JavaScript блокирует выполнение механизма рендеринга и вместо этого выполняет поток JS.Если это внешний файл JavaScript, браузер должен остановиться и дождаться получения скрипта из диск, кеш или удаленный сервер. , что может увеличить задержку от десятков до тысяч миллисекунд на критических путях рендеринга, если они не отмечены асинхронностью или отсрочкой. Добавление ключевого слова async к тегу скрипта может указать браузеру не блокировать построение DOM в ожидании доступности скрипта, что может значительно повысить производительность.
После проведенного выше анализа мы знаем критический путь. Мы можем использовать инструмент разработки Chrome, чтобы просмотреть каскадную диаграмму, проанализировать критический путь веб-сайта и проанализировать медленную загрузку и узкие места, влияющие на скорость веб-сайта.
Вы также можете использовать некоторые инструменты для обнаружения, такие как тест веб-производительности, упомянутый выше, или попробовать Lighthouse.
В следующих разделах я представлю API производительности, вы можете закопать точки во фронтенде, а затем проанализировать показатели производительности сайта, это также важное дополнение к другим методам анализа.
Показатели производительности браузера
несколько ключевых показателей
время белого экрана
Пользователь начинает с открытия страницы до тех пор, пока страница не начнет отображаться. Долгое время белого экрана невыносимо, поэтому есть много способов сократить время белого экрана. Например, уменьшить загрузку контента на первом экране, затемнить контент на первом экране и т. д. Самый старый метод измерения белого экрана таков:
<head>
<script>
var t = new Date().getTime();
</script>
<link src="">
<link src="">
<link src="">
<script>
tNow = new Date().getTime() - t;
</script>
</head>
Но в приведенной выше ситуации можно измерить только html-контент на первом экране, например, метод рендеринга на стороне клиента, такой как react, не будет работать. Если используется метод рендеринга на стороне клиента, его необходимо вернуть в интерфейсе первого экрана.
И запишите место, где отображается страница.
С помощью аналогичного метода мы также можем просмотреть время загрузки других ресурсов, таких как изображения.В качестве примера возьмем изображения:
<img src="xx" id="test" />
<script>
var startLoad = new Date().getTime()
document.getElementById('test').addEventListener('load', function(){
var duration = new Date().getTime() - startLoad();
}, false)
</script>
Этот метод слишком трудоемок, а API производительности браузера предоставляет множество полезных интерфейсов, облегчающих вам расчет различных показателей производительности. Производительность API будет подробно описана ниже.
Время загрузки выше сгиба
Время, которое мы говорим над сгибом, относится к времени, когда контент, который видит пользователь, когда нет прокрутки, отображается и с ним можно взаимодействовать. Что касается времени загрузки, то это когда вся страница прокручивается вниз, и все загружается и становится интерактивным. Пользователь может выполнять обычные действия по вводу событий.
firstscreenready - navigationStart
Это время, когда пользователь фактически воспринимает скорость веб-сайта. У firstscreenready нет этого API-интерфейса производительности, и различные методы рендеринга (рендеринг на стороне сервера и методы расчета рендеринга на стороне клиента также различаются) не могут быть обобщены. Конкретная схема расчета подробно написана в этой статье.Расчет времени выше сгиба
время полной загрузки
Обычно веб-страницы используют время срабатывания двух событий для определения времени загрузки страницы.
-
Событие DOMContentLoaded представляет содержимое, непосредственно написанное на HTML-странице, но не включает время загрузки внешнего ресурса, где внешний ресурс относится к содержимому, для которого необходимо генерировать дополнительные HTTP-запросы, такие как css, js, изображения и вспышка.
-
Событие onload указывает время завершения загрузки внешнего ресурса.
domComplete - domLoading
Performance API
Выше был представлен старый метод измерения ключевых показателей, основной принцип которого основан на принципе загрузки браузера сверху вниз. Просто вышеописанный метод более хлопотный и не подходит для использования в реальных проектах. В реальном проекте по-прежнему используется метод «наведи и снимай». То есть закопать точки в ключевых местах, а затем рассчитать точечную информацию по мере необходимости, чтобы получить различные показатели, которые мы хотим видеть.API производительности такая штука.
The Performance interface provides access to performance-related information for the current page. It's part of the High Resolution Time API, but is enhanced by the Performance Timeline API, the Navigation Timing API, the User Timing API, and the Resource Timing API.
------ Выдержка из MDN
Введите performance.timing в консоли браузера.
Каждый возвращенный байт соответствует каждому состоянию процесса производительности, описанному ниже, и возвращает время. Это отличается от времени прямого new Date().getTime() в js. Это время не имеет ничего общего с реальным временем, а производительность API более точна.
С помощью этого API производительности мы можем легко рассчитать различные показатели производительности. Если нам недостаточно «скрытых точек» API-интерфейса performance, мы также можем настроить некоторые важные для нас показатели, такие как время запроса (отдельная статистика успеха и неудачи), более длительное время работы js или более важные функции. Короче говоря, пока нам нужна статистика, мы можем легко добиться ее с помощью API производительности.
Для получения дополнительной информации о производительности API, пожалуйста, проверьтеdeveloper.Mozilla.org/en-US/docs/…
средства контроля производительности
журнал
Мониторинг основан на журнале
Хорошо сформированный и исчерпывающий журнал является важным условием наблюдения, и можно сказать, что фундамент определяет надстройку. Хорошая система регистрации обычно состоит из следующих частей:
- уровень доступа
Журнал формируется в процессе входа в систему логов. Например, журналы, сгенерированные rsyslog, подключаются к системе журналов через logstash (переносят и обрабатывают ваши журналы, события или другие данные). Это относительно просто, потому что это система журналов, основанная на собственном Linux, и стоимость обучения и использования относительно невелика. Если разные системы компании должны вмешиваться в систему журналов, им нужно только записывать журналы в каталог журналов и собирать их через logstash.
- слой обработки журнала
Эта часть является ядром системы журналов, и уровень обработки может анализировать журналы, созданные уровнем доступа. Фильтрация журналов и отправка в центр мониторинга (мониторинг состояния системы) и центр хранения (агрегация данных, запрос и т. д.)
- уровень хранения журналов
Журналы хранятся на складе, а индексы устанавливаются в соответствии с деловой ситуацией. Эта часть обычно также может быть подключена к библиотекам, таким как эластичный поиск, для предоставления запросов к журналу.Приведенный выше logstash относится к семейству эластичных.
В дополнение к основным слоям выше обычно есть другие слои, которые выполняют более детальную работу.
Вообще говоря, журнал компании имеет следующие аспекты
- журнал производительности
Некоторые ключевые показатели записываются. Конкретные ключевые показатели см. в разделе «Показатели производительности браузера».
- журнал ошибок
Ошибки сервера журнала на бэкэнде (500, 502 и т. д.) и ошибки скрипта на внешнем интерфейсе.
- Журнал аппаратных ресурсов
Записывайте использование аппаратных ресурсов, таких как память, пропускная способность сети и жесткий диск.
- бизнес-журнал
Записывайте операции пользователей, которые больше беспокоят бизнес-сторону. Удобно локализовать проблему в соответствии с аномалией, о которой сообщил пользователь.
- Журнал статистики
Журналы статистики обычно основаны на содержимом сохраненных журналов. Статистический журнал немного похож на представление базы данных: представление скрывает структурную информацию базы данных и предоставляет пользователю часть базы данных. Анализ поведения пользователей обычно основан на статистическом анализе журнала. Некоторые компании даже вмешиваются в системы визуальной статистики журналов (например, Kibana). С появлением искусственного интеллекта искусственный интеллект + журнал — это одно направление.
журнал производительности
Вывод журналов производительности
В дополнение к ключевым показателям, записанным выше. Обычно нас больше волнует скорость отклика интерфейса. В это время мы можем записывать точками.
Использование журналов производительности
Мы подготовили журналы и имеем источники данных журналов. Итак, как вы потребляете данные? Обычной практикой сейчас является то, что сервер выгружает собранные журналы и отображает их администратору с помощью визуальных средств (иконки и т. д.). Другой заключается в том, что пользователи потребляют сами, то есть производят и продают сами. Пользователи генерируют данные и потребляют их сами, обеспечивая лучший пользовательский опыт. Узнать подробностиlocusОднако журналы производительности, очевидно, не могут производиться и продаваться нами самостоятельно, поэтому мы пока рассматриваем только первый.
Платформа мониторинга производительности
Крупные компании, занимающиеся платформами мониторинга, в основном имеют свои собственные системы. Например, есть такие, как Hawk, и SunFire от Ali. Небольшие компании часто используют системы мониторинга с открытым исходным кодом или вообще не используют их. Моя предыдущая компания не имела никакой платформы мониторинга, в лучшем случае это были только данные мониторинга, предоставляемые Alibaba Cloud. Поэтому я провел небольшое исследование в этом отношении. и начать разработкуПлатформа Сузаку, но из-за ограниченности энергии жаль, что план не был реализован в итоге. Суть мониторинга производительности заключается в предоставлении удобного запроса и визуальной статистики на основе отслеживаемых данных. И предупреждать о превышении порога (и обычно ограничения продолжительности). В предыдущем разделе была представлена платформа мониторинга производительности и упомянуты два компонента платформы мониторинга производительности: один — производитель, а другой — потребитель. В этом разделе описывается, как создать платформу мониторинга. Итак, позвольте мне сначала взглянуть на общую структуру
Для удобства пояснения здесь реализована лишь упрощенная модель, и читатели могут дополнительно разделить по этому признаку подсистемы, такие как доступ к SSO, разделение хранилища и отображения и т.д.
клиент
С одной стороны, клиент сообщает информацию о скрытой точке, а с другой стороны, сообщает информацию о дорожке.
Сообщить информацию о закопанных точках
Эта часть в основном использует некоторые средства, такие как API-интерфейс производительности, чтобы сообщать соответствующую информацию о времени загрузки веб-страницы серверной части.
performance.getEntriesByType("resource").forEach(function(r) {
console.log(r.name + ": " + r.duration)
})
С другой стороны, для определенного интерфейса асинхронных запросов RBI. Управляйте всеми взаимодействиями с пользователем (клики, наведения и т. д.)
const startTime = new Date().getTime();
fetch(url)
.then(res => {
const endTime = new Date().getTime();
report(url , 'success', endTime - startTime);
})
.catch(err => {
const endTime = new Date().getTime();
report(url, 'failure', endTime - startTime);
})
сообщить информацию о треке
Загрузить информацию о треке очень просто. Если это гранулярность страницы, об этом можно сообщить прямо на странице. Если используется внешняя маршрутизация, об этом также можно сообщить в функции перехватчика маршрутизации.
pageA.js
// 上报轨迹
report('pageA',{userId: '876521', meta: {}})
Итак, у нас есть источник данных.
Сервер
На сервере уже есть данные, а серверной части необходимо отформатировать и вывести данные.
locus server
Клиент сообщает серверу свою собственную информацию, а сервер выполняет статистику и сводку и, при необходимости, отправляет обработанные данные клиенту, чтобы управлять поведением клиента (например, предварительной загрузкой).
Как упоминалось ранее, информация, загруженная клиентом, вероятно,
{
userId: 876521,
page: "index",
area: "",
age: "",
// 其他群体特征
}
Групповыми характеристиками мы называем регион, возраст и т. д. Групповые характеристики очень важны для статистического анализа.
Мы можем агрегировать отдельных пользователей или групповые характеристики для прогнозирования поведения клиентов.
Данные, которые мы собираем, могут быть:
{
userId: '876521',
pages: {
"index": {
"detail": 0.5,
"return": 0.4,
"personnal-center": 0.1
}
}
}
Приведенный выше анализ основан на пользователе как на широте. Приведенные выше данные означают, что если пользователь 876521 находится на главной странице (индексе), то вероятность того, что на следующем шаге он посетит страницу подробностей (деталей), составляет 50%, вероятность посещения персонального центра (персональный центр) составляет 10%, а вероятность выхода со страницы составляет 40%.
Мы можем предварительно загрузить страницу сведений, когда пользователь остается на главной странице, когда это возможно.
Мы также можем агрегировать групповые характеристики,
Совокупный результат может быть:
{
age: 'teen',
pages: {
"index": {
"detail": 0.5,
"return": 0.4,
"personnal-center": 0.1
}
}
}
По сути, он аналогичен предыдущему, но используется не только для руководства определенного пользователя, но и может направлять пользователей с такими же групповыми характеристиками (здесь имеется в виду одна и та же возрастная группа).
zhuque server
Клиент будет загружать информацию о производительности на сервер zhuque.
Здесь сервер zhuque выполняет две основные функции:
- Визуально выводите данные, сообщаемые пользователями, для просмотра разными людьми.
Эту часть обычно легче сделать, чем трудно сделать хорошо. У меня нет большого опыта в этой области, поэтому я не буду вводить вас в заблуждение.
- Предоставление услуг оповещения, таких как страницы, которые не отвечают в течение длительного времени, не могут быть открыты, и проблемы с ключевым ресурсом 404.
Существует много типов предупреждений, таких как электронные письма, телефонные звонки, текстовые сообщения, DingTalk и т. д. Нам нужно только установить условия срабатывания, затем написать запланированную задачу или проверить на уровне запроса и вызвать сигнал тревоги, если он удовлетворен. Логика очень проста.
Запланированные задачи оказывают меньшую нагрузку на систему, но имеют низкую своевременность и подходят для предприятий, которым не требуется высокая производительность в режиме реального времени. Система проверки на уровне запроса находится под большим давлением, но своевременность гарантируется, что подходит для предприятий, которым требуется очень высокая производительность в режиме реального времени.
Средства оптимизации производительности
Чтобы выполнить оптимизацию производительности, вы должны сначала иметь полное представление о процессе работы системы, а затем проанализировать каждое звено, чтобы найти узкое место в системе, чтобы провести оптимизацию. Здесь я не перечисляю различные средства оптимизации производительности, а описываю общие направления оптимизации и средства оптимизации производительности один за другим с точки зрения трех уровней внешнего интерфейса. Если вам нужен полный список оптимизаций, вот более полныйFront-End-Checklist, для оптимизации производительности есть определенное эталонное значение. Также вы можете посетитьwebpagetestТестирование производительности вашего сайта и пошаговая оптимизация скорости загрузки вашего сайта на основе отзывов, которые предоставляет ваш сайт, выходит за рамки этой статьи. Одним из наиболее важных принципов оптимизации производительности является:Всегда представляйте необходимый контент, мы можем загружать данные «по запросу», лениво загружая ресурсы ниже сгиба или используя разбиение на страницы. Некоторые конкретные методы оптимизации описаны ниже. Многие знают, что внешний интерфейс делит приложение на три уровня, а именно структурный уровень, уровень представления и уровень поведения. Поговорим о направлении оптимизации производительности с точки зрения трех слоев.
структурный слой
Структурный слой относится к структуре DOM, а структура DOMкак правилоЭто определяется структурой HTML, поэтому мы в основном анализируем точки оптимизации производительности структуры HTML. Мы знаем, что манипуляции с DOM обходятся очень дорого, что также упоминалось в предыдущей истории фронтенд-разработки. Как уменьшить количество DOM, уменьшить количество манипуляций с DOM, необходимых для оптимизации Где сосредоточиться.
AMP HTML
Когда дело доходит до оптимизации HTML, следует упомянуть AMP HTML. Основная идея AMP — обеспечить лучший пользовательский опыт на мобильных устройствах. Он состоит из трех основных частей: AMP HTML, AMP JS и AMP Cache.
AMP HTML is HTML with some restrictions for reliable performance.
Ниже приведен типичный HTML-код AMP.
<!doctype html>
<html ⚡>
<head>
<meta charset="utf-8">
<link rel="canonical" href="hello-world.html">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<script async src="https://cdn.ampproject.org/v0.js"></script>
</head>
<body>Hello World!</body>
</html>
Видно, что AMP HTML состоит из обычных тегов HTML и тегов amp. Что делает тег amp? Позвольте мне сказать вам, операции DOM дороги, но разные эффективности DOM бессмысленны. Например, рендеринг тега и рендеринг img или табличного времени определенно не одно и то же. Мы называем элементы, которые отображаются быстрее, такие как тег a, световыми элементами. Такие элементы, как table и img, называются тяжелыми элементами. Тогда нужно стараться избегать появления тяжелых элементов, например, таблицу можно реализовать с помощью ul li. Причина, по которой img работает относительно медленно, заключается в том, что хотя загрузка изображения является асинхронной, она будет занимать сетевые потоки и отправлять еще один запрос (количество одновременных запросов браузера ограничено), поэтому он может дополнительно инкапсулировать легкие элементы (такие как x-image . , компонент может отложить отправку запроса изображения и дождаться рендеринга основной структуры перед отправкой запроса изображения). Рассмотрите возможность инкапсуляции его как веб-компонента или других компонентов (например, реагирующих компонентов).
Возвращаясь к AMP HTML только что, на самом деле есть интерфейс, такой как amp-image в amp, который, вероятно, может быть реализован в соответствии с вашими потребностями X-image, о котором мы упоминали выше, на самом деле является компонентом, который реализует спецификацию интерфейса amp. .
уменьшить ненужную вложенность
Как упоминалось ранее, используйте как можно больше легких элементов. Таким образом, помимо использования световых элементов, также очень важно уменьшить количество DOM. Важным способом уменьшения объема DOM является сокращение количества избыточных тегов. Например, мы очищаем поплавок, добавляя новый элемент.
<div class="clear"></div>
Однако в настоящее время он реализован с использованием псевдоэлементов. Другой способ — уменьшить уровень вложенности. много людей будет<form>
или<ul>
Внешний мешок<div>
Ярлыки, зачем добавлять тот, который вам вообще не нужен<div>
А этикетки? На самом деле вы можете использовать селекторы CSS для достижения того же эффекта. Важно отметить, что я видел много такого кода.
<div class="form">
<form>
...
</form>
</div>
Это можно написать так:
<form class="form">
...
</form>
уровень представления
Уровень представления — это то, что мы обычно используем в CSS. После того, как браузер проанализирует CSS, будет сгенерировано ДЕРЕВО CSS, а затем ДЕРЕВО ВОСПРОИЗВЕДЕНИЯ будет синтезировано с ДЕРЕВО DOM. Иногда некоторые функции могут быть реализованы полностью через CSS, и нет необходимости использовать javaScript, особенно для анимации, CSS3 добавляет переход и преобразование для достижения анимации, и разработчики даже могут использовать 3D-ускорение, чтобы полностью использовать производительность графического процессора. Поэтому очень важно уметь пользоваться CSS и владеть навыками оптимизации CSS. Оптимизация производительности CSS обычно фокусируется на двух аспектах:
Улучшить производительность загрузки CSS
Для повышения производительности загрузки необходимо сократить время, необходимое для загрузки. Проще говоря, это уменьшение размера файла CSS, повышение скорости загрузки страницы и максимальное использование http-кэша. На уровне кода мы должны избегать введения ненужных стилей и разумно использовать наследование для сокращения кода.
<body>
<div class="list" />
</body>
body { color: #999 }
.list {color: #999} // color 其实可以继承body,因此这一行没必要
Другими свойствами, которые можно наследовать, являются цвет, размер шрифта, семейство шрифтов и т. д.
Вообще говоря, эта часть аналогична оптимизации статических ресурсов, таких как JS. Просто текущий веб-сайт имеет концепцию приоритета фреймворка, то есть сначала загружается тематический фрейм веб-сайта, который обычно является статической частью, а затем данные загружаются динамически, что дает пользователю ощущение, что веб-сайт является «быстрым». Статическое содержимое этой части обычно можно дополнить простой структурой HTML (Nav + нижний колонтитул) и стилями CSS. Для этого сначала нужно загрузить CSS фреймворка темы.Мы можем установить для этой части стиля фреймворка сдержанный стиль, но некоторые считают, что это не способствует сопровождению кода.
Улучшить производительность кода CSS
Браузеры имеют разную эффективность выполнения для разных кодов, а сложные стили (многоуровневая вложенность) также снижают эффективность синтаксического анализа CSS, поэтому сложные вложенные стили можно преобразовывать.
.wrapper .list .item .success {}
// 可以写成如下:
.wrapper .list .item-success {}
Другая часть — это анимация веб-сайта.Вообще говоря, анимация должна быть в пределах 16 мс, чтобы пользователь чувствовал себя очень плавно. Кроме того, мы также можем полностью использовать производительность графического процессора за счет 3D-ускорения. Вот цитата из Цзян Шуй:
Только на очень сложных страницах с большим количеством стилей будет выделено узкое место производительности CSS, и в это время следует больше думать о том, нужно ли делать такие сложные страницы.
поведенческий слой
Уровень поведения относится к содержанию взаимодействия с пользователем, которое в основном реализуется во внешнем интерфейсе с помощью JavaScript. Текущая спецификация JavaScript достигла es2017. Передняя часть более впечатляющая ES6 (то есть ES2015), потому что ES5 был выпущен в 2009 году, через 6 лет официально вышел ES6 в 2015 году, который добавил много интересных новых функций, Знаком с большинством фронтенда. Он даже однажды сказал, что текущий статус интерфейса — 536 (HTML5, CSS3, ES6), что показывает его влияние.
Не будет преувеличением сказать, что большинство современных интерфейсных проектов написаны на javascript. Поскольку js используется так часто, почему мало кто говорит об оптимизации производительности js? Во-первых, из-за развития промышленных технологий и повышения производительности аппаратных устройств производительность интерфейсных вычислений обычно не считается узким местом системы. Во-вторых, с выпуском движка V8 скорость выполнения js значительно увеличилась. В-третьих, потому что вычислительная производительность — это работа локального ЦП и памяти, которая не на порядок меньше по отношению к сетевому вводу-выводу, поэтому люди уделяют больше внимания оптимизации ввода-вывода. Так зачем оптимизировать производительность js? С одной стороны, внешний интерфейс будет использовать узел для выполнения некоторых средних слоев и даже внутреннего интерфейса, поэтому вам нужно сосредоточиться на использовании памяти, что сильно отличается от браузеров. С другой стороны, из-за того, что интерфейс иногда пишет сложный расчет, также будут проблемы с производительностью. Последний вопрос заключается в том, можем ли мы оптимизировать производительность сетевого ввода-вывода через JS, например, используя JS API для работы с webWorker или используя кеш localStorage.
Вычислительный кеш
Во внешнем интерфейсе иногда есть некоторые вычисления с относительно большими данными. Для некоторых сложных операций мы обычно кэшируем результаты вычислений для последующего использования. Концепция чистой функции упоминалась ранее.Чтобы использовать кеш вычислений, функция должна быть чистой функцией. Простой код функции кэширования выглядит следующим образом:
// 定义
function memoize(func) {
var cache = {};
var slice = Array.prototype.slice;
return function() {
var args = slice.call(arguments);
if (args in cache)
return cache[args];
else
return (cache[args] = func.apply(this, args));
}
}
// 使用
function cal() {}
const memoizeCal = memoize(cal);
memoizeCal(1) // 计算,并将结果缓存
memoizeCal(1) // 直接返回
Кэш сетевого ввода-вывода
Оптимизация вычислений обсуждалась ранее, и область ее оптимизации относительно невелика. Потому что не во всех системах будут сложные расчеты. Но сетевой ввод-вывод существует во всех системах, а сетевой ввод-вывод нестабилен. Сетевые ввод-выводы Скорость и локальные вычисления вообще не на порядок, к счастью, наш браузер обрабатывает сетевые запросы асинхронно (конечно, синхронность кода можно контролировать). Один из способов — сохранить результат сетевого запроса локально через локальный кеш и прочитать его непосредственно в следующем запросе, без необходимости повторной отправки запроса. Простая реализация:
function cachedFetch(url, options) {
const cache = {};
if (cache[url]) return cache[url];
else {
return fetch(url, options).then(res) {
cache[url] = res
return res;
}
}
}
Конечно, приведенная выше грубая реализация имеет много проблем, таких как отсутствие стратегии аннулирования кеша (такой как стратегия LRU или TTL), но основная идея заключается в следующем. Преимущество этого метода очевиднозначительноеВремя обратной связи системы уменьшено, конечно, недостатки столь же очевидны. Поскольку используется кеш, при обновлении данных необходимо учитывать синхронизацию обновления кеша, иначе данные будут несогласованными, что приведет к плохому взаимодействию с пользователем.
Структура данных, оптимизация алгоритмов
Оптимизация структур данных и алгоритмов менее связана с внешним интерфейсом. Однако, если вы столкнулись с операцией с относительно большим объемом вычислений, помимо использования кеша, вы также должны использовать определенную оптимизацию структуры данных и оптимизацию алгоритма. Например, сейчас имеется 50 000 данных о заказах.
const orders = [{name: 'john', price: 20}, {name: 'john', price: 10}, ....]
Мне нужно часто просматривать информацию о заказе одного из них на определенный день. Мы можем взять следующую структуру данных:
const mapper = {
'john|2015-09-12': []
}
Таким образом, мы находим, что скорость информации о заказе определенного человека станет O (1), что является постоянным временем. Вы можете понять, что индекс — это структура данных, тогда мы также можем применять свои собственные уникальные проекты, используя другие структуры данных и алгоритмы. Для оптимизации алгоритма мы можем сначала потребовать, чтобы мы могли идентифицировать сложность, общую сложность с O (N) O (NLOGN) O (N2). Для внешнего интерфейса самый простой код для выявления плохой сложности, например, код N-три или N-шаг. Хотя нам не нужно писать очень хороший код, постарайтесь не писать код высокой сложности.
Многопоточный расчет
С помощью нового веб-оператора API HTML5 разработчики могут передать расчет рабочему процессу, а затем отправить результат расчета обратно в основной процесс посредством обмена данными между процессами. Нет сомнений в том, что этот метод имеет очень явное преимущество, так как требует большого объема вычислений.
Код взят изGoogle performance:
var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);
// The main thread is now free to continue working on other things...
dataSortWorker.addEventListener('message', function(evt) {
var sortedData = evt.data;
// Update data on screen...
});
Поскольку WebWorker настолько ограничен, что не может получить доступ к таким объектам, как окно и документ, если вам нужно его использовать, вам придется найти другие методы.
Один из способов использования веб-воркеров — «разделяй и властвуй», разбивая большую задачу на несколько маленьких задач, а затем суммируя результаты вычислений.Мы обычно используем структуру данных массива для его выполнения.Вот пример:
// 很多小任务组成的数组
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
// 使用更新的api requestAnimationFrame而不是setTimeout可以提高性能
requestAnimationFrame(processTaskList);
function processTaskList(taskStartTime) {
var taskFinishTime;
do {
// Assume the next task is pushed onto a stack.
var nextTask = taskList.pop();
// Process nextTask.
processTask(nextTask);
// Go again if there’s enough time to do the next task.
taskFinishTime = window.performance.now();
} while (taskFinishTime - taskStartTime < 3);
if (taskList.length > 0)
requestAnimationFrame(processTaskList);
}
Проблемы безопасности потоков вызваны глобальными переменными и статическими переменными. Если в каждом потоке есть только операции чтения глобальных и статических переменных, но нет операций записи, в общем случае эта глобальная переменная является потокобезопасной; если несколько потоков одновременно выполняют операции записи, необходимо учитывать синхронизацию потоков, которая может Возникают проблемы с безопасностью потоков.
Вам не нужно слишком беспокоиться, веб-воркеры проделали большую работу в этой области, например, у вас нет доступа к компонентам, не поддерживающим потоки, или к DOM, и вам нужно сериализовать объекты для взаимодействия с потоком. -конкретные данные. Поэтому, если вы хотите написать небезопасный для потоков код, это действительно не так просто.
Оптимизация кода под движок V8
V8 был предложен Google для повышения производительности за счет компиляции кода js в машинный код вместо кодов кортежей или их интерпретации. V8 также включает в себя множество эффективных алгоритмов, и многие разработчики будут изучать исходный код V8, чтобы совершенствоваться. Вот некоторые методы оптимизации js, подробности см. ниже.эта статьяи много другихинтересное исследование.
Бенедикт Мёрер (технический руководитель отдела оптимизации выполнения JavaScript в Chrome/V8) занимается исследованиями производительности V8, написал множество подробных статей и многое сделал с открытым исходным кодом.интересный проект, вы можете обратить на это внимание, если вы заинтересованы.
утечка памяти
Утечки памяти во фронтенде встречаются не очень часто, но знать о них все же необходимо, хотя бы для того, чтобы иметь возможность решить проблему при ее возникновении. В языках более низкого уровня, таких как C, есть операции применения memory malloc и уничтожения свободной памяти. В высокоуровневых языках, таких как java и js, детали выделения и уничтожения памяти экранируются, а затем ненужная память очищается GC (сборщиком мусора).
Только сам разработчик знает, когда память должна быть уничтожена.
К счастью, уничтожение памяти по-прежнему требует соблюдения определенных правил.В настоящее время существует две основные стратегии сборки мусора GC: подсчет ссылок и обнаружение недоступных объектов. В настоящее время основные браузеры реализуют два вышеуказанных алгоритма и будут использовать оба алгоритма для оптимизации памяти. Но действительно есть точки, которые не могут быть покрыты вышеуказанными алгоритмами, например замыкания. Поэтому все еще зависит от осведомленности самих разработчиков, поэтому очень полезно понимать принципы и решения утечек памяти. Ниже описаны несколько ситуаций, которые могут вызвать утечку памяти.
хвостовой вызов
Вызовы функций имеют определенные накладные расходы, в частности необходимость выделения пространства стека для функций. Если он вызывается рекурсивно, это может привести к взрыву стека.
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(100) // 一切正常
factorial(1000) // 有可能爆栈,但是现在浏览器做了优化,通常会输出Infinite
}
Хуже, если вы используете здесь более сложные операции, и еще хуже, когда вы добавляете замыкания.
Закрытие
Поскольку у js нет приватных свойств, если js хочет реализовать функцию приватных свойств, то это нужно реализовать с помощью замыканий.
function closure() {
var privateKey = 1;
return function() {
return privateKey
}
}
Однако, из-за механизма сборки мусора js, js будет периодически освобождать память, на которую не ссылаются.Если используется замыкание, функция сохранит ссылку на переменную, чтобы ее нельзя было уничтожить во время цикла сборки мусора. Злоупотребление замыканием может вызвать утечку памяти.
Оптимизация изображения
Средства оптимизации производительности анализируются с трехуровневой точки зрения внешнего интерфейса, но есть еще один ресурс, который не упоминается, а именно изображения. Как говорится, картинка стоит тысячи слов, а изображения составляют большую часть трафика на текущем веб-сайте. Хотя изображения не блокируют взаимодействие с пользователем и не влияют на критический путь, скорость загрузки изображений очень важна для взаимодействия с пользователем. Тем более, когда сегодня так развит мобильный интернет, также очень важно экономить трафик для пользователей. Таким образом, в оптимизации изображений есть два основных момента: один — использовать изображения, когда это необходимо, и использовать другие методы, когда это не нужно. Другой - сжать размер изображения.
Дайте понять, если вам нужно изображение
Первое, что нужно спросить себя, действительно ли вам нужно изображение для достижения желаемого эффекта. Хороший дизайн должен быть простым и всегда обеспечивать наилучшую производительность. Эта стратегия оптимизации всегда является лучшей стратегией, если вы можете исключить ресурсы изображения (которые обычно требуют больше байтов, чем HTML, CSS, JavaScript и другие ресурсы на странице). Однако при правильном использовании изображения также могут передать более тысячи слов, поэтому вам нужно найти баланс.
Сжать размер изображения
Давайте сначала посмотрим на детерминанты размера изображения. Здесь могут потребоваться некоторые знания иконографии. Картинки делятся на растровые и векторные. Растровое изображение использует биты для представления пикселей, а затем пиксели формируют изображение. Bitmap имеет концепцию битовой глубины, которая относится к количеству битов, используемых для хранения каждого пикселя. Затем есть формула для расчета размера растрового изображения, которое представляет собой количество пикселей изображения * битовая глубина в битах. Обратите внимание, что единицей измерения являются биты, которые также можно преобразовать в килобайты или мегабайты для удобства просмотра.
Количество пикселей изображения = количество пикселей изображения по горизонтали * количество пикселей изображения по вертикали
Векторная диаграмма состоит из математических векторов, а размер файла небольшой, и изображение не будет искажаться при увеличении, уменьшении или вращении. Затем векторная диаграмма вычисляется компьютером по данным, поэтому она занимает мало места. Обычно векторную и растровую графику также конвертируют друг в друга, например, векторную графику конвертируют в растровую при печати.
Оптимизация изображений, упомянутая ниже, относится к растровым изображениям. Зная определители размера изображения, способ уменьшения размера изображения - снизить разрешение или использовать более низкий формат изображения глубины бита.
уменьшить разрешение
Когда мы обычно разрабатываем, дизайнер нам даёт картинки 1х2х3х, и количество пикселей у этих картинок разное. 2x имеет в 2x2=4 раза больше пикселей, чем 1x, а 3x имеет до 3x3=9 раз больше пикселей. Картинка прямо в 9 раз больше. Поэтому при использовании изображений на фронтенде лучше не использовать 3x изображения напрямую, а затем разбивать их на разные устройства, такой подход потребует от браузера его масштабирования (что также потребует дополнительных ресурсов ЦП) и уменьшения энергопотребление, разрешение дисплея, что снижает производительность. Приведенные ниже табличные данные взяты из Google Developers.
Обратите внимание, что во всех вышеперечисленных случаях размер экрана всего «на 10 пикселей CSS меньше», чем ресурсы, необходимые для каждого разрешения экрана. Однако количество дополнительных пикселей и связанные с ними накладные расходы могут быстро возрастать по мере увеличения размера отображаемого изображения! Таким образом, хотя вы не можете гарантировать, что каждый ресурс будет отображаться точно с размером дисплея, вы должны убедиться, что количество лишних пикселей сведено к минимуму, и что особенно большие ресурсы отображаются с размером, максимально приближенным к размеру экрана. размер их дисплея.
Мы можем использовать медиа-запросы или srcset для загрузки разных ресурсов для разных экранов. Но 9-кратный размер нам все еще трудно принять. Отсюда следующий метод.
уменьшить битовую глубину
Разрядность — это количество байтов, используемых для представления цвета. Разрядность составляет 24 бита, что означает, что 256 (2 в степени 24/3) бит используются для представления цвета. Таким образом, чем больше битовая глубина, тем четче изображение. Если возможно, уменьшение разрядности может уменьшить громкость.
компрессия
сказал ранее图片大小 = 图片像素数 * 位深
на самом деле строже图片大小 = 图片像素数 * 位深 * 图片质量
, поэтому чем ниже качество изображения (q), тем меньше будет изображение. Существует множество факторов, влияющих на качество сжатия изображения, таких как количество цветовых типов изображения, количество соседних пикселей одного цвета и так далее. В соответствии со многими алгоритмами сжатия изображений, наиболее популярным сжатием изображений является формат webp. Поэтому, если позволяют условия, попробуйте использовать формат webp.
Конкретная практика оптимизации изображений
С вышеизложенной теорией нам нужно применить теорию на практике. Когда мы обычно разрабатываем, нам понадобятся миниатюры.Если вы загрузите исходное изображение и отобразите его с помощью элемента управления CSS, вы обнаружите, что загрузите очень большое изображение, но оно должно быть маленьким. Было бы неплохо, если бы мы могли контролировать загрузку только тех частей, которые нам нужны для отображения в миниатюрах. Мы надеемся, что черезhttps://test.imgix.net/some_file?w=395&h=96&crop=faces
способ указать размер изображения, тем самым уменьшая потери передаваемых байтов. Уже есть поставщики услуг изображений, которые предоставляют такую функцию. Например, имгикс.
Одним из преимуществ imgix является то, что он умеет находить интересные области на изображении и делать обрезку. вместо того, чтобы просто обрезать центр изображения
Упомянутый выше webp лучше всего поддерживается производителем CDN, то есть, когда мы загружаем картинки, производитель CDN сохраняет соответствующую копию webp. Например, мы загружаем изображение pnghttps://img.alicdn.com/test/TB1XFdma5qAXuNjy1XdXXaYcVXa-29-32.png
. Тогда мы можем пройтиhttps://img.alicdn.com/test/TB1XFdma5qAXuNjy1XdXXaYcVXa-29-32.webp
Получите доступ к соответствующему ресурсу webp. Мы можем загружать изображения webp или png в зависимости от поддержки браузера.
Второй эффективный способ — отложенная загрузка, важная идея — загружать только те изображения, которые должны отображаться в данный момент. Если вы используете реакцию, вы можете использовать react-lazyload для ленивой загрузки изображений. Другие фреймворки могут искать самостоятельно.
import LazyLoad from 'react-lazyload';
<LazyLoad once height={200} offset={50}>
<img
srcSet={xxx}
sizes={xxxxx}
/>
</LazyLoad>
Суммировать
Если вы предприняли множество мер по оптимизации, но пользователи по-прежнему чувствуют себя очень медленно, что мне делать? Вы знаете, хорошая ли производительность измеряется не данными, а интуитивным ощущением пользователей, как я сказал в начале. Один из способов заставить пользователя чувствовать себя быстрее без изменения скорости — это разумно использовать анимацию. Например, индикатор выполнения, показывающий текущий прогресс на 90%, бегущий медведь? Но пользоваться им нужно с осторожностью, так как непродуманное анимационное оформление вызывает у пользователей отвращение.Представьте себе, когда вы видите долгожданную кнопку ОК, но она заблокирована бегущим медведем, вы никак не сможете ее нажать. ваше сердце быть как?
Кроме того, я привожу здесь только идеи по оптимизации производительности, а не освещаю все моменты оптимизации производительности.Например, протобуфер Google может уменьшить объем данных, передаваемых на фронт и бэкенд, тем самым повышая производительность. но мы Я считаю, что с помощью приведенной выше теории и идей оптимизации эти вещи можно увидеть и сделать.
Добро пожаловать, чтобы обратить внимание на мой общедоступный номер