Que — Qu.when) — откладывайте выполнение функций до момента, когда произойдут одно или несколько событий (DOMReady, загрузка библиотеки, кастомный сигнал).
multi (все события), series (последовательные) и ordered (строгий порядок).useCache: true по умолчанию).on, off, trigger) — подписка на события с делегированием, поддержкой коллекций элементов и возможностью собирать ответы от обработчиков.def) — реагируйте на момент, когда глобальная переменная или её вложенное свойство становится определённым или изменяется. Поддерживается интервальный режим для отслеживания изменений вне прямой записи.loadAssets — загружайте скрипты, стили и изображения с контролем порядка и промисами.Qu.lib(). Управляем зависимостями модулей через Qu.when на события типа qu:Module*:ready и инициализация модулей через Qu.libs().scrollTo — плавная прокрутка с промисом; scrollToAccurate — повторная прокрутка после завершения первой для компенсации смещений (например, из-за загрузки шрифтов или изображений).dragScroll — перетаскивание для прокрутки.scrollFollowCursor — следование за мышью.up, down, only, between) с автоматическим обновлением и событием qu:breakpoint:change.Que — от queue, Qu — от query. Ключевая идея проста: задачи попадают в очередь и выполняются по запросу.
Это просто коллекция инструментов, изначально спроектированных для использования с async/defer — в моих проектах такой подход оказался удобным. Часть кода сгенерирована нейросетями и затем вручную доработана, утилиты ориентированы на современные браузеры.
Это не набор ES-модулей. Qu.js подключается целиком и работает через глобальный объект Qu. Импорт отдельных функций не предусмотрен и пока что не планируется.
Если вам близок такой подход, можете смело юзать.
<!-- Загрузчик в <head> самым первым синхронным скриптом --> <script>window.Que||function(){var e=window.Qu&&window.Qu.bus||document,n=e.dispatchEvent,w=window._QueFired||(window._QueFired={}),i=window._QueQ||(window._QueQ=[]),t=window._QueDetails||(window._QueDetails=Object.create(null));e.dispatchEvent=function(i){return i&&i.type&&(w[i.type]=(w[i.type]||0)+1,void 0!==i.detail&&(t[i.type]=i.detail,window.Qu&&"function"==typeof window.Qu._touchEventCache&&window.Qu._touchEventCache(i.type,i.detail))),n.call(e,i)},window.Que=function(e,n,w,t,u){if(window.Qu&&"function"==typeof window.Qu.when)return window.Qu.when(e,n,w,t,u);i.push([e,n,w,t,u])}}();</script> <link rel="dns-prefetch" href="//cdn.jsdelivr.net"> <link rel="preconnect" href="//cdn.jsdelivr.net"> <!-- Файлы Qu.js --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/qujs-dev/core@1.3.6/css/qu.min.css" media="print" onload="this.media='all'"> <script src="https://cdn.jsdelivr.net/gh/qujs-dev/core@1.3.6/qu.min.js" defer></script>
<script> // готовность переменных Que(function() { console.log('DOM ready'); Qu.def(['fancybox', 'jQuery'], function(vars) { console.log('Все библиотеки загружены!'); // vars.fancybox и vars.jQuery уже доступны как глобальные переменные Qu.trigger(Qu.bus, 'app:ready', { detail: { fancyboxLoaded: true, jQueryLoaded: true } }); }); }); // вызов на свой тригер Que(function() { console.log('✅ App ready!'); // todo }, 'app:ready'); // инициация библиотеки написанной в стиле Qu Que(function() { Qu.libs(['Lazy'], { autoInit: true, initParams: {} }).then(function(loadedLibs) { console.log('Lazy alredy'); }); }); </script>
В реальных задачах модули могут быть загружены в разных частях приложения и в разное время. В этом примере показана организация модульной архитектуры на основе Qu:
init(), возвращающая Promise;qu:ModuleC:ready и qu:ModuleB:ready для ожидания готовности зависимостей внутри init();:init и :ready на каждом этапе (отслеживаются через Qu.when);Qu.libs() с autoInit: true.Модуль C инициализируется первым, затем B (после события qu:ModuleC:ready), и только потом A (после qu:ModuleB:ready). Это гарантирует правильный порядок выполнения.
<script> // ========== ФУНКЦИЯ ДЛЯ ВРЕМЕННОЙ МЕТКИ ========== const startTime = performance.now(); const getTimestamp = () => { const elapsed = (performance.now() - startTime) / 1000; return `[${elapsed.toFixed(3)}s]`; }; // ================================================= setTimeout(function() { Que(function() { console.log(getTimestamp(), '================Пример связь модулей async'); // ======== Модуль C (базовый, не зависит от других) ======== var ModuleC = { name: 'ModuleC', version: '1.0', loaded: function() { console.log(getTimestamp(), '📦 ModuleC loaded'); }, init: function(quInstance, params) { console.log(getTimestamp(), '🔄 ModuleC init started (асинхронно)', params); return new Promise(function(resolve) { setTimeout(function() { console.log(getTimestamp(), '✅ ModuleC init finished'); resolve(); }, 2500); }); }, doSomething: function() { console.log(getTimestamp(), '⚡ ModuleC doSomething '); } }; // ======== Модуль B (зависит от C) ======== var ModuleB = { name: 'ModuleB', version: '1.0', loaded: function() { console.log(getTimestamp(), '📦 ModuleB loaded'); }, init: function(quInstance, params) { console.log(getTimestamp(), '🔄 ModuleB init started (асинхронно) - ждём C...', params); return new Promise(function(resolve) { Qu.when(function(Qu) { console.log(getTimestamp(), '✅ ModuleB получил сигнал, что C готов'); Qu.ModuleC.doSomething(); setTimeout(function() { console.log(getTimestamp(), '✅ ModuleB init finished'); resolve(); }, 2500); }, 'qu:ModuleC:ready'); }); }, doSomething: function() { console.log(getTimestamp(), '⚡ ModuleB doSomething'); } }; // ======== Модуль A (зависит от B) ======== var ModuleA = { name: 'ModuleA', version: '1.0', loaded: function() { console.log(getTimestamp(), '📦 ModuleA loaded'); }, init: function(quInstance, params) { console.log(getTimestamp(), '🔄 ModuleA init started (асинхронно) - ждём B...', params); return new Promise(function(resolve) { Qu.when(function(Qu) { Qu.ModuleB.doSomething(); resolve(); }, 'qu:ModuleB:ready'); }); }, doSomething: function() { console.log(getTimestamp(), '⚡ ModuleA doSomething'); } }; Qu.lib('ModuleC', ModuleC); Qu.lib('ModuleB', ModuleB); Qu.lib('ModuleA', ModuleA); // ======== Подписки на события ======== // алиас Que Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleC:init (инициализация началась)'); }, 'qu:ModuleC:init'); Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleC:ready (инициализация завершена)'); }, 'qu:ModuleC:ready'); Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleB:init (инициализация началась)'); }, 'qu:ModuleB:init'); Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleB:ready (инициализация завершена)'); }, 'qu:ModuleB:ready'); Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleA:init (инициализация началась)'); }, 'qu:ModuleA:init'); Qu.when(function(Qu) { console.log(getTimestamp(), '🎯 qu:ModuleA:ready (инициализация завершена)'); }, 'qu:ModuleA:ready'); // алиас Que Qu.when(function(Qu, data) { console.log(getTimestamp(), '🎯 qu:libs:done (все библиотеки готовы):', data); if (data.detail && data.detail.libNames) { var names = data.detail.libNames; console.log(getTimestamp(), '🎯 qu:libs:done имена:', names.join(', ')); var instances = {}; names.forEach(function(name) { instances[name] = Qu[name]; }); console.log(getTimestamp(), 'Инстансы:', instances); } }, 'qu:libs:done', { once: false, useCache: false }); // ======== Запуск ======== Qu.libs(['ModuleA', 'ModuleB', 'ModuleC'], { autoInit: true, initParams: { ModuleA: { debug: true }, ModuleB: { debug: true }, ModuleC: { debug: true } } }).then(function({ ModuleA, ModuleB, ModuleC }) { console.log(getTimestamp(), '✅ Все библиотеки готовы (Promise от libs разрешён)'); ModuleA.doSomething(); ModuleB.doSomething(); ModuleC.doSomething(); }); }); }, 2000); </script>
Результат выполнения в текущей консоли.
TODO обновить документацию :D + глобальные темы