w3resource
Vue Tutorial

Migrating from Vue 1.x

FAQ

About 90% of the API of Vue 2.X is the same with the API of Vue 1.X and there is no change in the core concept. This is going to be a long tutorial because we will like to offer very detailed explanations and include a lot of examples. You don?t have to read from top to bottom!

where should I start migrating?

  1. You can Start by running the migration helper on a current project. We have carefully compressed and minified a senior Vue dev into a simple command line interface. They will let you know whenever they recognize an obsolete feature, they will offer suggestions, and also provide links to more info.
  2. next, you need to browse through the table of contents for this page in the sidebar. If you notice a topic you may be affected by, and the migration helper didn't catch, then check it out.
  3. If you have written any tests, run the tests and see if anything still fails. If you do not have tests, you just need to open the app in your browser and keep an eye out for errors or warnings as you navigate around.
  4. Your app should be fully migrated now; you can read the rest of the page if you are hungry for more- or use the new guide from the beginning. You can skim through many parts since you are already familiar with the core concepts.

How long will it take to migrate a Vue 1.x app to 2.0?

This depends:

  • On the size of your app (small to medium-sized apps will probably be less than a day)
  • On how many times you get distracted and start playing with cool new features.
  • On which obsolete features you're using. Most can be upgraded by find-and-replace, however, others might take a few minutes. If you are not currently following best practices, Vue 2.0 will try harder to force you to. This will prove to be a good thing in the long run, but could mean a significant (though possibly overdue) refactor.

If I upgrade to Vue 2, will I also have to upgrade Vuex and Vue Router?

Vue 2 is only compatible with Vue Router 2, hence yes, you will have to follow the migration path for Vue Router as well. Most applications don't have lots of router codes thus this should not take more than an hour.

You will not be forced to upgrade Vuex, because even version 0.8 is compatible with Vue 2. But given that you may want to take advantage of new Vuex 2 features like modules and reduced boilerblate, you will most likely want to immediately upgrade.

Templates

Fragment Instances removed

In Vue 2 every component must have exactly one root element. Fragment instances are not allowed anymore. Thus given a template like this:

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

It is recommended to wrap the entire contents in a new element, as shown:

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

Lifecycle Hooks

beforeCompile removed

Instead use the created hook.

Upgrade Path

To find all examples of this hook, run the migration helper on your codebase.

compiled replaced

Use the new mounted hook instead.

attached removed

Use custom in-DOM check in other hooks. For instance, replacing:

attached: function () {
  doSomething()
}
 This could suffice:
```mounted: function () {
  this.$nextTick(function () {
    doSomething()
  })
}

Upgrade Path

To find all examples of this hook, run the migration helper on your codebase.

detached removed

Use a custom in-DOM check in other hooks. For instance, in replacing:

detached: function () {
  doSomething()
}```
This could suffice:
destroyed: function () {
  this.$nextTick(function () {
    doSomething()
  })
}

Upgrade Path

To find all examples of this hook, run the migration helper on your codebase.

init renamed

instead use the new beforeCreate hook, they are essentially the same thing. init was renamed for consistency with other lifecycle methods.

Upgrade Path

To find all examples of this hook, run the migration helper on your codebase.

ready replaced

Instead use the new mounted hook. Note that with mounted, there is no guarantee to be in-document. Hence, also include Vue.nextTick/vm.$nextTick. For instance:

mounted: function () {
  this.$nextTick(function () {
    // code that assumes this.$el is in-document
  })
}

Upgrade Path

To find all examples of this hook, run the migration helper on your codebase.

v-for

v-for Argument Order for Arrays changed

The argument order for arrays used to be (index, value) when including an index. Now it is (value, index) to be more consistent with JavaScript?s native array methods such as forEach and map.

Upgrade Path

You can run the migration helper on your codebase to find examples of the obsolete argument order. Note that if you name your index arguments something unusual like num or position, the helper will not flag them.

v-for Argument Order for Objects changed

When we are including a property name/key, the argument order for objects used to be (name, value) previously. Now it is (value, name) to be more consistent with common object iterators such as lodash's.

Upgrade Path

Run the migration helper on your codebase to find examples of the obsolete argument order. Note that if you name your key arguments something like name or property, the helper will not flag them.

$index and $key removed

The implicitly assigned $key and $index variables have been removed in favor of explicitly defining them in v-for. This has made the code easier to read for developers less experienced with Vue and has also resulted in much clearer behavior when dealing with nested loops.

Upgrade Path

Run the migration helper on your codebase to find examples of these variables that are removed. you should also see console errors such as: "Uncaught ReferenceError: $index is not defined" if you miss any.

track-byreplaced

key has replaced track-by, key works like any other attribute: without the v-bind: or: prefix, it will be treated as a literal string. In most cases, you would want to use a dynamic binding which expects a full expression rather than a key. For instance, instead of:

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

You will now write:

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

Upgrade Path

Please run the migration helper on your codebase to find examples of track-by.

v-for Range Valueschanged

In previous versions, v-for="number in 10" will have number starting at 0 and ending at 9. Now it will start at 1 and end at 10.

Upgrade Path

Search your code for the regex /\w+ in \d+/. In all the places it appears in a v-for, check to see if you may be affected.

Props

coerce Prop Option removed

If you like to coerce a prop, then setup a local computed value based on it instead. For instance, rather than:

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

Instead you could write:

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

The advantages are:

  • You will still have access to the original value of the prop.
  • You are forced to be much more explicit, by giving your coerced value a name that will differentiate it from the value passed in the prop.

Upgrade Path

You have to run the migration helper on your codebase to find examples of the coerce option.

twoWay Prop Optionremoved

Props are now only one-way down. A component needs to explicitly emit an event if it need to produce side effects.

Upgrade Path

You have to run the migration helper on your codebase to find examples of the twoWay option.

.once and .sync Modifiers on v-bind removed

Props are now only one-way down. A component needs to explicitly emit an event if it need to produce side effects.

Upgrade Path

Run the migration helper on your codebase to find examples of the.sync and .once modifiers.

Prop Mutation deprecated

It is now considered an anti-pattern to mutate a prop locally, e.g. declaring a prop and then setting this.myProp = 'someOtherValue' in the component. As a result of the new rendering mechanism, anytime the parent component re-renders, the child component?s local changes is overwritten.

Most of the use cases of mutating a prop can be replaced by one of these options:

  • a data property and the prop used to set its default value
  • a computed property

Upgrade Path

You will have to run your end-to-end test suite or app after upgrading and look for console warnings about prop mutations.

Props on a Root Instance replaced

you must use propsData instead of props on root Vue instances (i.e. instances created with new Vue({ ... })),

Upgrade Path

If you have an end-to-end test suite, run it. The failed tests will alert to you to the fact that props passed to root instances are no longer working.

Computed properties

cache: false deprecated

Caching invalidation of computed properties will be removed in future major Vue versions. Replace all uncached computed properties with methods, this will have the same result.

For instance:

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

Or using component methods:

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

Upgrade Path

To find examples of the cache: false option, run the migration helper on your codebase.

Built-In Directives

Truthiness/Falsiness with v-bind changed

When you use with v-bind, the only falsy values you get are now: null, undefined, and false. This means that empty strings and 0 will render as truthy. So, v-bind:draggable="''" will be rendered as draggable="true".

In the case of enumerated attributes, in addition to the falsy values given above, the string "false" will also be rendered as attr="false".

It should be noted that for other directives (e.g. v-if and v-show), the JavaScript?s normal truthiness still applies.

Upgrade Path

If you have an end-to-end test suite, run it. The failed tests should alert to you to any parts of your app that may have been affected by this change.

Listening for Native Events on Components with v-on changed

Now when v-on is used on a component, it only listens to custom events $emitted by that component. Use the .native modifier to listen for a native DOM event on the root element. For instance:

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

Upgrade Path

Run your end-to-end test suite, if you have one. The failed tests should alert to you to any parts of your app that may be affected by this change.

debounce Param Attribute for v-modelremoved

we use debouncing to limit how often we execute Ajax requests and any other expensive operation. Vue's debounce attribute parameter for v-model made this easy for cases that are very simple, but this actually debounced state updates rather than the expensive operations themselves. It is a subtle difference, but as an application grows, it comes with limitations.

