Lesson 02-Vue3 Basic Syntax

Templates

Interpolation Expressions

Interpolation expressions are the most common way to insert dynamic data, using double curly braces {{ }} to wrap expressions. For example:

<p>{{ message }}</p>

Here, message is a data property in the component instance.

Directives

Directives are special attributes prefixed with v- that perform specific operations in templates. Vue 3 provides several built-in directives:

  • v-bind: Binds attributes. For example, dynamically binding a class name to an element:
<div v-bind:class="activeClass"></div>
  • v-on: Listens to DOM events. For example, listening to a button click event:
<button v-on:click="handleClick">Click me</button>

Since Vue 2.4.0, event modifiers like .stop, .prevent, and .capture are supported. For example:

<button v-on:click.stop="handleClick">Click me</button>
  • v-model: Enables two-way data binding, commonly used with form elements like <input>, <textarea>, and <select>:
<input v-model="searchTerm" placeholder="Search...">
  • v-if/v-else/v-else-if: Conditional rendering, determining whether to render a template based on the truthiness of an expression:
<div v-if="seen">
  Now you see me!
</div>
  • v-for: List rendering, iterating over arrays or objects to generate template instances for each item:
<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

Conditional Rendering

Vue 3 supports conditional rendering with v-if and v-show:

  • v-if: Removes the element and its children from the DOM when the expression evaluates to false.
  • v-show: Always renders the element to the DOM, toggling visibility with CSS.

List Rendering

The v-for directive iterates over arrays or objects. Always include a unique key attribute for each item to optimize list rendering performance:

<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

Event Handling

Use v-on (or its shorthand @) to listen to DOM events and invoke component methods:

<button @click="handleClick">Click me</button>

Form Input Binding

The v-model directive creates two-way data binding between form controls and component data:

<input v-model="message" placeholder="type something">

Dynamic Components

Use <component :is="currentView"></component> to dynamically switch between rendered components.

Slots

Slots allow parent components to inject content into reserved areas of child components. Vue 3 introduces named slots and scoped slots with the <slot> tag:

<!-- Parent Component -->
<my-component>
  <template #header>
    This is the header
  </template>
</my-component>

<!-- Child Component -->
<template>
  <header>
    <slot name="header"></slot>
  </header>
</template>

Custom Directives

In addition to built-in directives, you can define custom directives to encapsulate reusable behavior:

// Register a custom directive in the Vue instance
Vue.directive('focus', {
  // When the bound element is inserted into the DOM...
  inserted: function (el) {
    // Focus the element
    el.focus();
  }
});

Teleport

Teleport is a new global component in Vue 3 that allows rendering a subtree to a different DOM location:

<teleport to="#app">
  <div>Teleported content</div>
</teleport>

Suspense

The Suspense component handles loading states for asynchronous components:

<suspense>
  <template #default>
    <!-- Async Component -->
    <AsyncComponent />
  </template>
  <template #fallback>
    <!-- Loading Indicator -->
    Loading...
  </template>
</suspense>

Fragment

Vue 3 introduces Fragments, allowing templates to have multiple root nodes using <template> with <slot>:

<template>
  <div>Content</div>
  <div>More content</div>
</template>

Attribute Binding

Use v-bind (or its shorthand :) to bind attributes:

<img :src="imageSrc" alt="A picture">

Class and Style Binding

Dynamically bind class names and inline styles with v-bind:class and v-bind:style:

<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

Props

Declaring Props

In Vue 3, all incoming props must be declared in the component’s props option, aiding type checking, documentation, and code readability. For example:

export default {
  props: {
    message: String,
    items: Array,
    status: {
      type: Boolean,
      default: false
    }
  }
}

Here, message and items are declared as String and Array types, while status is a Boolean with a default value.

Using Props

Once declared, props can be used in the component’s template:

<template>
  <div>
    <p>{{ message }}</p>
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
    <p>Status: {{ status ? 'Active' : 'Inactive' }}</p>
  </div>
</template>

Validating Props

Vue 3 allows detailed prop validation, including specifying types, setting required flags, defining custom validators, or using TypeScript annotations:

export default {
  props: {
    age: {
      type: Number,
      required: true,
      validator: function (value) {
        return value > 0 && value < 150;
      }
    }
  }
}

