# Vue.js 2 User Guide

## 1. Quick Start

Vue.js is a progressive JavaScript framework for building user interfaces. It provides a reactive data-binding system with a component-oriented architecture that makes it straightforward to build everything from simple widgets to complex single-page applications.

**Key features:**

- Reactive data binding with automatic DOM updates
- Component-based architecture with props, events, and slots
- Template syntax with directives (`v-if`, `v-for`, `v-model`, `v-on`, `v-bind`)
- Computed properties and watchers for derived state
- Transition and animation system for enter/leave effects
- Vue 3 Composition API backported (`ref`, `reactive`, `computed`, `watch`, `setup()`)
- Single File Components (`.vue` files) with scoped CSS
- Server-side rendering via `vue-server-renderer`

**Prerequisites:** Node.js and a package manager (npm, yarn, or pnpm).

**Install and verify:**

```bash
npm install vue@2.7.16
```

**Minimal example:**

```html
<div id="app">{{ message }}</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>
```

```text
Hello Vue!
```

> [!TIP]
> For production, use `vue.min.js` or the runtime-only build (`vue.runtime.common.js`) for smaller bundle size. The full build includes the template compiler; the runtime build requires pre-compiled templates via a build tool.

**What to try next:** explore the Template Syntax section for directives, then build your first component.

## 2. Installation

### System Requirements

| Requirement | Minimum |
|---|---|
| Node.js | 12+ (for build tooling) |
| Browser | IE9+ (with polyfills), all modern browsers |
| Package Manager | npm, yarn, or pnpm |

### Installation Methods

=== "npm"
    ```bash
    npm install vue@2.7.16
    ```

=== "yarn"
    ```bash
    yarn add vue@2.7.16
    ```

=== "pnpm"
    ```bash
    pnpm add vue@2.7.16
    ```

=== "CDN"
    ```html
    <!-- Development (includes warnings) -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

    <!-- Production (minified) -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.min.js"></script>
    ```

### Build Variants

| Build | File | Includes Compiler | Use Case |
|---|---|---|---|
| Full (UMD) | `dist/vue.js` | Yes | CDN, direct `<script>` |
| Runtime (UMD) | `dist/vue.runtime.js` | No | With build tools |
| Full (CommonJS) | `dist/vue.common.js` | Yes | Bundlers (older) |
| Runtime (CommonJS) | `dist/vue.runtime.common.js` | No | Bundlers (default `main`) |
| Full (ESM) | `dist/vue.esm.js` | Yes | Bundlers (ESM) |
| Runtime (ESM) | `dist/vue.runtime.esm.js` | No | Bundlers (default `module`) |
| Runtime (MJS) | `dist/vue.runtime.mjs` | No | Node.js ESM import |

> [!IMPORTANT]
> The runtime-only build is approximately 30% lighter than the full build. If you use a build tool (webpack, Vite, Rollup), use the runtime build and let `vue-loader` or `@vitejs/plugin-vue2` pre-compile your templates. The full build is needed only when compiling templates in the browser at runtime (e.g., passing a string to the `template` option).

### Post-Install Verification

```javascript
const Vue = require('vue')
console.log(Vue.version)
```

```text
2.7.16
```

### Additional Packages

| Package | Install | Purpose |
|---|---|---|
| `@vue/compiler-sfc` | Included with vue | SFC compilation (`.vue` files) |
| `vue-server-renderer` | `npm install vue-server-renderer@2.7.16` | Server-side rendering |
| `vue-template-compiler` | `npm install vue-template-compiler@2.7.16` | Standalone template compilation |

> [!WARNING]
> The version of `vue-server-renderer` and `vue-template-compiler` **must** match the version of `vue` exactly. A version mismatch causes a startup error: `Vue packages version mismatch`.

## 3. Configuration

Vue exposes a global configuration object at `Vue.config` that controls framework-wide behavior. Set individual properties — do not replace the entire object.

### Configuration Reference

| Option | Type | Default | Description |
|---|---|---|---|
| `silent` | `boolean` | `false` | Suppress all Vue warnings |
| `productionTip` | `boolean` | `true` (dev) | Show production mode tip on boot |
| `devtools` | `boolean` | `true` (dev) | Enable Vue DevTools inspection |
| `performance` | `boolean` | `false` | Enable component init/compile/render performance tracing in DevTools |
| `errorHandler` | `Function` | `null` | Global error handler for uncaught errors in components |
| `warnHandler` | `Function` | `null` | Custom handler for Vue warnings |
| `ignoredElements` | `Array<string\|RegExp>` | `[]` | Ignore custom elements outside Vue (e.g., Web Components) |
| `keyCodes` | `Object` | `{}` | Custom key alias(es) for `v-on` keyboard events |
| `optionMergeStrategies` | `Object` | `{}` | Custom merge strategies for component options |
| `async` | `boolean` | `true` | Async update batching (set `false` only for testing) |

```javascript
// Suppress all warnings
Vue.config.silent = true

// Global error handler
Vue.config.errorHandler = function (err, vm, info) {
  console.error(`Error in ${info}:`, err)
}

// Custom warning handler
Vue.config.warnHandler = function (msg, vm, trace) {
  console.warn(`[Custom warn]: ${msg}${trace}`)
}

// Ignore Web Components
Vue.config.ignoredElements = ['my-web-component', /^ion-/]

// Custom key aliases for v-on
Vue.config.keyCodes = {
  f1: 112,
  mediaPlayPause: 179
}
```

> [!NOTE]
> `productionTip` and `devtools` are automatically disabled in production builds (when `process.env.NODE_ENV === 'production'`). Setting `Vue.config.performance = true` requires a browser that supports the `performance.mark` API.

See the Admin Guide for the complete internal configuration reference and option merge strategy details.

## 4. Core Usage

### Creating a Vue Instance

Every Vue application starts by creating a root Vue instance:

```javascript
const vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello',
    count: 0
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
})
```

> [!TIP]
> When `el` is provided, Vue mounts automatically. Otherwise, call `vm.$mount('#app')` manually to control mount timing.

### Template Syntax

Vue uses an HTML-based template syntax with special directives:

#### Text Interpolation

```html
<span>{{ message }}</span>
<span v-once>This will never change: {{ message }}</span>
<span v-html="rawHtml"></span>
<span v-text="message"></span>
```

> [!WARNING]
> `v-html` renders raw HTML and can expose your application to XSS attacks. Only use it with trusted content — never on user-supplied input.

#### Directives Reference

| Directive | Purpose | Example |
|---|---|---|
| `v-bind` (`:`) | Bind attribute to expression | `:href="url"` |
| `v-on` (`@`) | Attach event listener | `@click="handler"` |
| `v-model` | Two-way data binding | `v-model="searchText"` |
| `v-if` | Conditional rendering (removes from DOM) | `v-if="isVisible"` |
| `v-else-if` | Else-if chain | `v-else-if="type === 'B'"` |
| `v-else` | Else block | `v-else` |
| `v-show` | Toggle visibility (CSS `display`) | `v-show="isActive"` |
| `v-for` | List rendering | `v-for="item in items"` |
| `v-slot` (`#`) | Named/scoped slot | `v-slot:header` |
| `v-pre` | Skip compilation for this element | `v-pre` |
| `v-cloak` | Hide until compiled | `v-cloak` |
| `v-once` | Render once, skip future updates | `v-once` |

#### Conditional Rendering

```html
<div v-if="type === 'A'">Type A</div>
<div v-else-if="type === 'B'">Type B</div>
<div v-else>Other</div>

<!-- v-show toggles CSS display (element stays in DOM) -->
<div v-show="isVisible">Toggled by CSS</div>
```

#### List Rendering

```html
<ul>
  <li v-for="(item, index) in items" :key="item.id">
    {{ index }}: {{ item.name }}
  </li>
</ul>

<!-- Object iteration -->
<div v-for="(value, key, index) in object" :key="key">
  {{ key }}: {{ value }}
</div>
```

> [!IMPORTANT]
> Always provide a unique `:key` attribute when using `v-for`. Without it, Vue uses an "in-place patch" strategy that can cause subtle bugs with stateful components or transitions.

#### Event Handling

```html
<!-- Method handler -->
<button @click="increment">+1</button>

<!-- Inline handler -->
<button @click="count++">+1</button>

<!-- Event modifiers -->
<form @submit.prevent="onSubmit">...</form>
<a @click.stop="doThis">Stop propagation</a>
<input @keyup.enter="submit">
<button @click.once="doOnce">Once only</button>

<!-- Key modifiers -->
<input @keyup.enter="submit">
<input @keyup.esc="cancel">
```

**Event modifiers:** `.stop`, `.prevent`, `.capture`, `.self`, `.once`, `.passive`

**Key modifiers:** `.enter`, `.tab`, `.delete`, `.esc`, `.space`, `.up`, `.down`, `.left`, `.right`

**System modifiers:** `.ctrl`, `.alt`, `.shift`, `.meta`, `.exact`

#### Two-Way Binding with `v-model`

```html
<!-- Text input -->
<input v-model="message" placeholder="edit me">

<!-- Checkbox -->
<input type="checkbox" v-model="checked">

<!-- Radio -->
<input type="radio" v-model="picked" value="a">

<!-- Select -->
<select v-model="selected">
  <option value="">Choose</option>
  <option value="A">A</option>
</select>

<!-- Modifiers -->
<input v-model.lazy="msg">    <!-- sync on change instead of input -->
<input v-model.number="age">  <!-- auto-typecast to number -->
<input v-model.trim="msg">    <!-- trim whitespace -->
```

### Reactivity System

Vue's reactivity system uses `Object.defineProperty` to convert data properties into getter/setter pairs. When a property is accessed during rendering, it is tracked as a dependency. When it changes, dependent watchers are notified and the view updates.

**Reactivity caveats — arrays:**

Vue intercepts these array mutation methods to trigger reactivity: `push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse`.

Setting an item by index or changing length directly is **not** reactive:

```javascript
// NOT reactive:
this.items[index] = newValue
this.items.length = newLength

// Use instead:
Vue.set(this.items, index, newValue)     // or this.$set(...)
this.items.splice(newLength)
```

**Reactivity caveats — objects:**

Vue cannot detect property addition or deletion on already-created objects:

```javascript
// NOT reactive (adding new property):
this.obj.newProp = 'value'

// Use instead:
Vue.set(this.obj, 'newProp', 'value')    // or this.$set(...)

// Removing:
Vue.delete(this.obj, 'prop')             // or this.$delete(...)
```

> [!WARNING]
> `Vue.set` and `Vue.delete` (aliased as `this.$set` / `this.$delete`) are essential when adding or removing properties from reactive objects or setting array items by index. Without them, the UI will not update.

### Computed Properties and Watchers

```javascript
new Vue({
  data: {
    firstName: 'John',
    lastName: 'Doe',
    question: ''
  },
  computed: {
    // Getter only
    fullName() {
      return `${this.firstName} ${this.lastName}`
    },
    // Getter + setter
    fullNameRW: {
      get() { return `${this.firstName} ${this.lastName}` },
      set(newValue) {
        const names = newValue.split(' ')
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
      }
    }
  },
  watch: {
    // Simple watcher
    question(newVal, oldVal) {
      this.fetchAnswer()
    },
    // Deep watcher
    someObject: {
      handler(newVal) { /* ... */ },
      deep: true
    },
    // Immediate watcher (fires on creation)
    searchQuery: {
      handler(val) { this.search(val) },
      immediate: true
    }
  }
})
```

### Components

```javascript
// Global registration
Vue.component('my-component', {
  props: {
    title: { type: String, required: true },
    count: { type: Number, default: 0 }
  },
  template: '<div>{{ title }}: {{ count }}</div>'
})

// Local registration
const ChildComponent = {
  props: ['message'],
  template: '<span>{{ message }}</span>'
}

new Vue({
  components: { ChildComponent },
  template: '<child-component message="hello" />'
})
```

#### Props

```javascript
props: {
  // Type check
  age: Number,
  // Multiple types
  value: [String, Number],
  // Required with type
  name: { type: String, required: true },
  // Default value
  count: { type: Number, default: 0 },
  // Default function (for objects/arrays)
  items: { type: Array, default: () => [] },
  // Custom validator
  status: {
    type: String,
    validator: (val) => ['active', 'inactive'].includes(val)
  }
}
```

#### Events

```html
<!-- Parent -->
<child-component @update="handleUpdate" />

<!-- Child -->
<script>
export default {
  methods: {
    save() {
      this.$emit('update', this.localData)
    }
  }
}
</script>
```

#### Slots

```html
<!-- Parent -->
<layout-component>
  <template #header>
    <h1>Page Title</h1>
  </template>
  <template #default>
    <p>Main content</p>
  </template>
  <template #footer="{ year }">
    <p>&copy; {{ year }}</p>
  </template>
</layout-component>

<!-- Child (LayoutComponent) -->
<div>
  <header><slot name="header"></slot></header>
  <main><slot></slot></main>
  <footer><slot name="footer" :year="currentYear"></slot></footer>
</div>
```

### Lifecycle Hooks

Components go through a defined lifecycle. Use hooks to run code at specific stages:

| Hook | When It Fires |
|---|---|
| `beforeCreate` | Instance initialized, before data/events setup |
| `created` | Instance created, data/computed/methods available, no DOM yet |
| `beforeMount` | Before initial render |
| `mounted` | DOM rendered, `this.$el` available |
| `beforeUpdate` | Data changed, before DOM re-render |
| `updated` | DOM re-rendered after data change |
| `beforeDestroy` | Before teardown |
| `destroyed` | Instance destroyed, all bindings/listeners removed |
| `activated` | `<keep-alive>` component activated |
| `deactivated` | `<keep-alive>` component deactivated |
| `errorCaptured` | Error from descendant component captured |
| `serverPrefetch` | SSR only — resolved before server render |
| `renderTracked` | Reactive dependency tracked (dev only) |
| `renderTriggered` | Re-render triggered by dependency change (dev only) |

### Filters

Vue 2 supports template filters for text formatting:

```javascript
// Global filter
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  return value.charAt(0).toUpperCase() + value.slice(1)
})

// Local filter
new Vue({
  filters: {
    truncate(v) {
      const newline = v.indexOf('\n')
      return newline > 0 ? v.slice(0, newline) : v
    },
    formatDate(v) {
      return v.replace(/T|Z/g, ' ')
    },
    pluralize(n) {
      return n === 1 ? 'item' : 'items'
    }
  }
})
```

```html
<!-- In mustaches -->
{{ message | capitalize }}

<!-- In v-bind -->
<div :id="rawId | formatId"></div>

<!-- Chained -->
{{ message | capitalize | truncate }}
```

### Custom Directives

```javascript
// Global directive
Vue.directive('focus', {
  inserted(el) {
    el.focus()
  }
})

// Shorthand (bind + update)
Vue.directive('color', function (el, binding) {
  el.style.color = binding.value
})
```

```html
<input v-focus>
<span v-color="'red'">Colored text</span>
```

**Directive hook functions:** `bind`, `inserted`, `update`, `componentUpdated`, `unbind`

### Plugins

```javascript
// Using a plugin
Vue.use(MyPlugin, { someOption: true })

// Plugin structure
const MyPlugin = {
  install(Vue, options) {
    Vue.mixin({ /* ... */ })
    Vue.directive('my-directive', { /* ... */ })
    Vue.prototype.$myMethod = function () { /* ... */ }
  }
}
```

> [!NOTE]
> `Vue.use()` automatically prevents double-installation of the same plugin. Calling it multiple times with the same plugin has no additional effect.

### Mixins

```javascript
const myMixin = {
  created() {
    console.log('mixin hook called')
  },
  methods: {
    sharedMethod() { /* ... */ }
  }
}

// Local mixin
new Vue({
  mixins: [myMixin]
})

// Global mixin (affects ALL instances — use with caution)
Vue.mixin({
  created() { /* runs in every component */ }
})
```

