Creating a Vue 3 Project
Creating with Vue CLI
Vue CLI is currently in maintenance mode, and the official Vue documentation recommends creating new projects with Vite instead.
Creating with Vite
Vite is a next-generation frontend build tool that offers key benefits:
- Hot module replacement for fast local development
- Out-of-the-box support for TypeScript, JSX, and CSS
- On-demand compilation, no need to wait for the entire application to build before starting development
To create a new project, run the following command in your terminal:
npm init vite@latest
Follow the interactive prompts to complete setup. You can also directly specify your project name and preferred template via command line flags. For a basic Vite + Vue project, run:
# npm 6.x
npm init vite@latest my-vue-app --template vue
# npm 7+ requires an extra double dash
npm init vite@latest my-vue-app -- --template vue
Project Structure for Vite + Vue + TypeScript
- .vscode
- extensions.json: VS Code extension recommendation for the project
- public: Root directory for static assets that don't need processing
- src: Project source code
- main.ts: Entry file that imports global styles, mounts the root component to the DOM
- App.vue: Root component of the application
- components: Directory for reusable Vue components
- assets: Directory for processed static assets (images, styles, etc.)
- vite-env.d.ts: TypeScript declaration file for Vite-specific environment types
- .gitignore: Git ignore rule configuration
- index.html: Application entry HTML file
- package-lock.json: Lock file for dependency versions
- package.json: Project metadata and dependency manifest
- README.md: Project introduction and documentation
- tsconfig.json: TypeScript compiler configuration
- vite.config.ts: Vite build tool configuration
Core Syntax: Composition API
Vue 2's Options API splits related logic across data, methods, computed, and other options, making it hard to maintain and reuse code when working on large features. Vue 3's Composition API lets you organize related code together logically using functions, improving maintainability for large projects.
The setup Option
setup is a new configuration option introduced in Vue 3, which must be a function. this is undefined inside the setup function, and setup runs before the beforeCreate lifecycle hook from Options API.
Basic usage example:
<script>
export default {
name: 'UserProfile',
setup() {
// Define data and functions here
let username = 'alex'; // Non-reactive data
let userAge = 25;
const updateName = () => {
username = 'alex2'; // Changes will not trigger UI updates because data is not reactive
}
const incrementAge = () => {
userAge += 1;
}
return {
username,
userAge,
updateName,
incrementAge
}
}
}
</script>
Vue 3 provides a <script setup> syntax sugar that automatically exposes all top-level variables declared in the script to the template:
<script lang="ts" setup>
// Define data and functions here
let username = 'alex'; // Non-reactive data
let userAge = 25;
const updateName = () => {
username = 'alex2'; // Changes will not trigger UI updates
}
const incrementAge = () => {
userAge += 1;
}
</script>
If you need to set a custom component name different from the file name when using <script setup>, follow these steps:
- Install the required Vite plugin
npm i vite-plugin-vue-setup-extend -D
- Add the plugin to your Vite configuration
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [VueSetupExtend()],
})
Reactive Data
ref: Create Reactive Primitive Values
ref is used to create reactive variables, the syntax is const variable = ref(initialValue). It returns a RefImpl instance (commonly called a ref object), and the value property of the ref object holds the reactive data.
Notes: You need to access the reactive value via variable.value in JavaScript/TypeScript, but you don't need .value directly in Vue templates. ref must be imported from vue first.
Example:
<script setup lang="ts">
import { ref } from 'vue';
const username = ref('alex');
const userAge = ref(25);
const updateName = () => {
username.value = 'alex2';
}
const incrementAge = () => {
userAge.value += 1;
}
</script>
reactive: Create Reactive Reference Types
Use reactive() to wrap object/array/other reference types to get a reactive object. You can access and modify properties directly via object.property. It returns a Proxy instance wrapping the original object.
Notes: reactive provides deep reactivity out of the box, meaning nested properties are also reactive. The main limitation is that reassigning the entire variable to a new object will break reactivity. To work around this, update the existing reactive object with Object.assign(oldReactive, newObject) instead of replacing the variable.
Example:
const product = reactive({
brand: 'Tesla',
price: 39999
})
const increasePrice = () => {
product.price += 1000;
}
ref for Reference Types
When you wrap a reference type with ref, it still returns a RefImpl instance, so you need to access properties via ref.value.property. Under the hood, ref automatically wraps the input reference type with reactive, so RefImpl.value equals the reactive Proxy instance.
Best Practices for Choosing Between ref and reactive
- Use
reffor reactive primitive values - Use either
reforreactivefor shallow reactive objects - Use
reactivefor deeply nested reactive objects
Auto-Insert .value for ref in VS Code
To enable automatic .value insertion for ref variables, go to VS Code Settings > Extensions > Vue > check the Auto Insert: Dot Value option.
toRef and toRefs
toRefs converts all properties of a reactive object into individual ref variables. This lets you destructure a reactive object while preserving reactivity, and each destructured ref maintains a reference to the original property in the reactive object.
Syntax example:
// Destructure while preserving reactivity
const { name, age, gender, location } = toRefs(userData);
Computed Properties
Computed properties derive new values from existing reactive data. Key differences from regular methods:
- Computed properties cache their results based on their dependencies, and only recalculate when dependencies change
- Computed properties are reactive, while methods only execute when called
- Computed are ideal for derived values that benefit from caching, while methods are best suited for event handlers and one-off actions that don't need caching
Full example:
<template>
<div class="profile-card">
First name: <input type="text" v-model="firstName">
Last name: <input type="text" v-model="lastName">
Full name: <span>{{ fullName }}</span>
<button @click="setDefaultFullName">Set Default Name</button>
</div>
</template>
<script setup lang="ts" name="ProfileCard">
import { ref, computed } from "vue";
const firstName = ref("");
const lastName = ref("");
// Read-only computed
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
// Read-write computed with getter and setter
const editableFullName = computed({
get() {
return `${firstName.value} ${lastName.value}`;
},
set(newVal: string) {
const [first, last] = newVal.trim().split(" ");
firstName.value = first;
lastName.value = last;
}
});
const setDefaultFullName = () => {
editableFullName.value = "John Doe";
console.log(editableFullName.value);
}
</script>