Default Prop Values

Props can have default values to ensure the component functions correctly if a prop is not provided:

export default {
  props: {
    title: {
      type: String,
      default: 'Default Title'
    }
  }
}

Watching Prop Changes

Use the watch option or reactive references in the setup function to monitor prop changes:

export default {
  props: ['counter'],
  watch: {
    counter(newVal, oldVal) {
      console.log(`Counter changed from ${oldVal} to ${newVal}`);
    }
  }
}

Or in the setup function:

const props = defineProps(['counter']);
const { counter } = toRefs(props);
watch(counter, (newVal, oldVal) => {
  console.log(`Counter changed from ${oldVal} to ${newVal}`);
});

Avoiding Prop Mutations

Props are read-only, and attempting to modify them triggers a warning. Use computed properties or methods for operations based on props:

export default {
  props: ['basePrice'],
  computed: {
    finalPrice() {
      return this.basePrice * 1.2; // 20% tax
    }
  }
}

Dynamic Props

Dynamically bind prop names using expressions or variables:

<!-- Parent Component -->
<child-component :[propName]="value"></child-component>

<!-- Child Component -->
export default {
  props: {
    [propName]: String
  }
}

Passing Complex Data

Props can be any JavaScript data type, including objects and arrays. Since objects and arrays are passed by reference, changes in the child component affect the parent’s data:

<!-- Parent Component -->
const parentData = { name: 'John Doe' };
<child-component :data="parentData"></child-component>

<!-- Child Component -->
export default {
  props: ['data'],
  methods: {
    updateData() {
      this.data.name = 'Jane Doe'; // This affects `parentData` in the parent
    }
  }
}

To avoid this, use a deep copy with the spread operator or JSON methods:

<child-component :data="JSON.parse(JSON.stringify(parentData))"></child-component>

Using TypeScript for Props

Vue 3 integrates seamlessly with TypeScript, allowing type annotations for props in .vue files:

<script lang="ts">
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  props: {
    user: {
      type: Object as PropType<{ name: string; age: number }>,
      required: true
    }
  }
});
</script>

Attribute Inheritance

Vue 3 allows components to inherit attributes from the parent, useful for reusable component libraries:

<!-- Parent Component -->
<my-button :size="large">Click me</my-button>

<!-- MyButton Component -->
export default {
  inheritAttrs: false, // Prevent overriding component’s own attributes
  props: ['size'],
  setup(props, { attrs }) {
    const size = props.size || attrs.size;
    // ...
  }
}

Events

Vue 3’s event system is a key mechanism for component communication, enabling components to trigger events that other components can listen to. Events facilitate decoupling and interaction, critical for building complex, dynamic user interfaces.

Event Binding

Use the v-on directive (or its shorthand @) to bind DOM event handlers:

<!-- Bind a click event to a button -->
<button @click="handleClick">Click me</button>

Here, handleClick is a method in the component instance.

Event Modifiers

Vue 3 supports event modifiers to alter event behavior, added to the v-on directive. Common modifiers include:

  • .stop: Prevents event bubbling.
  • .prevent: Prevents the default event behavior.
  • .capture: Uses event capture mode.
  • .self: Triggers the handler only if the event originates from the element itself.
  • .once: Triggers the event only once.

Example:

<!-- Prevent event bubbling -->
<div @click="doThis" @click.stop="doThat">...</div>

<!-- Prevent default form submission -->
<form @submit.prevent="onSubmit">...</form>

Custom Events

Components can emit custom events that parent or other components can listen to. Use the $emit method to trigger events:

export default {
  methods: {
    emitCustomEvent() {
      this.$emit('custom-event', 'Hello World');
    }
  }
}

In the parent component, listen to these events with v-on or @:

<child-component @custom-event="handleCustomEvent"></child-component>

Event Arguments

Custom events can pass any number of arguments, which the listener’s callback function can receive:

<!-- Child Component -->
this.$emit('custom-event', payload);

<!-- Parent Component -->
<child-component @custom-event="handleCustomEvent"></child-component>

methods: {
  handleCustomEvent(payload) {
    console.log(payload);
  }
}

Event Listener Priority

When multiple listeners are bound to the same event, they execute in the order they appear in the template. The first listener in the template is called first.

