Сергей Мелюков
705 subscribers
6 photos
2 videos
3 files
46 links
Про веб-разработку, JS, Webpack, open source, etc...
Ведет @smelukov
Канал для обсуждения - @smelukov_chat
Download Telegram
to view and join the conversation
Статистику можно собирать двумя путями:

1) Оборачивать стейт в рамках А/Б тестирования, например, собрать статистику только с 1% пользователей
Более правдивые результаты. И чем на больший % раскатываем - тем более правдивые результаты получаем.
Минус такого подхода - импакт по производительности клиента из-за постоянных оберток (зависит от размера стейта и частоты обращений к нему)

2) Собирать статистику во время e2e-тестов
Менее правдивые результаты, т.к. e2e-тесты лишь эмулируют поведение пользователя
🎉 Замержили мой PR в вебпак, который добавляет возможность очищать содержимое выходной директории перед сборкой:
{ output: { clean: true } }
Эдакая замена WebpackCleanPlugin
Всем привет! В блоге Яндекса на Хабре я опубликовал свои мысли по поводу типизации и ее применимости к JS
https://habr.com/ru/company/yandex/blog/541338/
Делитесь своими мыслями в комментах, приятного чтения ☺️
Привет! Небольшая заметка о том, как я затаскивал поддержку больших статов в генерируемые Статоскопом HTML-отчеты.
Как мы с вами уже знаем, у JS-движков есть ограничение на максимальный размер строки (например 512мб для V8)
Это значит, что при попытке получить строку большего размера, мы получим ошибку RangeError: Invalid string length
Очевидно, что для статов размером больше 512mb тот же JSON.parse работать не будет, но как раз JSON.parse нам и нужен.
Обойти ограничение можно при помощи поточного JSON-парсера. Такому парсеру нужен источник строки, который будет отдавать ее не целиком, а кусочками (например по 64кb). По мере того, как парсер получает кусочки, он пытается их распарсить - превратить в объект. Очевидно, что этот объект растет по мере парсинга. Смысл в том, что мы не обрабатываем большие строки, а следовательно, у нас не возникает исключения RangeError, а сам объект путь себе растет пока хватает оперативной памяти.
Ну хорошо, делаю первый подход - взял поточный парсер от @rdvornov, формирую HTML и инъекчу в него код вроде такого:

const chunks = [
сюда вставляю большой JSON порубленный на строки по 64kb
];
const data = await jsonExt.parseChunked(() => chunks);
Statoscope(data);


Запускаю - не работает, браузер просто зависает 🤔
Методом проб и ошибок выяснил, что бразуер просто не переваривает такой большой тег скрипт (несколько сот мегабайт). Никакой особо полезной информации я по этому ограничению не нашел, но стало ясно, что теперь я воткнулся в проблему лимитов самого браузера. Стал думать что здесь можно сделать. Пришла идея попилить на куски не только JSON, но и теги script:


<script>
chunks.push(КУСОК_JSON)
</script>
<script>
chunks.push(СЛЕДУЮЩИЙ_КУСОК_JSON)
</script>

..... здесь еще много подобных тегов script

<script>
const data = await jsonExt.parseChunked(() => chunks);
Statoscope(data);
</script>


Запустил, заработало! Теперь я смог обойти и ограничение на JSON.parse и ограничение браузера на размер тега script.
Учитывая то, что загружать в отчет можно сразу несколько файлов со статами, например, для сравнения сборок, предусмотрел пуш чанка по идентификатору стата:


<script>
api.pushChunk("stat1.json", КУСОК_JSON)
</script>

<script>
api.pushChunk("stat2.json", КУСОК_JSON)
</script>

<script>
api.pushChunk("stat1.json", КУСОК_JSON)
</script>


Таким образом даже не важен порядок, в котором статы сбрасываются в отчет.

Прошло какое-то время и @rdvornov задал интересный вопрос: "Слушай, а почему бы не использовать теги script не как скрипт, а как текст, тогда можно было бы сэкономить на парсинге?"
И действительно, если сказать браузеру, что содержимое script - это не скрипт, а текст, то браузер не будет тратить время на парсинг сожержимого. А, на минуточку, 64kb x Nk тегов script - это ощутимо.

В итоге получилось что-то вроде:


<script type="text/plain" data-id="stat1.json">КУСОК_JSON</script>
<script type="text/plain" data-id="stat1.json">КУСОК_JSON</script>

......

<script>
for (const element of document.querySelectorAll('script')) {
api.pushChunk(element.dataset.id, element.textContent);
}

const data = await jsonExt.parseChunked(() => api.getChunks());
Statoscope(data);
</script>


Там конечно чуть сложнее, полную версию изменений можно посмотреть тут

Итог этой простой манипуляции такой: время загрузки отчета со статами в 650mb сократилась с 21 секунды до 14 (профит в 33%!!!)
Мораль: почти всегда можно что-то придумать, чтобы стало лучше (даже когда кажется, что нельзя) 😉
Выпустил Statoscope 3.5
Туда вошло изменение, ускоряющее получения списка npm-пакетов и их инстансов https://github.com/smelukov/statoscope/pull/43
Это значительно уменьшило время генерирования отчета при помощи плагина.
Кстати, это первый сторонний ПР в Statoscope, который что-то улучшает 🚀

А еще, видимо что-то пошло ТАК и Statoscope начали использовать на CI, т.к. количество скачиваний резко возросло https://npm-stat.com/charts.html?package=%40statoscope%2Fui-webpack 🤘🏻
Хотя, если вы большой проект, то лучше использовать кеширующий npm-proxy, например Verdaccio
Привет!
Году в 2017 я залип на соревновании return true
Вам дается функция, нужно изучить исходный код и понять что нужно передать в функцию в качестве аргумента так, чтобы функция вернула true.
Проблема в том, что задание нужно не просто решить, а сделать это за минимальное количество символов.
Есть простые задания, а есть и зубодробительные.