### Transitions and Animations

```html
<transition name="fade">
  <p v-if="show">Hello</p>
</transition>

<transition-group name="list" tag="ul">
  <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</transition-group>
```

```css
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
```

Vue applies CSS classes during enter/leave transitions:

| Class | When Applied |
|---|---|
| `{name}-enter` | Starting state for enter |
| `{name}-enter-active` | Active state during enter |
| `{name}-enter-to` | Ending state for enter |
| `{name}-leave` | Starting state for leave |
| `{name}-leave-active` | Active state during leave |
| `{name}-leave-to` | Ending state for leave |

### Built-in `<keep-alive>` Component

`<keep-alive>` caches inactive component instances instead of destroying them:

```html
<!-- Cache all -->
<keep-alive>
  <component :is="currentView"></component>
</keep-alive>

<!-- Include/exclude by name -->
<keep-alive include="ComponentA,ComponentB">
  <router-view></router-view>
</keep-alive>

<!-- Regex or array -->
<keep-alive :include="/^Todo/">
  <component :is="view"></component>
</keep-alive>

<!-- Max cached instances (LRU eviction) -->
<keep-alive :max="10">
  <component :is="view"></component>
</keep-alive>
```

| Prop | Type | Description |
|---|---|---|
| `include` | `String \| RegExp \| Array` | Only cache matching component names |
| `exclude` | `String \| RegExp \| Array` | Never cache matching component names |
| `max` | `String \| Number` | Maximum number of cached instances |

Cached components receive `activated` and `deactivated` lifecycle hooks instead of `mounted`/`destroyed`.

## 5. Common Tasks

### Composition API (Vue 2.7)

Vue 2.7 backports the Composition API from Vue 3. Use `setup()` to organize logic by concern rather than by option type:

```javascript
import { ref, computed, watch, onMounted, defineComponent } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const doubled = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    watch(count, (newVal, oldVal) => {
      console.log(`count changed: ${oldVal} → ${newVal}`)
    })

    onMounted(() => {
      console.log('component mounted')
    })

    return { count, doubled, increment }
  }
})
```

> [!TIP]
> `<script setup>` syntax is supported in Vue 2.7 SFCs via `@vue/compiler-sfc`. It provides a more concise way to use the Composition API.

#### Composition API Functions Available in Vue 2.7

**Reactivity:**

| Function | Purpose |
|---|---|
| `ref(value)` | Create a reactive reference |
| `shallowRef(value)` | Shallow reactive reference |
| `computed(getter)` | Computed value |
| `reactive(object)` | Deep reactive object |
| `shallowReactive(object)` | Shallow reactive object |
| `readonly(object)` | Deep readonly proxy |
| `shallowReadonly(object)` | Shallow readonly proxy |
| `toRef(obj, key)` | Create ref from reactive property |
| `toRefs(obj)` | Convert reactive object to plain object of refs |
| `unref(ref)` | Unwrap ref (returns `.value` or the value itself) |
| `isRef(value)` | Check if value is a ref |
| `isReactive(value)` | Check if value is reactive |
| `isReadonly(value)` | Check if value is readonly |
| `isProxy(value)` | Check if value is reactive or readonly |
| `triggerRef(ref)` | Force trigger a shallow ref |
| `customRef(factory)` | Create ref with custom track/trigger |
| `markRaw(object)` | Mark object to never be made reactive |
| `toRaw(proxy)` | Get the raw object from a reactive proxy |

**Watchers:**

| Function | Purpose |
|---|---|
| `watch(source, cb, options?)` | Watch reactive source(s) with callback |
| `watchEffect(effect)` | Run effect immediately and re-run on dependency change |
| `watchPostEffect(effect)` | `watchEffect` with `flush: 'post'` |
| `watchSyncEffect(effect)` | `watchEffect` with `flush: 'sync'` |

**Watch options:** `immediate` (fire on creation), `deep` (deep watch), `flush` (`'pre'` | `'post'` | `'sync'`).

**Lifecycle hooks (Composition API):**

| Composition API | Options API Equivalent |
|---|---|
| `onBeforeMount` | `beforeMount` |
| `onMounted` | `mounted` |
| `onBeforeUpdate` | `beforeUpdate` |
| `onUpdated` | `updated` |
| `onBeforeUnmount` | `beforeDestroy` |
| `onUnmounted` | `destroyed` |
| `onActivated` | `activated` |
| `onDeactivated` | `deactivated` |
| `onErrorCaptured` | `errorCaptured` |
| `onServerPrefetch` | `serverPrefetch` |
| `onRenderTracked` | `renderTracked` |
| `onRenderTriggered` | `renderTriggered` |

**Dependency Injection:**

```javascript
import { provide, inject } from 'vue'

// Parent
setup() {
  provide('theme', 'dark')
}

// Child (any depth)
setup() {
  const theme = inject('theme', 'light') // default: 'light'
}
```

**Effect Scope:**

```javascript
import { effectScope, onScopeDispose } from 'vue'

const scope = effectScope()
scope.run(() => {
  const counter = ref(0)
  watch(counter, () => { /* ... */ })
  onScopeDispose(() => { console.log('scope disposed') })
})
scope.stop() // disposes all effects in scope
```

> [!NOTE]
> The `suspensible` option for `defineAsyncComponent` is not supported in Vue 2 and is silently ignored. The `useListeners()` helper is Vue 2-only (Vue 3 merged listeners into `$attrs`).

### Provide/Inject (Options API)

```javascript
// Parent component
export default {
  provide() {
    return {
      theme: this.theme
    }
  }
}

// Descendant component (any depth)
export default {
  inject: ['theme']
}
```

### Async Components

```javascript
// Simple async component
Vue.component('async-comp', () => import('./MyComponent.vue'))

// With loading/error states (Vue 2.7 defineAsyncComponent)
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent({
  loader: () => import('./HeavyComponent.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorDisplay,
  delay: 200,       // delay before showing loading (ms)
  timeout: 10000,   // timeout before showing error (ms)
  onError(error, retry, fail, attempts) {
    if (attempts <= 3) retry()
    else fail()
  }
})
```

### Using `Vue.extend` for Subclasses

```javascript
const Profile = Vue.extend({
  template: '<p>{{ fullName }}</p>',
  data() {
    return { firstName: 'Jane', lastName: 'Doe' }
  },
  computed: {
    fullName() { return `${this.firstName} ${this.lastName}` }
  }
})

// Create an instance
new Profile().$mount('#profile')
```

### Programmatic Event Bus

```javascript
// Create a shared event bus
const EventBus = new Vue()

// Component A: emit
EventBus.$emit('item-added', { id: 1 })

// Component B: listen
EventBus.$on('item-added', (item) => {
  console.log('Received:', item)
})

// Clean up (in beforeDestroy)
EventBus.$off('item-added')
```

### `Vue.observable` for Lightweight State

```javascript
const state = Vue.observable({
  count: 0,
  message: 'hello'
})

// Use in any component
new Vue({
  computed: {
    count: () => state.count
  },
  methods: {
    increment() { state.count++ }
  }
})
```

### Using `nextTick` for DOM Updates

```javascript
this.message = 'updated'
this.$nextTick(() => {
  // DOM is now updated
  console.log(this.$el.textContent) // 'updated'
})

// Or with async/await
async handler() {
  this.message = 'updated'
  await this.$nextTick()
  // DOM updated
}

// Static method
Vue.nextTick(() => { /* ... */ })
```

### Error Handling

```javascript
// Global error handler
Vue.config.errorHandler = function (err, vm, info) {
  // info is a Vue-specific error info, e.g. which lifecycle hook
  console.error('Global error:', err, info)
  // Report to tracking service
}

// Component-level with errorCaptured
export default {
  errorCaptured(err, childVm, info) {
    console.error('Caught from child:', err)
    return false // stop propagation
  }
}

// Composition API
import { onErrorCaptured } from 'vue'
setup() {
  onErrorCaptured((err, instance, info) => {
    console.error(err)
    return false
  })
}
```