Event Capture

The .capture modifier changes the event propagation to use capture mode:

<div @click.capture="handleClick">...</div>

Event Delegation

For handling events on many elements, event delegation improves performance by binding a single handler to a parent element and determining the triggering child element:

<ul @click="handleClick">
  <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>
methods: {
  handleClick(event) {
    const target = event.target;
    if (target.tagName.toLowerCase() === 'li') {
      // Handle click on li elements
    }
  }
}

TypeScript Event Types

When using TypeScript with Vue 3, define event types for better type checking and code completion:

<!-- Child Component -->
export default {
  emits: ['custom-event']
}

<!-- Parent Component -->
<child-component @custom-event="handleCustomEvent"></child-component>

methods: {
  handleCustomEvent(event: CustomEvent) {
    // TypeScript knows the type of `event`
  }
}

Dynamic Event Binding

Dynamically bind event handlers using dynamic attributes:

<button :@click="handlerName">Click me</button>

Here, handlerName is a string determining the event handler’s name.

Event Namespaces

Vue 3 doesn’t natively support event namespaces, but you can simulate them using colons or underscores in event names:

<child-component @child:custom-event="handleChildEvent"></child-component>

Rendering

Vue 3’s rendering mechanism, built on virtual DOM and an efficient diff algorithm, is the foundation of its performance and reactivity.

Virtual DOM

Vue 3 uses a virtual DOM, a lightweight in-memory representation of the DOM tree, to minimize actual DOM operations. The virtual DOM is a JavaScript object, enabling fast comparisons and updates in memory.

Render Functions and Template Compilation

Vue 3 supports both template syntax and render functions to describe UI. Templates are closer to HTML and easier to read, while render functions offer greater control. Templates are compiled into render functions, which generate the virtual DOM.

Reactive System

Vue 3’s reactive system, based on Proxy objects, tracks data changes and automatically updates the view. Modifying reactive data triggers Vue to re-render affected components.

Component Lifecycle

Vue 3 components have lifecycle hooks called at different stages: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, and unmounted. These hooks enable initialization, pre/post-rendering operations, and cleanup.

Asynchronous Components

Vue 3 supports asynchronous components for on-demand loading, improving initial load times. Use defineAsyncComponent to create them:

import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() => import('./MyComponent.vue'));

Suspense Component

The Suspense component manages loading states for asynchronous components, providing an elegant way to handle loading and error states:

<Suspense>
  <template #default>
    <AsyncComponent />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

Fragment

Vue 3’s Fragments allow templates to have multiple root nodes, eliminating the need for extra wrapper elements:

<template>
  <div>{{ item.text }}</div>
  <div>More content</div>
</template>

Teleport Component

The Teleport component renders content to a different DOM location, useful for modals or popovers:

<teleport to="#modal-root">
  <div class="modal">Modal content</div>
</teleport>

Conditional Rendering

Vue 3 provides v-if, v-else, and v-else-if for conditional rendering. v-if removes DOM nodes when false, while v-show toggles visibility with CSS.

List Rendering

The v-for directive renders lists, with a unique key recommended for performance and correctness:

<ul>
  <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</ul>

Rendering Optimizations

Vue 3’s rendering optimizations include:

  • Keys: Unique keys for list items improve update efficiency.
  • Fragments and Teleport: Reduce unnecessary DOM nodes for better performance.
  • Suspense: Manages async component loading to prevent flickering.
  • Event Listeners: Use event modifiers and delegation to minimize handlers.

Virtual DOM Update Algorithm

Vue 3’s efficient diff algorithm minimizes DOM updates by comparing virtual DOM differences and updating only changed parts.

Rendering Optimization Techniques

  • Use v-once: For static content, v-once prevents unnecessary re-renders.
  • Use v-memo: Cache rendering results for unchanged dependencies.
  • Avoid Unnecessary Computations: Use computed properties instead of repeated logic.

Form Handling and Validation

Form Binding

The v-model directive enables two-way data binding between form controls and component data, the most common method for handling form inputs:

<template>
  <form>
    <input v-model="username" placeholder="Username">
    <input v-model="password" type="password" placeholder="Password">
    <button type="submit" @click.prevent="submitForm">Submit</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: ''
    };
  },
  methods: {
    submitForm() {
      // Form submission logic
    }
  }
};
</script>

