Vue 3 expose

В этой статье будет рассказано про новшество под названием expose появившееся во Vue версии 3.2. Эта возможность будет использована в развитии примера компонента-таблицы на Vue, создание которого описывается в предыдущих статьях.

Содержание

Что делает expose

Допустим у нас есть некий компонент, который используется внутри шаблона другого компонента (последний – внешний или родительский компонент). Во внешнем компоненте можно получить ссылку на объект вложенного компонента через refs и затем обратиться к его методам.

Если вы создаете библиотеку компонентов, в каждом из которых есть масса внутренних методов, то разумно скрыть этим методы и оставить доступ лишь к тем, которые предназначены для использования клиентским кодом. Это похоже на закрытые и открытые методы в ООП.

Такой подход имеет смысл, так как закрытые (как и любая внутренняя реализация) методы могут часто меняться. Если оставить часть таких методов доступными, то код использующий данный компонент (клиентский код) или библиотеку станет зависеть от этих методов. Когда методы изменятся (например, число параметров или имя), клиентский код перестанет работать. Поэтому во Vue появилась возможность создавать список открытых методов.

Простой пример для expose

Рассмотрим следующий пример. Есть компонент MyCounter.vue с кнопкой и полем. При нажатии на кнопку счетчик увеличивается и новое значение отображается в поле ввода.

<template>
  <label>Counter: <input type="text" readonly :value="count" /></label>
  <button @click="increment()">+</button>
</template>

<script>
import { ref } from 'vue'

export default {
  setup(props, { expose }) {
    const count = ref(0)
    const reset = () => (count.value = 0)
    const increment = () => count.value++

    // Только метод reset будет доступен извне, например через $refs
    expose({
      reset
    })

    // Внутри у шаблона будет доступ к count и increment
    return { count, increment }
  }
}
</script>

Методы count и increment доступны в шаблоне компонента. А метод reset будет доступен в родительском компоненте. При этом первые два метода в родительском доступны не будут.

<template>
  <main>
    <my-counter ref="counter"></my-counter>
    <button @click="counter.reset()">Reset</button>
  </main>
  <footer>
    <button @click="counter.increment()">counter.increment()</button>
  </footer>
</template>
<script>
import MyCounter from '@/components/MyCounter.vue'
import { ref } from 'vue'

export default {
  components: {
    MyCounter
  },
  setup() {
    const counter = ref()

    return { counter }
  }
}
</script>

При нажатии на кнопку Reset счетчик сбросится, а при нажатии на кнопку counter.increment() появится ошибка.

Как выглядит пример в браузере.

Если в коде выше убрать блок с expose, то ошибки при нажатии на нижнюю кнопку больше не будет, так как все методы вложенного компонента будут доступны во внешнем.

Использование expose для script setup

В примере выше показано применение этой возможности при описании компонента через функцию setup() в Composition API. Однако, если вы используете блок <script setup>, то синтаксис будет отличаться:

<script setup>
// ...

defineExpose({
  reset
})
</script>

Использование expose для Options API

Также возможно применение подхода и в Options API:

export default {
  // only `publicMethod` will be available on the public instance
  expose: ['publicMethod'],
  methods: {
    publicMethod() {
      // ...
    },
    privateMethod() {
      // ...
    }
  }
}

Полный код предыдущего примера – в репозитории.

Применяем expose к компоненту-таблице

Теперь вспомним таблицу с данными из прошлых статей. В последних изменениях мы добавили кнопки для редактирования и удаления строк. Было бы хорошо, чтобы после удаления строки таблица перезагружала данные с сервера. Для этого нужно в PostDataTable.vue добавить ref для компонента data-table и вызвать его метод loadData() в нужном месте:

<template>
  <data-table :headers="headers" ref="dataTable" :load-data-function="getPosts">

    <!-- ... -->

  </data-table>
</template>

<script setup>

// ...

const dataTable = ref()

// ...

function handleDelete(post) {
  if (confirm(`Delete post #${post.id}?`)) {
    alert('TODO: Call server API')
    dataTable.value.loadData()
  }
}
</script>

В это же время хорошим тоном будет открыть только нужную функции в компоненте DataTable.vue. Для этого, конечно, используем expose:

defineExpose({
  loadData
})

Теперь после операций удаления и редактирования таблица с данными будет перезагружаться. Другие методы вложенного компонента не будут даже доступны в компоненте PostDataTable.vue

Полный код примера с таблицей данных – в репозитории.

Что будет в следующих статьях

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

Чтобы не пропустить новую статью, вы можете подписаться

Оставить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *