Вездесущая модель публикации-подписки — на этот раз точно

Шаблоны проектирования
Вездесущая модель публикации-подписки — на этот раз точно

предисловие

Режим публикации-подписки также называется режимом наблюдателя. Он определяет отношения «один ко многим» между объектами, позволяя нескольким объектам-наблюдателям одновременно отслеживать определенный подчиненный объект. будут контролироваться будут уведомлены.

Это не конкретная реализация, а шаблон, разработанный компьютерным языком, чтобы привести живой пример.

Бомба с дистанционным управлением - это своеобразное приложение жизни "опубликуй и подпишись". Закапываете бомбу 💣 под определенной машиной, а потом садитесь в Старбакс напротив машины пить кофе. Как только добыча садится в машину, нажимаете кнопку и бомба взрывается. Во время всего этого процесса бомба "подписывается" на вас, а право "опубликовать" - это кнопка на вашей руке.

Интерфейсные приложения

Фактически, как разработчик интерфейса, вы уже использовали шаблон проектирования "публикация и подписка". Если вы мне не верите, посмотрите на следующий код:

document.body.addEventListener('click', () => {
  console.log('监听点击事件')
})

Приведенный выше код проходитaddEventListenerметод подписанbodyсобытие щелчка, щелкните любойbodyТеги внутри вызовут выполнение функции обратного вызова. Это принцип делегирования событий,jQueryРеализация в этом отношении также аналогична следующему:

$('.demo').on('click', () => {
  // dosomethiong
})

Еще одно классическое применение модели «публикация и подписка» —Vue 2.xПринцип двустороннего связывания вObject.defineProperty, см. следующий код:

const obj = { name: 'Nick' }
Object.defineProperty(obj, 'name', {
  set: function () {
    console.log('触发更新')
  }
})

подписался в кодеnameсвойство, как только оно изменится,setФункция будет выполнена. Также нам не нужно заботитьсяnameКогда атрибут изменится, пока он смеет меняться,setбудет запущен.

скажи еще одинVueМодель «опубликовать и подписаться», о которой все часто пишут в разработке:

<Child @submit="sendPost"></Child>

Я верю, что это было написаноVueМоим одноклассникам не чужды.Это способ передачи значений между компонентами, а подкомпоненты передаются понемногу.emitВыпуск методаsubmit, родительский компонентsendPostметод будет запущен.

Таким образом, применение модели «публикация и подписка» во фронтенде достигло своего пика.

Написание простого EventBus от руки

Кратко опишу требования, класс EventBus бросает три метода, а именно:

  • on: метод подписки, введите метод on в компоненте или на странице и определите метод запускаемой функции.
  • emit: метод триггера, в соответствии с указанным выше методом подписки, активируйте его.
  • off: тип подписки, которую нужно уничтожить, аналогичноdocument.removeEventListener.

Скопируй парня, открой весь

class EventBus {
  constructor() {
    this.handleMaps = {} // 初始化一个存放订阅回调方法的执行栈
  }
  
  // 订阅方法,接收两个参数
  // type: 类型名称
  // handler:订阅待执行的方法
  on(type, handler) {
    if (!(handler instanceof Function)) {
      throw new Error('别闹了,给函数类型') // handler 必须是可执行的函数
    }
    // 如果类型名不存在,则新建对应类型名的数组
    if (!(type in this.handleMaps)) {
      this.handleMaps[type] = []
    }
    // 将待执行方法塞入对应类型名数组
    this.handleMaps[type].push(handler)
  }
  // 发布方法,接收两个参数
  // type:类型名称
  // params:传入待执行方法的参数
  emit(type, params) {
    if (type in this.handleMaps) {
      this.handleMaps[type].forEach(handler => {
        // 执行订阅时,塞入的待执行方法,并且带入 params 参数
      	handler(params)
      })
    }
  }
  // 销毁方法
  off(type) {
    if (type in this.handleMaps) {
      delete this.handleMap[type]
    }
  }
}

export default new EventBus()

Просто напишите мини EventBus, основная идея такова.

применяется на практике

Уровень всегда должен быть проверен, чтобы увидеть, работает он или нет! ! Далее мы проходимVue CLIИнициализируйте базовый проект и импортируйте код, написанный выше. как показано на рисунке:image.png

новыйutils/event_bus.js, в котором хранится написанный выше код.

Проверка 1: связь между родительским и дочерним компонентами

ИсправлятьHome.vue Следующим образом:

<template>
  <div class="home">
    技能:{{ skill }}
    <Child />
  </div>
</template>

<script>
import Child from '@/components/Child'
import eventBus from '@/utils/event_bus'
import { onMounted, ref } from 'vue'
export default {
  name: 'Home',
  components: {
    Child
  },
  setup() {
    const skill = ref('')
    onMounted(() => {
      // 订阅 skill 类型名
      eventBus.on('skill', (key) => {
        skill.value = key
        console.log('key', key)
      })      
    })

    return {
      skill
    }
  }
}
</script>

Добавить кcomponents/Child.vue ,Следующим образом:

<template>
  <div>
    <button @click="play">释放子技能</button>
    <Grandson />
  </div>
</template>

<script>
import eventBus from '@/utils/event_bus'
export default {
  name: 'Child',
  setup() {
    const play = () => {
      // 发布 skill 类型方法,并且传参数
      eventBus.emit('skill', '狮子歌歌')
    }
    
    return {
      play
    }
  }
}
</script>

Давайте посмотрим на эффект отображения в браузере:Очевидно, что нажатие кнопки «Освобождение вспомогательного навыка» вызывает событие подписанного навыка.

Проверка 2: Связь между компонентами внука и внука

Добавляем еще один компонент-внукcomponents/Grandson.vue, код показан ниже:

<template>
  <div>
    <button @click="play">释放孙技能</button>
  </div>
</template>

<script>
import eventBus from '@/utils/event_bus'
export default {
  name: 'Grandson',
  setup() {
    const play = () => {
      eventBus.emit('skill_2', '三千烦恼')
    }
    return {
      play
    }
  }
}
</script>

Child.vueДобавьте в компонент следующий код:

<template>
	...
  <Grandson />
</template>
<script>
import Grandson from './Grandson'
export default {
  name: 'Child',
  components: {
    Grandson
  }
}
</script>

Давайте посмотрим на эффект отображения в браузере:

Проверка 3: Межкомпонентная коммуникация

Эту проблему должен решить EventBus, модифицируя исходный проектviews/About.vueКод компонента выглядит следующим образом:

<template>
  <div class="about">
    <button @click="play">释放技能</button>
  </div>
</template>

<script>
import eventBus from '@/utils/event_bus'
export default {
  name: 'About',
  setup() {
    const play = () => {
      eventBus.emit('skill', '跨组件的狮子歌歌')
    }
    return {
      play
    }
  }
}
</script>

В браузере отображается следующее:

Суммировать

Плагины управления состоянием на рынке, будь то Vuex, Redux, Mobx и т. д., используют модель «публикация и подписка». Его дизайнерские идеи достойны нашего глубокого осмысления и исследования.Вышеприведенные рукописные предложенияEventBusОн универсален, будь то Vue, React, Angular или нативные проекты.