Использование с Vue

Awesomplete изначально создавалась как лёгкая vanilla-библиотека автодополнения без привязки к фреймворкам, поэтому при использовании в Vue основная задача заключается в правильной интеграции жизненного цикла компонента и DOM-элементов, чтобы избежать конфликтов между реактивностью Vue и прямыми DOM-манипуляциями Awesomplete.

Ключевой момент: Awesomplete работает напрямую с DOM-элементом <input>, поэтому инициализация должна происходить после монтирования компонента.

import Awesomplete from "awesomplete";
import "awesomplete/awesomplete.css";

export default {
  data() {
    return {
      value: "",
      awesomplete: null,
      list: ["Apple", "Apricot", "Avocado", "Banana", "Blueberry"]
    };
  },
  mounted() {
    this.awesomplete = new Awesomplete(this.$refs.input, {
      list: this.list,
      minChars: 1,
      autoFirst: true
    });
  }
};
<input ref="input" v-model="value" />

Важно учитывать, что v-model и Awesomplete работают параллельно: Vue управляет значением, а Awesomplete управляет UI подсказок.

Синхронизация v-model и Awesomplete

При выборе элемента из списка Awesomplete не всегда автоматически синхронизирует значение с Vue-состоянием. Поэтому необходимо вручную отслеживать событие выбора.

mounted() {
  this.awesomplete = new Awesomplete(this.$refs.input, {
    list: this.list
  });

  this.$refs.input.addEventListener("awesomplete-selectcomplete", (e) => {
    this.value = e.text.value;
  });
}

Это гарантирует, что реактивное состояние Vue всегда соответствует выбранному значению.

Дополнительно полезно отслеживать ввод:

<input
  ref="input"
  v-model="value"
  @input="onInput"
/>
methods: {
  onInput() {
    if (this.awesomplete) {
      this.awesomplete.list = this.filteredList;
    }
  }
}

Динамическое обновление списка

Во Vue данные часто приходят асинхронно или фильтруются на лету. Awesomplete позволяет обновлять список без пересоздания экземпляра.

computed: {
  filteredList() {
    return this.list.filter(item =>
      item.toLowerCase().includes(this.value.toLowerCase())
    );
  }
},
watch: {
  filteredList(newList) {
    if (this.awesomplete) {
      this.awesomplete.list = newList;
    }
  }
}

Такой подход позволяет связать реактивность Vue с внутренним состоянием Awesomplete без конфликтов.

Использование с асинхронными запросами

Частый сценарий — получение подсказок с API.

methods: {
  async fetchSuggestions(query) {
    const res = await fetch(`/api/search?q=${query}`);
    return await res.json();
  },

  async onInput() {
    if (this.value.length < 2) return;

    const results = await this.fetchSuggestions(this.value);

    if (this.awesomplete) {
      this.awesomplete.list = results;
      this.awesomplete.evaluate();
    }
  }
}

В этом случае важно явно вызывать evaluate(), чтобы Awesomplete пересчитал совпадения после обновления данных.

Debounce для оптимизации запросов

Без ограничения частоты вызовов API автодополнение может создавать лишнюю нагрузку. В Vue удобно использовать простой debounce.

methods: {
  debounce(fn, delay) {
    let timeout;
    return (...args) => {
      clearTimeout(timeout);
      timeout = setTimeout(() => fn.apply(this, args), delay);
    };
  }
},

created() {
  this.onIn put = this.debounce(this.onInput, 300);
}

Теперь запросы выполняются только после паузы ввода.

Обработка кастомного отображения

Awesomplete позволяет задавать форматирование элементов списка через item и replace.

mounted() {
  this.awesomplete = new Awesomplete(this.$refs.input, {
    list: this.list,

    item: (text, input) => {
      const li = document.createElement("li");
      li.innerHTML = text.replace(
        new RegExp(input, "gi"),
        match => `<strong>${match}</strong>`
      );
      return li;
    },

    replace: (text) => {
      this.value = text.value;
    }
  });
}

Такой подход даёт полный контроль над тем, как выглядят подсказки.

Инкапсуляция в Vue-компонент

Более структурированный вариант — обёртка в переиспользуемый компонент.

export default {
  props: {
    suggestions: {
      type: Array,
      default: () => []
    }
  },

  data() {
    return {
      value: null,
      awesomplete: null
    };
  },

  mounted() {
    this.awesomplete = new Awesomplete(this.$refs.input, {
      list: this.suggestions
    });
  },

  watch: {
    suggestions(newVal) {
      if (this.awesomplete) {
        this.awesomplete.list = newVal;
      }
    }
  }
};
<input ref="input" v-model="value" />

Такой компонент становится изолированным и не зависит от логики внешнего приложения.

Управление фокусом и UX-поведение

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

this.$refs.input.addEventListener("awesomplete-open", () => {
  // можно, например, добавить класс активности
});

this.$refs.input.addEventListener("awesomplete-close", () => {
  // очистка состояния UI
});

Дополнительно можно программно открывать список:

this.awesomplete.evaluate();

Особенности работы с Vue 3

Во Vue 3 рекомендуется использовать Composition API и onMounted.

import { ref, onMounted, watch } from "vue";
import Awesomplete from "awesomplete";

export default {
  setup() {
    const input = ref(null);
    const value = ref("");
    let awesomplete = null;

    const list = ref(["Berlin", "Bern", "Barcelona"]);

    onMounted(() => {
      awesomplete = new Awesomplete(input.value, {
        list: list.value
      });

      input.value.addEventListener("awesomplete-selectcomplete", (e) => {
        value.value = e.text.value;
      });
    });

    watch(list, (newList) => {
      if (awesomplete) {
        awesomplete.list = newList;
      }
    });

    return { input, value, list };
  }
};

Управление состоянием при размонтировании

Поскольку Awesomplete работает с DOM напрямую, важно очищать обработчики при уничтожении компонента.

beforeUnmount() {
  if (this.awesomplete) {
    this.awesomplete.destroy();
    this.awesomplete = null;
  }
}

Это предотвращает утечки памяти и некорректное поведение при повторном создании компонента.

Типичные ошибки интеграции

Часто встречается ситуация, когда список обновляется, но UI не пересчитывается. Причина — отсутствие вызова evaluate() или неправильное обновление list.

Другой распространённый сценарий — попытка полностью управлять input через Vue без учёта того, что Awesomplete также изменяет его значение напрямую. Это приводит к конфликту состояний.

Корректная стратегия всегда заключается в том, чтобы считать Awesomplete только UI-слоем, а Vue — источником истины для данных.