Guia
Essenciais
- Instalação
- Introdução
- A Instância Vue
- Sintaxe de Templates
- Dados Computados e Observadores
- Interligações de Classe e Estilo
- Renderização Condicional
- Renderização de Listas
- Manipulação de Eventos
- Interligações em Formulários
- Básico sobre Componentes
Componentes em Detalhes
- Registro de Componentes
- Propriedades
- Eventos Personalizados
- Slots
- Dinâmicos & AssÃncronos
- Lidando com Casos Extremos
Transições & Animações
- Transições de Visibilidade e Listas
- Transições de Estado
Reuso & Composição
- Mixins
- Diretivas Personalizadas
- Funções de Renderização & JSX
- Plugins
- Filtros
Ferramentas
- Componentes Single-File
- Testes Unitários
- Testing
- Suporte ao TypeScript
- Publicando em Produção
Escalonando
- Roteamento
- Gerenciamento de Estado
- Renderizando no Lado do Servidor
- Segurança
Internamente
- Reatividade em Profundidade
Migração
- Migração do Vue 1.x
- Migração do Vue Router 0.7.x
- Migração do Vuex 0.6.x para 1.0
Diversos
- Comparação com Outros Frameworks
- Junte-se à Comunidade Vue.js!
- Conheça a Equipe
Você está navegando a documentação da v2.x e anterior. Para a v3.x, clique aqui.
Dados Computados e Observadores
Dados Computados
Expressões dentro de templates são muito convenientes, mas são destinadas a operações simples. Colocar muita lógica neles pode fazer com que fiquem inchados e que a sua manutenção fique mais complicada. Por exemplo:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
Neste ponto, o template não é mais tão simples e declarativo. Você tem que olhá-lo por alguns segundos antes de entender que ele exibe o valor de message
na ordem reversa. O problema é agravado quando se deseja incluir uma mensagem na ordem reversa em mais algum lugar do template, gerando-se repetições de código.
Por isso que, para qualquer lógica mais complexa, usamos dados computados (computed properties no inglês, traduzidos como âdadosâ pois, durante a utilização em templates, se parecem efetivamente com propriedades definidas em data
).
Exemplo Básico
<div id="example">
<p>Mensagem original: "{{ message }}"</p>
<p>Mensagem ao contrário: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Olá Vue'
},
computed: {
// uma função "getter" computada (computed getter)
reversedMessage: function () {
// `this` aponta para a instância Vue da variável `vm`
return this.message.split('').reverse().join('')
}
}
})
Resultado:
Mensagem original: "{{ message }}"
Mensagem ao contrário: "{{ reversedMessage }}"
Aqui nós declaramos um dado computado denominado reversedMessage
. A função que fornecemos será usada como uma função getter para a propriedade vm.reversedMessage
.
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
Pode abrir o console e testar o exemplo você mesmo. O valor de vm.reversedMessage
sempre dependerá do valor em vm.message
.
à possÃvel vincular os dados computados em templates como se fossem dados comuns. Vue sabe que vm.reversedMessage
depende de vm.message
, então ele irá atualizar qualquer ligação que dependa de vm.reversedMessage
sempre que vm.message
for alterado. E o melhor é que criamos tal relação de dependência de forma declarativa: a função getter computada não tem efeitos colaterais, o que a torna fácil de testar e de compreender.
Cache de computed
vs. Métodos
Você pode ter notado que é possÃvel obter o mesmo resultado chamando um método:
<p>Mensagem ao contrário: "{{ reverseMessage() }}"</p>
// no componente
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
Em vez de um dado computado, podemos definir a mesma funcionalidade como um método. Como resultado final, ambas abordagens têm o mesmo efeito. No entanto, a diferença é que dados computados são cacheados de acordo com suas dependências reativas. Um dado computado somente será reavaliado quando alguma de suas dependências for alterada. Isso significa que enquanto message
não sofrer alterações, múltiplos acessos ao reversedMessage
retornarão o último valor calculado sem precisar executar a função novamente.
Isto inclusive significa que o seguinte dado computado nunca se alterará, pois Date.now()
não é uma dependência reativa por natureza:
computed: {
now: function () {
return Date.now()
}
}
Em comparação, a invocação de um método sempre irá rodar a função, toda vez que uma nova renderização ocorrer.
Por que realmente precisamos de cache? Imagine que temos um dado computado A muito pesado ao ser processado, exigindo um laço por um Array enorme e fazendo diversos cálculos a cada iteração. Aà temos outros dados computados que dependem de A para processar. Sem cache, nós acabarÃamos executando o getter de A muito mais vezes do que o necessário! Em casos em que você realmente não deseje cache, simplesmente use um método no lugar.
Dados Computados vs. Observadores
Vue oferece uma forma mais genérica para observar e reagir a mudanças de dados em uma instância: observadores (em inglês, watchers). Quando se tem alguns dados que necessitam mudar com base na alteração de outros dados, é tentador usar excessivamente o watch
- especialmente se você está vindo do AngularJS. No entanto, frequentemente é melhor usar um dado computado em vez do watch
. Considere este exemplo:
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
O código acima é imperativo e repetitivo. Compare-o com a versão com dados computados:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
Muito melhor, não é mesmo?
Atribuição em Dados Computados
Dados computados são por padrão getter-only (somente retornam valor), mas é possÃvel fornecer um setter se precisar dele:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names.shift()
this.lastName = names.pop()
}
}
}
// ...
Se você executar vm.fullName = 'John Doe'
, o setter será automaticamente executado, fazendo com que vm.firstName
e vm.lastName
sejam atribuÃdos corretamente.
Observadores
Enquanto dados computados são mais adequados na maioria dos casos, há momentos em que um observador personalizado é necessário. Por isso o Vue fornece uma maneira mais genérica para reagir a alterações de dados, o watch
. Isto é particularmente útil quando se precisa executar operações assÃncronas ou operações complexas antes de responder a uma alteração de dados.
Por exemplo:
<div id="watch-example">
<p>
Faça uma pergunta do tipo sim/não:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Como já existe um rico ecossistema de bibliotecas para Ajax -->
<!-- e de coleções de métodos utilitários com propósitos gerais, o -->
<!-- núcleo Vue permanece pequeno não os reinventando. Isto também -->
<!-- dá a você liberdade de usar apenas o que estiver familiarizado. -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'Eu não posso responder até que você faça uma pergunta!'
},
watch: {
// sempre que a pergunta mudar, essa função será executada
question: function (newQuestion, oldQuestion) {
this.answer = 'Esperando você parar de escrever...'
this.debouncedGetAnswer()
}
},
created: function () {
// _.debounce é uma função fornecida pelo lodash para limitar
// a frequência que uma operação complexa pode ser executada.
// Neste caso, queremos limitar a frequência com que acessamos
// a yesno.wtf/api, esperando que o usuário termine completamente
// a digitação antes de realizar a chamada Ajax. Para aprender
// mais sobre a função _.debounce (e sua prima _.throttle),
// visite: https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Perguntas geralmente têm uma interrogação. ;-)'
return
}
this.answer = 'Pensando...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = response.data.answer === 'yes' ? 'Sim.' :
response.data.answer === 'no' ? 'Não.' : 'Talvez!'
})
.catch(function (error) {
vm.answer = 'Erro! Não pode executar a API. ' + error
})
}
}
})
</script>
Resultado:
Faça uma pergunta do tipo sim/não:
{{ answer }}
Neste caso, utilizar a opção watch
nos permitiu realizar uma operação assÃncrona (acessar uma API), limitar a frequência com que realizamos essa operação, e definir estados intermediários até que se obtenha a resposta final. Nada disso seria possÃvel com um dado computado.
Além da opção watch
, também é possÃvel utilizar a API imperativa vm.$watch.