### Render Functions and `h()`

```javascript
// Options API render function
export default {
  render(createElement) {
    return createElement('div', { class: 'container' }, [
      createElement('h1', this.title),
      this.$slots.default
    ])
  }
}

// Composition API h()
import { h } from 'vue'
export default {
  setup() {
    return () => h('div', { class: 'container' }, 'Hello')
  }
}
```

### Functional Components

```javascript
Vue.component('functional-comp', {
  functional: true,
  props: ['level'],
  render(createElement, context) {
    return createElement(
      `h${context.props.level}`,
      context.data,
      context.children
    )
  }
})
```

## 6. Troubleshooting

### Error: `Vue packages version mismatch`

**Cause:** `vue-server-renderer` or `vue-template-compiler` version does not match the `vue` package version.

**Solution:**
1. Check installed versions:
   ```bash
   npm ls vue vue-server-renderer vue-template-compiler
   ```
2. Align all versions to the same release:
   ```bash
   npm install vue@2.7.16 vue-server-renderer@2.7.16 vue-template-compiler@2.7.16
   ```

**Prevention:** Pin all three packages to the same exact version in `package.json`.

### Error: `Property or method "X" is not defined on the instance`

**Cause:** You referenced a property in the template that is not declared in `data`, `computed`, `methods`, or `props`.

**Solution:**
1. Verify the property name matches exactly (case-sensitive)
2. Ensure the property is declared in the correct option (`data`, `computed`, or `methods`)
3. If using Composition API, ensure you return the property from `setup()`

**Prevention:** Always declare all template-referenced properties in your component definition.

### Error: `Avoid mutating a prop directly`

**Cause:** A child component is modifying a prop passed from its parent.

**Solution:**
1. Use a local `data` property initialized from the prop:
   ```javascript
   data() {
     return { localValue: this.initialValue }
   }
   ```
2. Or use a computed property to transform the prop:
   ```javascript
   computed: {
     normalizedValue() { return this.value.trim().toLowerCase() }
   }
   ```

**Prevention:** Follow one-way data flow — props down, events up.

### Error: `Do not replace the Vue.config object`

**Cause:** Code is assigning a new object to `Vue.config` rather than setting individual properties.

**Solution:** Set properties individually:
```javascript
// Wrong:
Vue.config = { silent: true }

// Right:
Vue.config.silent = true
```

### Warning: `Event "X" is emitted but the handler is registered for "Y"`

**Cause:** Case mismatch between emitted event name and the listener in a template. HTML attributes are case-insensitive, so camelCase event names don't work in in-DOM templates. (GitHub issue #13287)

**Solution:** Use kebab-case for event names:
```javascript
// Emit
this.$emit('item-selected', data)
```
```html
<!-- Listen -->
<child @item-selected="handleSelect" />
```

### Reactivity not working — `Vue.set` / `this.$set` needed

**Cause:** Adding a new property to an already-reactive object or setting an array item by index does not trigger updates because `Object.defineProperty` can only intercept existing properties.

**Solution:**
```javascript
// Objects: use Vue.set
this.$set(this.obj, 'newKey', 'value')

// Arrays: use Vue.set or splice
this.$set(this.items, index, newValue)
this.items.splice(index, 1, newValue)
```

### Warning: `Vue is a constructor and should be called with the new keyword`

**Cause:** Calling `Vue()` as a function instead of `new Vue()`.

**Solution:** Always use the `new` keyword:
```javascript
const vm = new Vue({ /* options */ })
```

### SFC compilation error: `SFC contains no <script> tags`

**Cause:** `@vue/compiler-sfc`'s `compileScript()` was called on a `.vue` file that has no `<script>` block.

**Solution:** Ensure your `.vue` file contains a `<script>` or `<script setup>` block. If the component is template-only, add a minimal script block:
```html
<script>
export default {}
</script>
```

### `<keep-alive>` not caching components

**Cause:** The component is missing a `name` option, or the `include`/`exclude` pattern does not match the component's registered name.

**Solution:**
1. Add a `name` option to cached components
2. Verify `include`/`exclude` patterns match the component name string
3. Check that `max` is not set too low, causing LRU eviction

### Source maps broken in Firefox

**Cause:** Known issue with Vue 2 source map generation in certain browser debugging scenarios. (GitHub issue #12689)

**Solution:** Use Chrome DevTools for debugging, or disable source maps in your build configuration if Firefox debugging is critical.

### Race condition in reactive updates

**Cause:** Potential race condition reported in the reactivity system. (GitHub issue #13309)

**Solution:** Ensure reactive mutations are performed synchronously within the same tick. Avoid concurrent async operations that mutate the same reactive data without serialization.

### Memory leaks with large component trees

**Cause:** Components or watchers not being properly cleaned up. (GitHub issue #13308)

**Solution:**
1. Clean up event listeners and timers in `beforeDestroy`
2. Remove event bus listeners: `EventBus.$off('event', this.handler)`
3. Cancel pending async operations in `beforeDestroy`

## 7. FAQ

**Q: What is the difference between Vue 2 and Vue 3?**

Vue 3 introduced Proxy-based reactivity (replacing `Object.defineProperty`), the Composition API, `<Teleport>`, `<Suspense>`, improved TypeScript support, and multiple root nodes in templates (fragments). Vue 2.7 backports the Composition API but retains the `Object.defineProperty`-based reactivity system. Vue 2 reached End of Life on December 31, 2023.

**Q: Can I use the Composition API in Vue 2?**

Yes. Vue 2.7 includes native support for `setup()`, `ref`, `reactive`, `computed`, `watch`, `provide`/`inject`, lifecycle hooks (`onMounted`, etc.), and `<script setup>` in SFCs. No additional plugin is needed.

**Q: When should I use `v-if` vs `v-show`?**

Use `v-if` when the condition rarely changes — it has higher toggle cost (adds/removes elements from DOM) but no initial render cost for hidden elements. Use `v-show` when toggling frequently — it has higher initial render cost (always renders) but very cheap toggle cost (CSS only).

**Q: How do I register a component globally vs locally?**

Global: `Vue.component('name', definition)` — available in all templates. Local: add to the `components` option of the parent component. Prefer local registration to keep bundle size down with tree-shaking.

**Q: What is `Vue.extend` used for?**

`Vue.extend` creates a reusable component constructor (a "subclass" of Vue). It is used internally by `Vue.component` when you pass a plain object, and can be used directly for programmatic component instantiation (e.g., modals, toasts).

**Q: How do I handle errors from async components?**

Use `defineAsyncComponent` with an `onError` callback. The callback receives the error, `retry()` and `fail()` functions, and the attempt count, letting you implement retry logic.

**Q: What is `Vue.observable` and when should I use it?**

`Vue.observable(obj)` makes a plain object reactive — useful for lightweight shared state across components without Vuex. Available since Vue 2.6.

**Q: How does `nextTick` work?**

`Vue.nextTick(cb)` / `this.$nextTick(cb)` defers the callback until after the next DOM update cycle. Vue batches data changes and updates the DOM asynchronously, so use `nextTick` when you need to read or manipulate the DOM immediately after a data change.

**Q: Can I use TypeScript with Vue 2?**

Yes. Vue 2.7 ships TypeScript type definitions at `types/index.d.ts`. Use `defineComponent()` to enable proper type inference for component options. `<script setup lang="ts">` is supported in SFCs with `@vue/compiler-sfc`.

**Q: How do `provide` and `inject` work?**

`provide` makes data available to all descendant components (any depth). `inject` retrieves that data. In the Options API, use them as component options. In the Composition API (Vue 2.7), import `provide()` and `inject()` from `vue` and call them in `setup()`. The prototype chain is used internally — child provides shadow parent provides.

---

_Generated May 2026 from vuejs/vue at 9e88707. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
