Table of contents
Vue 3.0 was (finally!) released a little while ago. It is mostly compatible with the old Vue 2 way of doing things... but lots have changed and there are many new ways of doing things.
This is a guide to setting things up in Vue 3 using the Vue 3 composition API, and instructions for adding packages such as Vuex, Vue Router, Vue Test Utils and setting up Typescript support with Vue 3. It is intended for readers who are already familiar with Vue 2 but are looking for a tutorial/guide to move their codebase to use Vue 3.
I've created a sample app, along with the Vue 3 app source code on Github.
The app is quite basic and is meant just for demonstration purposes. I find tutorials and guides much easier to see when they have a bigger application to refer to, rather than just short snippets.
A quick guide to Vue 3's composition API
There are tons of guides on Vue 3's composition API. I am really excited to start using it in some projects (although I still have some reservations about how useful it will be - but I felt the same about React hooks, and the have really picked up in popularity)
In Vue 2 (and also in Vue 3 as you can still use the options API) you would probably be used to seeing things like this:
// ...
export default {
data() {
return {
postTitle: 'a default title',
}
},
methods: {
updateTitle(newValue) {
this.postTitle = newValue;
}
},
computed: {
numWordsInTitle() {
return this.postTitle.split(' ').length;
}
}
}
And then in your <template>
you could reference those data/methods/computed properties:
<template>
<h1>{{ postTitle }}</h1>
<p>Post has {{ numWordsInTitle }} words</p>
<button @click="updateTitle('something new')">Update to 'something new'</button>
</template>
To convert from that 'old' Vue 2 options API to using the Vue 3 composition API, you would instead do the following:
import {defineComponent, ref, computed} from 'vue';
export default defineComponent({
name: 'BlogSummary',
components: {},
props: {
post: Object,
},
setup(props) {
const postTitle = ref('postTitle');
const numWordsInTitle = computed(() => postTitle.value.split(' ').length);
const updateTitle = (newValue) => postTitle.value = newValue;
return { postTitle, numWordsInTitle, updateTitle }
}
});
The <template>...</template>
part would be the same as the above. Anything returned from setup()
's object will be available to use in the <template>
.
For a very small example of a Vue 3 component which uses setup()
and then uses the data returned from that function within <template>
, check out this example component
There is much more that can be said about the composition API. But this is just a brief introduction. I have a more in-depth overview of Vue 3's composition API here.
Set up Vue 3 with Sass/SCSS
It is very easy to set up Sass with Vue 3, which I always prefer as it makes writing CSS a little nicer.
Basic setup of Sass in Vue 3 is quite easy: yarn add -D sass-loader sass
. I have written more about setting up Sass in Vue 3 here (including global config)
Setting up Vuex in Vue 3
There are some arguments that Vuex will not be needed as often, thanks to the composition API in Vue 3. But I think for large applications it will still be a staple part of using Vue.
yarn add vuex
(I'm using 4.0.0, which works with Vue 3)
In your main.js
or main.ts
file you will need to set it up like this:
import { createApp } from 'vue'
import {createStore} from 'vuex'
import App from './App.vue'; // Your main component
const store = createStore({
state: {
posts: [],
},
getters: {
allPosts(state) {
return state.posts
},
},
mutations: {},
actions: {},
modules: {}
})
createApp(App)
.use(store) // << this is important
.mount('#app')
Once you have your Vue 3 Vuex config set up, you just need to access the store in your Vue 3 components.
One way to do this is via the composition API:
import {computed, defineComponent} from 'vue';
import {useStore} from "vuex";
export default defineComponent({
name: 'SinglePost',
components: {},
setup() {
const store = useStore();
const post = computed(() => store.getters.allPosts.find((post:any) => post.slug === route.params.slug))
return {
post,
}
},
});
For a more in-depth example of how to use Vuex 4 in Vue 3, I recommend looking at the sample Vue 3 boilerplate app, which includes Vue 3 and Vuex config.
Setting up Vue Router in Vue 3
Vue router works well with Vue 3. To get started you must install it with: yarn add vue-router
Then update your main.ts
or main.js
to something like this:
import { createApp } from 'vue'
import App from './App.vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import BlogIndex from "@/components/blog/views/BlogIndex.vue";
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'blog.index',
component: BlogIndex, // without webpack code splitting
},
{
path: '/post/:slug',
name: 'blog.show',
// with webpack code splitting (best for larger apps, it can lazy load then):
component: () => import(/* webpackChunkName: "blog-show" */ '../components/blog/views/SinglePost.vue')
},
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
createApp(App)
.use(router) // << important!
.mount('#app')
For a full example of using vue router in Vue 3, I recommend looking at the sample Vue 3 boilerplate app, which includes Vue 3 and vue router.
Testing in Vue 3 with Vue Test Utils
Testing in Vue 3 with Vue Test Utils is largely similar as it was in Vue 2.
Here is an example of a Vue Test Utils test (using Jest) for a component in Vue 3 which uses <router-link>
(Vue Router).
import {mount, RouterLinkStub} from "@vue/test-utils";
import {postFactory} from "@/components/blog/model/Post";
import YourComponent from "@/components/blog/components/YourComponent.vue";
it('emits "deletePost"', emitsDeletePost);
function emitsDeletePost(): void {
const post = {slug: 'example-slug'};
const $router = {
push: jest.fn()
}
const $route = {
params: {
id: 1
}
}
const wrapper = mount(YourComponent, {
props: {post},
global: {
components: {
RouterLink: RouterLinkStub
},
mocks: {$route, $router}
}
})
const button = wrapper.get('button');
button.trigger('click');
expect(wrapper.emitted('deletePost')).toEqual([[post.slug]])
}
Using Typescript in Vue 3
Vue 3 fully supports Typescript. The fact that it works so well now with Typescript is one of my favourite new things about Vue 3. It was always a bit hard to get everything typed correctly in Vue 2.
It is still possible to use plain Javascript with Vue 3, if you are not keen on Typescript.
This section of my Vue 3 guide explains how to install and set up Typescript with a Vue 3 installation.
For the configuration to set up Typescript in Vue 3 I would recommend looking at this tsconfig.json.
There are some packages which will need to be installed.
If you originally set up Vue via the Vue CLI and are adding Typescript manually then run yarn add -D @vue/cli-plugin-typescript
.
For custom setups you will need yarn add -D typescript vue-loader
(check out package.json in that repo).
You will also need to update or create your webpack.config.js
config file. The important lines are marked below with <<
.
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader'); // <<
module.exports = {
entry: './src/main.ts',
plugins: [
new VueLoaderPlugin() // <<
],
output: {
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader' // <<
}
]
}
}
Once set up, all you have to do is change <script>
to <script lang="ts">
in your .vue
files.
You can also find more information on setting up Typescript with Webpack here.
Comments →Guide to setting up Vue 3 (with example snippets and configuration)