These limitations become more apparent when designing a search indicator, like this one for instance:

There?d be no way to detect the "Typing" state while using the debounce attribute, this is because we lose access to the input?s real-time state. However, by decoupling the debounce function from Vue, we are able to debounce only the operation we want to limit, thus removing the limits on features we can develop:

<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.js"></script>
<div id="debounce-search-demo">
  <input v-model="searchQuery" placeholder="Type something">
  <strong>{{ searchIndicator }}</strong>
</div>
new Vue({
  el: '#debounce-search-demo',
  data: {
    searchQuery: '',
    searchQueryIsDirty: false,
    isCalculating: false
  },
  computed: {
    searchIndicator: function () {
      if (this.isCalculating) {
        return '? Fetching new results'
      } else if (this.searchQueryIsDirty) {
        return '... Typing'
      } else {
        return '? Done'
      }
    }
  },
  watch: {
    searchQuery: function () {
      this.searchQueryIsDirty = true
      this.expensiveOperation()
    }
  },
  methods: {
    // This is where the debounce will actually belongs.
    expensiveOperation: _.debounce(function () {
      this.isCalculating = true
      setTimeout(function () {
        this.isCalculating = false
        this.searchQueryIsDirty = false
      }.bind(this), 1000)
    }, 500)
  }
})

Another advantage of this approach is there will be times when debouncing is not quite the right wrapper function. For instance, when we hit an API for search suggestions, waiting to offer suggestions until after the user has stopped typing for a period of time is not an ideal experience. What you probably want instead is a throttling function. Now since you already using a utility library like lodash, refactoring to use its throttle function instead takes only a few seconds.

Upgrade Path

To find examples of the debounce attribute, run the migration helper on your codebase.

lazy or number Param Attributes for v-model replaced

The number param and lazy attributes are now modifiers, to make it more clear what That means that rather than:

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

You will use:

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

Upgrade Path>

To find examples of the these param attributes, run the migration helper on your codebase.

value Attribute with v-model removed

v-model will no longer care about the initial value of an inline value attribute. it instead always treats the Vue instance data as the source of truth.

That means that this element:

<input v-model="text" value="foo">```
When backed by this data:

```data: {
  text: 'bars'
}```
will render with a value of ?bars? instead of "foo". This also goes for a <textarea> with existing content. Rather than:
```<textarea v-model="text">
  hello world
</textarea>

You should ensure that your initial value for text is "hello world".

Upgrade Path

Run your end-to-end test suite or app after upgrading and look for console warnings about inline value attributes with v-model.

v-model with v-for Iterated Primitive Values removed

Cases like the one below no longer work:

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

The reason for this is that this is the equivalent JavaScript that the <input> would compile to:

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

It can be seen that, the v-model's two-way binding doesn?t make sense here. Setting str to another value in the iterator function will do nothing because it is only a local variable in the function scope.

rather, it is recommended that you use an array of objects so that v-model can update the field on the object. For instance:

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

Upgrade Path

If you have a test suite, run it. The failed tests should alert to you to all parts of your app that may be affected by this change.

v-bind:style with Object Syntax and !important removed

This no longer works:

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

If you really must override another !important, then you must use the string syntax:

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

Upgrade Path

To find examples of style bindings with !important in objects, run the migration helper on your codebase.

v-el and v-ref replaced

For simplicity sake, v-ref and v-el have been merged into the ref attribute, which is accessible on a component instance via $refs. This means v-el:my-element would become ref="myElement" and v-ref:my-component would then become ref="myComponent". When we use this on a normal element, the ref will become the DOM element, and when we use this on a component, the ref will become the component instance.

Since v-ref is not a directive anymore, but rather a special attribute, it can be defined dynamically. This is especially useful when used in combination with v-for. For instance:

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

Previously, when v-el/v-ref is combined with v-for, it would produce an array of elements/components, this is because there was no way to give each item a unique name. This behavior can still be achieved by giving each item the same ref:

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

Unlike in version 1.x, the $refs in 2.x are not reactive, this is because they are registered/updated during the render process itself. Making them reactive would require duplicate renders for every change.

$refs On the other hand, are designed primarily for programmatic access in JavaScript - it is not recommended to rely on $refs in templates, because that would mean referring to states that does not belong to the instance itself. This would violate the data-driven view model of Vue.

Upgrade Path

To find examples of v-el and v-ref, Run the migration helper on your codebase.

v-else with v-show removed

The v-else no longer works with v-show. Use the v-if with a negation expression instead. An example is shown below is invalid:

<p v-if="foo">Foo</p>
<p v-else v-show="bar">Not foo, but bar</p>

You should use:

<p v-if="foo">Foo</p>
<p v-if="!foo && bar">Not foo, but bar</p>

Upgrade Path

To find examples of the v-else with v-show, run the migration helper on your codebase.

Custom Directives simplified

Directives now have a greatly reduced scope of responsibility: now they are only used for applying low-level direct DOM manipulations. In most of the cases, you should use components as the main code-reuse abstraction.

Some of the most notable differences in the simplified directives include:

  • Directives no more have instances. This means there is no more this inside directive hooks. rather, they receive everything they may need as arguments. If you must persist state across hooks, you can do so on the el.
  • Options such as acceptStatement, priority, deep, etc have all been removed. So as to replace twoWay directives.
  • Some of the current hooks have different behavior and there are also a couple new hooks.

Fortunately, since the new directives are very much simpler, you can master them much more easily.

Upgrade Path

To find examples of defined directives, run the migration helper on your codebase. The helper will flag them all, as it is likely in most cases that you will want to refactor to a component.

Directive .literal Modifier removed

The .literal modifier no longer exist, as it can be easily achieved by providing a string literal as the value.

For instance, you could update:

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

to:

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

Upgrade Path

To find examples of the `.literal` modifier on a directive, run the migration helper on your codebase.

Transitions

transition Attribute replaced

The transition system of Vue has changed quite drastically and now it uses <transition> and <transition-group> wrapper elements, and not the transition attribute. Read the new Transitions guide to learn more.

Upgrade Path

To find examples of the transition attribute, run the migration helper on your codebase.

Vue.transition for Reusable Transitions replaced

You can now use components for reusable transitions with the new transition system.

Upgrade Path

To find examples of Vue.transition, run the migration helper on your codebase.

Transition stagger Attributeremoved

if you need to stagger the list transitions, then you can control timing by setting and accessing a data-index (or similar attribute) on an element.

Upgrade Path

Run the migration helper on your codebase to find examples of the transition attribute. During your update, you can transition (pun very much intended) to the new staggering strategy as well.

Events

events option removed

The events option no longer exists. Now event handlers should be registered in the created hook instead. Check the $dispatch and $broadcast migration guide for a detailed example.

Vue.directive('on').keyCodes replaced

A new and more concise way to configure keyCodes is through Vue.config.keyCodes. for instance:

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

Upgrade Path

To find examples of the the old keyCode configuration syntax, run the migration helper on your codebase.

$dispatch and $broadcast replaced

$dispatch and $broadcast have been removed to favor more explicitly cross-component communication and also more maintainable state management solutions, like Vuex.

The problem is that event flows that depend on a component?s tree structure can be hard to reason about and are so brittle when the tree becomes large. They do not scale well and only set you up for serious pain later. And also $dispatch and $broadcast do not solve communication between sibling components.

One of the most common uses you will see for these methods is when we want to communicate between a parent and its direct children. In cases as these, you can actually listen to an $emit event from a child with v-on. This will allow you to keep the convenience of events with added explicitness.

When we want to communicate between distant descendants/ancestors, in such cases $emit won?t help you. Instead of the $emit, the simplest possible upgrade you can use would be to use a centralized event hub. This has the added benefit of allowing you to communicate between components no matter where they may be in the component tree- that includes communications between siblings! Because Vue instances will implement an event emitter interface, you can actually use an empty Vue instance for this purpose as well.

Take for example, say we have a todo app that is structured like this:

Todos
+- NewTodoInput
+- Todo
   +- DeleteTodoButton

We could manage communication between components with this single event hub:

