<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"><title>Статьи For Web</title><link href="https://forweb.dev/ru/blog/feed.xml" rel="self"/><link href="https://forweb.dev"/><updated>2025-04-05T00:00:00Z</updated><id>https://forweb.dev/ru/blog/feed.xml</id><author><name>Андрей Романов</name><email>me@andreyromanov.com</email></author><entry><title>8 вещей, которые я хотел бы знать в начале карьеры</title><link href="https://forweb.dev/ru/blog/8-things-for-beginners/"/><updated>2015-01-17T00:00:00Z</updated><id>https://forweb.dev/ru/blog/8-things-for-beginners/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Я работаю в сфере веб-разработки больше пяти лет, и это похоже на приключение — знакомство с новыми технологиями и эксперименты с разными подходами. Временами я был успешен, временами я разочаровывался в себе. В этой статье я хочу рассказать вам о некоторых идеях, которые я постепенно осознал, работая веб-разработчиком. Надеюсь, что вас научат чему-то мои ошибки.&lt;/p&gt; &lt;h2&gt;1. Пишите чистый код&lt;/h2&gt; &lt;p&gt;Первое, что вы понимаете, когда начинаете работу над большим приложением — это то, что для отладки требуется куча времени. Возможно вы проведёте больше времени за отладкой, чем за написанием нового кода.&lt;/p&gt; &lt;p&gt;В таких ситуациях чрезвычайно важно писать правильно отформатированный и задокументированный код, соответствующий лучшим практикам. Представьте, что у вас есть сотни строк кода, и вы не имеете понятия о том, какая из них вызывает маленький баг. Ещё хуже будет, если вы напишете нечитаемый код — через какое-то время вы просто не будете знать, что делает тот или иной кусок кода. Вы точно хотите всех этих проблем? Посмотрите &lt;a href=&quot;http://www.sitepoint.com/6-tips-for-writing-better-code&quot;&gt;советы по написанию чистого кода&lt;/a&gt;.&lt;/p&gt; &lt;h2&gt;2. Язык в первую очередь, фреймворк потом&lt;/h2&gt; &lt;p&gt;Люди зачастую сперва изучают фишки фреймворка, и только потом обращают внимание на язык. Это неправильный путь.&lt;/p&gt; &lt;p&gt;Популярность Django можно отнести к преимуществам Python — поэтому важно познакомиться с Python перед тем, как браться за Django. Нельзя поступать наоборот.&lt;/p&gt; &lt;p&gt;Простая причина такого подхода заключается в том, что если вы знаете язык — вы можете понять механизм работы фреймворка. Если вы не имеете никакого представления об языке, вы никак не сможете понять, почему что-то во фреймворке сделано определённым способом.&lt;/p&gt; &lt;h2&gt;3. Изучайте JavaScript, а не jQuery&lt;/h2&gt; &lt;p&gt;JavaScript является самым доступным языком в мире. Любое устройство с браузером может выполнять JavaScript-приложения.&lt;/p&gt; &lt;p&gt;Ошибка, которую совершают начинающие разработчики, заключается в изучении jQuery. Вопросы &lt;a href=&quot;https://www.quora.com/I-only-know-jQuery-and-am-not-very-well-versed-with-vanilla-Javascript-Will-this-limit-me-if-I-am-ultimately-trying-to-get-a-job-as-a-junior-front-end-developer-If-so-what-are-some-free-resources-to-expand-my-Javascript-knowledge-Should-I-even-be-thinking-about-things-like-ember-or-angular-yet&quot;&gt;вроде этого&lt;/a&gt; на Quora дают понять, что jQuery весьма популярен среди людей, которые не имеют понятия о базовых принципах и возможностях JavaScript!&lt;/p&gt; &lt;p&gt;jQuery — это всего лишь набор вспомогательных функций, написанных на JavaScript. Единственная причина, по которой люди предпочитают jQuery — это то, что jQuery требует написания меньшего количества кода. Однако последние версии JavaScript (или ECMAScript в оригинале) имеют более дружелюбный синтаксис, благодаря чему &lt;a href=&quot;http://youmightnotneedjquery.com/&quot;&gt;в jQuery больше нет нужды&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Я не утверждаю, что использовать jQuery неправильно. Я всего лишь говорю вам о том, что надо следовать правильному пути обучения. Если вы не знаете о замыканиях и пространствах имён в JavaScript, как вы объясните использование &lt;code&gt;$&lt;/code&gt; в jQuery?&lt;/p&gt; &lt;h2&gt;4. Не просто читайте, а делайте&lt;/h2&gt; &lt;p&gt;Я часто видел разработчиков, которые читали туториалы или даже целые книги без всякой практики. Меня беспокоит одно — многое ли вы усвоите, только читая?&lt;/p&gt; &lt;p&gt;Если вы хотите изучить Ruby on Rails, попробуйте разработать небольшое приложение в процессе чтения туториалов и документации. Если вы хотите попробовать стэк &lt;abbr title=&quot;MongoDB, Express.js, Angular, Node.js&quot;&gt;MEAN&lt;/abbr&gt;, то запустите его на вашем компьютере и познакомьтесь с различными возможностями — это лучший способ изучения!&lt;/p&gt; &lt;h2&gt;5. Дилетант, но не специалист&lt;/h2&gt; &lt;p&gt;Хорошо изучать новые технологии, но следует иметь одну технологию для решения большинства задач. Для новичков изучение сразу нескольких языков выглядит весьма заманчиво, но лучше всего изучать только один язык пока вы не достигнете в нём определённого уровня мастерства.&lt;/p&gt; &lt;p&gt;После обретения языка для решения повседневных задач вы можете начать изучать новый язык. Вы также можете поменять ваши предпочтения, но мудрым решением будет освоить новый язык на приемлемом уровне, прежде чем переходить на него. Посмотрите некоторые советы по выбора языка для изучения.&lt;/p&gt; &lt;h2&gt;6. Изучайте системы контроля версий&lt;/h2&gt; &lt;p&gt;В наше время работа над проектом в одинучку — редкость. Чтобы взаимодействовать с командой, вам придётся изучить системы контроля версий.&lt;/p&gt; &lt;p&gt;Обычно разработчики не изучают контроль версий до тех пор, когда им это не понадобится позарез. Однако контроль версий необходим при работе в команде, и хорошо было бы изучить то, как он работает, а также изучить базовые команды системы контроля версий.&lt;/p&gt; &lt;h2&gt;7. Учитесь у других&lt;/h2&gt; &lt;p&gt;Самостоятельное изучение технологии — это круто, но иногда можно многому научиться, всего лишь посмотрев на чужой код. Будь то ваши коллеги или какие-то туториалы в интернете — пытайтесь найти чьи-то подходы к решению определённых проблем. И задавайте вопросы, если это необходимо.&lt;/p&gt; &lt;p&gt;Также для разработчиков важно понимать, что невозможно знать всё на свете, но знания доступны — всего лишь требуется загуглить их. Если вы — новичок, и вы застряли на какой-то проблеме, то велика вероятность того, что кто-то до вас уже сталкивался с ней — надо всего лишь найти решение в интернете.&lt;/p&gt; &lt;h2&gt;8. Просите о кодревью и наслаждайтесь им&lt;/h2&gt; &lt;p&gt;На протяжении многих лет кодревью улучшали мои навыки программирования. Просите коллег или знакомых о том, чтобы они проводили кодревью. Это помогает учиться и находить проблемы в вашем подходе к разработке. Если вы найдёте кого-то, кто искренне заинтересован в рассмотрении вашего кода, то относитесь к его ревью серьёзно. Можете взглянуть на &lt;a href=&quot;http://www.sitepoint.com/integrated-collaborative-code-reviewing-beanstalk/&quot;&gt;продвинутый способ проведения кодревью&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Напоследок — никогда не делайте кодревью самому себе. Код похож на искусство — тяжело принять чью-то точку зрения о ваших ошибках, но только это позволит вам стать лучше.&lt;/p&gt; &lt;hr /&gt; &lt;p&gt;Это перевод статьи &lt;a href=&quot;http://www.sitepoint.com/8-things-wish-id-known-started-developer&quot;&gt;8 Things I Wish I’d Known When I Started as a Web Developer&lt;/a&gt; Шомика Дайтари.&lt;/p&gt;</content></entry><entry><title>Принципы написания однородного HTML</title><link href="https://forweb.dev/ru/blog/principles-for-writing-idiomatic-html/"/><updated>2015-03-03T00:00:00Z</updated><id>https://forweb.dev/ru/blog/principles-for-writing-idiomatic-html/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Этот документ представляет собой общие рекомендации по стилю написания HTML-разметки. Данное руководство призывает к использованию общепринятых и устоявшихся подходов к написанию кода.&lt;/p&gt; &lt;h2&gt;1. Основные принципы&lt;/h2&gt; &lt;p&gt;Весь код в любой кодовой базе должен выглядеть так, будто его написал один человек, вне зависимости от реального количества разработчиков.&lt;/p&gt; &lt;p&gt;Строго следуйте принятому стилю.&lt;/p&gt; &lt;p&gt;Если вы сомневаетесь в том, какой стиль использовать, пользуйтесь общепринятыми и устоявшимися подходами.&lt;/p&gt; &lt;h2&gt;2. Пустое пространство&lt;/h2&gt; &lt;p&gt;В вашей кодовой базе должен быть лишь один стиль написания кода. Всегда согласованно используйте пустое пространство. Используйте пустое пространство для улучшения читаемости кода.&lt;/p&gt; &lt;p&gt;Никогда не смешивайте табы и пробелы в отступах.&lt;/p&gt; &lt;p&gt;Выберите между мягкой табуляцией (пробелы) и реальными табами. Придерживайтесь выбранного подхода (предпочтительны пробелы).&lt;/p&gt; &lt;p&gt;Если вы используете пробелы, выберите количество символов для отступа (предпочтительно 4 пробела).&lt;/p&gt; &lt;p&gt;Совет: настройте свой текстовый редактор так, чтобы он показывал непечатные символы. Это позволит вам устранить появление лишних пробелов и табов в конце строк или ещё где-либо.&lt;/p&gt; &lt;h2&gt;3. Форматирование&lt;/h2&gt; &lt;p&gt;Названия тегов и атрибутов всегда пишите в нижнем регистре.&lt;/p&gt; &lt;p&gt;На одной строке размещайте только один элемент.&lt;/p&gt; &lt;p&gt;Используйте дополнительный уровень отступов для каждого вложенного элемента.&lt;/p&gt; &lt;p&gt;Указывайте атрибуты без их значения, если это возможно (например, &lt;code&gt;checked&lt;/code&gt; вместо &lt;code&gt;checked=&amp;quot;checked&amp;quot;&lt;/code&gt;).&lt;/p&gt; &lt;p&gt;Всегда используйте двойные кавычки для значений атрибутов.&lt;/p&gt; &lt;p&gt;Не указывайте атрибут &lt;code&gt;type&lt;/code&gt; в элементах &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt; и &lt;code&gt;script&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;Всегда закрывайте теги.&lt;/p&gt; &lt;p&gt;Не используйте слеш в единичных тегах (&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; — правильно, &lt;code&gt;&amp;lt;img/&amp;gt;&lt;/code&gt; — неправильно).&lt;/p&gt; &lt;p&gt;Сохраняйте длину строки в пределах разумного максимума, например 80 символов.&lt;/p&gt; &lt;p&gt;Пример:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;tweet&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;path/to/somewhere&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;path/to/image.png&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;[tweet text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;disabled&lt;/span&gt;&amp;gt;&lt;/span&gt;Reply&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;button&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;Исключения и небольшие допущения&lt;/h3&gt; &lt;p&gt;В элементах с несколькими атрибутами эти атрибуты можно расположить на отдельных строках, чтобы улучшить читабельность:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-action&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-id&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;4. Порядок атрибутов&lt;/h2&gt; &lt;p&gt;HTML-атрибуты должны быть перечислены в порядке, учитывающем то, что названия классов — базовая вещь, которая позволяет выбирать элементы с помощью JavaScript или CSS:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;code&gt;class&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;id&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;code&gt;data-*&lt;/code&gt;&lt;/li&gt; &lt;li&gt;Любые другие атрибуты&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Пример:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-name&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;5. Именование&lt;/h2&gt; &lt;p&gt;Именование — вещь сложная, но очень важная. Это ключевая часть процесса написания поддерживаемого кода. Без правильного именования невозможно иметь относительно масштабируемый интерфейс между HTML и CSS/JS.&lt;/p&gt; &lt;p&gt;Используйте простые, понятные и уместные имена для классов. Имена должны быть информативны вне зависимости от контекста — как в разметке, так и в CSS-файлах.&lt;/p&gt; &lt;p&gt;Избегайте систематического сокращения имён классов. Не усложняйте код.&lt;/p&gt; &lt;p&gt;Пример с плохим именованием классов:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;cb s-scr&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;hljs-selector-class&quot;&gt;.s-scr&lt;/span&gt; { &lt;span class=&quot;hljs-attribute&quot;&gt;overflow&lt;/span&gt;: auto; } &lt;span class=&quot;hljs-selector-class&quot;&gt;.cb&lt;/span&gt; { &lt;span class=&quot;hljs-attribute&quot;&gt;background&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;#000&lt;/span&gt;; } &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Хороший пример именования:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;column-body is-scrollable&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;hljs-selector-class&quot;&gt;.is-scrollable&lt;/span&gt; { &lt;span class=&quot;hljs-attribute&quot;&gt;overflow&lt;/span&gt;: auto; } &lt;span class=&quot;hljs-selector-class&quot;&gt;.column-body&lt;/span&gt; { &lt;span class=&quot;hljs-attribute&quot;&gt;background&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;#000&lt;/span&gt;; } &lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;6. Практический пример&lt;/h2&gt; &lt;p&gt;Пример различных соглашений.&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;&amp;lt;!DOCTYPE &lt;span class=&quot;hljs-keyword&quot;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;head&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;meta&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;charset&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt;Document&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;title&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;rel&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;main.css&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;main.js&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;head&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;body&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;id&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;1234&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;March 15, 2012&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;time&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-id&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;1234&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-analytics-category&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-analytics-action&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[value]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;ul&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[text]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;ul&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;link-complex&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;[url]&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;link-complex__target&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;[text]&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;span&lt;/span&gt;&amp;gt;&lt;/span&gt; [text] &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;value&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;readonly&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;article&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;body&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;hr /&gt; &lt;p&gt;Это перевод &lt;a href=&quot;https://github.com/necolas/idiomatic-html&quot;&gt;Principles of writing consistent, idiomatic HTML&lt;/a&gt; Николаса Галлахера. Оригинал лицензирован по &lt;a href=&quot;http://creativecommons.org/licenses/by/3.0&quot;&gt;Creative Commons Attribution 3.0 Unported License&lt;/a&gt;. Лицензия распространяется на все документы и переводы.&lt;/p&gt;</content></entry><entry><title>О PostCSS от его создателя: интервью с Андреем Ситником</title><link href="https://forweb.dev/ru/blog/about-postcss/"/><updated>2015-07-18T00:00:00Z</updated><id>https://forweb.dev/ru/blog/about-postcss/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt; В последнее время сообществом фронтенд-разработчиков довольно активно обсуждается PostCSS. О том, что это и где оно применяется мы расспросили создателя PostCSS &lt;a href=&quot;http://sitnik.ru/&quot;&gt;Андрея Ситника&lt;/a&gt;. &lt;/p&gt; &lt;div class=&quot;chat&quot;&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Привет! Спасибо, что согласился побеседовать. Я думаю, разговор всё равно более-менее спонтанный получится, так что начну с одного вопроса, а дальше уже как пойдёт. Расскажи немного о себе? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Родился во Владивостоке. Детство прожил в Йемене. Университет закончил в Питере. Сейчас работаю фронтендером над Амплифером в Злых марсианах, поддерживаю Автопрефиксер и развиваю PostCSS. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; А как ты вообще стал программистом? Когда и с чего начал увлекаться этим? Какой путь совершил прежде чем прийти к фронтенду? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; В детстве я не хотел быть программистом — скорее хотел стать учёным. Но у меня была большая мечта — путешествовать по миру. И я подумал, что программирование — самый простой путь к этому. А потом я написал свою первую программу, запустил её и увидел, что она заработала. Это было непередаваемое ощущение — когда что-то, что ты сделал, живёт дальше само по себе. &lt;/p&gt; &lt;p&gt; Дальше был Basic, Pascal, школьные олимпиады. Затем я увидел мир PHP и веба — он мне сразу очень понравился. Там была настоящая свобода. Динамический язык для творчества. И результат доступный каждому. А потом я впервые увидел Ruby on Rails. Видео «блог за 5 минут» просто будоражило, так как оно показывало, что появляется совершенно новая эпоха. &lt;/p&gt; &lt;p&gt; Я бы не сказал, что я стремился во фронтенд. Просто мне было проще решать задачи с визуальной точки зрения. Проще понять дизайн. Так со временем становилось всё больше фронтенда и меньше бэкенда. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Получается, ты изначально немного занимался бэкендом, писал на PHP и Ruby? Насчёт PHP ничего не скажу, но вот Ruby весьма выразительный и красивый язык. Не жалеешь о переходе на JavaScript после него? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Скажем так, на чистом JS я бы вообще не смог программировать — но CoffeeScript, ES6 сильно развивают язык до какого-то приемлемого уровня. Многих вещей мне всё ещё не хватает, но уже терпимо. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; JavaScript по сравнению с CSS развивается довольно быстро. Если с появлением ES6 в языке появилось достаточно нововведений, благодаря которым можно обойтись без дополнительных инструментов вроде CoffeeScript, то в случае CSS использование различных препроцессоров уже стало стандартом де-факто. Раньше ты использовал какой-то препроцессор? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Ты так говоришь, как будто ES6 поддерживается во всех браузерах. Мы просто сменили один препроцессор, CoffeeScript, на следующий — Babel. Даже когда браузеры будут поддерживать ES6 мы всё равно будем использовать Babel ради ES7 и так далее. Но в сравнении с этим мне действительно не нравится застой в мире препроцессоров CSS — за последний год в Sass, Less, Stylus ничего не изменилось, и это приговор. &lt;/p&gt; &lt;p&gt; Раньше я использовал Sass, так как я из мира Ruby. &lt;/p&gt; &lt;/div&gt; &lt;div class=&quot;chat__message chat__message--out&quot;&gt; &lt;p&gt; Я раньше тоже использовал Ruby Sass, но он ооочень медленный. &lt;/p&gt; &lt;p&gt; В мире Sass/Less/Stylus действительно практически ничего не изменилось за последнее время. Хотя свои задачи они решают неплохо. Но стабильность без всякого развития губительна. &lt;/p&gt; &lt;p&gt; Зато в мире CSS появились такие инструменты как Rework, а затем PostCSS. Можешь для непросвещённых читателей вкратце рассказать, что это за инструмент такой — PostCSS, а главное, зачем он нужен? &lt;/p&gt; &lt;/div&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Технически &lt;a href=&quot;https://github.com/postcss/postcss&quot;&gt;PostCSS&lt;/a&gt; — это инструмент, который позволяет его JS-плагинам перестраивать CSS-файл. Но в реальности этот инструмент позволяет нам совсем по другому подойти к написанию CSS. На PostCSS можно написать совершенно новые инструменты, которые будут помогать писать CSS-код. Инструменты, о которых мы даже раньше и не думали. Речь идёт не о переменных или примесях — PostCSS это следующий этап, где эти вещи уже не так важны. &lt;/p&gt; &lt;p&gt; Например, &lt;a href=&quot;https://github.com/postcss/autoprefixer&quot;&gt;Автопрефиксер&lt;/a&gt; — он сам расставляет префиксы только там, где вам нужно. Не нужно писать примеси и тому подобное — он работает с обычным CSS. Или &lt;a href=&quot;http://cssnext.io/&quot;&gt;cssnext&lt;/a&gt;, который откомпилирует CSS4 в обычный CSS (как Babel для ES6). Или &lt;a href=&quot;https://github.com/luisrudge/postcss-flexbugs-fixes&quot;&gt;postcss-flexbugs-fixes&lt;/a&gt; — он содержит в себе базу данных ошибок реализации флексбокса в браузерах. И он автоматически исправляет или предупреждает вас, когда код будет не работать из-за этих ошибок. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; То есть по сути PostCSS получает написанный нами CSS (возможно невалидный), производит с ним определённые манипуляции, как-то его модифицирует и отдаёт нам уже преобразованный, валидный CSS? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Да. Например, плагин &lt;a href=&quot;https://github.com/postcss/postcss-nested&quot;&gt;postcss-nested&lt;/a&gt; в 60 строк просто проходиться по CSS и заменяет &lt;code&gt;a { b { } }&lt;/code&gt; на &lt;code&gt;a b {}&lt;/code&gt;. Но PostCSS не только про трансформацию CSS. &lt;/p&gt; &lt;p&gt; Например, Твиттер использует PostCSS, чтобы &lt;a href=&quot;https://github.com/postcss/postcss-bem-linter&quot;&gt;проверять БЭМ-нотацию&lt;/a&gt;. Или плагин &lt;a href=&quot;https://github.com/anandthakker/doiuse&quot;&gt;doiuse&lt;/a&gt;,который проверяет по Can I Use все ли используемые свойства поддерживаются в нужных вам браузерах. Ну и недавно анонсированный &lt;a href=&quot;https://github.com/stylelint/stylelint&quot;&gt;stylelint&lt;/a&gt; — очень умный CSS-линтер, построенный по модульный архитектуре, как ESLint. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Интересно. Ты упомянул Твиттер. На страничке PostCSS на гитхабе сказано, что PostCSS также используют Google, Alibaba и Shopify. А можно об этом подробнее? Какие задачи они решают с помощью PostCSS? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Shopify использует Автопрефиксер. Google использует Автопрефиксер и рекомендует его как единственный инструмент для работы с браузерными префиксами. Точный список плагинов у Alibaba не знаю, но они разрабатывают &lt;a href=&quot;https://github.com/cssdream/cssgrace&quot;&gt;cssgrace&lt;/a&gt; и несколько плагинов для cssnext. Так что точно используют эти два. В Китае просто большая проблема с IE — там до сих пор популярные версии 8, 7 и даже иногда 6. Поэтому cssgrace — это такой cssnext наоборот. Он находит вещи, которые не поддерживаются в старом IE и вставляет хаки для него. &lt;/p&gt; &lt;p&gt; Твиттер пошли дальше — они совсем отказались от препроцессоров. Когда я последний раз говорил с &lt;a href=&quot;https://github.com/necolas&quot;&gt;Николасом&lt;/a&gt;, он сказал, что они сейчас на Rework и переходят на PostCSS. &lt;/p&gt; &lt;/div&gt; &lt;div class=&quot;chat__message chat__message--out&quot;&gt; &lt;p&gt; А ещё я слышал, что bootstrap 5, вероятно, &lt;a href=&quot;https://twitter.com/mdo/status/591364406816079873&quot;&gt;будет переписан на PostCSS&lt;/a&gt;. Это похоже прямо-таки на революцию в мире CSS. &lt;/p&gt; &lt;p&gt; Опыт твиттера весьма любопытен. Многие разработчики боятся полностью переходить на PostCSS. Они в лучшем случае используют какой-нибудь препроцессор вместе с Автопрефиксером. Можно ли уже сейчас полностью отказаться от препроцессоров и продолжить решать те же задачи, но уже с PostCSS? Или он всё же не рассчитан на то, чтобы полностью заменить препроцессоры? &lt;/p&gt; &lt;/div&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; PostCSS сразу создавался чтобы заменить препроцессоры. Если мы можем делать такие сложные вещи, как Автопрефиксер или cssnext, то сделать вложенность или переменные очень легко. Как раз на этой неделе вышел &lt;a href=&quot;https://github.com/jonathantneal/precss&quot;&gt;PreCSS&lt;/a&gt; — набор плагинов для PostCSS, чтобы сделать что-то типа препроцессора. &lt;/p&gt; &lt;p&gt; Но тут есть важный нюанс. Если ваш проект уже написан на Sass — не переводите его на PreCSS, добавьте PostCSS после Sass. Дело в том, стагнация Sass длилась очень долго. В итоге у нас есть очень много не очень правильных способов организации код. Я видел как на Sass писали прямо целые программы — и это, конечно же, плохой подход. Это как делать SQL-запросы в шаблонах — сильное смешивание кода. Поэтому в PostCSS мы немного меняем философию. Ваши стили должны быть простыми. @for нужно использовать просто — чтобы не повторять одинаковые блоки, а не чтобы писать алгоритмы. Если вам нужна сложная логика — лучше всего её вынести в JS. И когда старый проект переписывается с Sass на PostCSS — это не просто вопрос смены синтаксиса, но скорее вопрос смены мышления. Лучше всего попробовать только PostCSS без препроцессоров на новом проекте — там вам буде легче понять философию PostCSS. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Расскажи, как вообще тебе в голову пришла идея создать PostCSS? Была необходимость решить какую-то задачу, которую не могли решить препроцессоры? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Всё началось из-за ненависти. Ненависти к Compass. У них было ужасное управление проектом — ты посылаешь PR, а они молчат по месяцам. Последней каплей было, когда моя знакомая закрыла ИП в российской бюрократии проще и быстрее, чем Compass принял мой PR. &lt;/p&gt; &lt;p&gt; Я понял, что Compass надо заменять. И главной вещью в Compass были префиксы. Я начал думать — как можно сделать управление префиксами ещё удобнее чем сейчас. Всё по ТРИЗ — как можно было бы управлять ими вообще идеально. И пришёл к идее Автопрефиксера, который сразу вырисовывался как проект, невозможный на препроцессорах. &lt;/p&gt; &lt;p&gt; Но полностью идею модульных процессоров придумал TJ Holowaychuk в проекте Rework. Первая версия Автопрефиксера даже называлась rework-vendors. Однако очень быстро Автопрефиксер перерос Rework: нам требовался парсер лучше, лучше поддержка карт кода. Ребята из Rework не хотели менять архитектуру. Так что я начал PostCSS как развитие идей Rework. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Теперь ты полностью отказался от препроцессоров и на работе используешь только PostCSS? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Да. Но мне от препроцессоров было нужно весьма мало: вложенность, какой-то простой конфиг с цветами сайта, простейшие циклы и примеси, чтобы не повторять код. Единственное чего не хватает из препроцессоров — простого синтаксиса типа Stylus. Но мы работаем над этим. В PostCSS 4.2 будут разные парсеры. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; О, вот это действительно круто. А как решать вопрос с подсветкой синтаксиса? Ведь, насколько мне известно, нет никаких цветовых схем, заточенных конкретно под PostCSS. Да и обилие разных плагинов усложняет эту задачу. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Плагины никакой проблемы не создают — они же не вводят новые синтаксические конструкции. Только новые свойства или @директивы. Сейчас SCSS-подсветка прекрасно работает с PostCSS — собственно PostCSS же работает с обычным CSS, только с возможностью вложенности. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; В нашем &lt;a href=&quot;http://vk.com/forwebdev&quot;&gt;сообществе&lt;/a&gt; в комментариях к одному из постов сказали, что было бы разумно ввести какое-нибудь специальное расширение для файлов стилей, написанных для PostCSS — ну чтобы хотя бы никакой путаницы не возникало. Что-то вроде main.pcss. Ты задумывался об этом? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; У нас точно будет специальное расширение для сокращённого синтаксиса — SugarSS. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; А как быть с обычным синтаксисом? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Пока не решили :). Некоторые плагины типа Автопрефиксера, cssnext или postcss-focus ничего в CSS не добавляют, им расширение не нужно. Может быть внутри проекта PreCSS придумают какое-о расширение. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; PostCSS называют постпроцессором. А чем постпроцессор отличается от препроцессора? Откуда вообще взялся этот термин? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Я сам не знаю откуда он взялся. Сейчас он уже потерял свой смысл — грань слишком стёрлась. Изначально постпроцессорами называли плагины PostCSS, которые работают с обычным CSS — например, Автопрефиксер, полифиллы будущих стандартов. Но для PostCSS есть набор плагинов PreCSS, который работает полностью как препроцессор. Я стараюсь уже не употреблять термин постпроцессоры :). &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Раз сам создатель рекомендует не использовать этот термин, то грех теперь его произносить :). И, наверное, последний вопрос — сколько человек на данный момент работает над PostCSS и какие у вас планы на будущее? &lt;/p&gt; &lt;div class=&quot;chat__message chat__message--in&quot;&gt; &lt;p&gt; Поскольку у нас модульный проект, то нельзя сказать о какой-то явной команде. Скорее это такое переплетение важных модулей. &lt;/p&gt; &lt;p&gt; Над ядром PostCSS работаю в основном я, хотя все остальные активно помогают мне с английским. Maxime Thirouin из Франции разрабатывает cssnext и stylelint и активно участвует в сообществе. Ben Briggs из Англии ведёт разработку важных инфраструктурных плагинов типа postcss-use и помогает новичкам в &lt;a href=&quot;https://gitter.im/postcss/postcss&quot;&gt;gitter-чате&lt;/a&gt;. Jonathan Neal из США разрабатывает PreCSS. 一丝 из Китая внедряет PostCSS в Таобао и другие проеты Алибабы и ответственен за пиар в Китае. David Clark из США занимается линтерами (Stylelint и postcss-bem-linter) и пишет отличные статьи, объясняющие PostCSS новичкам. &lt;/p&gt; &lt;p&gt; Это самое явное ядро проекта, но так или иначе участвует примерно 90 человек — кто-то пишет плагины, кто-то рассказывает про PostCSS. И жёсткой грани между «ядром» и остальными не существует. &lt;/p&gt; &lt;/div&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Ого, неплохо. А что в планах? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; В версии 4.2 добавим сменяемые парсеры. Можно будет парсить SCSS, Less, и специальный синтаксис типа Stylus, где не надо будет вводить фигурные скобки ({}) и точки с запятыми (;). В версии 4.3 добавим больше API — парсер селекторов, парсер значений. А потом будет большой релиз 5.0, где мы исправим API, сделав его более логичным — всё-таки некоторые части проекта развивались стихийно. Ещё в планах сайт и маленький митапчик PostCSSConf где-нибудь в Европе. &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Класс! А в специальном синтаксисе типа Stylus двоеточия останутся или нет? &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--in&quot;&gt; Пока ещё не решили. Хочется сохранить возможность писать многострочные значения (например для длинных градиентов). &lt;/p&gt; &lt;p class=&quot;chat__message chat__message--out&quot;&gt; Было бы круто реализовать лаконичный синтаксис из Stylus без двоеточий. Будем ждать новостей. Спасибо за беседу! Интересно было пообщаться. Желаем успехов тебе и твоему проекту! &lt;/p&gt; &lt;/div&gt;</content></entry><entry><title>Состояние загружаемых данных</title><link href="https://forweb.dev/ru/blog/2020-04-29-data-state/"/><updated>2020-04-29T00:00:00Z</updated><id>https://forweb.dev/ru/blog/2020-04-29-data-state/</id><content type="html">&lt;p&gt;Одна из повседневных задач каждого фронтендера — загрузка данных с сервера. На время загрузки обычно требуется показывать пользователю спиннер, а в случае неудачи — сообщение об ошибке. В коде состояние загрузки часто описывают булевыми флагами:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; comments = { &lt;span class=&quot;hljs-attr&quot;&gt;isLoading&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;isLoaded&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;isError&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;errorText&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;data&lt;/span&gt;: [], }; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;На первый взгляд такие флаги выглядят удобно. При их использовании код загрузки данных выглядит примерно так:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; comments = { &lt;span class=&quot;hljs-attr&quot;&gt;isLoading&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;isLoaded&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;isError&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;errorText&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;data&lt;/span&gt;: [], }; &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;loadComments&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;/span&gt;) { comments.&lt;span class=&quot;hljs-property&quot;&gt;isLoaded&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;isError&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;isLoading&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;; &lt;span class=&quot;hljs-title function_&quot;&gt;fetch&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;/comments&amp;#x27;&lt;/span&gt;) .&lt;span class=&quot;hljs-title function_&quot;&gt;then&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;response&lt;/span&gt;) =&amp;gt;&lt;/span&gt; response.&lt;span class=&quot;hljs-title function_&quot;&gt;json&lt;/span&gt;()) .&lt;span class=&quot;hljs-title function_&quot;&gt;then&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;data&lt;/span&gt;) =&amp;gt;&lt;/span&gt; { comments.&lt;span class=&quot;hljs-property&quot;&gt;isLoading&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;isLoaded&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;data&lt;/span&gt; = data; }) .&lt;span class=&quot;hljs-title function_&quot;&gt;catch&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;error&lt;/span&gt;) =&amp;gt;&lt;/span&gt; { commments.&lt;span class=&quot;hljs-property&quot;&gt;isLoading&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;isError&lt;/span&gt; = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;errorText&lt;/span&gt; = error.&lt;span class=&quot;hljs-property&quot;&gt;message&lt;/span&gt;; }); } &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Получается запутанно и многословно. Нужно внимательно следить, значения каких флагов следует сбросить, иначе в вашем приложении появятся бессмысленные комбинации флагов вроде &lt;code&gt;isLoading: true&lt;/code&gt; и &lt;code&gt;isLoaded: true&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;Более практичный подход, решающий эту проблему — описание состояния одним полем &lt;code&gt;dataState&lt;/code&gt; и автоматическое вычисление флагов на основе значения этого поля:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; dataStates = { &lt;span class=&quot;hljs-attr&quot;&gt;notAsked&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;notAsked&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;loading&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;loading&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;loaded&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;loaded&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;failed&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;failed&amp;#x27;&lt;/span&gt;, }; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; comments = { &lt;span class=&quot;hljs-attr&quot;&gt;dataState&lt;/span&gt;: dataStates.&lt;span class=&quot;hljs-property&quot;&gt;notAsked&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;errorText&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-attr&quot;&gt;data&lt;/span&gt;: [], &lt;span class=&quot;hljs-keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;isLoading&lt;/span&gt;() { &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; === dataStates.&lt;span class=&quot;hljs-property&quot;&gt;loading&lt;/span&gt;; }, &lt;span class=&quot;hljs-keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;isLoaded&lt;/span&gt;() { &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; === dataStates.&lt;span class=&quot;hljs-property&quot;&gt;loaded&lt;/span&gt;; }, &lt;span class=&quot;hljs-keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;isError&lt;/span&gt;() { &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; === dataStates.&lt;span class=&quot;hljs-property&quot;&gt;failed&lt;/span&gt;; }, }; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Кода стало чуть больше, зато значения всех флагов меняются автоматически, и вероятность ошибки из-за неправильного обновления одного из флагов сведена к нулю. Функция загрузки комментариев становится значительно проще:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;loadComments&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;/span&gt;) { comments.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; = dataStates.&lt;span class=&quot;hljs-property&quot;&gt;loading&lt;/span&gt;; &lt;span class=&quot;hljs-title function_&quot;&gt;fetch&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;/comments&amp;#x27;&lt;/span&gt;) .&lt;span class=&quot;hljs-title function_&quot;&gt;then&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;response&lt;/span&gt;) =&amp;gt;&lt;/span&gt; response.&lt;span class=&quot;hljs-title function_&quot;&gt;json&lt;/span&gt;()) .&lt;span class=&quot;hljs-title function_&quot;&gt;then&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;data&lt;/span&gt;) =&amp;gt;&lt;/span&gt; { comments.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; = dataStates.&lt;span class=&quot;hljs-property&quot;&gt;loaded&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;data&lt;/span&gt; = data; }) .&lt;span class=&quot;hljs-title function_&quot;&gt;catch&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;error&lt;/span&gt;) =&amp;gt;&lt;/span&gt; { comments.&lt;span class=&quot;hljs-property&quot;&gt;dataState&lt;/span&gt; = dataStates.&lt;span class=&quot;hljs-property&quot;&gt;failed&lt;/span&gt;; comments.&lt;span class=&quot;hljs-property&quot;&gt;errorText&lt;/span&gt; = error.&lt;span class=&quot;hljs-property&quot;&gt;message&lt;/span&gt;; }); } &lt;/code&gt;&lt;/pre&gt;</content></entry><entry><title>Когда можно дублировать код?</title><link href="https://forweb.dev/ru/blog/2020-05-04-code-duplication/"/><updated>2020-05-04T00:00:00Z</updated><id>https://forweb.dev/ru/blog/2020-05-04-code-duplication/</id><content type="html">&lt;p&gt;Анализируя большую кодовую базу, мы сразу видим случаи, в которых следовало переиспользовать код. Но если вы только начинаете проект, случаи переиспользования кода не так очевидны — сложно предугадать, как будет эволюционировать тот или иной участок кода.&lt;/p&gt; &lt;p&gt;Дублировать код хорошо на начальном этапе, когда вы ещё не знаете, как в дальнейшем он будет развиваться. Подумать о переиспользовании можно будет в будущем, когда кодовая база достаточно разрастётся и устаканится.&lt;/p&gt; &lt;p&gt;На начальном этапе переиспользование усложняет дальнейшее изменение и переписывание кода. При переиспользовании участок кода выносится в модуль, используемый в разных частях программы. Изменение такого модуля требует проверки работоспособности всех зависящих от него частей программы. Дублирование позволяет свести количество внешних зависимостей к минимуму — участок кода используется непосредственно там, где он написан. Это позволяет переписывать этот участок и не бояться, что в другой части программы что-то сломается.&lt;/p&gt; &lt;p&gt;Итак, дублирование кода позволяет минимизировать количество зависимых от него участков приложения, а значит упростить его дальнейшую доработку. Из этого следует ещё одна рекомендация: дублируйте код, который в будущем с большой вероятностью будет меняться. Чем более код специфичен, с тем большей вероятностью он изменится в будущем. Например, бизнес-логика меняется гораздо чаще, чем низкоуровневая логика общения с сервером (стандарт HTTP обновляется раз в несколько лет).&lt;/p&gt;</content></entry><entry><title>UX drag-and-drop загрузки файлов</title><link href="https://forweb.dev/ru/blog/2020-05-05-drag-and-drop-ux/"/><updated>2020-05-05T00:00:00Z</updated><id>https://forweb.dev/ru/blog/2020-05-05-drag-and-drop-ux/</id><content type="html">&lt;p&gt;При реализации формы с полем загрузки файлов хорошим тоном считается поддержка drag-and-drop, чтобы пользователь мог перетащить нужный файл из проводника в форму.&lt;/p&gt; &lt;p&gt;Часто размеры области, на которую нужно бросить перетаскиваемый файл, ограничивают:&lt;/p&gt; &lt;img class=&quot;bordered&quot; src=&quot;https://forweb.dev/ru/blog/2020-05-05-drag-and-drop-ux/vk.jpg&quot; alt=&quot;Скриншот сайта «ВКонтакте» в момент перетаскивания файла&quot; height=&quot;919&quot; width=&quot;1200&quot; /&gt; &lt;p&gt;Если поле загрузки файлов всего одно, такое ограничение обосновать сложно, а пользователю оно мешает: чем меньше доступная область, тем сложнее пользователю в неё попасть и тем проще промахнуться.&lt;/p&gt; &lt;p&gt;Более дружелюбный к пользователям подход — растянуть доступную для перетаскивания область на весь экран. Так, например, сделано в &lt;a href=&quot;https://amplifr.com/&quot;&gt;Амплифере&lt;/a&gt; и на ГитХабе:&lt;/p&gt; &lt;img src=&quot;https://forweb.dev/ru/blog/2020-05-05-drag-and-drop-ux/amplifr.jpg&quot; alt=&quot;Скриншот сервиса Amplifr в момент перетаскивания файла&quot; height=&quot;919&quot; width=&quot;1200&quot; /&gt; &lt;p&gt;Если полей для загрузки файлов на странице несколько (например, при загрузке сканов нескольких документов), доступную для перетаскивания область всё равно можно растянуть на весь экран и разделить на области для каждого поля. Скорее всего, эти области будут всё ещё достаточно большими.&lt;/p&gt; &lt;h2&gt;Нюанс: перетаскивание текста&lt;/h2&gt; &lt;p&gt;Чтобы при случайном или намеренном перетаскивании текста на странице не включалась полноэкранная область загрузки файлов, проверяйте тип содержимого в событиях перетаскивания:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;isFileDragEvent&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;event&lt;/span&gt;) { &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; event.&lt;span class=&quot;hljs-property&quot;&gt;dataTransfer&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;types&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;includes&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;Files&amp;#x27;&lt;/span&gt;); } &lt;span class=&quot;hljs-variable language_&quot;&gt;document&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;addEventListener&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;dragenter&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;event&lt;/span&gt;) =&amp;gt;&lt;/span&gt; { &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;hljs-title function_&quot;&gt;isFileDragEvent&lt;/span&gt;(event)) { &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;; } &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt; }); &lt;/code&gt;&lt;/pre&gt;</content></entry><entry><title>URL.createObjectURL вместо FileReader.readAsDataURL</title><link href="https://forweb.dev/ru/blog/2020-05-05-object-url/"/><updated>2020-05-05T00:00:00Z</updated><id>https://forweb.dev/ru/blog/2020-05-05-object-url/</id><content type="html">&lt;p&gt;Если вам нужно отобразить картинку, которая изначально представлена в виде &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File&quot;&gt;файла&lt;/a&gt; или &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Blob&quot;&gt;блоба&lt;/a&gt;, не используйте для этого &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL&quot;&gt;&lt;code&gt;FileReader.readAsDataURL&lt;/code&gt;&lt;/a&gt; — он требует значительных ресурсов для чтения содержимого блоба и его конвертации в data URL, хоть это и происходит асинхронно и не блокирует основной поток.&lt;/p&gt; &lt;p&gt;Вместо него лучше применить синхронный &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL&quot;&gt;&lt;code&gt;URL.createObjectURL&lt;/code&gt;&lt;/a&gt; — он моментально сгенерирует и свяжет с блобом временный URL, который можно использовать как угодно, например, в качестве &lt;code&gt;src&lt;/code&gt; для &lt;code&gt;&amp;lt;img /&amp;gt;&lt;/code&gt;. Генерация URL для блоба не требует чтения его содержимого, поэтому работает быстро и не расходует ресурсы (&lt;a href=&quot;https://w3c.github.io/FileAPI/#url-model&quot;&gt;подробное описание алгоритма в спецификации&lt;/a&gt;).&lt;/p&gt; &lt;p&gt;Пока для блоба существуют временные URL, он не может быть удалён из памяти сборщиком мусора, поэтому по завершении использования URL не забудьте отвязать его от блоба с помощью вызова &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL&quot;&gt;&lt;code&gt;URL.revokeObjectURL&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>Считаем время в JavaScript</title><link href="https://forweb.dev/ru/blog/js-timing/"/><updated>2020-05-10T00:00:00Z</updated><id>https://forweb.dev/ru/blog/js-timing/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Во фронтенде встречаются задачи, требующие измерения времени: например, расчёт длительности загрузки какого-либо ресурса или расчёт прогресса анимации.&lt;/p&gt; &lt;p&gt;Велик соблазн использовать для расчёта &lt;code&gt;Date.now&lt;/code&gt;:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; start = &lt;span class=&quot;hljs-title class_&quot;&gt;Date&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;now&lt;/span&gt;(); &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; result = &lt;span class=&quot;hljs-keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;fetch&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;/data&amp;#x27;&lt;/span&gt;); &lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;Elapsed time in milliseconds:&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-title class_&quot;&gt;Date&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;now&lt;/span&gt;() - start); &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Казалось бы, задача решена и можно отправляться пить кофе. Но есть нюанс: вычисленная таким способом длительность может оказаться отрицательной.&lt;/p&gt; &lt;h2&gt;Что не так с Date.now?&lt;/h2&gt; &lt;p&gt;Главные проблемы функции &lt;code&gt;Date.now&lt;/code&gt; в контексте описанной задачи:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;code&gt;Date.now&lt;/code&gt; не &lt;a href=&quot;https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%BE%D1%82%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F&quot;&gt;монотонна&lt;/a&gt;, то есть она может как возрастать, так и убывать (это и приводит к получению отрицательных длительностей в вычислениях).&lt;/li&gt; &lt;li&gt;Она не гарантирует равномерность роста возвращаемых значений.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Корень этих проблем в том, что &lt;code&gt;Date&lt;/code&gt; и его функции используют системные часы, которые подвержены внешним воздействиям:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;системное время может быть изменено пользователем;&lt;/li&gt; &lt;li&gt;службы операционной системы регулярно &lt;a href=&quot;https://ru.wikipedia.org/wiki/NTP&quot;&gt;синхронизируют время c точными часами через интернет&lt;/a&gt;, что может приводить к скачкам, если локальные часы сильно отстали или наоборот ушли вперёд.&lt;/li&gt; &lt;/ul&gt; &lt;h2&gt;Что использовать вместо Date.now?&lt;/h2&gt; &lt;h3&gt;В браузере&lt;/h3&gt; &lt;p&gt;В спецификации &lt;a href=&quot;https://www.w3.org/TR/hr-time&quot;&gt;High Resolution Time&lt;/a&gt; описана функция &lt;code&gt;performance.now&lt;/code&gt;, которая не только решает озвученные проблемы, но и повышает точность измерений до микросекунд (&lt;a href=&quot;https://github.com/w3c/hr-time/issues/56&quot;&gt;в зависимости от браузера&lt;/a&gt;). Подробнее о ней можно прочитать в самой спецификации или &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance/now&quot;&gt;на MDN&lt;/a&gt;.&lt;/p&gt; &lt;h3&gt;В Node.js&lt;/h3&gt; &lt;p&gt;Node.js предоставляет функции &lt;a href=&quot;https://nodejs.org/api/process.html#process_process_hrtime_time&quot;&gt;&lt;code&gt;process.hrtime&lt;/code&gt;&lt;/a&gt; и &lt;a href=&quot;https://nodejs.org/api/process.html#process_process_hrtime_bigint&quot;&gt;&lt;code&gt;process.hrtime.bigint&lt;/code&gt;&lt;/a&gt;, которые тоже не зависят от системных часов и повышают точность измерений до наносекунд.&lt;/p&gt;</content></entry><entry><title>Скачать или показать: заголовок Content-Disposition</title><link href="https://forweb.dev/ru/blog/content-disposition/"/><updated>2020-07-06T00:00:00Z</updated><id>https://forweb.dev/ru/blog/content-disposition/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Открытие файла по прямой ссылке может привести либо к отображению файла прямо в браузере, либо к его скачиванию. Контролируется это поведение очень просто — HTTP-заголовком &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition&quot;&gt;&lt;code&gt;Content-Disposition&lt;/code&gt;&lt;/a&gt; в ответе сервера.&lt;/p&gt; &lt;p&gt;Если нужно отобразить файл прямо в браузере (если отображение не поддерживается, браузер скачает файл):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Content-Disposition: inline &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Если нужно сразу скачивать файл (дополнительно можно указать имя файла в параметре &lt;code&gt;filename&lt;/code&gt;):&lt;/p&gt; &lt;pre&gt;&lt;code&gt;Content-Disposition: attachment Content-Disposition: attachment; filename=&amp;quot;kitten.jpg&amp;quot; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Будьте внимательны: если вы используете &lt;code&gt;Content-Disposition: inline&lt;/code&gt; для видео, ваш сервер должен поддерживать HTTP-запросы с &lt;a href=&quot;https://en.wikipedia.org/wiki/Byte_serving&quot;&gt;байтовыми диапазонами&lt;/a&gt;, иначе видео &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP40006514-SW6&quot;&gt;не будет воспроизводиться в Safari&lt;/a&gt;, а скачать его оттуда же не получится.&lt;/p&gt;</content></entry><entry><title>Неочевидная особенность регулярных выражений в JavaScript</title><link href="https://forweb.dev/ru/blog/stateful-regexps/"/><updated>2021-05-03T00:00:00Z</updated><id>https://forweb.dev/ru/blog/stateful-regexps/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Когда одна и та же регулярка используется в нескольких местах, велик соблазн вынести её в отдельную переменную и избежать дублирования. Чтобы код после этого неожиданно не сломался, важно знать о неочевидной особенности регулярных выражений с флагом &lt;code&gt;g&lt;/code&gt; в JavaScript.&lt;/p&gt; &lt;p&gt;Рассмотрим пример:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; digits = &lt;span class=&quot;hljs-regexp&quot;&gt;/&#92;d+/&lt;/span&gt;; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;123&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;456&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;789&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Всё работает ожидаемо. А теперь добавим флаг &lt;code&gt;g&lt;/code&gt;, с которым регулярка должна искать все совпадения:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; digits = &lt;span class=&quot;hljs-regexp&quot;&gt;/&#92;d+/g&lt;/span&gt;; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;123&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;456&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; false&lt;/span&gt; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;789&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;Теперь проверка срабатывает через раз. Дело в том, что флаг &lt;code&gt;g&lt;/code&gt; включает сохранение состояния поиска в поле &lt;code&gt;lastIndex&lt;/code&gt;, регулярка становится stateful, а не stateless. При вызове методов &lt;code&gt;test&lt;/code&gt; или &lt;code&gt;exec&lt;/code&gt; поиск начинается с позиции &lt;code&gt;lastIndex&lt;/code&gt;. При отсутствии совпадений &lt;code&gt;lastIndex&lt;/code&gt; обнуляется, &lt;s&gt;как президентские сроки&lt;/s&gt;, а при обнаружении совпадения в &lt;code&gt;lastIndex&lt;/code&gt; записывается позиция после совпадения.&lt;/p&gt; &lt;p&gt;Чтобы избежать этой проблемы, &lt;code&gt;lastIndex&lt;/code&gt; можно перезаписывать вручную:&lt;/p&gt; &lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; digits = &lt;span class=&quot;hljs-regexp&quot;&gt;/&#92;d+/g&lt;/span&gt;; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;123&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; digits.&lt;span class=&quot;hljs-property&quot;&gt;lastIndex&lt;/span&gt; = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;456&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; digits.&lt;span class=&quot;hljs-property&quot;&gt;lastIndex&lt;/span&gt; = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; digits.&lt;span class=&quot;hljs-title function_&quot;&gt;test&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;789&amp;quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// -&amp;gt; true&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;К сожалению, это не самое изящное решение, оно добавляет когнитивной нагрузки — нужно помнить о необходимости сброса индекса при каждом поиске. Можно намекать на природу регулярки префиксом &lt;code&gt;stateful&lt;/code&gt; или &lt;code&gt;global&lt;/code&gt; в названии переменной, чтобы при использовании не забывали обнулять &lt;code&gt;lastIndex&lt;/code&gt;; можно писать и использовать собственные функции-обёртки, которые будут обнулять &lt;code&gt;lastIndex&lt;/code&gt; при каждом вызове; а можно продолжать хардкодить регулярки в местах их использования — выбор за вами.&lt;/p&gt;</content></entry><entry><title>Почему стоит хранить зависимости в системе контроля версий</title><link href="https://forweb.dev/ru/blog/check-in-your-node-dependencies/"/><updated>2022-01-16T00:00:00Z</updated><id>https://forweb.dev/ru/blog/check-in-your-node-dependencies/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt;Везде, где я раньше работал, было простое правило: не храните &lt;code&gt;node_modules&lt;/code&gt; в системе контроля версий (в этой статье я буду для краткости называть её git).&lt;/p&gt; &lt;p&gt;Это правило выглядело разумным по нескольким причинам:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Код из &lt;code&gt;node_modules&lt;/code&gt; не пишется командой.&lt;/li&gt; &lt;li&gt;Код из &lt;code&gt;node_modules&lt;/code&gt; вызвал бы много шума в диффах git и пулреквестах.&lt;/li&gt; &lt;li&gt;Код из &lt;code&gt;node_modules&lt;/code&gt; может быть легко воспроизведён через npm install.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Сейчас я работаю в Google в команде Chrome DevTools, и мы храним &lt;code&gt;node_modules&lt;/code&gt; в системе контроля версий. Поначалу это показалось мне необычным, но со временем я обнаружил существенные преимущества такого подхода, которые, по моему мнению, достойны рассмотрения.&lt;/p&gt; &lt;h2&gt;Нет нужды в &lt;code&gt;npm install&lt;/code&gt;&lt;/h2&gt; &lt;p&gt;Если вы храните &lt;code&gt;node_modules&lt;/code&gt; в git, вам не нужно отдельно устанавливать зависимости, чтобы запустить проект. Это полезно не только для локальной разработки, но и для ботов на &lt;abbr title=&quot;Continuous integration, непрерывная интеграция&quot;&gt;CI&lt;/abbr&gt;-платформах: они могут полностью пропустить шаг установки зависимостей. Я видел проекты, в которых установка зависимостей с нуля занимала 1–2 минуты — а в CI это могло быть ещё дольше. У вас легко может быть 50+ запусков ботов каждый день, если они запускаются на каждый пулреквест и деплой. Это большая экономия времени (и трафика!).&lt;/p&gt; &lt;h2&gt;Гарантированно воспроизводимые сборки&lt;/h2&gt; &lt;p&gt;Хранение &lt;code&gt;node_modules&lt;/code&gt; в git гарантирует, что два разработчика запускают один и тот же код с одним и тем же набором зависимостей. Да, этого можно добиться, используя локфайлы или другие инструменты, однако я встречал случаи, когда они не спасали от изменения минорной версии какой-либо зависимости. Когда зависимости лежат в git, невозможно запустить проект с чем-то другим, каждый разработчик будет работать с одним и тем же кодом.&lt;/p&gt; &lt;h2&gt;Лучшая осведомлённость о поставляемом коде&lt;/h2&gt; &lt;p&gt;Я был удивлён тем, насколько я стал осведомлённее касательно зависимостей, когда стал видеть в диффе git весь код, попадающий в проект при их подключении. Это побудило нас поработать над инструментами, помогающими оценить влияние зависимостей на размер бандла и оптимизировать занимаемое на диске место.&lt;/p&gt; &lt;h2&gt;Более вдумчивое добавление зависимостей, потому что они больше не скрыты&lt;/h2&gt; &lt;p&gt;Я уже упоминал, что шум в диффах воспринимается людьми как недостаток хранения зависимостей в git, и я признаю, что это действительно может быть проблемой. Однако для меня этот шум оказался полезным знаком. Раньше я часто подключал новые зависимости, просто потому что не хотел сам писать несколько строчек кода. Теперь же я гораздо внимательнее отношусь к добавлению новых зависимостей, потому что я могу увидеть добавленный ими код и решить, стоит ли он того.&lt;/p&gt; &lt;p&gt;Примечание: это не означает, что у нас нет зависимостей! Бывают ситуации, когда подключить зависимость выгодно, но видимость кода в системе контроля версий сделала меня более осознанным — цена добавления зависимостей больше не скрыта.&lt;/p&gt; &lt;h2&gt;C большими диффами можно справиться&lt;/h2&gt; &lt;p&gt;Нельзя игнорировать тот факт, что добавление или обновление зависимостей может вызвать много шума в диффе. Одна из наших зависимостей — TypeScript, и каждое его обновление приносит огромный дифф в git, который, честно говоря, не стоит просмотра (за пределами CHANGELOG). Мы пришли к правилу, которое помогает в таких ситуациях: изменения в &lt;code&gt;node_modules&lt;/code&gt; должны быть отделены от любых изменений в основной кодовой базе. Так что если я обновлю &lt;code&gt;node_modules/typescript&lt;/code&gt; до свежей версии, наш инструментарий предупредит меня при обнаружении изменений за пределами &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;Это правило помогает нам в большинстве случаев, потому что любая задача, требующая добавления или обновления зависимостей, может быть разделена на две части:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Обновление или добавление зависимости&lt;/li&gt; &lt;li&gt;Использование зависимости в коде&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Иногда это не работает: например, обновление TypeScript может потребовать от нас исправления в коде ошибок, которые теперь обнаруживает новая версия TypeScript. В таких случаях мы отменяем правило.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Как и во всём, что касается программной инженерии, большинство «правил» — это рекомендации, которые мы можем при необходимости обойти.&lt;/p&gt; &lt;/blockquote&gt; &lt;h2&gt;Защита от очередного left_pad&lt;/h2&gt; &lt;p&gt;Печально известный &lt;a href=&quot;https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/&quot;&gt;инцидент с left_pad&lt;/a&gt; (популярный npm-пакет был внезапно удалён, ломая сборки проектов по всему миру) не затронул бы команду, которая хранит зависимости в git. В долгосрочной перспективе команде всё ещё пришлось бы разбираться с вопросом «что теперь делать с более не поддерживаемой зависимостью», но в краткосрочной перспективе их сборки бы не сломались, а релизы бы не были заблокированы.&lt;/p&gt; &lt;h2&gt;Выводы&lt;/h2&gt; &lt;p&gt;Если бы я на этой неделе заводил новую кодовую базу или присоединялся к небольшому стартапу, только начинающему разработку, я бы настоял на хранении &lt;code&gt;node_modules&lt;/code&gt; в системе контроля версий. Это определённо требует привыкания, но опыт применения этого подхода в течение последних двух лет убедил меня, что перечисленные выше преимущества существенно перевешивают дополнительный шум в git и небольшие накладные расходы.&lt;/p&gt; &lt;hr /&gt; &lt;p&gt;Это перевод статьи Джека Франклина &lt;a href=&quot;https://www.jackfranklin.co.uk/blog/check-in-your-node-dependencies/&quot;&gt;Why you should check-in your node dependencies&lt;/a&gt;. Переведено с разрешения автора.&lt;/p&gt;</content></entry><entry><title>За пределами NPM: выбираем зависимости с умом</title><link href="https://forweb.dev/ru/blog/npm-tools/"/><updated>2025-04-05T00:00:00Z</updated><id>https://forweb.dev/ru/blog/npm-tools/</id><content type="html">&lt;p class=&quot;paragraph--lead&quot;&gt; Немногие фронтенд-проекты обходятся без внешних зависимостей. Выбор зависимостей должен быть осознанным и вдумчивым процессом: неудачные решения могут привести к ухудшению &lt;abbr title=&quot;User experience&quot;&gt;UX&lt;/abbr&gt; и даже юридическим последствиям. В этой статье мы рассмотрим несколько инструментов, которые помогут вам с выбором зависимостей. &lt;/p&gt; &lt;h2&gt;Поиск пакетов&lt;/h2&gt; &lt;p&gt;Поиск пакетов на &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npmjs.com&lt;/a&gt; работает не лучшим образом. Например, поиск по запросам &lt;a href=&quot;https://www.npmjs.com/search?q=couchdb+promise&quot;&gt;couchdb promise&lt;/a&gt; и &lt;a href=&quot;https://www.npmjs.com/search?q=couchdb+promises&quot;&gt;couchdb promises&lt;/a&gt; выдаёт разные результаты.&lt;/p&gt; &lt;p&gt;Сервис &lt;a href=&quot;https://npms.io/&quot;&gt;npms&lt;/a&gt; стремится решить проблему поиска благодаря использованию продвинутых возможностей Elasticsearch и уникальной системе ранжированя пакетов.&lt;/p&gt; &lt;h2&gt;Анализ зависимостей пакетов&lt;/h2&gt; &lt;p&gt;Не лишним будет проверить транзитивные зависимости, которые попадут в ваш проект вместе с пакетом, который вы планируете использовать. Особенное внимание стоит обратить на:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;лицензии, не подходящие вашему проекту&lt;/li&gt; &lt;li&gt;общее количество зависимостей и их мейнтейнеров (обычно чем больше зависимостей, тем хуже)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Для такого анализа есть два довольно похожих сервиса: &lt;a href=&quot;https://npm.anvaka.com/&quot;&gt;npm.anvaka.com&lt;/a&gt; и &lt;a href=&quot;https://npmgraph.js.org/&quot;&gt;npmgraph&lt;/a&gt;. Первый сервис генерирует более красивую визуализацию, а второй позволяет целиком загрузить ваш &lt;code&gt;package.json&lt;/code&gt;, чтобы сразу проверить все уже добавленные пакеты.&lt;/p&gt; &lt;h2&gt;Размер пакета на диске и в бандле&lt;/h2&gt; &lt;p&gt;Наверняка вы видели мемы о размере &lt;code&gt;node_modules&lt;/code&gt;. Будем честны: мем смешной, а ситуация страшная.&lt;/p&gt; &lt;p&gt;&lt;img class=&quot;bordered&quot; src=&quot;https://forweb.dev/ru/blog/npm-tools/nodemodules.png&quot; alt=&quot;Heaviest objects in the universe: sun, neutron star, black hole, node_modules&quot; width=&quot;800&quot; height=&quot;575&quot; /&gt;&lt;/p&gt; &lt;p&gt;Когда речь заходит о размере пакета, есть два аспекта:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;em&gt;install size&lt;/em&gt;, размер установки: количество байтов, занимаемое пакетом на машине разработчика после установки&lt;/li&gt; &lt;li&gt;&lt;em&gt;bundle size&lt;/em&gt;, размер бандла: количество байтов, занимаемое пакетом в конечном бандле приложения, который скачивается браузерами пользователей&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Размер установки влияет на удобство разработки, время сборки и утилизацию ресурсов в &lt;abbr title=&quot;Continuous integration&quot;&gt;CI&lt;/abbr&gt;. Размер бандла влияет в первую очередь на пользовательский опыт.&lt;/p&gt; &lt;p&gt;Через &lt;a href=&quot;https://packagephobia.com/&quot;&gt;Packagephobia&lt;/a&gt; можно проверить размера установки пакета.&lt;/p&gt; &lt;p&gt;Через &lt;a href=&quot;https://bundlephobia.com/&quot;&gt;Bundlephobia&lt;/a&gt; и &lt;a href=&quot;https://bundlejs.com/&quot;&gt;bundlejs&lt;/a&gt; можно проверить, как пакет повлияет на ваш бандл. Bundlephobia — первый инструмент подобного рода, без каких-либо особенных фич. Bundlejs — новый и гораздо более продвинутый инструмент, поддерживающий тришейкинг, сборку нескольких пакетов одновременно, и более тонкую настройку бандлинга.&lt;/p&gt; &lt;p&gt;Наконец, самый новый из всех сервис &lt;a href=&quot;https://pkg-size.dev/&quot;&gt;pkg-size&lt;/a&gt; позволяет сразу проверить и размер установки, и размер бандла для выбранного пакета.&lt;/p&gt; &lt;h2&gt;Исследование содержимого пакета&lt;/h2&gt; &lt;p&gt;Иногда может понадобиться заглянуть внутрь опубликованного пакета. Например, чтобы убедиться, что опубликованный код делает ровно то, что должен, и не включает ничего неожиданного и потенциально опасного.&lt;/p&gt; &lt;p&gt;Также иногда бывает полезно проверить разницу между двумя версиями пакета, чтобы убедиться в наличии нужного исправления бага (иногда ченджлоги врут) или провести аудит безопасности при обновлении зависимостей.&lt;/p&gt; &lt;p&gt;Сервис &lt;a href=&quot;https://npmfs.com/&quot;&gt;npmfs&lt;/a&gt; — подходящий для этих задач инструмент: он поддерживает просмотр содержимого пакета, сравнение версий пакета, ссылки на конкретные строки кода и диффы, а также скачивание любых файлов или целых директорий из пакета.&lt;/p&gt; &lt;p&gt;Просто посмотреть содержимое пакета также можно через сервис &lt;a href=&quot;https://unpkg.com/&quot;&gt;unpkg&lt;/a&gt;, но это не его основное предназначение.&lt;/p&gt;</content></entry></feed>