Form Validation

a. Using v-model and Computed Properties

Validate form fields with computed properties:

<template>
  <form>
    <input v-model="username" :class="{ error: !isValidUsername }">
    <p v-if="!isValidUsername">Invalid username</p>
  </form>
</template>

<script>
export default {
  data() {
    return {
      username: ''
    };
  },
  computed: {
    isValidUsername() {
      return this.username.length >= 3;
    }
  }
};
</script>
b. Using Third-Party Libraries

The Vue community offers robust form validation libraries like VeeValidate. Example with VeeValidate:

npm install vee-validate --save
import { defineComponent } from 'vue';
import { useField } from 'vee-validate';

export default defineComponent({
  setup() {
    const { value: username, errorMessage, handleChange } = useField('username');

    return {
      username,
      errorMessage,
      handleChange
    };
  }
});

<template>
  <form>
    <input v-model="username" @input="handleChange">
    <div v-if="errorMessage">{{ errorMessage }}</div>
  </form>
</template>

Custom Validation Rules

Define custom validation rules, e.g., with VeeValidate:

import { extend, localize } from 'vee-validate';
import { required, email } from 'vee-validate/dist/rules';

extend('required', {
  ...required,
  message: '{_field_} is required'
});

extend('email', {
  ...email,
  message: '{_field_} must be a valid email address'
});

localize('en', {
  messages: {
    required: '{_field_} is required',
    email: '{_field_} must be a valid email address'
  }
});

Form State Management

Use the Composition API to manage form state, including errors and validation:

import { ref, reactive } from 'vue';

export default {
  setup() {
    const formState = reactive({
      username: '',
      password: '',
      errors: {}
    });

    const validateForm = () => {
      let valid = true;

      if (!formState.username) {
        formState.errors.username = 'Username is required.';
        valid = false;
      }

      if (!formState.password) {
        formState.errors.password = 'Password is required.';
        valid = false;
      }

      return valid;
    };

    const submitForm = () => {
      if (validateForm()) {
        // Submit form
      }
    };

    return {
      formState,
      validateForm,
      submitForm
    };
  }
};

Using Element Plus for Form Validation

Element Plus, a popular Vue 3 UI library, provides comprehensive form components and validation:

<template>
  <el-form :model="form" :rules="rules" ref="formRef">
    <el-form-item prop="username">
      <el-input v-model="form.username"></el-input>
    </el-form-item>
    <el-form-item prop="password">
      <el-input v-model="form.password" show-password></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submitForm">Submit</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';

export default {
  setup() {
    const formRef = ref(null);
    const form = ref({
      username: '',
      password: ''
    });
    const rules = {
      username: [
        { required: true, message: 'Please enter username', trigger: 'blur' }
      ],
      password: [
        { required: true, message: 'Please enter password', trigger: 'blur' }
      ]
    };

    const submitForm = () => {
      formRef.value.validate((valid) => {
        if (valid) {
          ElMessage.success('Submission successful!');
        } else {
          ElMessage.error('Validation failed!');
        }
      });
    };

    return {
      formRef,
      form,
      rules,
      submitForm
    };
  }
};
</script>

Computed Properties and Watchers

Computed Properties

Computed properties derive new data based on other data and are reactive, automatically updating when dependencies change.

Options API Example

export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  }
};

Composition API Example

import { computed } from 'vue';

export default {
  setup() {
    const firstName = ref('John');
    const lastName = ref('Doe');

    const fullName = computed(() => `${firstName.value} ${lastName.value}`);

    return { firstName, lastName, fullName };
  }
};

Watchers

Watchers observe data changes and execute side effects. Vue 3 offers watch and watchEffect functions.

Options API Example

export default {
  data() {
    return {
      count: 0
    };
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`Count changed from ${oldVal} to ${newVal}`);
    }
  }
};

Composition API Example

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);

    watch(count, (newVal, oldVal) => {
      console.log(`Count changed from ${oldVal} to ${newVal}`);
    });

    return { count };
  }
};

Computed Properties vs. Watchers

Computed Properties:

  • Define properties dependent on other data.
  • Cache results, recalculating only when dependencies change.
  • Ideal for data transformation and display.

Watchers:

  • Monitor data changes and execute side effects (e.g., network requests, DOM operations).
  • Do not cache results, executing side effects on each trigger.
  • Suited for reactive operations on data changes.

Complex Computed Properties and Watchers

Handle complex logic with multiple dependencies.

Computed Property Example

import { computed } from 'vue';

export default {
  setup() {
    const price = ref(100);
    const quantity = ref(1);
    const discount = ref(0.1); // 10% discount

    const totalPrice = computed(() => {
      return price.value * quantity.value * (1 - discount.value);
    });

    return { price, quantity, discount, totalPrice };
  }
};

Watcher Example

import { ref, watch } from 'vue';

export default {
  setup() {
    const price = ref(100);
    const quantity = ref(1);
    const discount = ref(0.1); // 10% discount

    watch([price, quantity, discount], ([newPrice, newQuantity, newDiscount]) => {
      console.log(`Price: ${newPrice}, Quantity: ${newQuantity}, Discount: ${newDiscount}`);
    });

    return { price, quantity, discount };
  }
};

Immediate Watchers

To execute a watcher immediately upon component initialization, use the immediate option:

import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);

    watch(count, (newVal) => {
      console.log(`Count is now ${newVal}`);
    }, { immediate: true });

    return { count };
  }
};

Deep Watchers

By default, watchers don’t deeply monitor object or array changes. Enable deep watching with the deep option:

import { ref, watch } from 'vue';

export default {
  setup() {
    const user = ref({ name: 'John', age: 30 });

    watch(user, (newUser) => {
      console.log(`User's name is now ${newUser.name}`);
    }, { deep: true });

    return { user };
  }
};

Performance Optimization

Optimize performance when using computed properties and watchers:

  • Minimize Dependencies: Ensure computed properties rely on minimal dependencies.
  • Use immediate and deep Judiciously: These options can increase computation overhead.
  • Use watchEffect: For side effects without needing old values, watchEffect is more efficient.

Data Binding

Text Interpolation

Text interpolation is the simplest way to bind data, using double curly braces {{ }} to insert data into DOM text content:

<template>
  <div>
    {{ message }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!'
    };
  }
};
</script>

Attribute Binding

Use v-bind (shorthand :) to bind data to element attributes:

<template>
  <a :href="url">Visit Us</a>
</template>

<script>
export default {
  data() {
    return {
      url: 'https://www.example.com'
    };
  }
};
</script>

Dynamic CSS Class and Style Binding

Dynamically bind CSS classes and inline styles with v-bind:class and v-bind:style:

<template>
  <div :class="{ active: isActive }" :style="{ color: textColor }">
    Dynamic styles
  </div>
</template>

<script>
export default {
  data() {
    return {
      isActive: true,
      textColor: 'red'
    };
  }
};
</script>

Event Binding

Use v-on (shorthand @) to bind event listeners:

<template>
  <button @click="handleClick">Click Me</button>
</template>

<script>
export default {
  methods: {
    handleClick() {
      console.log('Button clicked');
    }
  }
};
</script>

Expression Binding

Expressions are allowed in bindings but should remain simple to avoid complex logic:

<template>
  <span>{{ message.split('').reverse().join('') }}</span>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!'
    };
  }
};
</script>

Dynamic Component Binding

Use the is attribute or <component> tag to dynamically switch components:

<template>
  <component :is="currentView"></component>
</template>

<script>
import ViewA from './ViewA.vue';
import ViewB from './ViewB.vue';

export default {
  components: {
    ViewA,
    ViewB
  },
  data() {
    return {
      currentView: 'ViewA'
    };
  }
};
</script>

Template References

Use the ref attribute to reference DOM elements or component instances:

<template>
  <div ref="myDiv">Reference me</div>
  <button @click="focusDiv">Focus Div</button>
</template>

<script>
export default {
  methods: {
    focusDiv() {
      this.$refs.myDiv.focus();
    }
  }
};
</script>

Slots

Slots allow parent components to pass content to child components:

<!-- Parent.vue -->
<template>
  <Child>
    <template v-slot:header>Header Content</template>
    <template v-slot:footer>Footer Content</template>
  </Child>
</template>

<!-- Child.vue -->
<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

Conditional and Loop Rendering