// This is the event hub we will use in 
// every component to communicate between them.
var eventHub = new Vue()

Now in our components, we can use $on, $emit, $off to emit events, listen for events, and also to clean up event listeners, respectively:

// 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)
},
// It is good to clean up event listeners
// before a component is destroyed.
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
    })
  }
}

The pattern shown above can serve as a replacement for $dispatch and $broadcast in simple scenarios, but for when we have complex cases, it is recommended to use a dedicated state management layer such as Vuex.

Upgrade Path

To find examples of $dispatch and $broadcast, run the migration helper on your codebase.

Filters

Filters Outside Text Interpolations removed

Now filters can only be used inside text interpolations ({{ }} tags). In the past we have found using filters within directives such as v-model, v-on, etc led us to more complexity than convenience. For list filtering on a v-for, it is also better to move that logic into JavaScript as computed properties, so that you can reuse it throughout your component.

generally, whenever we can achieve something in plain JavaScript, we want to avoid introducing a special syntax like filters to take care of that same concern. Here is how you can replace Vue's built-in directive filters:

Replacing the debounce Filter

Rather than using:

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

Use the lodash's debounce (or possibly throttle) to directly limit calling the expensive method. The above can be achieved like this:

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

Replacing the limitBy Filter

Rather than using:

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

Use the JavaScript built-in .slice method in a computed property:

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

Replacing the filterBy Filter

Instead of you writing this:

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

You can use JavaScript's built-in .filter method in a computed property:

<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
    })
  }
}

The JavaScript's native .filter can also manage much more complex filtering operations, this is because you have access to the full power of JavaScript within computed properties. For instance, if you needed to find all active users and then case-insensitively match against both their name and email you could do this:

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)
  )
})

Replacing the orderBy Filter

Rather than:

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

You can use lodash's orderBy (or possibly sortBy) in a computed property:

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

You can also order by multiple columns:

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

Upgrade Path

To find examples of filters being used inside directives, run the migration helper on your codebase. In the case where you miss any, you should also get console errors.

Filter Argument Syntax changed

Filters'' syntax for arguments are now better aligned with JavaScript function invocation. So instead of us taking space-delimited arguments:

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

We can surround the arguments with parentheses and then delimit the arguments with commas:

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

Upgrade Path

To find examples of the old filter syntax, run the migration helper on your codebase.you will get console errors if you miss any.

Built-In Text Filters removed

Although filters that are within text interpolations are still allowed, it should be noted that all of the filters have been removed. Instead, it is recommended to use more specialized libraries for solving problems in each domain (e.g. date-fns to format dates and accounting for currencies).

For each of the Vue's built-in text filters, we will go through how you can replace them below. The example code below could exist in custom helper functions, methods, or computed properties.

Replacing the json Filter

You actually do not need to for debugging anymore, this because by default Vue will nicely format output for you automatically, whether it is a string, number, array, or plain object. If you would want the exact same functionality as JavaScript's JSON.stringify though, then you can use that in a computed property or method.

Replacing the capitalize Filter

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

Replacing the uppercase Filter

text.toUpperCase()

Replacing the lowercase Filter

text.toLowerCase()

Replacing the pluralize Filter

The pluralize package on NPM will serve this purpose nicely, you can also easily define your own pluralize functions if you only want to pluralize a specific word or want to have special output for cases like 0. For instance:

function pluralizeKnife (count) {
  if (count === 0) {
    return 'no knives'
  } else if (count === 1) {
    return '1 knife'
  } else {
    return count + 'knives'
  }
}

Replacing the currency Filter

A very na?ve approach to implement this is as shown below:

'$' + price.toFixed(2)

There are many cases where you'll still run into strange behavior (e.g. 0.035.toFixed(2) rounds up to 0.04, and 0.045 rounds down to 0.04). You can use the accounting library to more reliably format currencies and thus work around this issue.

Upgrade Path

To find examples of the obsolete text filters, run the migration helper on your codebase. You will get console errors if you miss any.

Two-Way Filters replaced

