Справочник
Архитектура
Четыре пакета, построенные по единому принципу: @macrulez/inview-core делает всю реальную работу вообще без зависимости от фреймворка — createScrollEngine, createVisibilityEngine, createElementTracker. Все три читают из одного общего rafLoop (один callback requestAnimationFrame на весь движок, вместо того чтобы каждая подписка на скролл/трекер запускала свой собственный), а наблюдение за видимостью идёт через общий пулящийся ObserverPool (нативные инстансы IntersectionObserver, пулящиеся по (root, rootMargin, threshold)). createRevealController и createScrollSpy — ещё два примитива ядра, построенные на том же пулящемся движке видимости: createRevealController — через сквозной проход по DOM через MutationObserver, а не подписку на отдельный элемент, createScrollSpy — через один общий обсервер на все переданные цели, сообщая индекс самой ранней из них по порядку, если сразу несколько пересекаются одновременно. scrollToElement — отдельная самостоятельная утилита, вообще не построенная на движке видимости — она просто один раз вычисляет getBoundingClientRect() элемента и вызывает нативный scrollTo() контейнера.
@macrulez/inview-vue и @macrulez/inview-react — оба тонкие адаптеры поверх этих же движков: реактивные Ref для Vue, useSyncExternalStore для React — с useParallaxLayer, useScrollSpy и обёртками жизненного цикла (useRevealController по обе стороны, useReveal у React) как composables/хуками, которые добавляют собственную логику поверх, а не просто прокидывают состояние движка; usePrefersReducedMotion, useStagger и useScrollTo — более тонкие реактивные обёртки/проброс поверх соответствующей утилиты ядра. Оба адаптера также реэкспортируют всю поверхность @macrulez/inview-core, так что для createRevealController, createScrollSpy и остальных движков никогда не нужна отдельная установка ядра. Vue дополнительно оборачивает движок видимости в директиву v-reveal и renderless-компонент <InView> — оба тонкие, используют тот же инстанс движка, что и useElementVisibility. У React свой <InView> (render-prop компонент, а не scoped-слот) плюс пара <InviewProvider>/useInviewDefaults() на контексте — React-эквивалент viewportDefaults/setViewportDefaults() из Vue, который читают и useElementVisibility, и <InView> — и хук useReveal, React-эквивалент v-reveal, построенный точно так же поверх useElementVisibility. @macrulez/inview-nuxt не добавляет ничего своего, кроме авто-импортов, глобальной регистрации v-reveal и <InView>, и клиентского плагина, который применяет общие значения по умолчанию Vue-пакета из nuxt.config.ts.
Совместимость с SSR
Каждый движок ядра и каждый composable/хук сам по себе SSR-safe: без window (или без IntersectionObserver) каждый из них возвращает статичный нулевой снапшот вместо ошибки, и запускает реальную подписку только тогда, когда реально оказывается на клиенте. Ни одному пакету не требуется <ClientOnly> где бы то ни было — включая модуль Nuxt, чей клиентский плагин только применяет значения по умолчанию, а не обеспечивает базовую SSR-безопасность composables.
Размер бандла и peer-зависимости
У @macrulez/inview-core вообще нет peer-зависимостей — он пригоден для самостоятельного использования в любом окружении, в том числе вообще без фреймворка. @macrulez/inview-vue зависит от @macrulez/inview-core и указывает peer на vue: ^3.3.0. @macrulez/inview-react зависит от @macrulez/inview-core и указывает peer на react: ^18.0.0 || ^19.0.0. @macrulez/inview-nuxt зависит от @macrulez/inview-vue и @nuxt/kit, и указывает peer на nuxt: ^3.9.0 || ^4.0.0. Все четыре поставляются с sideEffects: false.
Разработка
Монорепо на pnpm workspaces (packages/* + playground на Vite + Vue).
pnpm install
pnpm test # тесты для каждого пакета (vitest)
pnpm build # сборка core/vue/react/nuxt (tsup)
pnpm dev # playground на Vite
pnpm typecheck # сборка + tsc/vue-tsc по всем пакетамПакеты связаны через workspace:* — pnpm build сначала собирает core, а потом пакеты, которые от него зависят, автоматически (в топологическом порядке pnpm). Тестовые наборы vue/react резолвят @macrulez/inview-core прямо из его src/ через алиас в Vitest, так что pnpm test не требует предварительной сборки — но тайпчекинг адаптеров резолвит core через собранный dist, так что после правок в core перед pnpm typecheck нужно выполнить pnpm build.
Лицензия
MIT.