# <script setup>
do SFC
<script setup>
é um açúcar sintático em tempo de compilação para usar API de Composição dentro de Componentes Single-File (SFCs). É a sintaxe recomendada se você estiver usando SFCs e API de Composição. Ele fornece uma série de vantagens sobre a sintaxe normal do <script>
:
- Código mais sucinto com menos boilerplate
- Capacidade de declarar props e eventos emitidos usando TypeScript puro
- Melhor desempenho em tempo de execução (o template é compilado em uma função de renderização no mesmo escopo, sem um proxy intermediário)
- Melhor desempenho de inferência de tipo na IDE (menos trabalho para o servidor de linguagem extrair tipos de código)
# Sintaxe Básica
Para ativar a sintaxe, adicione o atributo setup
ao bloco <script>
:
<script setup>
console.log('olá script setup')
</script>
2
3
O código dentro é compilado como o conteúdo da função setup()
do componente. Isso significa que, diferentemente do <script>
normal, que é executado apenas uma vez quando o componente é importado pela primeira vez, o código dentro do <script setup>
executará toda vez que uma instância do componente for criada.
# Vínculos de Nível Superior são Expostos ao Template
Ao usar <script setup>
, quaisquer vínculos de nível superior (incluindo variáveis, declarações de função e importações) declaradas dentro de <script setup>
podem ser usadas diretamente no template:
<script setup>
// variável
const msg = 'Olá!'
// funções
function log() {
console.log(msg)
}
</script>
<template>
<div @click="log">{{ msg }}</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
As importações são expostas da mesma forma. Isso significa que você pode usar diretamente uma função auxiliar importada em expressões de template sem ter que expô-la através da opção methods
:
<script setup>
import { capitalize } from './helpers'
</script>
<template>
<div>{{ capitalize('olá') }}</div>
</template>
2
3
4
5
6
7
# Reatividade
O estado reativo precisa ser criado explicitamente usando APIs de Reatividade. Semelhante aos valores retornados de uma função setup()
, refs são automaticamente desempacotados quando referenciados em templates:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
2
3
4
5
6
7
8
9
# Usando Componentes
Os valores no escopo de <script setup>
também podem ser usados diretamente como nomes de tags de componentes personalizados:
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>
2
3
4
5
6
7
Pense em MyComponent
como sendo referenciado como uma variável. Se você usou JSX, o modelo mental é semelhante aqui. O equivalente kebab-case <my-component>
também funciona no template - no entanto, as tags do componente PascalCase são fortemente recomendadas para consistência. Também ajuda a diferenciar dos elementos personalizados nativos.
# Componentes Dinâmicos
Como os componentes são referenciados como variáveis em vez de registrados em chaves string, você deve usar a vinculação dinâmica :is
ao usar componentes dinâmicos dentro de <script setup>
:
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Foo" />
<component :is="someCondition ? Foo : Bar" />
</template>
2
3
4
5
6
7
8
9
Observe como os componentes podem ser usados como variáveis em uma expressão ternária.
# Componentes Recursivos
Um SFC pode se referir implicitamente a si mesmo por meio de seu nome de arquivo. Por exemplo, um arquivo chamado FooBar.vue
pode se referir a si mesmo como <FooBar/>
em seu template.
Observe que isso tem prioridade menor do que os componentes importados. Se você tiver uma importação nomeada que conflite com o nome inferido do componente, você pode usar o alias da importação:
import { FooBar as FooBarChild } from './components'
# Componentes com Namespace
Você pode usar tags de componentes com pontos como <Foo.Bar>
para se referir aos componentes aninhados nas propriedades do objeto. Isso é útil quando você importa vários componentes de um único arquivo:
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>rótulo</Form.Label>
</Form.Input>
</template>
2
3
4
5
6
7
8
9
# Usando Diretivas Customizadas
As diretivas customizadas registradas globalmente funcionam como esperado, e as locais podem ser usadas diretamente no template, assim como explicamos acima para componentes.
Mas há uma restrição a ser observada: Você deve nomear as diretivas customizadas locais de acordo com o seguinte esquema: vNameOfDirective
para que elas possam ser usadas diretamente no template.
<script setup>
const vMyDirective = {
beforeMount: (el) => {
// faz algo com o elemento
}
}
</script>
<template>
<h1 v-my-directive>Este é um Título</h1>
</template>
2
3
4
5
6
7
8
9
10
<script setup>
// importações também funcionam e podem ser nomeadas com o esquema necessário
import { myDirective as vMyDirective } from './MyDirective.js'
</script>
2
3
4
# defineProps
e defineEmits
Para declarar props
e emits
em <script setup>
, você deve usar as APIs defineProps
e defineEmits
, que fornecem suporte completo para inferência de tipos e estão automaticamente disponíveis dentro de <script setup>
:
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// código de configuração
</script>
2
3
4
5
6
7
8
defineProps
edefineEmits
são macros de compilador utilizáveis apenas dentro de<script setup>
. Eles não precisam ser importados e são compilados quando<script setup>
é processado.defineProps
aceita o mesmo valor que a opçãoprops
, enquantodefineEmits
aceita o mesmo valor que a opçãoemits
.defineProps
edefineEmits
fornecem inferência de tipo adequada com base nas opções passadas.As opções passadas para
defineProps
edefineEmits
serão içadas para fora do setup no escopo do módulo. Portanto, as opções não podem fazer referência a variáveis locais declaradas no escopo do setup. Fazer isso resultará em um erro de compilação. No entanto, pode fazer referência a vínculos importados, pois eles também estão no escopo do módulo.
Se você estiver usando TypeScript, também é possível declarar props e emits usando anotações de tipos puros.
# defineExpose
Componentes usando <script setup>
são fechados por padrão - ou seja, a instância pública do componente, que é recuperada via refs de template ou correntes de $parent
, não exporá nenhuma das vinculações declaradas dentro de <script setup>
.
Para expor explicitamente as propriedades em um componente <script setup>
, use o macro de compilação defineExpose
:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
2
3
4
5
6
7
8
9
10
11
Quando um pai obtém uma instância deste componente via refs de template, a instância recuperada terá a forma { a: number, b: number }
(refs são automaticamente desempacotadas como em instâncias normais).
# useSlots
e useAttrs
O uso de slots
e attrs
dentro de <script setup>
deve ser relativamente raro, já que você pode acessá-los diretamente como $slots
e $attrs
no template. No caso raro em que você precisar deles, use os auxiliares useSlots
e useAttrs
respectivamente:
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
2
3
4
5
6
useSlots
e useAttrs
são funções de tempo de execução reais que retornam o equivalente de setupContext.slots
e setupContext.attrs
. Eles também podem ser usados em funções de API de composição normal.
# Uso ao lado do <script>
normal
<script setup>
pode ser usado junto com o <script>
normal. Um <script>
normal pode ser necessário nos casos em que você precisa:
- Declarar opções que não podem ser expressas em
<script setup>
, por exemploinheritAttrs
ou opções customizadas habilitadas por meio de plugins. - Declarar exportações nomeadas (incluindo tipos TypeScript).
- Executar efeitos colaterais ou crie objetos que devem ser executados apenas uma vez.
<script>
// normal <script>, executado no escopo do módulo (apenas uma vez)
runSideEffectOnce()
// declara opções adicionais
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// executado no escopo setup() (para cada instância)
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
Aviso
A função render
não é suportada neste cenário. Por favor, use um <script>
normal com a opção setup
.
# await
de nível superior
O await
de nível superior pode ser usado dentro de <script setup>
. O código resultante será compilado como async setup()
:
<script setup>
const post = await fetch(`/api/post/1`).then(r => r.json())
</script>
2
3
Além disso, a expressão aguardada será compilada automaticamente em um formato que preserva o contexto da instância do componente atual após o await
.
Nota
async setup()
deve ser usado em combinação com Suspense
, que atualmente ainda é um recurso experimental. Planejamos finalizá-lo e documentá-lo em uma versão futura - mas se você estiver curioso agora, pode consultar seus testes (opens new window) para ver como funciona.
# Recursos somente do TypeScript
# Exportações de tipos adicionais
Como observado acima, para exportar tipos adicionais de um SFC, eles devem ser movidos para um bloco <script>
adicional ao lado do bloco <script setup>
.
Por exemplo
<script lang="ts">
export type SizeOptions = 'small' | 'medium' | 'large';
</script>
<script lang="ts" setup>
defineProps({
size: { type: String as PropType<SizeOptions> },
})
</script>
2
3
4
5
6
7
8
9
# Declarando somente o tipo de props/emits
Props e emits também podem ser declarados usando sintaxe de tipo puro passando um argumento de tipo literal para defineProps
ou defineEmits
:
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
2
3
4
5
6
7
8
9
defineProps
oudefineEmits
só podem usar declaração de tempo de execução OU declaração de tipo. Usar ambos ao mesmo tempo resultará em um erro de compilação.Ao usar a declaração de tipo, a declaração de tempo de execução equivalente é gerada automaticamente a partir da análise estática para eliminar a necessidade de declaração dupla e ainda garantir o comportamento correto em tempo de execução.
No modo dev, o compilador tentará inferir a validação em tempo de execução correspondente dos tipos. Por exemplo, aqui
foo: String
é inferido do tipofoo: string
. Se o tipo for uma referência a um tipo importado, o resultado inferido seráfoo: null
(igual ao tipoany
) já que o compilador não possui informações de arquivos externos.No modo de produção, o compilador gerará a declaração de formato de array para reduzir o tamanho do pacote (as props aqui serão compiladas em
['foo', 'bar']
)O código emitido ainda é TypeScript com tipagem válida, podendo ser posteriormente processado por outras ferramentas.
A partir de agora, o argumento de declaração de tipo deve ser um dos seguintes para garantir a análise estática correta:
- Um literal de tipo
- Uma referência a uma interface ou um literal de tipo no mesmo arquivo
Atualmente, tipos complexos e importações de tipos de outros arquivos não são suportados. É teoricamente possível suportar importações de tipo no futuro.
# Valores padrão de props ao usar declaração de tipo
Uma desvantagem da declaração defineProps
somente de tipo é que ela não tem uma maneira de fornecer valores padrão para as props. Para resolver este problema, uma macro do compilador withDefaults
também é fornecida:
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'olá',
labels: () => ['um', 'dois']
})
2
3
4
5
6
7
8
9
Isso será compilado para as opções default
de props equivalentes em tempo de execução. Além disso, o auxiliar withDefaults
fornece verificações de tipo para os valores padrão e garante que o tipo props
retornado tenha os sinalizadores opcionais removidos para propriedades que possuem valores padrão declarados.
# Restrição: Nenhuma Importação Src
Devido à diferença na semântica de execução do módulo, o código dentro de <script setup>
depende do contexto de um SFC. Quando movido para arquivos externos .js
ou .ts
, pode causar confusão tanto para desenvolvedores quanto para ferramentas. Portanto, <script setup>
não pode ser usado com o atributo src
.