Some developers have enjoyed using two-way filters with v-model to create interesting inputs with very little code. While this is seemingly simple however, take care using two-way filters, because they can also hide a great deal of complexity - and they even encourage poor UX by delaying state updates. Instead, components that wraps an input are recommended as a more explicit and feature-rich way of creating custom inputs.

Upgrade Path

To find examples of filters used in directives like v-model, run the migration helper on your codebase. If you miss any, you should also see console errors.

Slots

Duplicate Slots removed

Having <slot>s with the same name in the same template is no longer supported. Whenever a slot is rendered it is "used up" and we cannot be render it elsewhere in the same render tree.pass the content as a prop if you must render it multiple times.

Upgrade Path

Look for console warnings about duplicate slots v-model, after running your end-to-end test suite or after upgrading your app.

slot Attribute Styling removed

Content inserted via named <slot>, will no longer preserve the slot attribute. You have to use a wrapper element to style them, or modify the inserted content programmatically using render functions for advanced use cases.

Upgrade Path

To find CSS selectors targeting named slots (e.g. [slot="my-slot-name"]). run the migration helper on your codebase .

Special Attributes

keep-alive Attribute replaced

The keep-alive is no longer a special attribute, it is now a wrapper component, it is similar to <transition>. For instance:

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

This makes it possible to use <keep-alive> on multiple conditional children:

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

When the <keep-alive> has multiple children, the children should eventually evaluate to a single child. Any child apart from the first one will be ignored.

When <keep-alive> used together with <transition>, make sure to nest <keep-alive> inside:

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

Upgrade Path

To find keep-alive attributes, run the migration helper on your codebase.

Interpolation

Interpolation within Attributes removed

Interpolation that are within attributes is no longer valid. For instance:

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

Should either be updated so it uses an inline expression:

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

Or a data/computed property:

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

Upgrade Path

To find examples of interpolation used within attributes, run the migration helper on your codebase.

HTML Interpolation removed

The HTML interpolations ({{{ foo }}}) have been removed in favor of the v-html directive.

Upgrade Path

To find HTML interpolations, run the migration helper on your codebase.

One-Time Bindings replaced

The one time bindings ({{* foo }}) have been replaced by the new v-once directive.

Upgrade Path

To find one-time bindings, run the migration helper on your codebase.

Reactivity

vm.$watch changed

Watchers that created via vm.$watch are now fired before the associated component rerenders. This will give you the chance to further update state before the component re-renders, thus avoiding updates that are unnecessary. For instance, you can observe a component prop and update the component?s own data whenever the prop changes.

If you were previously relying on the vm.$watch to do something with the DOM after a component updates, you can do so in the updated lifecycle hook instead.

Upgrade Path

If you have an end-to-end test suite, run it. The failed tests should alert to you to the fact that there is a watcher that was relying on the old behavior.

vm.$set changed

The vm.$set is now an alias for Vue.set.

Upgrade Path

To find examples of the obsolete usage, run the migration helper on your codebase.

vm.$delete changed

The vm.$delete is now an alias for Vue.delete.

Upgrade Path

To find examples of the obsolete usage, run the migration helper on your codebase.

Array.prototype.$set removed

You should use Vue.set instead.

Upgrade Path

To find examples of .$set on an array, run the migration helper on your codebase.

Array.prototype.$remove removed

You should use Array.prototype.splice instead. For instance:

methods: {
  removeTodo: function (todo) {
    var index = this.todos.indexOf(todo)
    this.todos.splice(index, 1)
  }
}```
Or better yet, pass removal methods an index:
```methods: {
  removeTodo: function (index) {
    this.todos.splice(index, 1)
  }
}

Upgrade Path

To find examples of .$remove on an array, run the migration helper on your codebase. You will get a console warning if you miss any.

Vue.set and Vue.delete on Vue instances removed

Vue.set and Vue.delete no longer work on Vue instances. Now it is mandatory to properly declare all top-level reactive properties in the data option. If you would like to delete properties on a Vue instance or its $data, then set it to null.

Upgrade Path

To find examples of Vue.set or Vue.delete on a Vue instance, run the migration helper on your codebase. They'll trigger console warnings if you miss any.

Replacing vm.$data removed

It is now prohibited for you to replace a component instance's root $data. This will prevent some edge cases in the reactivity system and makes the component state more predictable (especially with type-checking systems).

Upgrade Path

To find examples of overwriting vm.$data, run the migration helper on your codebase. console warnings will be emitted if you miss any.

vm.$get removed

Retrieve reactive data directly instead.

Upgrade Path

To find examples of vm.$get, run the migration helper on your codebase. You'll see console errors if you miss any.

DOM-Focused Instance Methods

vm.$appendTo removed

You should use the native DOM API:

myElement.appendChild(vm.$el)

Upgrade Path

To find examples of vm.$appendTo, run the migration helper on your codebase. you'll see console errors if you miss any.

vm.$before removed

You should use the native DOM API:

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

Upgrade Path

To find examples of vm.$before, run the migration helper on your codebase. you'll see console errors if you miss any.

vm.$after removed

You should use the native DOM API:

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

Or if myElement is the last child then use:

myElement.parentNode.appendChild(vm.$el)

Upgrade Path

To find examples of vm.$after, run the migration helper on your codebase. you'll see console errors if you miss any.

vm.$remove removed

You should use the native DOM API:

vm.$el.remove()

Upgrade Path

To find examples of vm.$el.remove(), run the migration helper on your codebase. you'll see console errors if you miss any.

Meta Instance Methods

vm.$evalremoved

There is no real use.

Upgrade Path

To find examples of vm.$eval, run the migration helper on your codebase. you'll see console errors if you miss any.

vm.$interpolateremoved

this has no real use.

Upgrade Path

To find examples of vm.$interpolate, run the migration helper on your codebase. you'll see console errors if you miss any.

vm.$logremoved

Please use the Vue Devtools for the optimal debugging experience.

Upgrade Path

To find examples of vm.$log, run the migration helper on your codebase. you'll see console errors if you miss any.

Instance DOM Options

replace: false removed

Components will now always replace the element they are bound to. For you to simulate the behavior of replace: false, you can wrap your root component with an element similar to the one you are replacing. For instance:

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

Or with a render function:

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

Upgrade Path

To find examples of replace:false, run the migration helper on your codebase.

Global Config

Vue.config.debug removed

No longer necessary, since warnings come with stack traces by default now.

Upgrade Path

To find examples of Vue.config.debug, run the migration helper on your codebase.

Vue.config.async removed

The Async is now required for rendering performance.

Upgrade Path

To find examples of Vue.config.async, run the migration helper on your codebase.

Vue.config.delimiters replaced

This has been reworked as a component-level option. This will allow you to use alternative delimiters within your app without breaking 3rd-party components.

Upgrade Path

To find examples of Vue.config.delimiters, run the migration helper on your codebase.

Vue.config.unsafeDelimiters removed

The HTML interpolation has been removed in favor of v-html.

Upgrade Path

To find examples of Vue.config.unsafeDelimiters, run the migration helper on your codebase.

After this is done, the helper will also find instances of HTML interpolation so that you can replace them with `v-html`.

Global API

Vue.extend with elremoved

You can no longer use the el option in Vue.extend. It is only valid as an instance creation option.

Upgrade Path

Run your app after upgrading or your end-to-end test suite and then look for console warnings about the el option with Vue.extend.

Vue.elementDirective removed

Please use components instead.

Upgrade Path

To find examples of Vue.elementDirective, run the migration helper on your codebase.

Vue.partial removed

Partials have been removed from Vue in favor of more explicit data flow between components, using props. Unless you are using a partial in a performance-critical area, the recommendation is to use a normal component. If you were dynamically binding the name of a partial, then use a dynamic component.

If you are already using partials in a performance-critical part of your app, then it is recommended that you upgrade to functional components. The components must be in a plain JS/JSX file (rather than in a .vue file) and are instanceless and stateless, like partials. This will make rendering extremely fast.

One benefit of functional components over partials is that they can be much more dynamic, this is because they grant you access to the full power of JavaScript. However, this power comes at a cost. If you have never used a component framework with render functions before, then they may take a bit longer to learn.

Upgrade Path

To find examples of Vue.partial, run the migration helper on your codebase.