Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Vue 3.0 Hands-On Experience

Tech 1

The Vue 3.0 beta has been out for a while, so it's time to get hands-on!

Note: All demonstrations in this article are based on Vue 3.0 beta. The API may change before the official release. Please refer to the official documentation when it's available.

Environment Setup

Use the Vue CLI directly. If it's not installed locally, run the installation command:

npm install -g @vue/cli

If you already have it installed, try updating:

npm update -g @vue/cli

Check the version:

vue -V
@vue/cli 4.4.1

Now create a Vue project:

vue create vue3-demo

In the interactive prompt, choose "Manually select features":

Vue CLI v4.4.1
? Please pick a preset:
  常用配置 (router, vuex, sass, babel, eslint)
  sass (router, vuex, sass, babel, eslint)
  test (less, babel, eslint)
  default (babel, eslint)
❯ Manually select features

Then select the following features (typically needed for commercial projects):

Vue CLI v4.4.1
? Please pick a preset: Manually select features
? Check the features needed for your project:
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
 ◉ Vuex
❯◉ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Press Enter and complete the setup according to your preferences. The project will be created with Vue 2. Next, upgrade it to Vue 3.

Upgrade to Vue 3.0 Project

The Vue CLI does not directly support Vue 3.0 yet; it requires a plugin. Run:

cd vue3-demo
vue add vue-next

This will install the vue-cli-plugin-vue-next plugin, which upgrades the project to Vue 3.0 dependencies, including vue-router and vuex to version 4.x.

Latest Vue CLI Can Create Vue 3 Projects Directly (Updated 2020.09)

After updating Vue CLI to version 4.5.4, it is possible to create Vue 3 projects directly. Use vue create vue3-demo to create a test project, and you will see the following options:

You can either choose the default Vue 3 configuration or select "Manually select features" for custom configuration. Here, we demonstrate manual selection:

Select the desired features. Note that the first option indicates the ability to choose a Vue version. After selection, press Enter:

Choose version 3.x and press Enter. This will create a project based on Vue 3.

Exploring Vue 3.0 New Features

After upgrading, the project will automatically convert some files to Vue 3.0 syntax.

Note: Vue 3 retains Vue 2's development patterns, so you can still use Vue 2 syntax. However, this guide focuses on the new features.

Creating a Vue Instance

In Vue 3, you no longer use new Vue() to create an instance. Check src/main.js:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(router).use(store).mount('#app')

This is a functional style. Compare with Vue 2:

// Vue 2
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

Which style do you prefer? I like the functional approach in Vue 3.0.

Routing

In Vue 2, we used vue-router. In Vue 3, vue-router is still used, but its version is also in beta (4.0.0-beta7 at the time of writing).

Since the Vue 3 compatible version is in beta, you cannot install it with yarn add vue-router directly; you need to specify the version:

yarn add vue-router@4.0.0-beta7

Check the route configuration file src/router/index.js:

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

This is the new routing configuration. You now manually import createRouter and createWebHistory. This enables tree-shaking—only the APIs you use are bundled.

Reactive Data, Methods, Watch, and Computed

This is the most significant and controversial change. Let's see how it works.

In Vue 2, we declared reactive data like this:

// Vue 2
export default {
  data() {
    return {
      state: {
        count: 0
      }
    }
  }
}

In Vue 3, we do it like this:

// Vue 3
import { ref } from 'vue'
export default {
  setup() {
    const count = ref(0)
    const str = ref('hello')
    return {
      count,
      str
    }
  }
}

We import ref and use it to declare reactive variables. These operations happen in side the setup function, which also houses methods, watch, and computed. This differs from the options-based approach in Vue 2.

Here's a counter example using methods, watch, and computed:

<template>
  <div class="home">
    <p>count: {{count}}</p>
    <p>doubleCount: {{doubleCount}}</p>
    <button @click="add">Increase</button>
  </div>
</template>

