Totara 19 Vue 3 migration guide
In Totara 19, we have shifted to the latest major version of the Vue framework, Vue 3.
For the most part, you should be able to keep writing components just like before. There are a few changes to be aware of. Most of these are covered by eslint.
General workflow
npm ci
to make sure you have the correct version of all of the packagesRun eslint (
npm run tui-style-check
), fix the issues.Build tui, fix any issues the compiler complains about.
Test everything works in the browser, while keeping the console open to see any runtime warnings.
Testing
Test the Tui/Vue front-end of the features/code you have built. Test that everything looks and works okay, and that there aren’t any errors or warnings in the browser console.
Try all possible states and interfaces – especially interactive things and things that make GraphQL requests.
Breaking changes
You can see the full list of breaking changes in the Vue documentation, but some of the most impactful are summarised below.
It may also be useful to familiarise yourself with the Vue 3 documentation.
It is also safe to assume that any core component overrides will need to be checked and updated to Vue 3. Changes to core components' public API are documented in upgrade.txt.
We don’t recommend attempting to replace vue with the the @vue/compat
build -- in our experience, the compatibility layer is not reliable, and many options will need to be disabled as our codebase has been migrated to Vue 3 already.
Event handling (eslinted)
All component must now declare what events they emit, in an emits
array on the component. This is enforced by eslint.
The .native modifier no longer exists, instead when attaching a listener (e.g. @click="..."
), any event that is not declared in the emits
field of the component will now also be attached as a native event listener to the root element of the component.
false
attribute values
In Vue 2, false
would remove the attribute. In Vue 3, false
sets the attribute to the string "false"
If you want to remove the attribute, use null
instead.
Whitespace
Handling of whitespace inside templates has changed a little compared to Vue 2. The changes are:
Whitespace characters between elements that contain newlines are removed.
Consecutive whitespace characters in text nodes are condensed into a single space.
Render functions, vnodes, and h()
This has changed a lot between versions. If you’re using render functions, vnodes, or h(), expect to rework your code.
:key (eslinted)
When using with
<template v-for>
, the:key
should be placed on the template tag, instead of the childNo longer necessary on v-if/else/else-if branches
v-bind object order
When using v-bind="someObject"
, individual props will now only take precedence if they come before the v-bind. More info
v-model and the input event (eslinted)
Plain v-model is no longer allowed, except on native HTML elements. Use v-model:value
instead.
Avoid using the input
event on components, prefer update:value
(or update:something
).
Slots (eslinted)
$scopedSlots no longer exists. Use $slots instead for all cases, it now behaves like $scopedSlots used to.
this.$el
If your component has multiple root elements, or only renders a slot as its sole content (i.e. a renderless component), this.$el
will point to an HTML comment instead of an actual element.
A workaround is to call extractSingleChild
from tui/vue/vnode
on the slot in the render function.
watch
Watchers on arrays no longer fire if an item is added or removed from the array. You must pass deep: true as a watch option.
Jest tests
Some things that may catch you out in Jest tests:
propsData
is now justprops
scopedSlots
is now justslots
mocks
andstubs
have now moved under theglobal
field onmount
/render
mockQueries
works on both, but it is preferrable to put it under theglobal
field to align withmocks
wrapper.emitted()
now includes native events. It is recommended to just ask for the event you are looking for with e.g.wrapper.emitted('submit')
Lifecycle methods (eslinted)
The
destroyed
lifecycle option has been renamed tounmounted
The
beforeDestroy
lifecycle option has been renamed tobeforeUnmount
Mutating props (eslinted)
This was deprecated in Vue 2 too, but is riskier in Vue 3. This will be automatically flagged by an eslint rule.
Instead of mutating the prop, copy it and change the copy. produce()
from tui/immutable
can help with this.
$children
$children has been removed in Vue 3.
Vue namespaced methods
Vue.set
and Vue.delete
no longer exist -- they are no longer necessary with the proxy-based reactivity approach in Vue 3.
Other methods such as Vue.nextTick
are now just import { nextTick } from 'vue';
Mixins
Mixins should be avoided where possible, but there is a breaking change in Vue 3: data() on mixins is no longer merged with the component’s data().