Согласен, что программирование - это вера, вера в технологию, вера в стек. Почему? Потому что всем хочется ослабить ответственность за неверный выбор технологии или за некачественно решенную задачу.
Когда кто-то выбирает мейнстрим технологию, то обычно ссылается на ее авторитетность в глазах международного сообщества, намекая на то, что миллионы разработчиков не могут ошибаться. Чтобы не выглядеть идиотом, предварительно нагугливается какая-нибудь статья о том, как красиво решается подобная проблема. Собственно отсюда и идет вера в стек. А еще это приправляется вопросом: а кто это потом все поддерживать будет? Ответ будет простым: да кто угодно, кто также верит и интересуется этой же технологией. Самое главное, чтобы технология была от какой-то известной компании, а не ноунейма.
А если пойти по-другому: например, отказаться от мейнстрима, думать своей головой, создать что-то свое, пусть даже командой. То тогда очень возникает очень высокий груз ответственности на автора технологии, который сидит в соседнем кабинете. И до него очень легко добраться и высказать свое фи (в лучшем случае). Никаких преград.
Math.random никто не использует. В любом браузере сейчас можно так:
const array = new Uint32Array(10);
window.crypto.getRandomValues(array);
console.log("Your lucky numbers:");
for (const num of array) {
console.log(num);
}
А если у вас таких сервисов десять тысяч, то придётся делать десять тысяч изменений. Вы всего лишь передвинули один модуль (logger), а менять пути импорта придётся в десяти тысячах скриптов, которые этот модуль используют.
Я пытаюсь донести, что если не хотите указывать все зависимости в виде импортов в ваших пакетах, то можно использовать главный (базовый) модуль-контейнер (аля master of packages), в котором и будет все импортировано один раз. Собственно будет внесено исправление один раз.
Если нужно принимать решение в рантайме что импортировать в зависимости от окружения (с помощью динамического импорта), то Вы это можете сделать в модуле-контейнере, или создать вообще отдельный контейнер для решения специфических задач.
Далее по поводу проблемы имплементации логгера, а есть ли вообще проблема? Это вопрос конфигурирования ваших сервисов с учетом окружения. Это вообще другая тема.
Если мы говорим о "пакетах", то внутри папки пакета вполне допустимо и правильно использовать относительные пути.
Если же речь идет о ситуации, когда один пакет использует другой, и вообще нам нужно гарантировать, что все зависимости будут подключены, то и мой пример вполне годится для этого. Потому что при обращении к service.unknown_packet линтер укажет на необходимость подключения пакета к service.
Каждый подключаемый модуль пройдет в начале через импорт или его аналог (например, прямое внедрение на страницу через тег script). Это часть жизненного цикла. Ибо откуда он сам по себе возьмется? А это означает, что адрес модуля, его физическое расположение любом случае нужно будет учитывать (даже хотя бы один раз).
Причем эта концепция полностью отражена в моем простом коде service.js, где все модули грузятся только всего один раз и будут доступны в неймспейсе модуля service.js.
Если какой-то из модулей будет перемещен (кроме базового, разумеется), то изменения придется внести один раз, что в коде автора, что в моем примере. И в голове ничего не надо держать.
Нужно больше рантайма? Вот новый код service.js:
import config from './config.js';
import logger from './logger.js';
let service = {config, logger};
window.service = service;
export default service;
Теперь вообще можно не использовать js-импорт в модулях. Но рано радоваться, потому что для работы линтера как бы не пришлось в коде без js-импортов каждый раз указывать jsdoc-импорт типа service.
Например, чтобы линтер точно понял с чем мы имеем дело, все равно придется прописать прямую зависимость в каждом модуле что-то вроде этого:
/** @typedef {import("./service.js").service} service */
/** @type {service} */
let service = window.service;
Doctor_IT , у меня профессиональный интерес. А Вы можете поделиться ссылками на некоторые ваши особо сложные Google Docs, которые необходимо было конвертировать в статью на vc.ru?
К сожалению, в публикации примеры документов не указаны.
Вообще такая технология есть и ей лет 15-20 - называется IFRAME. Вставляете IFRAME со всеми нужными политиками безопасности на свои страницы. Тот грузит скрипты внутри себя. Далее получаете ссылку на объект window этого iframe (через свойство iframe.contentWindow) И вперед.
А вообще, общий кеш в браузере вряд ли будет, в первую очередь по причине безопасности. Например, с такой технологией подключить к вредоносному сайту банковский скрипт с данными станет легко, даже куки не надо воровать.
Ресурсы, такие как unpkg.com и jsdelivr.com, в некоторой степени помогают уменьшить дублирование, поскольку если несколько сайтов ссылается на один и тот же ресурс на unpkg.com, то все они будут использовать один и тот же объект из кэша браузера.
Как они помогают? Я бы еще могу согласиться в том случае, если бы речь шла о загрузке таких скриптов через IFRAME. Вот в таком случае мы точно с вами могли бы говорить об экономии трафика. А если идет речь об идентичных прямых ссылках на внешний скрипт на различных сайтах, то кэширование будет распространяться на каждый отдельный ресурс. Посещая таких 2 сайта, идентичный файл библиотеки вы скачаете 2 раза.
почему бы не хранить npm‑пакеты (или хотя бы транспилированные значимые объекты кода) прямо в браузере, учитывая версионность и другие свойства, присущие пакету?
Да никто и сейчас Вам не мешает это делать даже без бандлеров и хранить в браузере. Есть прекрасный механизм import maps, который по сути является картой импортов. Эту карту импортов можно и в ручную писать, если есть надобность, а так видел на npm пакет, который конвертирует package.json в карту.
При проектировании кода, в котором задействованы возможности фреймворка, приходится идти на уступки из-за т.н. инверсии управления (к слову, наличие которой является ключевым отличием библиотек от фреймворков). Поэтому та часть проекта, в которой используется фреймворк, с огромной вероятностью, будет использовать архитектуру, навязанную фреймворком. Собственно, если проект целиком и полностью состоит из системы, контролируемой фреймворком, то, что "прокричит" ваш фреймворк, такая архитектура и будет.
Сложилось впечатление, что Автор призывает использовать сервисную модель.
Чтобы скачков не было в бесконечных списках обычно используют position: absolute.
Полностью согласен, что упираемся в расчеты, которые не хочется проводить на клиенте. Но и это решаемо, потому что именно разработчик компонента (возможно с поддержкой дизайнера) решает как он должен отображаться. Я к тому что чем больше строгости, тем проще расчеты.
В том числе расчеты могут быть отложенными и незаметными для пользователя. И могут даже корректироваться, переходя от приближенных расчетов высот к более точным ) Потому что нам будет все равно, что условный компонент, который ниже вьюпорта на 5000px на самом деле должен быть 4950px. При скроллинге это незаметно.
Чтобы продемонстрировать что я имею в виду, накидал небольшой сниппет для замены комментариев Хабра теми самыми текстовыми блоками.
let nodes = Array.from(document.querySelectorAll(".tm-comment-thread__comment"));
for (let i=0; i<nodes.length; i++) {
let node = nodes[i];
let rect = node.getClientRects()[0];
let text = node.innerText;
let div = document.createElement("div");
div.style.height = `${rect.height}px`;
div.innerText = text;
node.replaceWith(div);
}
браузерному поиску это не поможет, ибо он будет не туда, куда надо.
<div style="height: 90px">long text </div>
<div style="height: 120px">long text 2 </div>
...
<div style="height: 20px">needle in a haystack </div>
...
<div style="height: 200px">text </div>
Div мог бы быть в исходном контенте параграфом или таблицей. Это неважно потому что тут главное позиционирование текстовых блоков (следование друг за другом с учетом их размеров).
Поэтому, как мне кажется, если искать needle, то браузер покажет его в нужном и правильном месте только в случае проведенных предварительных расчетов высот таких элементов. Или я что-то упускаю?
Если же блок получится слишком высоким, то очевидно, что браузер покажет найденный текст не там, где надо. А для небольших, которые вмещаются, например, в четверть экрана, то проблемы особой не вижу.
Кстати, можно не уничтожать невидимый контент, а в невидимых для пользователя частях страницы можно рисовать блок, который будет содержать исключительно текст без украшательств. Что это дает: во-первых не нужно на странице реализовывать свой поиск, и во-вторых очевидно памяти не так много потратится, потому что на каждый блок с известной высотой будет приходиться всего 1 тег.
При прокрутке уже отрисовывать нужную часть страницы так, как она должна выглядеть.
"Текстовые" блоки можно лениво рендерить, чтобы браузер не тормозил.
Решение на файлах - такое себе. Данные можно хранить хоть в json, но масштабируемости не будет. Наверняка потом надо будет учитывать время устаревания ссылки или аналитику провести.
Ну и кстати на ру-хостингах на некоторых тарифах еще любят ограничение делать в 10000 файлов на VPS.
Поэтому я за решение с использованием БД. Адрес короткого урла тоже довольно просто можно сгенерить, например: base62(index) . base62(md5(url)).
Согласен, что программирование - это вера, вера в технологию, вера в стек. Почему? Потому что всем хочется ослабить ответственность за неверный выбор технологии или за некачественно решенную задачу.
Когда кто-то выбирает мейнстрим технологию, то обычно ссылается на ее авторитетность в глазах международного сообщества, намекая на то, что миллионы разработчиков не могут ошибаться. Чтобы не выглядеть идиотом, предварительно нагугливается какая-нибудь статья о том, как красиво решается подобная проблема. Собственно отсюда и идет вера в стек. А еще это приправляется вопросом: а кто это потом все поддерживать будет? Ответ будет простым: да кто угодно, кто также верит и интересуется этой же технологией. Самое главное, чтобы технология была от какой-то известной компании, а не ноунейма.
А если пойти по-другому: например, отказаться от мейнстрима, думать своей головой, создать что-то свое, пусть даже командой. То тогда очень возникает очень высокий груз ответственности на автора технологии, который сидит в соседнем кабинете. И до него очень легко добраться и высказать свое фи (в лучшем случае). Никаких преград.
Я прекрасно понимаю о чем Вы говорите. Спасибо за развернутый ответ.
Спасибо за публикацию. Вопрос по поддержке типизации, если уместен.
Подскажите, пожалуйста, а Вы пробрасываете типы из динамически загружаемого модуля через контейнер в другие es-модули?
Рекомендую все такие операции проводить только в воркерах - они для этого и были придуманы.
Раньше сталкивался с проблемой определения IPad, так как ua врал как мог. Решение на заметку:
Math.random никто не использует. В любом браузере сейчас можно так:
Источник тут: https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
Причем getRandomValues реализована даже в IE11. https://caniuse.com/getrandomvalues
Я пытаюсь донести, что если не хотите указывать все зависимости в виде импортов в ваших пакетах, то можно использовать главный (базовый) модуль-контейнер (аля master of packages), в котором и будет все импортировано один раз. Собственно будет внесено исправление один раз.
Если нужно принимать решение в рантайме что импортировать в зависимости от окружения (с помощью динамического импорта), то Вы это можете сделать в модуле-контейнере, или создать вообще отдельный контейнер для решения специфических задач.
Далее по поводу проблемы имплементации логгера, а есть ли вообще проблема? Это вопрос конфигурирования ваших сервисов с учетом окружения. Это вообще другая тема.
В дополнение к моему ответу выше (https://habr.com/ru/articles/748132/comments/#comment_25753998) могу сказать, что получившийся service.js, который доступен через window.service является как раз таковым контейнером.
Если мы говорим о "пакетах", то внутри папки пакета вполне допустимо и правильно использовать относительные пути.
Если же речь идет о ситуации, когда один пакет использует другой, и вообще нам нужно гарантировать, что все зависимости будут подключены, то и мой пример вполне годится для этого. Потому что при обращении к service.unknown_packet линтер укажет на необходимость подключения пакета к service.
Раскрою мысль.
Каждый подключаемый модуль пройдет в начале через импорт или его аналог (например, прямое внедрение на страницу через тег script). Это часть жизненного цикла. Ибо откуда он сам по себе возьмется? А это означает, что адрес модуля, его физическое расположение любом случае нужно будет учитывать (даже хотя бы один раз).
Причем эта концепция полностью отражена в моем простом коде service.js, где все модули грузятся только всего один раз и будут доступны в неймспейсе модуля service.js.
Если какой-то из модулей будет перемещен (кроме базового, разумеется), то изменения придется внести один раз, что в коде автора, что в моем примере. И в голове ничего не надо держать.
Нужно больше рантайма? Вот новый код service.js:
Теперь вообще можно не использовать js-импорт в модулях. Но рано радоваться, потому что для работы линтера как бы не пришлось в коде без js-импортов каждый раз указывать jsdoc-импорт типа service.
Например, чтобы линтер точно понял с чем мы имеем дело, все равно придется прописать прямую зависимость в каждом модуле что-то вроде этого:
Я задаюсь вопросом: а зачем нам вообще нужен этот конструктор при условии, что в js этот механизм уже реализован через ключевое слово export?
Вот код service.js:
Код index.html:
По факту в каждый модуль Вы будете проставлять только одну зависимость от service.js.
Я что-то упустил?
Doctor_IT , у меня профессиональный интерес. А Вы можете поделиться ссылками на некоторые ваши особо сложные Google Docs, которые необходимо было конвертировать в статью на vc.ru?
К сожалению, в публикации примеры документов не указаны.
Вообще такая технология есть и ей лет 15-20 - называется IFRAME. Вставляете IFRAME со всеми нужными политиками безопасности на свои страницы. Тот грузит скрипты внутри себя. Далее получаете ссылку на объект window этого iframe (через свойство iframe.contentWindow) И вперед.
А вообще, общий кеш в браузере вряд ли будет, в первую очередь по причине безопасности. Например, с такой технологией подключить к вредоносному сайту банковский скрипт с данными станет легко, даже куки не надо воровать.
Как они помогают? Я бы еще могу согласиться в том случае, если бы речь шла о загрузке таких скриптов через IFRAME. Вот в таком случае мы точно с вами могли бы говорить об экономии трафика. А если идет речь об идентичных прямых ссылках на внешний скрипт на различных сайтах, то кэширование будет распространяться на каждый отдельный ресурс. Посещая таких 2 сайта, идентичный файл библиотеки вы скачаете 2 раза.
Да никто и сейчас Вам не мешает это делать даже без бандлеров и хранить в браузере. Есть прекрасный механизм import maps, который по сути является картой импортов. Эту карту импортов можно и в ручную писать, если есть надобность, а так видел на npm пакет, который конвертирует package.json в карту.
При проектировании кода, в котором задействованы возможности фреймворка, приходится идти на уступки из-за т.н. инверсии управления (к слову, наличие которой является ключевым отличием библиотек от фреймворков). Поэтому та часть проекта, в которой используется фреймворк, с огромной вероятностью, будет использовать архитектуру, навязанную фреймворком. Собственно, если проект целиком и полностью состоит из системы, контролируемой фреймворком, то, что "прокричит" ваш фреймворк, такая архитектура и будет.
Сложилось впечатление, что Автор призывает использовать сервисную модель.
Большое спасибо за подробное разъяснение.
С большим интересом читаю Ваши комментарии. Но я уже сам запутался. Можно как-то доступнее? :)
П.С. Я уже на старте проверять код на массивы и форичи.
Чтобы скачков не было в бесконечных списках обычно используют position: absolute.
Полностью согласен, что упираемся в расчеты, которые не хочется проводить на клиенте. Но и это решаемо, потому что именно разработчик компонента (возможно с поддержкой дизайнера) решает как он должен отображаться. Я к тому что чем больше строгости, тем проще расчеты.
В том числе расчеты могут быть отложенными и незаметными для пользователя. И могут даже корректироваться, переходя от приближенных расчетов высот к более точным ) Потому что нам будет все равно, что условный компонент, который ниже вьюпорта на 5000px на самом деле должен быть 4950px. При скроллинге это незаметно.
Чтобы продемонстрировать что я имею в виду, накидал небольшой сниппет для замены комментариев Хабра теми самыми текстовыми блоками.
Div мог бы быть в исходном контенте параграфом или таблицей. Это неважно потому что тут главное позиционирование текстовых блоков (следование друг за другом с учетом их размеров).
Поэтому, как мне кажется, если искать needle, то браузер покажет его в нужном и правильном месте только в случае проведенных предварительных расчетов высот таких элементов. Или я что-то упускаю?
Если же блок получится слишком высоким, то очевидно, что браузер покажет найденный текст не там, где надо. А для небольших, которые вмещаются, например, в четверть экрана, то проблемы особой не вижу.
Кстати, можно не уничтожать невидимый контент, а в невидимых для пользователя частях страницы можно рисовать блок, который будет содержать исключительно текст без украшательств. Что это дает: во-первых не нужно на странице реализовывать свой поиск, и во-вторых очевидно памяти не так много потратится, потому что на каждый блок с известной высотой будет приходиться всего 1 тег.
При прокрутке уже отрисовывать нужную часть страницы так, как она должна выглядеть.
"Текстовые" блоки можно лениво рендерить, чтобы браузер не тормозил.
Решение на файлах - такое себе. Данные можно хранить хоть в json, но масштабируемости не будет. Наверняка потом надо будет учитывать время устаревания ссылки или аналитику провести.
Ну и кстати на ру-хостингах на некоторых тарифах еще любят ограничение делать в 10000 файлов на VPS.
Поэтому я за решение с использованием БД. Адрес короткого урла тоже довольно просто можно сгенерить, например: base62(index) . base62(md5(url)).