Vue.js - это простой в использовании фреймворк для веб-приложений, который мы можем использовать для разработки интерактивных интерфейсных приложений.

В этой статье мы рассмотрим программные прослушиватели событий, рекурсию, циклические ссылки и альтернативные способы определения шаблонов.

Слушатели программных событий

В дополнение к отправке событий с помощью $emit и прослушиванию их с помощью v-on, Vue также предоставляет другие методы в своем интерфейсе событий. Доступны следующие функции:

  • Слушайте событие с $on(eventName, eventHandler)
  • Слушайте событие только один раз с $once(eventName, eventHandler)
  • Прекратить прослушивание события с $off(eventName, eventHandler)

Они доступны для случаев, когда нам нужно программно прослушивать события.

Мы можем использовать его следующим образом:

src/index.js :

Vue.component("foo-button", {
  template: `<button @click='$root.$emit("toggleFoo")'>Toggle Foo</button>`
});
new Vue({
  el: "#app",
  data: {
    foo: ""
  },
  mounted() {
    this.$root.$on("toggleFoo", function() {
      this.foo = this.foo === "foo" ? "" : "foo";
    });
  }
});

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo-button></foo-button>
      {{foo}}
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

В приведенном выше коде мы вызвали $root.$emit для генерации события в корневом экземпляре Vue. Затем корневой экземпляр Vue может прослушивать событие, когда он монтируется с помощью this.$root.$on, как мы это делали выше.

В конце, когда мы нажимаем Toggle Foo, мы видим, что слово foo включается и выключается, когда мы нажимаем кнопку.

Система событий Vue отличается от API целевого события браузера. Однако они по-прежнему работают одинаково.

$emit, $on и $off не являются псевдонимами для dispatchEvent, addEventListener и removeEventListener.

Циркулярные ссылки

Рекурсивные компоненты

Мы можем рекурсивно ссылаться на компоненты, добавив свойство name к нашему компоненту.

Например, мы можем написать следующее для отображения данных из рекурсивно вложенного объекта:

src/index.js :

Vue.component("foo-box", {
  name: "foo-box",
  props: ["foo"],
  template: `
    <div>
      <p>{{foo.val}}</p>      
      <foo-box v-if='foo.foo' :foo='foo.foo'></foo-box>
    </div>
  `
});
new Vue({
  el: "#app",
  data: {
    foo: {
      val: "foo",
      foo: {
        val: "foo",
        foo: {
          val: "foo"
        }
      }
    }
  }
});

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo-box :foo="foo"></foo-box>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

Тогда мы видим:

foo
foo
foo

отображается на экране.

Мы должны убедиться, что у нас есть v-if или другое условие для прекращения рекурсивного рендеринга в какой-то момент. В противном случае мы получим бесконечный цикл, что приведет к ошибке «Превышен максимальный размер стека».

Циркулярные ссылки между компонентами

Vue.component все автоматически разрешают циклические ссылки на компоненты.

Если мы используем модульную систему, нам нужно require или import явно в ловушке beforeCreate.

Чтобы потребовать это, мы можем написать:

beforeCreate() {
  this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}

Если мы используем import Webpack, мы можем написать что-то вроде:

components: {
  TreeFolderContents: () => import('./tree-folder-contents.vue')
}

Для компонентов, определенных с помощью Vue.components, мы можем использовать их без каких-либо изменений следующим образом:

src/index.js :

Vue.component("tree-folder-contents", {
  props: ["children"],
  template: `
    <ul>
      <li v-for="child in children">
        <tree-folder v-if="child.children" :folder="child"/>
        <span v-else>{{ child.name }}</span>
      </li>
    </ul>
  `
});
Vue.component("tree-folder", {
  props: ["folder"],
  template: `
    <p>
      <span>{{ folder.name }}</span>
      <tree-folder-contents :children="folder.children"/>
    </p>
  `
});
new Vue({
  el: "#app",
  data: {
    folder: {
      name: "folder",
      children: [{ name: "folder2" }]
    }
  }
});

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <tree-folder :folder="folder"></tree-folder>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

Мы будем рассматривать tree-folder-contents и tree-folder как родителей некоторых tree-folder и tree-folder-contents компонентов соответственно.

Встроенные шаблоны

Когда атрибут inline-template присутствует в дочернем компоненте, компонент будет использовать его внутреннее содержимое в качестве своего шаблона, а не рассматривать его как распределенное содержимое.

Однако объем данных, доступных внутри, может сбивать с толку, поскольку у нас есть доступ к области дочернего компонента, а не к родительскому компоненту внутри тегов.

Например, если у нас есть:

src/index.js :

Vue.component("bar", {
  data() {
    return {
      baz: "bar"
    };
  },
  template: `
    <p></p>
  `
});
new Vue({
  el: "#app",
  data: {
    foo: "foo"
  }
});

index.js :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <bar inline-template>
        <div>
          {{baz}}
        </div>
      </bar>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

Тогда {{bar}} ссылается на baz из bar компонента. Он также переопределяет шаблон, который мы определили в bar.

Заключение

Мы можем определять слушателей событий программно с помощью $on, $off и $once.

Чтобы рекурсивно ссылаться на компонент, мы должны добавить к компоненту атрибут name. Мы должны быть осторожны, чтобы не создать бесконечную рекурсию.

Циклические ссылки разрешаются автоматически, если мы используем Vue.component для определения наших компонентов. Однако если мы используем однофайловые компоненты, их будет require из import, в зависимости от используемого инструмента сборки.

Мы можем определять шаблоны внутри компонента с атрибутом inline-template.

Примечание от JavaScript In Plain English:

Мы всегда заинтересованы в продвижении качественного контента. Если у вас есть статья, которую вы хотите отправить в JavaScript In Plain English, отправьте нам электронное письмо по адресу [email protected] с вашим именем пользователя Medium, и мы добавим вас в качестве автора.