Use v-if and v-for for conditional and list rendering:

<template>
  <div v-if="show">
    Show this when show is true
  </div>
  <ul>
    <li v-for="item in items" :key="item.id">{{ item.text }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      show: true,
      items: [
        { id: 1, text: 'Item 1' },
        { id: 2, text: 'Item 2' }
      ]
    };
  }
};
</script>

Component Communication

Use props and emits for parent-child component communication:

<!-- Parent.vue -->
<template>
  <Child :message="parentMessage" @child-event="handleChildEvent"></Child>
</template>

<script>
import Child from './Child.vue';

export default {
  components: {
    Child
  },
  data() {
    return {
      parentMessage: 'Hello from parent'
    };
  },
  methods: {
    handleChildEvent(childData) {
      console.log('Received data from child:', childData);
    }
  }
};
</script>

<!-- Child.vue -->
<template>
  <div>{{ message }}</div>
  <button @click="$emit('child-event', 'Hello from child')">Emit Event</button>
</template>

<script>
export default {
  props: {
    message: String
  },
  emits: ['child-event']
};
</script>

Reactive System

Reactive System Overview

Vue 3’s reactive system is based on Proxy objects, a modern JavaScript feature that intercepts and controls object access. Vue 3 uses Proxies to track data reads and writes, automatically updating the view when data changes.

Key APIs

Vue 3 provides several APIs for creating and manipulating reactive data:

  • ref(): Creates a reactive wrapper for primitive or object data, returning an object with a .value property.
  • reactive(): Creates a reactive object, proxying its properties directly.
  • computed(): Creates a computed property based on other reactive data, with caching and lazy evaluation.
  • watch(): Watches reactive data changes and executes side-effect functions.

ref and reactive

ref and reactive are the primary ways to create reactive data, differing as follows:

  • ref: Suitable for primitives and objects, requiring .value to access or modify data.
  • reactive: Only for objects, returning the object itself without needing .value.
import { ref, reactive } from 'vue';

const count = ref(0); // Creates a reactive number wrapper
const state = reactive({ name: 'Alice' }); // Creates a reactive object

// Modify data
count.value++; // Modify ref data via .value
state.name = 'Bob'; // Modify reactive object properties directly

computed

computed creates reactive properties dependent on other data, automatically recomputing when dependencies change:

import { ref, computed } from 'vue';

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

console.log(doubleCount.value); // Outputs 0
count.value++;
console.log(doubleCount.value); // Outputs 2

watch

watch monitors reactive data changes and executes side-effect functions, offering more flexibility than computed properties:

import { ref, watch } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});

Reactive System Internals

Vue 3’s reactive system uses:

  • Dependency Tracking: When reading reactive data, Vue adds the current rendering function (effect) to the data’s dependency list.
  • Dependency Triggering: When reactive data is modified, Vue iterates the dependency list, re-executing related rendering functions to update the view.

Custom Reactive Logic

For custom reactive logic, create Proxy objects and use Vue’s effect API:

import { effect } from 'vue';

let count = 0;
const countProxy = new Proxy({ count }, {
  get(target, key) {
    effect(() => console.log('Count read'));
    return target[key];
  },
  set(target, key, value) {
    target[key] = value;
    effect(() => console.log('Count updated'));
    return true;
  }
});

countProxy.count++; // Outputs "Count updated"
console.log(countProxy.count); // Outputs "Count read", then 1

Performance Optimization

Vue 3’s reactive system is optimized for performance:

  • Computed Property Caching: Results are cached, recomputing only when dependencies change.
  • Side-Effect Scheduling: Vue uses an async queue to schedule side-effect execution, avoiding immediate redraws and improving performance.

Code Analysis

  • Templates: Directives like v-bind, v-on, and v-for simplify dynamic rendering.
  • Props: Structured prop declarations enhance type safety and maintainability.
  • Events: Custom events and modifiers enable flexible component communication.
  • Rendering: Virtual DOM and optimizations ensure efficient updates.
  • Forms: v-model and validation libraries streamline form handling.
  • Computed/Watchers: Provide reactive data transformation and side-effect management.
  • Data Binding: Comprehensive binding options support dynamic UIs.
  • Reactive System: Proxy-based reactivity drives Vue 3’s performance and flexibility.

Share your love