Разве не было бы неплохо, если бы параметры окна браузера были реактивными, и вы могли бы делать такие вещи, как просмотр значений window.scrollY
или window.innerWidth
во всем приложении? Такие параметры, как (но не ограничиваясь ими):
window.scrollX window.scrollY window.innerHeight window.innerWidth
На данный момент в VueJS нет возможности «наблюдать» за свойством окна или делать его реактивным. Я не мог найти способ сделать scrollY
реактивным во многих местах своего приложения эффективно. Я также не хотел добавлять шаблон, подобный приведенному ниже, в каждый из моих однофайловых компонентов Vue:
created () { this.$el.addEventListener('click', this.someMethod) }, destroyed () { this.$el.removeEventListener('click', () => this.someMethod) }
Выполнение этого в нескольких компонентах действительно заставляет меня чувствовать себя плохо из-за того, что приходится снова и снова выполнять такую громоздкую операцию. Это также неэффективно и открывает для меня возможность ошибки пользователя, если я забыл добавить destroyed()
жизненный цикл.
Почему Vuex не подходит для этого варианта использования?
Магазин Vuex - безусловно, один из моих любимых инструментов в экосистеме Vue. Однако в этом случае он не подходит для управления оконным интерфейсом. Причина в том, что очистка.
Я хочу, чтобы Vuex управлял только «состоянием приложения», и window.scrollY
определенно не попадает в категорию состояния приложения. Кроме того, я пытаюсь сделать так, чтобы мои инструменты разработки Vue не выглядели так при каждом запуске события прокрутки.
Это полностью избавляет от Vuex. А теперь давайте исследуем дальше.
Прежде чем мы продолжим, спасибо Томасу Финдли за исправление меня по поводу использования термина EventBus. EventBus - это подшаблон pub, используемый для взаимодействия компонентов друг с другом через разделенный экземпляр Vue. Часто не рекомендуется использовать магазин Vuex (архитектура потока).
Управление состоянием окна с помощью сопоставления экземпляров
Vue может создать еще один действительно независимый и реактивный экземпляр Vue, который отделен от основного экземпляра. Мы часто делаем это при запуске нового приложения Vue или создании EventBus. Вот как мы это делаем:
const WindowInstanceMap = new Vue()
Главное, чего мы здесь хотим добиться, - это реактивность. Например, если scrollY
изменяется, я хочу, чтобы это вызвало некоторые изменения в вычисляемых методах, расположенных в моих компонентах приложения.
Имея это в виду, давайте создадим WindowInstanceMap. Создайте реактивные scrollY
данные и прослушиватель событий, который незаметно «изменяет» их.
Таким образом, мы создали клон оконного интерфейса, и scrollY
является реактивным. А теперь попробуем где-нибудь его использовать.
Давайте сначала импортируем это в App.js, чтобы «инициализировать» компонент:
// App.js import WindowInstanceMap from './WindowInstanceMap.js'
Это запустит create()
жизненный цикл WindowInstanceMap и зарегистрирует прослушиватель событий прокрутки в окне.
Теперь по моим компонентам. Все, что мне нужно сделать, это сопоставить его с вычисляемой опорой.
// AppNav.vue import WindowInstanceMap from './WindowInstanceMap.js' export default { computed: { scrollY () { return WindowInstanceMap.scrollY } } }
Это похоже на использование mapState
, представленное Vuex. Здесь мы отображаем вычисленное значение this.scrollY
на WindowInstanceMap.scrollY
.
Теперь он является реактивным, и мы можем использовать его в других вычисляемых свойствах, таких как:
// AppNav.vue import WindowInstanceMap from './WindowInstanceMap.js' export default { computed: { scrollY () { return WindowInstanceMap.scrollY }, isCollapsed () { return this.scrollY < 100 } } }
При такой настройке у меня есть только ОДИН прослушиватель событий в WindowInstanceMap. Все мои компоненты безупречно чистые. Я могу даже делать сложные вещи, связанные с оконным интерфейсом в WindowInstanceMap; и они будут реактивными на протяжении всего приложения.
Самое лучшее в этом то, что он не засыпает мои инструменты разработки ненужными мутациями. Лучшее из обоих миров ИМО.
Единственный недостаток, который я вижу в этой настройке, - это то, что dev-tools не поддерживает несколько экземпляров Vue. Так что отладить WindowInstanceMap.js
будет сложнее. Я хотел бы сделать так, чтобы WindowInstanceMap был простым и понятным компонентом.
Я использую это как в своих приложениях Vue, так и в Nuxt, и они мне очень пригодились.
Вот как вы его используете в NuxtJS
Давайте создадим WindowInstanceMap.js
в папке «plugins».
// WindowInstanceMap.js
import Vue from 'vue'
const WindowInstanceMap = new Vue({
data() {
return {
scrollY: 0
}
},
created() {
window.addEventListener('scroll', e => {
// Debounce this
window.requestAnimationFrame(() => {
this.scrollY = window.scrollY
})
})
}
})
// Inject plugin as Vue.prototype.$window
export default (context, inject) => {
inject('window', EventBus)
}
Теперь давайте импортируем его в наш nuxt.config.js. Не забудьте отключить рендеринг на стороне сервера с помощью ssr: false
, потому что он будет работать только в браузере (когда окно доступно).
// nuxt.config.js plugins: [ { src: '~/plugins/WindowInstanceMap', ssr: false } ]
Это оно!
Спасибо за прочтение. Я надеюсь, что принес пользу и помог вам. Хотелось бы услышать, что вы думаете об этом подходе, и были бы рады услышать ваши предложения о том, как его улучшить.
Если вам нравится контент, поделитесь им со мной в Twitter и Instagram.
📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресенье утром и слышите самые интересные истории, мнения и новости недели, ожидающие вас в почтовом ящике: Получите примечательный информационный бюллетень›