Понимание Script Setup в Vue 3
Тип <script setup> — это предлагаемое изменение в RFC Vue Git . Чтобы внести ясность: это не предназначено для полной замены любого из существующих способов написания кода. Его цель — предоставить разработчикам более краткий синтаксис для написания однофайловых компонентов.
В этой статье мы рассмотрим, как именно это работает, и некоторые способы, которыми это может быть полезно.
Краткое описание script setup
В <script setup>
нам не нужно объявлять метод export default
и setup
— вместо этого все привязки верхнего уровня предоставляются шаблону.
В Composition API мы привыкли создавать метод настройки, а затем возвращать все, что мы хотим предоставить. Что-то вроде этого:
<script> import { ref, computed } from 'vue' export default { setup () { const a = ref(3) const b = computed(() => a.value + 2) const changeA = () => { a.value = 4 } return { a, b, changeA } // have to return everything! } } </script>
Но с помощью script setup
мы можем переписать тот же код вот так:
<script setup> import { ref, computed } from 'vue' // all of these are automatically bound to the template const a = ref(3) const b = computed(() => a.value + 2) const changeA = () => { a.value = 4 } </script>
И это не только данные, вычисляемые свойства и методы! Даже импортированные директивы и компоненты, находящиеся на верхнем уровне области установки, автоматически доступны в нашем шаблоне.
Посмотрите на этот пример импорта компонента.
<template> <component-b /> </template> <script setup> import ComponentB from './components/ComponentB.vue' // really that's it! </script>
Смысл использования
Короче говоря, этот синтаксис упрощает создание отдельных файловых компонентов.
Точные слова RFC: «Основная цель предложения — снизить многословность использования Composition API внутри SFC за счет прямого предоставления контекста настройки сценария шаблону».
Основная цель предложения — снизить многословность использования Composition API внутри SFC за счет прямого предоставления контекста <script setup>
шаблону.
И это именно то, что мы только что увидели: не беспокоясь о создании setup
метода и возврате именно того, что мы хотим предоставить, мы можем упростить наш код.
Кроме того, не нужно беспокоиться о том, что вы забудете вернуть что-то из нашего метода установки.
Более продвинутое использование
Теперь, когда мы знаем, что такое <script setup>
и почему это может быть полезно, давайте взглянем на некоторые из его более продвинутых функций.
Доступ к props, создание событий и т. д.
Прежде всего, вам может быть интересно, как выполнять стандартные операции Vue, такие как:
- доступ к props;
- emits событий;
- доступ к нашему объекту контекста.
В Composition API это были просто аргументы нашего метода установки:
export default { setup(props, context) { // context has attrs, slots, and emit }, }
Однако в синтаксисе настройки скрипта мы можем получить доступ к этим же параметрам с помощью трех импортов из Vue.
defineProps
– как следует из названия, позволяет нам определять props для нашего компонентаdefineEmits
– позволяет нам определить события, которые может генерировать наш компонентuseContext
– дает нам доступ к слотам и атрибутам нашего компонента
<template> <button @click="$emit('change')">Click Me</button> </template> <script setup> import { defineProps, defineEmit, useContext } from 'vue' const props = defineProps({ foo: String, }) const emit = defineEmit(['change', 'delete']) const { slots, attrs } = useContext() </script>
С помощью этих трех импортов мы можем получить функциональность, к которой мы привыкли при нашем традиционном методе установки.
Создание функции асинхронной настройки
Еще одна интересная особенность настройки скрипта — простота создания функции асинхронной настройки.
Это полезно для загрузки в API при создании вашего компонента и даже для привязки вашего кода к экспериментальной функции ожидания.
Все, что нам нужно сделать, чтобы сделать нашу функцию установки асинхронной, — это использовать ожидание верхнего уровня внутри нашей настройки сценария.
Например, если мы используем Fetch API , мы можем просто использовать await вот так:
<script setup> const post = await fetch(`/api/pics`).then((a) => a.json()) </script>
…и наша результирующая setup() функция будет именно такой асинхронной.
Использование нескольких тегов сценария
<script setup>
создает собственную область сценария для своих привязок верхнего уровня. Но в некоторых случаях существует код, который необходимо выполнить в области модуля.
Два конкретных примера в этом RFC:
- Объявление именованного экспорта
- Создание глобальных побочных эффектов, которые выполняются только один раз.
Это можно сделать, добавив обычный <script>
блок рядом с настройкой скрипта, как показано ниже.
<script> performGlobalSideEffect() // this can be imported as `import { named } from './*.vue'` export const named = 1 </script> <script setup> // code here </script>
Использование Vuex
В версии 2.0 мы можем использовать предоставленные vuex mapState
напрямую mapMutation
, а в версии 3.0 нам нужно обернуть их в наши собственные методы.
в 2.0:
<template> <div> {{ count }} {{ countIsOdd }} {{ countIsEven }} </div> </template> <script> import { mapGetters } from 'vuex' export default { computed: { ...mapGetters(['count', 'countIsOdd', 'countIsEven']) } } </script>
в 3.0 с использованием script setup:
<script setup> import { computed } from 'vue' import { useStore } from 'vuex' const store = useStore() const count = computed(() => store.getters.count) const countIsOdd = computed(() => store.getters.countIsOdd) const countIsEven = computed(() => store.getters.countIsEven) </script>
чтобы избежать избыточности, мы также можем определить внешний файл, в этом случае мы создаем файл с именем map-state.js
:
import { computed } from 'vue' import { useStore } from 'vuex' const mapGetters = () => { const store = useStore() return Object.fromEntries( Object.keys(store.getters).map( getter => [getter, computed(() => store.getters[getter])] ) ) } export { mapGetters }
и его можно использовать следующим образом:
<template> <div> {{ count }} {{ countIsOdd }} {{ countIsEven }} </div> </template> <script setup> import { mapGetters } from '../map-state' const { count, countIsOdd, countIsEven } = mapGetters() </script>
конечно, map-state.js
файл можно расширить:
import { computed } from 'vue' import { useStore } from 'vuex' const mapState = () => { const store = useStore() return Object.fromEntries( Object.keys(store.state).map( key => [key, computed(() => store.state[key])] ) ) } const mapGetters = () => { const store = useStore() return Object.fromEntries( Object.keys(store.getters).map( getter => [getter, computed(() => store.getters[getter])] ) ) } const mapMutations = () => { const store = useStore() return Object.fromEntries( Object.keys(store._mutations).map( mutation => [mutation, value => store.commit(mutation, value)] ) ) } const mapActions = () => { const store = useStore() return Object.fromEntries( Object.keys(store._actions).map( action => [action, value => store.dispatch(action, value)] ) ) } export { mapState, mapGetters, mapMutations, mapActions }
Глобальная конфигурация
Разница также возникает, когда вы пытаетесь использовать плагин или вешать глобальный компонент, например, с помощью $message
или $dialog
в js.
Создание плагина Amplitude (инструмент отслеживания данных с помощью js sdk):
/* Amplitude.js */ import amplitude from 'amplitude-js'; export default { install: (Vue, { apiKey, userId }) => { amplitude.getInstance().init(apiKey, userId, { includeUtm: true, includeReferrer: true, deviceIdFromUrlParam: true }); // in 2.0 it was Vue.prototype.$amplitude = amplitude; Vue.config.globalProperties.$amplitude = amplitude; } };
и используйте это в main.js
:
/* main.js */ import AmplitudePlugin from './plugins/amplitude'; const app = createApp(App); // in 2.0 it was Vue.use(......) app.use(AmplitudePlugin, { apiKey: process.env.VUE_APP_AMPLITUDE_API_KEY, userId: userInfo?.id });
для компонента сообщения после того, как вы его создали и зарегистрировали глобально, например:
// import all local fundamental components you build for your project, things like message, button, drawer, etc --> not business components import * as components from './components'; export default { install: app => { Object.keys(components).forEach(key => { app.component(key, components[key]); if (key === 'DPMessage') { // register your $message method globally app.config.globalProperties.$message = components[key]; } }); } };
конечно, вам нужно его использовать:
<template> <div><button @click="showSuccessMessage">click me to show success message</button> </template> <script setup> const { proxy } = getCurrentInstance(); const showErrorMessage = () => { //better to have a '?' here, just in case Vue does not find the method proxy?.$message({ type: 'error', text: 'hey this is sam test' }); }; </script>
в то время как другие полезные плагины, например, axios
могут работать глобально или нет, зависит от вас.