Я тут раскопал свои решения из первого сезона соревнования, но с тех пор вышло много других задач.
Интересно было бы видеть тут разбор этих задачек с моими размышлениями и комментариями? В виде ссылок на статьи в телеграфе (чтобы не спойлерить тем, кто хочет решить все сам)
Хотите разбор return true?
Final Results
95%
Да
5%
Нет
https://www.joinclubhouse.com/event/M1zov8YL
Сегодня в 19:00 по МСК мы с коллегами будем болтать про сборку и отвечать на вопросы, подключайтесь ;)
Сергей Мелюков
Хотите разбор return true?
Привет! Судя по результатам опроса, почти все проголосовавшие хотят разбора.
Подготовил для вас первую статью серии
Милости прошу в комменты с вопросами/предложениями по содержимому и формату 😉
За что люблю return true, так это за то, что он побуждает вгрызаться в особенности языка и иногда заставляет читать спеку, даже если не очень-то и планировалось. Например, с удивлением обнаружил, что при постинкременте, старое значение тоже приводится к числу:
f = () => 0
f++ // NaN

Я ожидал увидеть здесь f вместо NaN, т.к. это ПОСТинкремент.
Но спека говорит примерно следующее:
...
oldValue = ToNumeric(lhs)
...
Return oldValue

В результате чего, мы вполне логично получаем NaN при попытке привести функцию к числу.
О некоторых вещах часто не задумываешься и не замечаешь, пока дело не доходит до нестандартных кейсов 🤷🏻‍♂️
Друзья, спасибо за первые 300 ⭐️ Статоскопу на Гитхабе 🎉
А давайте немного поболтаем и порешаем задачки? 🙂
Как вы уже знаете, я люблю всякие челледжи и недавно я наткнулся на такую вот задачку:


You are asked to square every digit of a number and concatenate them.
For example, if we run 9119 through the function, 811181 will come out, because 9^2 is 81 and 1^2 is 1.
Note: The function accepts an integer and returns an integer


Иначе говоря: На вход функции поступает число, каждый разряд которого нужно возвести в квадрат. Полученные квадраты разрядов нужно объединить и вернуть из функции.
На входе и на выходе должны быть именно числа (`typeof x === 'number'`). Примеры:


fn(3212) // 9414
fn(9119) //811181


Как вы решали бы эту задачу? При условии, что вам надо сразу сделать так, чтобы с точки зрения кода все было круто и эффективно.
Будет круто, если пришлете кусочки кода в какой-нибудь code pen. Или просто поделитесь мыслями.

PS: очевидно, не все так просто, иначе я бы не поднял этот вопрос 😉
Чуть позже поделюсь своими мыслями и решением 😉
PPS: Возможно такой формат окажется интересным.
Читатели задают вопросы о том, что делает ~~(number / 10). Давайте разбираться, ведь одна из целей разборов этих задач - узнать что-то новое.

Оператор ~ - это "побитовое НЕ", он инвертирует биты числа на противоположные: 10101 -> 01010.

Итак, зачем это всё? Дело в том, что побитовые операции можно производить только с целыми числами, поэтому при попытка применить побитовые операции к числу с плавающей точкой, дробная часть будет отброшена и это дешевле нежели использовать Math.floor и т.п.

Тут встает вопрос в том, какой побитовый оператор использовать, ведь каждый их них как-то модифицирует число. То есть применив побитовый оператор, мы так или иначе модифицируем число, а значит нам нужно применить такой оператор, чтобы биты числа можно было восстановить в их изначальный вид. Таким оператор как раз является ~:

10101 -> 01010

А теперь, если мы снова применим этот оператор к получившемуся числу, то получим изначальный порядок бит:

01010 -> 10101

Таким образом мы очень дешево избавились от дробной части при помощи битовых операций, не повредив само число.
Завел несложный issue в Статоскоп и пометил его тегом Good for contribution
Такие задачки можно смело брать в работу если хочется поконтрибьютить 😉
Если берете себе задачку, отмечайте себя
В будущем буду еще заводить 😉
Привет!
Вышел Statoscope 4.0 📦
Накопилось 😊

- Ломающее: Удалена сборка с отдельными стилями. Теперь Статоскоп - это изолированный монолит и все его стили встроены в единый бандл.

-
Добавлена обработка дочерних комиляций
Теперь Статоскоп показывает информацию о дочерних компиляциях. Например, если вы собираете проект с веб-воркерами, то веб-воркеры собираются дочерней компиляцией и теперь Статоскоп показывает информацию о таких компиляциях.
По умолчанию такие компиляции скрыты, но при помощи настройки Hide child compilations в UI, вы можете это изменить.

-
Теперь Статоскоп показывает не просто размер чанка, а отдельно, размер чанка и размер ассетов, связанных с этим чанком (это тот самый issue, который я создавал для того, чтобы вы могли поконтрибьютит в Статоскоп, спасибо пользователю с ником immitsu на гитхабе).

- Обновил версию DiscoveryJS (спасибо @rdvornov)

-
Обновил стек сборки самого Статоскопа

-
Обновил Foam Tree

Так же хочется сказать спасибо @iamakulov и @vladkorobov за поддержку Статоскопа 🙏

PS: Впереди много планов на то, какие задачи должен решать Статоскоп и как именно он это будет делать. Оставайтесь на связи