Migração do Vue 1.x

FAQ

Wow - essa página é super longa! Isso significa que a versão 2.0 é completamente diferente, eu terei que aprender o básico de novo e a migração será praticamente impossivel?

Fico feliz que você tenha perguntado! A resposta é não. Cerca de 90% da API é a mesma e os conceitos básicos não mudaram. É longo porque queremos oferecer explicações muito detalhadas e incluir muitos exemplos. Tenha certeza, isso não é algo que você precisa ler de cima para baixo!

Por onde devo começar em uma migração?

  1. Comece executando o assistente de migração em um projeto atual. Nós cuidadosamente minificamos e comprimimos um desenvolvedor sênior do Vue em uma interface de linha de comando simples. Sempre que eles reconhecem um recurso obsoleto, eles irão te informar, oferecer sugestões e fornecer links para mais informações.

  2. Depois disso, navegue pela tabela de conteúdos desta página na barra lateral. Se você ver um tópico pelo o qual pode ser afetado, mas o assistente de migração não pegou, confira.

  3. Se você tiver testes, execute-os e veja o que ainda falha. Se não tiver, basta abrir o app em seu navegador e ficar atento para avisos ou erros ao navegar.

  4. Por enquanto, seu app deve ter migrado totalmente. Se você ainda deseja mais, pode ler o resto desta página - ou mergulhar no novo e melhorado guia de início. Muitas partes serão elimináveis, já que você já está familiarizado com os conceitos básicos.

Quanto demorará para migrar um app Vue da versão 1.x para a 2.0?

Depende de alguns fatores:

Se eu atualizar para o Vue 2, também terei que atualizar o Vuex e o Vue Router?

Somente o Vue Router 2 é compatível com o Vue 2, então sim, você também terá que seguir o caminho de migração do Vue Router. Felizmente, a maioria dos apps não tem muito código de roteador, então provavelmente não demorará mais de uma hora.

Quanto ao Vuex, até a versão 0.8 é compatível com o Vue 2, portanto, você não está obrigado a atualizar. A única razão pela qual você pode querer atualizar imediatamente é aproveitar os novos recursos do Vuex 2, como módulos e boilerplate reduzido.

Templates

Instâncias de Fragmento removido

Cada componente deve ter exatamente um elemento raiz. As instâncias de fragmento não são mais permitidas. Se você tem um template como este:

<p>foo</p>
<p>bar</p>

Recomenda-se envolver todo o conteúdo em um novo elemento, como este:

<div>
  <p>foo</p>
  <p>bar</p>
</div>

Caminho de atualização

Execute seu conjunto de testes de ponta a ponta ou app após a atualização e procure avisos no console sobre vários elementos raiz em um template.

Gatilhos do Ciclo de Vida

beforeCompile removido

Use o gatilho created no lugar.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

compiled substituído

Use o novo gatilho mounted no lugar.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

attached removido

Use uma verificação personalizada no DOM em outros gatilhos. Por exemplo, para substituir:

attached: function () {
  doSomething()
}

Você pode usar:

mounted: function () {
  this.$nextTick(function () {
    doSomething()
  })
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

detached removido

Use uma verificação personalizada no DOM em outros gatilhos. Por exemplo, para substituir:

detached: function () {
  doSomething()
}

Você pode usar:

destroyed: function () {
  this.$nextTick(function () {
    doSomething()
  })
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

init renomeado

Use o novo gatilho beforeCreate em vez disso, que é essencialmente o mesmo. Foi renomeado para consistência com outros métodos do ciclo de vida.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

ready substituído

Use o novo gatilho mounted em vez disso. Deve notar-se que, com mounted, não há garantia de estar no documento pronto. Para isso, também inclua Vue.nextTick/vm.$nextTick. Por exemplo:

mounted: function () {
  this.$nextTick(function () {
    // código que assume que o this.$el está no documento
  })
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar todos os exemplos desse gatilho.

v-for

Ordem dos Argumentos para Arrays no v-for mudado

Ao incluir um índice, a ordem dos argumentos para arrays costumava ser (índice, valor). Agora é (valor, índice) para ser mais consistente com os métodos de array nativos do JavaScript, como por exemplo, forEach e map.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de ordem de argumentos obsoleta. Observe que, se você nomear seus argumentos de índice algo incomum como position ou num, o assistente não irá sinalizá-los.

Ordem dos Argumentos para Objects no v-for mudado

Ao incluir um nome/chave de propriedade, a ordem dos argumentos para objetos costumava ser (nome, valor). Agora é (valor, nome) para ser mais consistente com os iteradores de objetos comuns, como o lodash.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de ordem de argumentos obsoleta. Observe que, se você nomear seus argumentos de chave como name ou property, o assistente não os sinalizará.

$index e $key removidos

As variáveis $index e $key implicitamente atribuídas foram removidas em favor de definí-las explicitamente no v-for. Isso torna o código mais fácil de ler para desenvolvedores menos experientes com Vue e também resulta em um comportamento muito mais claro ao lidar com loops aninhados.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos dessas variáveis removidas. Se você perder alguma, também poderá ver erros no console, como: Uncaught ReferenceError: $index is not defined

track-by substituído

track-by foi substituído por uma key, que funciona como qualquer outro atributo: sem o v-bind: ou o prefixo :, é tratado como uma string literal. Na maioria dos casos, você desejará usar uma vinculação dinâmica que espera uma expressão completa ao invés de uma chave. Por exemplo, no lugar de:

<div v-for="item in items" track-by="id">

Agora você irá escrever:

<div v-for="item in items" v-bind:key="item.id">

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de track-by.

Intervalo de Valores do v-for modificado

Antes, v-for="numero in 10" teria o numero começando em 0 e terminando em 9. Agora começa no 1 e termina no 10.

Caminho de atualização

Procure na sua base de código pelo regex /\w+ in \d+/. Onde quer que apareça em um v-for, verifique se você pode ser afetado.

Props

Opção coerce de Props removido

Se você quiser coagir uma prop, configure um valor computado local com base nisso. Por exemplo, em vez de:

props: {
  username: {
    type: String
    coerce: function (value) {
      return value
        .toLowerCase()
        .replace(/\s+/, '-')
    }
  }
}

Você pode escrever:

props: {
  username: String
},
computed: {
  normalizedUsername: function () {
    return this.username
      .toLowerCase()
      .replace(/\s+/, '-')
  }
}

Existem algumas vantagens:

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos da opção coerce.

Opção twoWay de Props removido

Props agora estão sempre em sentido único. Para produzir efeitos colaterais no escopo dos pais, um componente precisa emitir um evento explicitamente em vez de depender da vinculação implícita. Para mais informações, veja:

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos da opção twoWay.

Modificadores .once e .sync no v-bind removido

Props agora estão sempre em sentido único. Para produzir efeitos colaterais no escopo dos pais, um componente precisa emitir um evento explicitamente em vez de depender da vinculação implícita. Para mais informações, veja:

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos dos modificadores .once e .sync.

Mutação de Prop descontinuado

Mutar um prop local agora é considerado anti-padrão, por exemplo, declarando um suporte e em seguida, definindo this.myProp = 'someOtherValue' no componente. Devido ao novo mecanismo de renderização, sempre que o componente pai voltar a renderizar, as alterações locais do componente filho serão substituídas.

A maioria dos casos de mutar um prop pode ser substituído por uma dessas opções:

Caminho de atualização

Execute seu teste de ponta a ponta ou app depois da atualização e procure por avisos no console sobre mutação de props.

Props em uma Instância Raiz substituído

Nas instâncias raiz do Vue (criadas com new Vue ({ ... })), você deve usar propsData ao invés de props.

Caminho de atualização

Execute seu teste de ponta a ponta, se você tem um. Os testes com falha devem alertar o fato de que props passadas para instâncias raiz não funcionam mais.

Propriedades Computadas

cache: false descontinuado

A invalidação de cache das propriedades computadas será removida nas futuras versões principais do Vue. Substitua quaisquer propriedade computada não cacheada por métodos, que terá o mesmo resultado.

Por exemplo:

template: '<p>mensagem: {{ timeMessage }}</p>',
computed: {
  timeMessage: {
    cache: false,
    get: function () {
      return Date.now() + this.message
    }
  }
}

Ou com métodos de componente:

template: '<p>mensagem: {{ getTimeMessage() }}</p>',
methods: {
  getTimeMessage: function () {
    return Date.now() + this.message
  }
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos da opção cache: false.

Diretivas Nativas do Vue

Verdadeiro/Falso com v-bind modificado

Quando usado com v-bind, os únicos valores falsos são agora: null, undefined e false. Isso significa que 0 e arrays vazios renderizarão como verdadeiro. Então, por exemplo, v-bind: draggable = "''" renderizará como draggable = "true".

Para atributos enumerados, além dos valores falsos acima, a string "false" também será renderizada como attr = "false".

Observe que para outras diretivas (v-if e v-show por exemplo), o verdadeiro do Javascript normal ainda se aplica.

Caminho de atualização

Execute seus testes de ponta a ponta, se você tiver. Os testes com falha devem te alertar para qualquer parte do seu app que possa ser afetada por essa alteração.

Escutando Eventos Nativos nos Componentes com v-on modificado

Quando usando em um componente, v-on agora só escuta eventos customizados $emitidos por aquele componente. Para escutar um evento nativo do DOM no elemento raiz, você pode usar o modificador .native. Por exemplo:

<my-component v-on:click.native="doSomething"></my-component>

Caminho de atualização

Execute seu teste de ponta a ponta, se você tiver um. Os testes com falha devem te alertar para qualquer parte do seu app que possa ser afetada por essa alteração.

Atributo debounce de Parâmetro para v-model removido

Debounce é usado para limitar a frequência com que executamos os pedidos Ajax e outras operações custosas. O atributo debounce do Vue para v-model tornou isso fácil para casos muito simples, mas na verdade atrasa as atualizações de estado em vez das operações custosas. É uma diferença sutil, mas vem com limitações à medida que uma aplicação cresce.

Essas limitações tornam-se aparentes ao projetar um indicador de pesquisa, como este por exemplo:

{{ searchIndicator }}

Usando o atributo debounce, não haveria nenhuma maneira de detectar o estado “Digitação”, pois perdemos acesso ao estado em tempo real do input. Ao desacoplar a função de atraso do Vue no entanto, podemos atrasar apenas a operação que queremos limitar, eliminando os limites nos recursos que podemos desenvolver.

<!--
Usando a função `debounce` do lodash ou outra
biblioteca de utilidades dedicada, sabemos que a
implementação de atraso específica que usamos 
será a melhor da sua classe - e podemos usá-la em QUALQUER lugar. 
Não somente no nosso template.
-->
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.js"></script>
<div id="debounce-search-demo">
  <input v-model="searchQuery" placeholder="Digite algo">
  <strong>{{ searchIndicator }}</strong>
</div>
new Vue({
  el: '#debounce-search-demo',
  data: {
    searchQuery: '',
    searchQueryIsDirty: false,
    isCalculating: false
  },
  computed: {
    searchIndicator: function () {
      if (this.isCalculating) {
        return '⟳ Buscando novos resultados'
      } else if (this.searchQueryIsDirty) {
        return '... Digitando'
      } else {
        return '✓ Pronto'
      }
    }
  },
  watch: {
    searchQuery: function () {
      this.searchQueryIsDirty = true
      this.expensiveOperation()
    }
  },
  methods: {
    // É aqui que o debounce realmente deve ficar.
    expensiveOperation: _.debounce(function () {
      this.isCalculating = true
      setTimeout(function () {
        this.isCalculating = false
        this.searchQueryIsDirty = false
      }.bind(this), 1000)
    }, 500)
  }
})

Outra vantagem desta abordagem é que haverá momentos em que o debounce não é a função wrapper correta. Por exemplo, ao usar uma API para sugestões de pesquisa, esperar para oferecer sugestões até que o usuário pare de digitar por um período de tempo não é uma experiência ideal. O que você provavelmente quer, em vez disso, é uma função de limitação. Agora, já que você está usando uma biblioteca de utilidades como lodash, a refatoração para usar a função throttle, leva apenas alguns segundos.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do atributo debounce.

Atributos lazy ou number de Paramêtros para v-model substituído

Os atributos lazy e number agora são modificadores, para tornar mais claro o que isso significa, ao invés de:

<input v-model="name" lazy>
<input v-model="age" type="number" number>

Você usará:

<input v-model.lazy="name">
<input v-model.number="age" type="number">

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos destes atributos.

Atributo value com v-model removido

Agora o v-model não se preocupa mais com o valor inicial do atributo value. Em vez disso, tratará os dados da instância Vue como a fonte da verdade.

Isso significa que esse elemento:

<input v-model="text" value="foo">

apoiado por esses dados:

data: {
  text: 'bar'
}

vai renderizar um valor de “bar” ao invés de “foo”. O mesmo vale para <textarea> com conteúdo existente. Ao invés de:

<textarea v-model="text">
  hello world
</textarea>

Você deve garantir que seu valor inicial para text é “hello world”.

Caminho de atualização

Execute seu teste de ponta a ponta, após atualizar o app e busque por avisos no console sobre linhas de atributo value com v-model.

v-model com v-for Iterando Valores Primitivos removido

Casos como esse não funcionam mais:

<input v-for="str in strings" v-model="str">

A motivo é que este é o equivalente JavaScript para o qual o <input> compilaria:

strings.map(function (str) {
  return createElement('input', ...)
})

Como você pode ver, o vínculo bidirecional do v-model não faz sentido aqui. Definir str para outro valor na função de iteração não fará nada porque é apenas uma variável local no escopo da função.

Em vez disso, você deve usar um array de objects para que v-model possa atualizar o campo no objeto. Por exemplo:

<input v-for="obj in objects" v-model="obj.str">

Caminho de atualização

Execute seu teste de ponta a ponta, se você tiver um. Os testes com falha devem te alertar para qualquer parte do seu app que possa ser afetada por essa alteração.

v-bind:style com Sintaxe de Objeto e !important removido

Isso não irá mais funcionar:

<p v-bind:style="{ color: myColor + ' !important' }">Olá</p>

Se você realmente precisa substituir outro !important, você deve usar a sintaxe de string:

<p v-bind:style="'color: ' + myColor + ' !important'">Olá</p>

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vinculação de estilo com !important em objetos.

v-el e v-ref subtituído

Para simplificar, v-el e v-ref foram incorporados no atributo ref, acessível em uma instância de componente por meio de $refs. Isso significa que v-el:my-element se tornaria ref="myElement" e v-ref:my-component se tornaria ref="myComponent". Quando usado em um elemento normal, o ref será o elemento DOM, e quando usado em um componente, a ref será a instância do componente.

Como o v-ref não é mais uma diretiva, mas um atributo especial, ele também pode ser definido dinamicamente. Isto é especialmente útil em combinação com v-for. Por exemplo:

<p v-for="item in items" v-bind:ref="'item' + item.id"></p>

Anteriormente, v-el/v-ref combinado com v-for produziria um array de elementos/componentes, pois não havia como dar a cada item um nome único. Você ainda pode obter este comportamento dando a cada item o mesmo ref:

<p v-for="item in items" ref="items"></p>

Diferente das versões 1.x, esses $refs não são reativos, porque eles são registrados/atualizados durante o próprio processo de renderização. Fazê-los reativos exigiria renderizações duplicadas para cada mudança.

Por outro lado, $refs são projetados principalmente para acesso programático em JavaScript - não é recomendado confiar neles em templates, pois isso significaria referir-se ao estado que não pertence à instância em si. Isso violaria o modelo de visão orientado por dados do Vue.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de v-el e v-ref.

v-else com v-show removido

v-else não funciona mais com v-show. Use v-if com uma expressão de negação ao invés disso. Por exemplo, ao invés de:

<p v-if="foo">Foo</p>
<p v-else v-show="bar">Não foo, mas bar</p>

Você pode usar:

<p v-if="foo">Foo</p>
<p v-if="!foo && bar">Não foo, mas bar</p>

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de v-else e v-show.

Diretivas Customizadas simplificado

As diretivas têm um escopo de responsabilidade muito reduzido: agora são usadas apenas para a aplicação de manipulações DOM diretas de baixo nível. Na maioria dos casos, você preferirá usar componentes como a principal abstração de reuso de código.

Algumas das diferenças mais notáveis incluem:

Felizmente, desde que as novas diretivas estão mais simples, você pode dominá-las mais facilmente. Leia o novo Guia de customização de diretivas para aprender mais.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de diretivas definidas. O assistente sinalizará todos eles, é provável que na maioria dos casos você desejará refatorar para um componente.

Modificador de Diretiva .literal removido

O modificador literal foi removido pois o mesmo pode ser facilmente alcançado fornecendo uma string literal como valor.

Por exemplo, você pode atualizar:

<p v-my-directive.literal="foo bar baz"></p>

para:

<p v-my-directive="'foo bar baz'"></p>

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do modificador .literal em diretivas.

Transições

Atributo transition substituído

O sistema de transições do Vue mudou um pouco drasticamente e agora usamos <transition> e <transition-group> como elementos wrapper, ao invés do atributo transition. É recomendado ler o novo Guia de transições para aprendar mais.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do atributo transition.

Vue.transition para Transições Reutilizáveis substituído

Com o novo sistema de transições, agora você pode usar componentes para transições reutilizáveis.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do Vue.transition.

Atributo de Transição stagger removido

Se precisa escalonar as transições da lista, você pode controlar o tempo ajustando e acessando um data-index (ou atributo similar) em um elemento. Veja um exemplo aqui.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do atributo transition. Durante sua atualização, você pode fazer a transição (trocadilho muito intencional) para a nova estratégia de escalonação também.

Eventos

Opção events removido

Agora os manipuladores de eventos devem ser registrados no gatilho created ao invés disso. Veja o guia de migração para $dispatch e $broadcast para exemplo detalhado.

Vue.directive('on').keyCodes substituído

A nova e mais concisa maneira de configurar keyCodes é através de Vue.config.keyCodes. Por exemplo:

// ative v-on:keyup.f1
Vue.config.keyCodes.f1 = 112

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos da antiga sintaxe de configuração keyCode.

$dispatch e $broadcast substituído

$dispatch e $broadcast foram removidos em favor de uma comunicação mais explícita entre componentes e soluções de gerenciamento de estado mais sustentáveis, como o Vuex.

O problema é que os fluxos de eventos que dependem da estrutura de árvore de um componente podem ser difíceis de serem entendidos e muito frágeis quando a árvore se torna grande. Eles não escalam bem e apenas o prepara para dor mais tarde. $dispatch e $broadcast também não resolvem a comunicação entre componentes irmãos.

Um dos usos mais comuns para esses métodos é se comunicar entre um pai e seus filhos diretos. Nesses casos, você pode escutar um $emit de um filho com v-on. Isso permite que você mantenha a conveniência dos eventos com uma explicitação adicional.

No entanto, quando se comunica entre descendentes distantes e ancestrais, $emit não irá ajudá-lo. Em vez disso, a atualização mais simples possível seria usar um hub de eventos centralizado. Isso tem o benefício adicional de permitir que você se comunique entre componentes, independentemente de onde eles estão na árvore de componentes - mesmo entre irmãos! Como as instâncias do Vue implementam uma interface de emissor de eventos, você pode usar uma instância do Vue vazia para esse propósito.

Por exemplo, digamos que temos um app ToDo estruturado assim:

Todos
├─ NewTodoInput
└─ Todo
   └─ DeleteTodoButton

Podemos gerenciar a comunicação entre componentes com este único hub de eventos:

// Este é o hub de eventos que usaremos em cada
// componente para se comunicar entre eles
var eventHub = new Vue()

Então, em nossos componentes podemos usar $emit, $on, $off para emitir eventos, escutar eventos e limpar os escutadores de eventos, respectivamente:

// NewTodoInput
// ...
methods: {
  addTodo: function () {
    eventHub.$emit('add-todo', { text: this.newTodoText })
    this.newTodoText = ''
  }
}
// DeleteTodoButton
// ...
methods: {
  deleteTodo: function (id) {
    eventHub.$emit('delete-todo', id)
  }
}
// Todos
// ...
created: function () {
  eventHub.$on('add-todo', this.addTodo)
  eventHub.$on('delete-todo', this.deleteTodo)
},
// É bom limpar os escutadores de evento
// antes de um componente ser destruído.
beforeDestroy: function () {
  eventHub.$off('add-todo', this.addTodo)
  eventHub.$off('delete-todo', this.deleteTodo)
},
methods: {
  addTodo: function (newTodo) {
    this.todos.push(newTodo)
  },
  deleteTodo: function (todoId) {
    this.todos = this.todos.filter(function (todo) {
      return todo.id !== todoId
    })
  }
}

Esse padrão pode servir como um substituto para $dispatch e $broadcast em cenários simples, mas para casos mais complexos, é recomendável usar uma camada de gerenciamento de estado dedicada, como o Vuex.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de $dispatch e $broadcast.

Filtros

Filtros Fora da Interpolação de Texto removido

Os filtros agora só podem ser usados dentro das interpolações de texto (tags {{ }}). No passado, descobrimos que usar filtros dentro de diretivas, como v-model, v-on, etc levaram a mais complexidade do que o conveniente. Para a filtragem de lista no v-for, também é melhor mover essa lógica para JavaScript como propriedades computadas, para que também possa ser reutilizado em todo o seu componente.

Em geral, sempre que algo puder ser alcançado em JavaScript simples, queremos evitar a introdução de uma sintaxe especial como filtros para cuidar da mesma preocupação. Veja como você pode substituir as diretivas internas de filtros do Vue:

Substituíndo o Filtro debounce

Ao invés de:

<input v-on:keyup="doStuff | debounce 500">
methods: {
  doStuff: function () {
    // ...
  }
}

Use o debounce do lodash (ou possivelmente o throttle) para limitar diretamente a chamada do método custoso. Você pode alcançar o mesmo que acima, com isso:

<input v-on:keyup="doStuff">
methods: {
  doStuff: _.debounce(function () {
    // ...
  }, 500)
}

Para obter mais informações sobre as vantagens desta estratégia, veja o exemplo com v-model.

Substituíndo o Filtro limitBy

Ao invés de:

<p v-for="item in items | limitBy 10">{{ item }}</p>

Use o método do Javascript .slice em uma propriedade computada:

<p v-for="item in filteredItems">{{ item }}</p>
computed: {
  filteredItems: function () {
    return this.items.slice(0, 10)
  }
}

Substituíndo o Filtro filterBy

Ao invés de:

<p v-for="user in users | filterBy searchQuery in 'name'">{{ user.name }}</p>

Use o método do Javascript .filter em uma propriedade computada:

<p v-for="user in filteredUsers">{{ user.name }}</p>
computed: {
  filteredUsers: function () {
    var self = this
    return self.users.filter(function (user) {
      return user.name.indexOf(self.searchQuery) !== -1
    })
  }
}

O método nativo filter também pode gerenciar operações de filtragem muito mais complexas, porque você tem acesso ao poder total do JavaScript dentro das propriedades computadas. Por exemplo, se você quisesse encontrar todos os usuários ativos e buscar de forma insensível a maiúsculas e minúsculas ambos nome e email:

var self = this
self.users.filter(function (user) {
  var searchRegex = new RegExp(self.searchQuery, 'i')
  return user.isActive && (
    searchRegex.test(user.name) ||
    searchRegex.test(user.email)
  )
})

Substituíndo o Filtro orderBy

Ao invés de:

<p v-for="user in users | orderBy 'name'">{{ user.name }}</p>

Use o orderBy do lodash (ou possivelmente o sortBy) em uma propriedade computada:

<p v-for="user in orderedUsers">{{ user.name }}</p>
computed: {
  orderedUsers: function () {
    return _.orderBy(this.users, 'name')
  }
}

Você pode até ordenar por múltiplas colunas:

_.orderBy(this.users, ['name', 'last_login'], ['asc', 'desc'])

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de filtros sendo usados dentro de diretivas. Se você perder algum, também poderá ver erros no console.

Sintaxe de Argumentos para Filtros modificado

A sintaxe para argumentos agora se alinha melhor com a invocação de função Javascript. Então ao invés de usar argumentos delimitados por espaço:

<p>{{ date | formatDate 'YY-MM-DD' timeZone }}</p>

Nós cercamos os argumentos com parênteses e os delimitamos com vírgulas:

<p>{{ date | formatDate('YY-MM-DD', timeZone) }}</p>

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de filtros com a sintaxe antiga. Se você perder algum, também poderá ver erros no console.

Filtros de Texto Incorporados removido

Embora os filtros nas interpolações de texto ainda são permitidos, todos os filtros foram removidos. Em vez deles, é recomendável usar bibliotecas mais especializadas para resolver problemas em cada domínio (exemplos: date-fns para formatar datas e accounting para moedas.

Para cada um dos filtros de texto internos do Vue, mostraremos como você pode substituí-los abaixo. O código de exemplo poderá estar em funções auxiliares, métodos ou propriedades computadas.

Substituíndo o Filtro json

Você não precisa mais depurar, já que o Vue formatará bem a saída para você automaticamente, seja uma string, número, array ou objeto comum. Porém, se você quiser exatamente a mesma funcionalidade que o JSON.stringify do JavaScript, você pode usá-lo em um método ou propriedade computada.

Substituíndo o Filtro capitalize

text[0].toUpperCase() + text.slice(1)

Substituíndo o Filtro uppercase

text.toUpperCase()

Substituíndo o Filtro lowercase

text.toLowerCase()

Substituíndo o Filtro pluralize

O pacote pluralize no NPM atende bem este propósito, mas se você quer apenas pluralizar uma palavra específica ou ter uma saída especial para casos como 0, então também pode definir facilmente suas próprias funções de pluralize. Por exemplo:

function pluralizeKnife (count) {
  if (count === 0) {
    return 'sem facas'
  } else if (count === 1) {
    return '1 faca'
  } else {
    return count + 'facas'
  }
}

Substituíndo o Filtro currency

Para uma implementação muito ingênua, você poderia fazer algo como isto:

'R$' + price.toFixed(2)

Em muitos casos, porém, você ainda terá um comportamento estranho (exemplo: 0.035.toFixed(2) arredonda para 0.04, mas 0.045 arredonda para 0.04). Para contornar esses problemas, você pode usar a biblioteca accounting para formatar moedas de forma mais confiável.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de filtros de texto obsoletos. Se você perder algum, também poderá ver erros no console.

Filtros Bidirecionais substituído

Alguns usuários gostaram de usar filtros bidirecionais com v-model para criar inputs interessantes com muito pouco código. Embora aparentemente simples no entanto, os filtros bidirecionais também podem ocultar uma grande complexidade - e até mesmo encorajar uma UX pobre ao atrasar as atualizações do estado. Em vez disso, os componentes que envolvem um input são recomendados como uma maneira mais explícita e rica em recursos para criar inputs personalizados.

Como exemplo, agora vamos percorrer a migração de um filtro de moeda bidirecional:

Na maior parte funciona bem, mas as atualizações de estado atrasadas podem causar um comportamento estranho. Por exemplo, clique na guia Resulte tente digitar 9.999 em um desses inputs. Quando o input perde o foco, seu valor será atualizado para $10.00. Ao olhar para o total calculado no entanto, você verá que 9.999 é o que está armazenado em nossos dados. A versão da realidade que o usuário vê está fora de sincronia!

Para iniciar a transição para uma solução mais robusta usando o Vue 2.0, primeiro envolva esse filtro em um novo componente <currency-input>:

Isso nos permite adicionar um comportamento que um filtro por si só não poderia encapsular, como selecionar o conteúdo de um input no foco. Agora, o próximo passo será extrair a lógica de negócios do filtro. Abaixo, puxamos tudo para um objeto externo currencyValidator:

Esse aumento da modularidade não só facilita a migração para a Vue 2, mas também permite que a análise e formatação da moeda sejam:

Tendo este validador extraído, nós também construíremos isso mais confortavelmente em uma solução mais robusta. As peculiaridades do estado foram eliminadas e, na verdade, é impossível para os usuários inserirem algo errado, semelhante ao que o input numérico nativo do navegador tenta fazer.

Ainda estamos limitados, porém, por filtros e pelo Vue 1.0 em geral, então vamos completar a atualização para o Vue 2.0:

Você pode notar que:

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de filtros usados em diretivas como v-model. Se você perder algum, também poderá ver erros no console.

Slots

Slots Duplicados removido

Não é mais suportado ter vários <slot> com o mesmo nome no mesmo template. Quando um slot é renderizado, ele é “usado” e não pode ser renderizado em outro lugar na mesma árvore de renderização. Se você precisa renderizar o mesmo conteúdo em vários lugares, passe-o como uma prop.

Caminho de atualização

Execute seu conjunto de testes de ponta a ponta ou app após a atualização e procure avisos no console sobre slots v-model duplicados.

Estilizando o Atributo slot removido

Conteúdo inserido através de <slot> nomeado não preserva mais o atributo slot. Use um elemento wrapper para estilizá-los, ou para casos de uso avançados, modifique o conteúdo inserido programaticamente usando funções de renderização.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar seletores CSS mirando slots nomeados (exemplo: [slot="my-slot-name"]).

Atributos Especiais

Atributo keep-alive substituído

keep-alive não é mais um atributo especial, mas sim um componente wrapper, semelhante a <transition>. Por exemplo:

<keep-alive>
  <component v-bind:is="view"></component>
</keep-alive>

Isso torna possível usar <keep-alive> em múltiplos filhos condicionais:

<keep-alive>
  <todo-list v-if="todos.length > 0"></todo-list>
  <no-todos-gif v-else></no-todos-gif>
</keep-alive>

Quando <keep-alive> tem vários filhos, eventualmente somente um será avaliado. Qualquer filho que não seja o primeiro será ignorado.

Quando usado em conjunto com <transition>, certifique-se de aninhá-lo dentro:

<transition>
  <keep-alive>
    <component v-bind:is="view"></component>
  </keep-alive>
</transition>

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar atributos keep-alive.

Interpolação

Interpolação dentro de Atributos removido

Interpolação com atributos não é mais válido. Por exemplo:

<button class="btn btn-{{ size }}"></button>

Deve ser atualizado para usar uma expressão inline:

<button v-bind:class="'btn btn-' + size"></button>

Ou uma propriedade/dado computado:

<button v-bind:class="buttonClasses"></button>
computed: {
  buttonClasses: function () {
    return 'btn btn-' + size
  }
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de interpolação usada dentro de atributos.

Interpolação HTML removido

Interpolações HTML ({{{ foo }}}) foram removidas em favor da diretiva v-html.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar interpolações HTML.

Vinculações de Vez Única substituído

Vinculações de vez única ({{* foo }}) foram substituídas pela nova diretiva v-once.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar vinculações de vez única.

Reatividade

vm.$watch modificado

Observadores criados via vm.$watch agora são disparados antes que os componentes associados re-renderizem. Isso lhe dá a chance de atualizar o estado antes do componente re-renderizar, evitando atualizações desnecessárias. Por exemplo, você pode assistir um prop de componente e atualizar os próprios dados do componente quando o prop mudar.

Se você anteriormente confiava no vm.$watch para fazer algo com o DOM após as atualizações de um componente, você pode fazê-lo no gatilho updated do ciclo de vida.

Caminho de atualização

Execute seu conjunto de testes de ponta a ponta, se você tem um. Os testes com falha alertarão para o fato de que um observador está confiando no comportamento antigo.

vm.$set modificado

vm.$set agora é um apelido para Vue.set.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do uso obsoleto.

vm.$delete modificado

vm.$delete agora é um apelido para Vue.delete.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos do uso obsoleto.

Array.prototype.$set removido

Use Vue.set ao invés disso.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de .$set em um array. Se você perder algum, poderá ver os erros no console sobre o método ausente.

Array.prototype.$remove removido

Use Array.prototype.splice ao invés disso. Por exemplo:

methods: {
  removeTodo: function (todo) {
    var index = this.todos.indexOf(todo)
    this.todos.splice(index, 1)
  }
}

Ou melhor ainda, passe aos métodos de remoção um index:

methods: {
  removeTodo: function (index) {
    this.todos.splice(index, 1)
  }
}

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de .$remove em um array. Se você perder algum, poderá ver erros no console sobre o método ausente.

Vue.set e Vue.delete em Instâncias Vue removido

Vue.set e Vue.delete não funcionam mais em instâncias Vue. Agora é obrigatório declarar corretamente todas as propriedades reativas de nível superior na opção data. Se você quiser excluir propriedades em uma instância do Vue ou seu $data, defina-a como null.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.set ou Vue.delete em uma instância Vue. Se você perder algum, eles dispararão erros no console.

Substituíndo vm.$data removido

Agora é proibido substituir uma instância raiz $data de um componente. Isso evita alguns casos de borda no sistema de reatividade e torna o estado do componente mais previsível (especialmente com sistemas de verificação de tipos).

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de sobrescrita do vm.$data. Se você perder algum, erros no console serão emitidos.

vm.$get removido

Em vez disso, recupere dados reativos diretamente.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$get. Se você perder algum, verá erros no console.

Métodos de Instância Focados no DOM

vm.$appendTo removido

Use a API nativa do DOM:

myElement.appendChild(vm.$el)

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$appendTo. Se você perder algum, verá erros no console.

vm.$before removido

Use a API nativa do DOM:

myElement.parentNode.insertBefore(vm.$el, myElement)

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$before. Se você perder algum, verá erros no console.

vm.$after removido

Use a API nativa do DOM:

myElement.parentNode.insertBefore(vm.$el, myElement.nextSibling)

Ou se myElement for o último filho:

myElement.parentNode.appendChild(vm.$el)

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$after. Se você perder algum, verá erros no console.

vm.$remove removido

Use a API nativa do DOM:

vm.$el.remove()

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$remove. Se você perder algum, verá erros no console.

Métodos de Meta-Instância

vm.$eval removido

Nenhum uso real. Se acontecer de você contar com esse recurso de alguma forma e não sabe como contornar a falta dele, publique no fórum para obter ideias.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$eval. Se você perder algum, verá erros no console.

vm.$interpolate removido

Nenhum uso real. Se acontecer de você contar com esse recurso de alguma forma e não sabe como contornar a falta dele, publique no fórum para obter ideias.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$interpolate. Se você perder algum verá erros no console.

vm.$log removido

Ao invés disso use o Vue Devtools para uma experiência de depuração otimizada.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de vm.$log. Se você perder algum, verá erros no console.

Opções da Instância DOM

replace: false removido

Agora os componentes substituem sempre o elemento ao qual estão vinculados. Para simular o comportamento de replace: false, você pode envolver seu componente raiz com um elemento semelhante ao que você está substituíndo. Por exemplo:

new Vue({
  el: '#app',
  template: '<div id="app"> ... </div>'
})

Ou com uma função de renderização:

new Vue({
  el: '#app',
  render: function (h) {
    h('div', {
      attrs: {
        id: 'app',
      }
    }, /* ... */)
  }
})

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de replace: false.

Configuração Global

Vue.config.debug removido

Não é mais necessário, já que os avisos vêm com rastreamentos de pilha por padrão agora.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.config.debug.

Vue.config.async removido

Funcionamento assíncrono agora já é necessário para a performance de renderização.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.config.async.

Vue.config.delimiters substituído

Isso foi retrabalhado como uma opção no nível de componente. Isso permite que você use delimitadores alternativos dentro da sua aplicação sem quebrar componentes de terceiros.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.config.delimiters.

Vue.config.unsafeDelimiters removido

Interpolação do HTML já foi removida por causa do v-html.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.config.unsafeDelimiters. Depois disso, o assistente também encontrará instâncias de interpolação HTML para que você possa substituí-las por v-html.

API Global

Vue.extend com el removido

A opção el não pode mais ser usada no Vue.extend. Só é válida como uma opção na criação de instância.

Caminho de atualização

Execute seu conjunto de testes de ponta a ponta ou app após a atualização e procure avisos no console sobre a opção el no Vue.extend.

Vue.elementDirective removido

Use componentes ao invés disso.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.elementDirective.

Vue.partial removido

Parciais foram removidos em favor de fluxo de dados mais explícito entre componentes, usando props. A menos que você esteja usando um partial em uma área crítica para desempenho, a recomendação é usar um componente normal em vez disso. Se você estiver vinculando dinamicamente o name de um partial, poderá usar um componente dinâmico.

Se você estiver usando partial em uma parte crítica para o desempenho do seu aplicativo, então você deve atualizar para componentes funcionais. Eles devem estar em um arquivo JS/JSX simples (em vez de um arquivo .vue) e são sem estados ou instâncias, como partial. Isso torna a renderização extremamente rápida.

Um benefício de componentes funcionais sobre parciais é que eles podem ser muito mais dinâmicos, porque eles lhe permitem acessar todo o poder do JavaScript. No entanto, há um custo para este poder. Se você nunca usou um framework de componentes com funções de renderização antes, elas podem demorar um pouco mais para se aprender.

Caminho de atualização

Execute o assistente de migração em sua base de código para encontrar exemplos de Vue.partial.