<script>
import { ref, watch, computed } from 'vue'
export default {
  setup() {
    const count = ref(0)

    const add = () => {
      count.value++
    }

    watch(
      () => count.value,
      (val, oldVal) => {
        console.log(`new count: ${val}, old count: ${oldVal}`)
      }
    )

    const doubleCount = computed(() => count.value * 2)

    return {
      count,
      add,
      doubleCount
    }
  }
}
</script>

Watch usage:

Method 1: By returning a function:

watch(() => x.value, (newVal, oldVal) => { ... })

Method 2: By directly passing a ref:

watch(x, (newVal, oldVal) => { ... })

In the example, methods are defined inside setup and returned. Notice that inside setup, you access reactive variables with .value (e.g., count.value). This includes within watch and computed.

Declaring Reactive Data with reactive

You can also use reactive to declare multiple variables at once:

<template>
  <div class="home">
    <p>str: {{state.str}}</p>
    <p>count: {{state.count}}</p>
    <button @click="add">Increase</button>
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      count: 0,
      str: 'hello'
    })

    const add = () => {
      state.count++  // no .value
    }

    return {
      state,
      add
    }
  }
}
</script>

reactive vs ref

reactive takes a plain object and returns a reactive proxy of that object. It is equivalent to Vue.observable() in Vue 2.

const obj = reactive({ count: 0 })
// obj is now reactive
// Access or modify with obj.count

ref takes an argument and returns a reactive and mutable ref object. It is typically used for primitive values like numbers or strings. If the argument is an object, it will be deeply reactive via reactive. The ref object has a single property .value that points to the inner value. When used in templates after being returned from setup, you do not need .value because it is automatically unwrapped.

<template>
  <div>{{ count }}</div>
</template>

<script>
import { ref } from 'vue'
export default {
  setup() {
    return {
      count: ref(0)  // automatically unwrapped in template
    }
  }
}
</script>

Accessing Route Information

In Vue 3.0, use getCurrentInstance to get the current component instance, then access ctx.$router for the router instance. ctx.$router.currentRoute contains the current route information.

<script>
import { getCurrentInstance } from 'vue'
export default {
  setup() {
    const { ctx } = getCurrentInstance()
    console.log(ctx.$router.currentRoute.value)
  }
}
</script>

Getting DOM Elements

Method 1: Using ref

<template>
  <div ref="myRef">Get DOM element</div>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const myRef = ref(null)

    onMounted(() => {
      console.dir(myRef.value)
    })

    return {
      myRef
    }
  }
}
</script>

Method 2: Using a callback ref

<template>
  <div :ref="setRef">Get DOM element</div>
</template>

<script>
import { nextTick } from 'vue'

export default {
  setup() {
    let myRef = ''

    const setRef = el => {
      myRef = el
    }

    nextTick(() => {
      console.dir(myRef)
    })

    return {
      setRef
    }
  }
}
</script>

Vuex

Like vue-router, the new Vuex is also in beta (4.0.0-beta.4 at the time of writing).

Installing Vuex

yarn add vuex@4.0.0-beta.4

Check the store file src/store/index.js:

import Vuex from 'vuex'

export default Vuex.createStore({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
})

The way to create a store insatnce has changed. In Vue 2, you used new Vuex.Store(...).

A small example of using store in Vue 3:

Create the store:

import Vuex from 'vuex'

export default Vuex.createStore({
  state: {
    count: 0
  },
  mutations: {
    ADD(state) {
      state.count++
    }
  },
  actions: {
    add({ commit }) {
      commit('ADD')
    }
  }
})

Using the store in a component:

<template>
  <div class="home">
    <p>{{count}}</p>
    <button @click="add">Increase</button>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore()
    const count = computed(() => store.state.count)

    const add = () => {
      store.dispatch('add')
    }

    return {
      count,
      add
    }
  }
}
</script>

The Vuex API hasn't changed much; you just need to import useStore to get the store instance. Alternatively, you can get the store from the component context:

import { getCurrentInstance } from 'vue'

const store = getCurrentInstance().ctx.$store
Tags: vue

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.