5
Vue2 / Vuetify2 Migration to Vue3 / Nuxt3 / Vuetify3
source link: https://gist.github.com/mgd216/33d7805847f9bb1ef23a381fd76e22e6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Vue3 / Nuxt3 / Vuetify3 Migration Steps
- The following notes are a 'checklist' for migrating a large Vue2 / Vuetify2 project to Vue3 / Nuxt3 / Vuetify3. It is NOT intended to be a step-by-step migration guide, they are rough notes our team used for migration
- Our team decided to create a brand new Nuxt3 app, instead of tyring a 'bridge' or running Vue2/Vue3 side-by-side:
- nuxi init our-new-app-v3
- We also changed our store from Vuex to Pinia
- We decided to use the experimental @vue/reactivity-transform. This provides the ability to use a ref in the script block without having to use .value everywhere
- without reactivity-transform
let userName = ref<string>('')
userName.value = "John Doe"
- with reactivity-transform
let userName = $ref<string>('')
userName = "John Doe"
- to enable add the following to nuxt.config.ts
- experimental: { reactivityTransform: true }
- without reactivity-transform
- Props are defined using interfaces, this means you can access props without having to use props.myVar
- without interface definition
const props = defineProps(['userName'])
console.log("NAME", props.userName)
- with interface definition
interface Props { userName: string; }
const { userName = 'Joe Doe' } = defineProps<Props>();
console.log("NAME", userName)
- without interface definition
- Using reactivity-transform and defining props with interfaces keeps our resulting code very clean because you do not have to use myvar.value or props.myvar.
Structure Changes
- Copy each Vue2 file into a new Vue3 file using proper Nuxt folders:
- components/UserInfoCard.vue > components/User/InfoCard.vue
- Move the Script block above the Template block
Script Block Changes
- Add into the script tag: <script lang="ts" setup>
- Search and remove "this."
- Delete all imports statements, most are auto-imported by Nuxt
- Remove from the copied Vue2 export block {}:
- components
- Migrate Props
interface Props { sid: string;}
const { sid } = defineProps< Props >();
- Migrate Data
let var = $ref< string >()
- Migrate Computed
- mapGetters >
const { var } = $(useSomething())
const var = $computed(()=>{})
- mapGetters >
- Migrate Created
- onBeforeMount(() => {})
- Migrate Watchers
watch(() => varToWatch,() => { functionToCall(); });
watch(() => varToWatch,() => { functionToCall(); }, { immediate: true });
watch(() => varToWatch,() => { functionToCall(); }, { deep: true, immediate: true });
watch([() => var1ToWatch, () => var2ToWatch],() => { functionToCall(); })
- Migrate Methods
- mapActions >
const { function } = useStore()
- copy functions directly out of method {} block
- mapActions >
- Change constants to enums
- Paages
- Add the Nuxt Page metadata
definePageMeta({key: (route) => route.fullPath, middleware: ['auth'], layout: 'page',});
Template Block Changes
- Remove all Filters replace with functions or composables
<span>{{ updateDate | dateFormat}}</span>
><span>{{ formatDate(updateDate)}}</span>
- Change $refs
$refs
are no longer available for access to components in template as a ref- Include component with ref:
<UserDialog ref="UserDialogRef" />
- On UserDialog component expose function to open:
defineExpose({showDialog})
- In script of parent, create a ref:
const UserDialogRef = $ref<InstanceType<typeof UserDialog> | null>(null);
- from the parent call the exposed method:
MyDialogRef.showDialog();
Vuetify Changes
- Change Icons
- change fa-icon > v-icon icon="fa:fas fa-home"
- check text colors green--text > color="green"
- Change Text Typogrpahy
- title > text-h5
- caption > text-caption
- success--text > text-success
- warning--text > text-warning
- error--text > text-error
- white--text > text-white
- v-list Changes
- remove v-list-item-content > It is now the default slot on v-list-item
- v-chip Changes
- sizes are now size="small" not attributes
- colors are now color="primary" not a class
- default is a translucent background, need to use variant="flat"
- v-select and v-autocomplete
- @input= > @update:model-value=
- item-text > item-title
- v-tabs
- v-tabs-items > v-window
- v-tab-item > v-window-item
- v-simple-table
- v-simple-table > v-table
- dense > density="compact"
- v-virtual-scroll (Not available until Vuetify 3.1+)
- Use RecycleScroller (https://www.npmjs.com/package/vue-virtual-scroller)
- v-textfield
- filled > variant="plain"
- append-icon="mdi-magnify" > prepend-inner-icon="fa:fas fa-search"
- @input= > @update:model-value=
- Activators (v-tooltip)
- #activator="{ on }" > #activator="{ props }"
- v-on="on" > v-bind="props"
Code Example
Original Vue2 / Vuetify2 file
- components/PersonSoftwareCard.vue
<template>
<BaseCard :title="title">
<template #body>
<LoadingSpinner :loading="softwaresLoading" title="Software" :size="30">
<v-row>
<v-col cols="6">
<div class="title grey darken-2 pl-2"> Owned: </div>
<div v-if="softwareOwned.length > 0">
<v-virtual-scroll
:bench="10"
:items="softwareOwned"
:item-height="30"
:height="200"
class="scroller-blue"
>
<template #default="{ item, index }">
<v-row
dense
no-gutters
style="height: 30px"
:style="AppService.getAltRowColor(index, '#121212')"
align="center"
>
<v-col cols="8" class="text-truncate">
<span class="ml-2 font-weight-bold">{{ item.softwareName }}</span>
</v-col>
<v-col cols="1">
{{ item.quantity | numFormat('0,0') }}
</v-col>
<v-col cols="3" class="text-center">
<SoftwareStatusLabel :software="item" />
</v-col>
</v-row>
</template>
</v-virtual-scroll>
</div>
<div v-else>
<div class="text-center headline my-5">
There is no Software assigned to {{ userId }} as the Owned.
</div>
</div>
</v-col>
<v-col cols="6">
<div class="title grey darken-2 pl-2"> Subscribed: </div>
<div v-if="softwareSubscribed.length > 0">
<v-virtual-scroll :bench="10" :items="softwareSubscribed" :item-height="30" :height="200" class="scroller-blue">
<template #default="{ item, index }">
<v-row
dense
no-gutters
style="height: 30px"
:style="AppService.getAltRowColor(index, '#121212')"
align="center"
>
<v-col cols="8" class="text-truncate">
<span class="ml-2 font-weight-bold">{{ item.softwareName }}</span>
</v-col>
<v-col cols="1">
{{ item.quantity | numFormat('0,0') }}
</v-col>
<v-col cols="3" class="text-center">
<SoftwareStatusLabel :software="item" />
</v-col>
</v-row>
</template>
</v-virtual-scroll>
</div>
<div v-else>
<div class="text-center headline my-5">
There is no Software assigned to {{ userId }} as the Subscribed.
</div>
</div>
</v-col>
</v-row>
</LoadingSpinner>
</template>
</BaseCard>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import AppService from '@/services/app.service.js'
import BaseCard from '@/components/layout/BaseCard.vue'
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
import SoftwareStatusLabel from '@/components/software/SoftwareStatusLabel.vue'
export default {
name: 'PersonSoftwareCard',
components: {
BaseCard,
LoadingSpinner,
SoftwareStatusLabel,
},
props: {
userId: {
type: String,
default: null,
},
},
data() {
return {
AppService,
softwareOwned: [],
softwareSubscribed: [],
}
},
computed: {
...mapGetters(['softwares', 'softwaresLoading', 'userIdAuth']),
title() {
return this.userId === this.userIdAuth ? 'My Assigned Software' : `Software Assigned to: ${this.userId}`
},
},
created() {
this.initialize()
},
methods: {
...mapActions(['getSoftwares']),
async initialize() {
if (this.softwares.length === 0) {
await this.getSoftwares()
}
this.softwareOwned = this.softwares.filter((s) => {
if (s.ownedUserId?._id === this.userId) return true
return false
})
this.softwareSubscribed = this.softwares.filter((s) => {
if (s.subscribeUserId?._id === this.userId) return true
return false
})
},
},
}
</script>
New Vue3 / Nuxt3 / Vuetify3 file
- components/Person/SoftwareCard.vue
<script lang="ts" setup>
interface Props {
userId?: string;
}
const { userId } = defineProps<Props>();
const NumSvc = useNumeral();
let softwareOwned = $ref<any[]>([]);
let softwareSubscribed = $ref<any[]>([]);
const { userIdAuth } = useAuthStore();
const { getAltRowColor } = useAppStore();
const { getSoftwares, softwares, softwaresLoading } = useSoftwareStore();
const title = computed(() => {
return userId === userIdAuth ? 'My Assigned Software' : `Software Assigned to: ${userId}`;
});
onBeforeMount(async () => {
if (softwares.length === 0) {
await getSoftwares();
}
softwareOwned = softwares.filter((s) => {
if (s.ownedUserId?._id === userId) return true;
return false;
});
softwareSubscribed = softwares.filter((s) => {
if (s.subscribeUserId?._id === userId) return true;
return false;
});
});
</script>
<template>
<BaseCard :title="title">
<template #body>
<LoadingSpinner :loading="softwaresLoading" title="Software" :size="30">
<v-row>
<v-col cols="6">
<div class="text-h5 grey-darken-2 pl-2">Owned:</div>
<div v-if="softwareOwned.length > 0">
<RecycleScroller
v-slot="{ item, index }"
:items="softwareOwned"
:item-size="30"
class="scroller-200"
key-field="_id"
>
<v-row no-gutters style="height: 30px" :style="getAltRowColor(index, '#121212')" align="center">
<v-col cols="8" class="text-truncate">
<span class="ml-2 font-weight-bold">{{ item.softwareName }}</span>
</v-col>
<v-col cols="1">
{{ NumSvc.numberFormat(item.quantity, '0,0') }}
</v-col>
<v-col cols="3" class="text-center">
<SoftwareStatusLabel :software="item" />
</v-col>
</v-row>
</RecycleScroller>
</div>
<div v-else>
<div class="text-center text-h5 my-5">
There is no Software assigned to <b>{{ userId }}</b> as the Owned.
</div>
</div>
</v-col>
<v-col cols="6">
<div class="text-h5 grey-darken-2 pl-2">Subscribed:</div>
<div v-if="softwareSubscribed.length > 0">
<RecycleScroller
v-slot="{ item, index }"
:items="softwareSubscribed"
:item-size="30"
class="scroller-200"
key-field="_id"
>
<v-row no-gutters style="height: 30px" :style="getAltRowColor(index, '#121212')" align="center">
<v-col cols="8" class="text-truncate">
<span class="ml-2 font-weight-bold">{{ item.softwareName }}</span>
</v-col>
<v-col cols="1">
{{ NumSvc.numberFormat(item.quantity, '0,0') }}
</v-col>
<v-col cols="3" class="text-center">
<SoftwareStatusLabel :software="item" />
</v-col>
</v-row>
</RecycleScroller>
</div>
<div v-else>
<div class="text-center text-h5 my-5">
There is no Software assigned to <b>{{ userId }}</b> as the Subscribed.
</div>
</div>
</v-col>
</v-row>
</LoadingSpinner>
</template>
</BaseCard>
</template>
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK