MAD Study Hub

Home / MAD2 / Week 4

Week 4 · Vue basics: instances, directives and reactivity

Official syllabus line: Introduction to VueJS
About 100 minQuiz 1 material (see exam radar)BSCS2006 (+ project BSCS2006P) · Thejesh G N, Nitin Chandrachoodan
Before you start: Basic HTML, CSS and JavaScript (variables, functions, arrays, objects, arrow functions). MAD1 helps but is not required.

The one-minute story

Think of it like this

Vue is a smart notice board. Imagine a notice board in your hostel. You write the notice once as a template: "Dinner today: _ at _ o'clock". The blanks are connected by invisible threads to a small notebook (your data). The moment someone changes a number in the notebook, the thread pulls and the board updates itself. You never walk to the board with a marker.

Plain JavaScript makes you do the walking: find the element, change its text, remember to change it again. Vue lets you declare what the page should look like for the current data, and it keeps the page in sync. That is called declarative rendering and reactivity.

A Vue instance also has a life: it is born, put on the wall, updated many times, and taken down. Vue lets you hook into each of those moments (the lifecycle hooks). Exam questions love asking "in which order do things happen".

By the end of this week you can:

  • add Vue 2 to a page with one script tag and create an instance with el, data, methods, computed and watch;
  • use {{ }} and the directives v-bind, v-on, v-model, v-if, v-show and v-for correctly;
  • bind classes and styles in the object, array and ternary forms;
  • choose between a computed property, a method and a watcher, and explain caching;
  • predict what the template renders and what the console prints, including lifecycle hook order;
  • explain reactivity caveats (adding properties, array index assignment) and fix them with Vue.set.

Words you need

Words you need
  • Vue - a JavaScript framework for building user interfaces. This course uses Vue 2 (options API).
  • Instance - the object you make with new Vue({...}). It owns data, methods and one piece of the page.
  • Template - the HTML with Vue extras ({{ }} and directives) that describes the page.
  • Reactive - when data changes, the page changes automatically.
  • Interpolation - printing a value into the page with {{ expression }}.
  • Directive - a special attribute starting with v- that tells Vue to do something to an element.
  • Computed property - a value worked out from other data, cached until those data change.
  • Watcher - a function that runs whenever a chosen piece of data changes (for side effects).
  • Lifecycle hook - a function Vue calls at a fixed moment: created, mounted, updated, beforeDestroy and so on.
  • Virtual DOM - Vue's light copy of the page; it compares old and new copies and touches the real page only where needed.
  • Ref - a name (ref="box") that gives you direct access to a real element or child through this.$refs.
  • nextTick - run code after Vue has finished updating the page.
  • CDN - a public server that hosts library files, so one script tag is enough to load them.

Idea 1: What Vue is and how to add it

Vue is a progressive framework: you can use it on one corner of a normal HTML page, or build a whole application with it. In MAD2 you first use it the simple way, with a CDN script tag, and later (week 6) with the Vue CLI and .vue files.

<!DOCTYPE html>
<html>
  <body>
    <div id="app">
      <p>{{ message }}</p>
      <button @click="count++">Clicked {{ count }} times</button>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <script>
      new Vue({
        el: "#app",
        data: { message: "Hello Vue", count: 0 },
      });
    </script>
  </body>
</html>

How it works:

  1. The browser loads Vue, which defines the global Vue.
  2. new Vue({...}) creates an instance and mounts it on the element matching el (#app).
  3. Vue reads the HTML inside #app as the template, finds {{ message }} and @click, and renders.
  4. When you click, count++ changes data, and Vue updates only that text.
The idea

The Vue instance options you must know

OptionWhat it holds
elWhich existing element to take over, e.g. "#app" (alternative: .$mount("#app"))
dataThe reactive state (an object on the root instance; a function returning an object in components)
methodsFunctions you can call from the template or from other code
computedValues derived from data, cached
watchFunctions that run when a data property changes
lifecycle hookscreated, mounted, ...

Inside any of these, this is the instance, so data is this.message, a method is this.greet().

Watch out - real exam trap
  • Vue only controls the HTML inside el. A {{ message }} written outside #app is printed literally.
  • Use arrow functions for callbacks inside methods, but do not write a method itself as an arrow function: greet: () => this.message has the wrong this. Write greet() { ... }.
  • el can be omitted if you call .$mount("#app") later; the instance is then not on the page until you mount it.
In your project

Examiners ask "why did you pick Vue and not plain JavaScript?". Give a concrete reason from your project: "the dashboard list changes after every API call, and Vue re-renders it from the data, so I do not write document.getElementById code".

Idea 2: Interpolation and declarative rendering

{{ expression }} prints the value of any single JavaScript expression: a property, arithmetic, a ternary, a method call. It is text only, and it works only in the template (HTML text and attribute values never get {{ }}).

<div id="app">
  <p>{{ name }}</p>
  <p>{{ price * qty }}</p>
  <p>{{ qty > 5 ? "bulk" : "single" }}</p>
  <p>{{ name.toUpperCase() }}</p>
  <p>{{ items }}</p>
  <p v-text="name"></p>
  <p v-html="raw"></p>
</div>
<script>
  new Vue({
    el: "#app",
    data: { name: "pen", price: 10, qty: 3, items: [1, 2], raw: "<b>bold</b>" },
  });
</script>

Renders: pen, 30, single, PEN, the JSON text of the array (arrays and objects are printed as JSON), pen, and bold text (v-html inserts real HTML).

The idea

Rules for {{ }}

  • Only expressions (one value). A statement such as {{ let x = 1 }} or {{ if (a) { } }} is an error.
  • undefined or null prints as an empty string. {{ user.name }} when user is null throws an error, because you cannot read a property of null (more in Idea 6).
  • It escapes HTML, so it is safe. v-html does not escape; never use it on user-typed text (cross-site scripting).
  • v-once renders once and never updates.
  • Do not use {{ }} inside an attribute. Write :title="name", not title="{{ name }}".
Your turn: 1. What renders

Data is { a: 4, b: "4", list: ["x", "y"], o: { k: 1 } }. Write what each prints: {{ a + b }}, {{ a * b }}, {{ list.length }}, {{ o.k + 1 }}, {{ list[1] }}, {{ o.z }}.

Need a hint?

Remember how JavaScript adds a number and a string.

Show the full solution

44 (number plus string joins), 16 (multiplication converts the string), 2, 2, y, and an empty string (o.z is undefined, which prints nothing; it does not crash because o itself exists).

Idea 3: Directives

A directive is an attribute starting with v-. Its value is a JavaScript expression (except v-for and v-on, which have their own forms).

v-bind (shorthand :) and v-on (shorthand @)

<img v-bind:src="photo" :alt="caption" :disabled="busy">
<button v-on:click="save" @click.prevent="save">Save</button>
  • :attr="expr" sets an HTML attribute from data. If the expression is false, null or undefined, the attribute is removed (so :disabled="false" enables the button).
  • @event="handler" listens to a DOM event. The handler can be a method name (@click="save"), a call with arguments (@click="remove(item.id)") or a short statement (@click="count++"). Inside an inline handler, $event is the event.

Event modifiers (chain them after a dot):

ModifierEffect
.preventcalls event.preventDefault() (stops a form reloading the page, a link navigating)
.stopcalls event.stopPropagation() (the click does not bubble to the parent)
.oncethe handler runs at most once
.selfonly if the event target is the element itself
.enter, .esc, .space, .tab, .up ...key modifiers: only for that key, e.g. @keyup.enter="send"

v-model: two-way binding on form inputs

<input v-model="name">
<input v-model.trim="name">      <!-- strips spaces at both ends -->
<input v-model.number="age">     <!-- converts the text to a number -->
<input v-model.lazy="name">      <!-- updates on change (leaving the box), not on every key -->
<input type="checkbox" v-model="agree">             <!-- true / false -->
<input type="checkbox" v-model="chosen" value="A">  <!-- chosen is an array -->
<input type="radio" v-model="size" value="L">
<select v-model="city"><option>Chennai</option><option>Delhi</option></select>
<textarea v-model="notes"></textarea>

v-model is shorthand for two things: :value="name" (data to input) and @input="name = $event.target.value" (input to data). Typing updates name and changing name in code updates the box. I checked v-model.number and .trim in Vue 2: typing 42 stores the number 42, typing hi stores "hi".

Watch out - real exam trap
  • v-model.number is needed because input values are always strings. Without it, age + 1 with age "20" gives "201".
  • .prevent on a form: <form @submit.prevent="save">. Without it the page reloads and your Vue state is lost.
  • .stop stops bubbling; .prevent stops the default action. They are different.
  • A key modifier is lower case and goes with a key event: @keyup.enter, not @click.enter.

Idea 4: Conditionals and lists

v-if, v-else-if, v-else and v-show

<p v-if="score >= 80">Distinction</p>
<p v-else-if="score >= 40">Pass</p>
<p v-else>Fail</p>
<p v-show="showHelp">Help text</p>

With score = 55 Vue renders only Pass. The v-else and v-else-if element must come immediately after a v-if element.

The idea

v-if versus v-show

v-ifv-show
False caseelement is not in the DOMelement is in the DOM with display: none
Costhigher toggle cost (creates and destroys)higher first-render cost, cheap toggle
Supports elseyesno
Works on templateyesno
Use whencondition rarely changes, or element must not exist (loading states)you toggle often

A real exam option says "v-show and v-if cannot work together". That is false: you can use both (for example a loading message with v-if and a panel with v-show).

v-for: lists

<ul>
  <li v-for="(item, index) in items" :key="item.id">{{ index }}: {{ item.name }}</li>
</ul>
<span v-for="(value, key, i) in person">{{ i }}{{ key }}={{ value }}; </span>
<b v-for="n in 3">{{ n }}</b>
  • Over an array: (item, index) in items.
  • Over an object: (value, key, index) in obj (value first, then key).
  • Over a range: n in 3 gives 1 2 3 (starts at 1, not 0).
  • :key must be a unique, stable id (not the index when the list is reordered or items are deleted). It lets Vue move the right element instead of re-using the wrong one.
  • Never put v-if and v-for on the same element; wrap with template v-for or filter with a computed property.

Running this in Vue 2: o = {a:1,b:2} with (v,k,i) in o prints 0a=1;1b=2;, and n in 3 prints 123.

Solved example: 1. A small to-do list
<div id="app">
  <input v-model.trim="text" @keyup.enter="add" placeholder="new task">
  <button @click="add" :disabled="!text">Add</button>
  <ul>
    <li v-for="(t, i) in tasks" :key="t.id">
      <span :class="{ done: t.done }" @click="t.done = !t.done">{{ t.title }}</span>
      <button @click="tasks.splice(i, 1)">x</button>
    </li>
  </ul>
  <p v-if="tasks.length === 0">Nothing to do.</p>
  <p v-else>{{ remaining }} left</p>
</div>
<script>
  new Vue({
    el: "#app",
    data: { text: "", nextId: 1, tasks: [] },
    computed: {
      remaining() { return this.tasks.filter(t => !t.done).length; },
    },
    methods: {
      add() {
        if (!this.text) return;
        this.tasks.push({ id: this.nextId++, title: this.text, done: false });
        this.text = "";
      },
    },
  });
</script>

Walk through: pressing Enter in the box or clicking Add runs add; push is a reactive array method, so the list re-renders; text = "" clears the box through v-model; :disabled="!text" is true while the box is empty; remaining recomputes when any done flag changes.

Idea 5: Class and style binding

:class and :style accept more than a string. Given the data:

data: {
  classObj: { is_active: true, completed: false },
  myClass: "is_active",
  status: true,
}
<div :class="classObj"></div>                       <!-- class="is_active" -->
<div :class="[myClass, { completed: status }]"></div> <!-- class="is_active completed" -->
<div class="base" :class="status ? 'on' : 'off'"></div> <!-- class="base on" -->
<div :style="{ color: 'red', fontSize: '14px' }"></div>

I ran these in Vue 2 and they render exactly the comments shown.

The idea

The three forms of :class

  1. Object: { className: booleanExpression }; the class is present when the value is truthy.
  2. Array: [a, b]; every item (a string from data) is added. Objects can be mixed inside the array.
  3. Ternary inside the array or alone: status ? 'on' : 'off'.

A static class="base" and a :class merge; the static one is kept. For :style use an object with camelCase (fontSize) or quoted kebab-case ('font-size') keys.

Watch out - real exam trap

A real question gives classObj and an array form and asks which statements are true. Read each carefully: an object entry adds the class only when its value is true; with completed: false the class completed is not present. Two different-looking bindings can produce the same rendered HTML.

Idea 6: Computed, methods and watch

Three ways to run logic. They look similar but are used for different jobs.

new Vue({
  el: "#app",
  data: { counter: 0, first: "Ada", last: "Lovelace" },
  computed: {
    fullName() { return this.first + " " + this.last; },     // cached
    double() { return this.counter * 2; },
  },
  methods: {
    stamp() { return Date.now(); },                           // runs every call
  },
  watch: {
    counter(newVal, oldVal) {                                 // side effect
      console.log("counter changed from " + oldVal + " to " + newVal);
    },
  },
});
ComputedMethodWatch
Used asa property: {{ fullName }}a call: {{ stamp() }}not called; runs by itself
Returns a valueyesyesno (do something)
Cachedyes, until a dependency changesno, runs on every call/re-rendernot applicable
Typical usederived values, filtered listsevent handlers, actionsasync calls, logging, reacting to a route or one property

Facts proved by running Vue 2:

  • Reading a computed property three times with unchanged data runs its function once.
  • Changing data twice in a row (n = 2; n = 3) calls the watcher once, with old value 1 and new value 3, because updates are batched.
  • A computed property is read-only unless you give it a get and a set.
  • A watcher can be written { handler, deep: true, immediate: true }: deep watches nested changes, immediate also runs it once at start. To watch a nested key use a string: 'o.a'() {...} (and '$route.path'() for the router).
Solved example: 2. Predict the console
new Vue({
  el: "#app",
  data: { counter: 0 },
  computed: { computedValue() { return this.counter * 2; } },
  watch: {
    counter(newVal, oldVal) { console.log(`Counter changed from ${oldVal} to ${newVal}`); },
  },
  mounted() { setInterval(() => { this.counter++; }, 1000); },
});

The watcher watches counter (not the computed value). After 1 second it prints Counter changed from 0 to 1, then from 1 to 2, then from 2 to 3. The page shows Counter: n and Computed Value: 2n. (A real paper offers "from 0 to 2" and "from 1 to 3 ... 6 ... 12" as wrong options: those would print the doubled value, which the watcher never sees.)

Watch out - real exam trap
  • Use computed when the result is a value for the template. Use watch when you must do something (call an API, save to storage) after a change. Using a watcher to calculate a display value is the classic wrong answer to "which is cached".
  • A computed property must return something and must not change data.
  • The question "use cases of watchers" has these true: react when a property changes; perform side effects. False: caching values (that is computed) and "creating reactive data" (that is data).
Your turn: 2. Pick the tool

For each job say computed, method or watch: (a) show "first last" from two fields; (b) button click that saves the form; (c) call the server whenever the search box text changes; (d) a filtered list shown in the page.

Need a hint?

Ask: does it return a value for the template, or do something?

Show the full solution

(a) computed; (b) method (event handler); (c) watch on the search text (a side effect, often with a delay); (d) computed.

Idea 7: Reactivity and its caveats

When you create an instance, Vue 2 walks through data and converts every property into a getter and setter (using Object.defineProperty). The getter records "this render used me"; the setter tells Vue "re-render". That only works for properties that exist when the instance is created.

Caveats (Vue 2 only) that I confirmed by running the code:

data: { arr: [1, 2, 3], o: { a: 1 } }

this.arr[0] = 99;          // data changes to [99,2,3] but the page does NOT update
this.arr.length = 1;       // data changes but the page does NOT update
this.o.b = 2;              // new property: o has b, but the page does NOT update

Vue.set(this.o, "b", 2);   // reactive new property (same as this.$set)
this.$set(this.arr, 0, 99);        // reactive index assignment
this.arr.splice(0, 1, 99);         // reactive too
this.o = { ...this.o, b: 2 };      // replace the whole object: reactive
  • Array methods that Vue wraps and which do update the page: push, pop, shift, unshift, splice, sort, reverse.
  • Declare every property you will use in data up front, even as null or "".
  • Vue updates the page asynchronously in batches: many changes in one tick lead to one re-render. So right after this.m = "b" the element still shows the old text; use this.$nextTick(() => { ... }) to run after the update.
The idea

Remember: (1) properties must be declared in data to be reactive; (2) add new ones with Vue.set or by replacing the object; (3) change arrays with array methods or Vue.set, not arr[i] = x; (4) DOM updates are batched, so read the DOM inside nextTick.

this.message = "changed";
console.log(this.$refs.p.textContent);        // still the OLD text
this.$nextTick(() => console.log(this.$refs.p.textContent));  // the NEW text

Template refs. Put ref="box" on an element, and this.$refs.box is the real DOM element (after mount). It is the Vue way to call focus() on an input: mounted() { this.$refs.box.focus(); }. Refs exist only after mounting, and they are not reactive.

In newer Vue 3: reactivity uses Proxy, so adding properties and arr[0] = x do work, and Vue.set is gone. The instance is made with Vue.createApp({...}).mount("#app"), there is the Composition API (ref, reactive), and beforeDestroy/destroyed are renamed beforeUnmount/unmounted. This course tests Vue 2.

Idea 8: The lifecycle

Every instance goes through the same stages. Vue calls your hook at each.

new Vue()
  |
beforeCreate   (data and methods do NOT exist yet)
  |  (Vue sets up data, computed, methods, watchers)
created        (data and methods work; no DOM yet)
  |  (find the template, compile it)
beforeMount    (just before the first render)
  |  (render, create elements, insert in page)
mounted        (this.$el is in the page; refs work)
  |
  |-- data changes -->  beforeUpdate  ->  (DOM re-rendered)  ->  updated   (repeats)
  |
  |-- vm.$destroy() / component removed
beforeDestroy  (still fully working: clean up here)
  |
destroyed      (watchers and listeners are removed)
HookTypical use
beforeCreaterarely used; this.message is undefined
createdstart API calls to load data; set initial data
beforeMountrarely used
mountedwork with the DOM or refs, start timers, third-party widgets
beforeUpdate / updatedreact to a re-render (avoid changing data here, it can loop)
beforeDestroyclean up: clearInterval, remove listeners
destroyedfinal tidy-up (everything is already torn down)
Solved example: 3. Hooks that change data (a real paper question)
new Vue({
  el: "#app",
  template: `<div>Status: {{message}} ETA: {{ETA}}</div>`,
  data: { message: "", ETA: 10 },
  beforeCreate() { this.message += "Creating,"; this.ETA -= 1; },
  created()      { this.message += "Created,";  this.ETA -= 1; },
  beforeMount()  { this.message += "Mounting,"; this.ETA -= 1; },
  mounted()      { this.message += "Mounted";   this.ETA -= 1; },
});

Output (checked in Vue 2): Status: Created,Mounting,Mounted ETA: 7.

Why? In beforeCreate the data does not exist yet, so the two lines touch nothing useful (they set plain properties that Vue then overwrites when it initialises data). So message starts as "" and ETA as 10. Then created adds Created, and makes ETA 9, beforeMount gives Mounting, and 8, and mounted gives Mounted and 7. The page shows the final values because the update after mounted is rendered in the next tick.

Solved example: 4. created versus mounted, with a delayed update
new Vue({
  el: "#app",
  data() { return { message: "Initial" }; },
  created() { console.log("Created Hook"); this.message = "Created Hook Called"; this.fetchData(); },
  mounted() { console.log("Mounted Hook"); },
  methods: {
    async fetchData() {
      const res = await new Promise(resolve => setTimeout(() => resolve("Data Loaded"), 1000));
      this.message = res;
      console.log("Data updated after fetch");
    },
  },
});

Console: Created Hook, Mounted Hook, (one second later) Data updated after fetch. Page after 1 second: Data Loaded. created runs, starts the async work and returns at once (the await only pauses fetchData), then Vue mounts and prints Mounted Hook. The timer finishes later. I verified this order by running it.

Solved example: 5. When does beforeUpdate run?
<div id="app">
  <p>{{ message }}</p>
  <button @click="changeMessage">Change Message</button>
  <button @click="changeCount">Change Count</button>
</div>

With data: { message: "Hello, Vue!", count: 0 }, changeCount() { this.count++; } and beforeUpdate() { console.log("beforeUpdate called"); }: clicking Change Count prints nothing, because count is not used in the template, so nothing needs re-rendering. Clicking Change Message prints beforeUpdate called. An update hook fires only when data used by the render changes. (I tested both.)

Solved example: 6. beforeCreate with data
new Vue({
  el: "#app",
  data() { return { message: "Hello" }; },
  beforeCreate() { console.log("Before Create: " + this.message); this.message = "Before Create"; },
  mounted() { this.message = "Mounted"; console.log("Mounted: " + this.message); },
  template: `<div>{{ message }}</div>`,
});

Console: Before Create: undefined then Mounted: Mounted. The first value is undefined because data is not set up yet; the assignment is then overwritten when Vue initialises data.

Watch out - real exam trap
  • beforeCreate: data and methods are not available. created: they are, but this.$el and the DOM are not.
  • mounted does not guarantee that child components or fetched data are ready.
  • beforeDestroy is where cleanup goes: a timer started in mounted must be cleared in beforeDestroy, otherwise it keeps running. destroyed is after teardown, not the place to declare data.
  • Parent and child hook order (week 6): parent created first, but the child's mounted runs before the parent's mounted.
Your turn: 3. Order the hooks

A component has created, mounted, updated and beforeDestroy, each logging its name. It loads, the user clicks a button that changes a displayed value, then the component is removed. Write the console output.

Need a hint?

Which of these run once, and which after a change?

Show the full solution

created, mounted, (click) updated, (removal) beforeDestroy. If the changed value is not used in the template, updated would not print.

Your turn: 4. Reactivity fix

data: { cart: {} } and a click does this.cart[item] = 1 but the page does not change. Give two fixes.

Need a hint?

Think about adding a new property to an existing object.

Show the full solution

this.$set(this.cart, item, 1) (or Vue.set), or replace the object: this.cart = { ...this.cart, [item]: 1 }.

Idea 9: Templates in HTML and in strings

Two ways to give an instance a template:

<!-- 1. In the page: Vue reads the HTML inside #app (in-DOM template) -->
<div id="app"><p>{{ message }}</p></div>
<script>new Vue({ el: "#app", data: { message: "Hi" } });</script>

<!-- 2. As a string: the template option replaces what is inside #app -->
<div id="app"></div>
<script>
  new Vue({ el: "#app", data: { message: "Hi" }, template: "<p>{{ message }}</p>" });
</script>

The template option needs the full build of Vue (the CDN default, which includes the compiler). The .vue files of week 6 are compiled in advance, so they need no compiler at run time. One extra rule in in-DOM templates: HTML is case-insensitive, so write <my-comp> (kebab-case) and player-name="...", not <MyComp> or playerName.

Exam radar

Exam radar

What the real papers test. The evidence file counts questions that match this week's keywords: about 258 of 848 Quiz 1, 181 of 608 Quiz 2 and 300 of 1471 End Term questions matched. That is a loose keyword match (Vue words also catch router, component and API questions), but about 180 distinct questions were found, and the Vue ones are the largest block in the MAD2 papers. Recurring shapes:

  • Lifecycle prediction (very common). Hooks that modify message and ETA (answer: Created, Mounting, Mounted and ETA 7), beforeCreate printing undefined, created and mounted with an async fetch, "will beforeUpdate run?" when the changed value is not in the template.
  • Watch versus computed. A watcher on counter logging old and new values; "which statements about watchers are true".
  • Class binding. Object, array and mixed forms; which statements are true.
  • Template rendering with components and slots (see week 6): global versus local registration, default props, what happens to content placed inside a component that has no slot.
  • v-if and v-show with a loading message that disappears after a click or a timer.
  • True/false statements on .vue files (they contain template, script and style, and must be compiled by Vue CLI or Vite; they cannot be loaded straight in a script tag).
Watch out - real exam trap
  • Predicting the console: write the hook order first, then apply each statement. Do not forget that beforeCreate sees no data.
  • "Will it re-render?" Only data the template reads triggers beforeUpdate and updated.
  • {{ }} renders undefined as empty, but {{ obj.a.b }} with obj.a undefined is an error.
  • Watchers see raw data; they do not see computed values changing.
  • Two bindings can look different but give the same HTML: always evaluate each to its final class string.

Quick quiz

Quick quiz

A Vue 2 instance has data: { message: "", ETA: 10 }. Each of beforeCreate, created, beforeMount and mounted appends text to message ("Creating,", "Created,", "Mounting,", "Mounted") and subtracts 1 from ETA. The template shows both. What is rendered?

  • Status: Creating,Created,Mounting,Mounted ETA: 6
  • Status: Created,Mounting,Mounted ETA: 7
  • Status: Mounting,Mounted ETA: 8
  • Status: Mounted ETA: 9
Data is initialised after beforeCreate, which overwrites what that hook wrote, so only three hooks count.

What does console.log(this.message) print inside beforeCreate, when message is declared in data?

  • the initial value
  • undefined
  • null
  • an error is thrown
Data is set up between beforeCreate and created, so it does not exist yet.

A button runs this.count++, count is declared in data but is not used in the template, and the instance has a beforeUpdate hook that logs. What happens on click?

  • beforeUpdate is logged
  • nothing is logged
  • an error is thrown
  • the whole page reloads
No data used by the render changed, so there is no re-render and no update hooks.

Select all TRUE statements about Vue 2. (select all that apply)

  • A watcher is used to run side effects when a property changes.
  • A computed property is recomputed on every access.
  • v-show keeps the element in the DOM and toggles display.
  • this.arr[0] = 5 is a reactive update.
Computed values are cached; direct index assignment is not tracked in Vue 2.

With data: { classObj: { is_active: true, completed: false }, status: true }, what class does <div :class="[classObj.is_active ? 'is_active' : '', { completed: status }]"> render?

  • is_active only
  • completed only
  • is_active completed
  • no class
The array adds is_active from the ternary and completed because status is true.

You must call the server every time a search box value changes. The best Vue feature is...

  • computed property
  • v-show
  • a watcher on the search text
  • a static method called in the template
Calling the server is a side effect, not a derived value.

Which hook is the right place to stop a setInterval started in mounted?

  • created
  • mounted
  • beforeDestroy
  • beforeMount
Clean up just before the component is torn down.

Viva: say it out loud

Viva: say it out loud
What is Vue and what does reactive mean?

Vue is a JavaScript framework that builds the page from a template and your data. Reactive means that when a data property changes, Vue automatically updates exactly the parts of the page that used it. I do not change the DOM by hand.

Explain the difference between computed, methods and watch.

A computed property is a cached value derived from other data; it only recomputes when its dependencies change. A method runs every time it is called. A watcher runs a side effect when a chosen property changes, like calling an API. I use computed for display values, methods for event handlers, watch for side effects.

v-if or v-show: which did you use and why?

v-if removes the element from the DOM when false and supports v-else; I use it for loading and error states that change rarely. v-show only toggles display: none, so it is cheaper when something toggles often, for example a dropdown. In my project I used v-if for the loading message.

Why do we need a key in v-for?

The key is a unique, stable id per item. It lets Vue match old and new elements correctly when the list is reordered or items are removed, so the right DOM node and its state are reused. I use the database id, not the array index.

Walk me through the Vue lifecycle. Where do you call your API?

beforeCreate, created, beforeMount, mounted, then beforeUpdate and updated on each change, and finally beforeDestroy and destroyed. I call the API in created (or mounted) because data and methods exist by then. I clear timers in beforeDestroy.

I add a new field to an object in your data and the page does not update. Why?

Vue 2 makes only the properties present at creation reactive, with getters and setters. A property added later has none, so Vue does not notice it. I fix it with this.$set(obj, key, value) or by replacing the whole object. In Vue 3 this limitation is gone because it uses Proxy.

How does v-model work?

It is two-way binding on a form control. It binds the input's value to a data property and listens to the input event to write back. So typing updates data and changing data updates the box. Modifiers: .trim, .number and .lazy.

What is nextTick for?

Vue updates the DOM asynchronously after data changes. If I need to read the updated DOM, for example to focus an element that has just appeared, I put the code in this.$nextTick(() => {...}), which runs after the update.

Recap

Remember this
  • Vue renders a template from data. Add it with a CDN script and new Vue({ el, data, methods, computed, watch }).
  • {{ }} prints one expression as text; v-bind (:) sets attributes; v-on (@) handles events with .prevent, .stop, .once and key modifiers; v-model is two-way with .trim, .number, .lazy.
  • v-if adds or removes the element; v-show only hides it. v-for needs a unique :key; over an object the order is (value, key, index).
  • :class and :style take objects, arrays and ternaries.
  • Computed is cached and returns a value; methods run each time; watch performs side effects.
  • Vue 2 reactivity: declare data up front, use Vue.set for new properties and index changes, use array methods. Updates are batched, so use nextTick.
  • Lifecycle: beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed. created has data but no DOM; mounted has the DOM; beforeDestroy is for cleanup.
Code and facts to keep
  • CDN: <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
  • Create: new Vue({ el: "#app", data: {...}, methods: {...}, computed: {...}, watch: {...} })
  • :attr="x" is v-bind:attr; @click="f" is v-on:click; @submit.prevent, @click.stop, @click.once, @keyup.enter
  • v-model.trim, v-model.number, v-model.lazy
  • v-for="(item, i) in list", (value, key, i) in obj, n in 5; always :key
  • :class="{ active: isOn }", :class="[a, { b: flag }]", :style="{ color: c }"
  • Caveat fixes: Vue.set(obj, key, val), this.$set(arr, i, val), arr.splice(i, 1, val)
  • this.$nextTick(fn), this.$refs.name
  • Hook order: beforeCreate, created, beforeMount, mounted, (beforeUpdate, updated)*, beforeDestroy, destroyed
  • Vue 3: createApp, Proxy reactivity, beforeUnmount and unmounted
Connected weeks (learn them together)

Practise with real exam questions

These come straight from past papers (with the real figures and the official answer key). Open 60 real questions on this week   All Quiz 1 papers