Modern Web Development
A Complete Beginner's Guide
JavaScript · [Link] · Frontend Architecture
Based on the Modern Application Development — II Course Notes
Weeks 1 – 4 · Complete Expanded Edition
Preface
Welcome to this beginner's guide to modern web application development. This book is
based on four weeks of course notes from the Modern Application Development — II
curriculum and has been expanded significantly to give you, the complete beginner, all the
context, explanation, and examples you need to genuinely understand each concept — not
just skim bullet points.
The notes themselves are concise by design — they are lecture slide summaries. But
learning to code requires depth. You need to know why something works the way it does,
see multiple examples of the same concept, and understand common mistakes before you
make them. That is precisely what this book aims to provide.
Who is this book for?
This book assumes you have a basic understanding of HTML and CSS — you know what a
tag is, and you have probably written a simple web page. You may have heard of JavaScript
but never written it seriously. You definitely have not used [Link] before. If that describes
you, you are in exactly the right place.
What will you learn?
• The history and foundations of JavaScript
• JavaScript syntax, data types, functions, and scope
• Working with collections: arrays, maps, and sets
• How JavaScript modules and the npm ecosystem work
• Asynchronous programming: callbacks, Promises, and async/await
• Frontend architecture concepts: state, reactivity, and declarative UI
• The [Link] framework: directives, components, and the MVVM pattern
• How Vue's reactivity system works under the hood
Each chapter builds on the previous one. Read them in order the first time, then use the
detailed table of contents as a reference when you need to revisit specific concepts.
Note: All code examples in this book can be run directly in your browser's developer console
(press F12), in [Link] on your computer, or on free online sandboxes such as CodePen or
JSFiddle.
Modern Web Development — A Complete Beginner's Guide Page 3
Chapter 1
JavaScript — Origins and the
Modern Web
1.1 What Is JavaScript?
JavaScript is the programming language of the web. Today it runs in every major browser, on
servers via [Link], in mobile apps, in desktop applications, and even on microcontrollers.
But its origins are humble — almost comically so.
In 1995, a programmer named Brendan Eich at Netscape Communications was given ten
days to create a scripting language for the Netscape Navigator browser. The resulting
language was originally called Mocha, then LiveScript, and finally JavaScript — largely as a
marketing decision to ride the wave of Java's popularity at the time. Despite the name,
JavaScript and Java are entirely different languages with very different design philosophies.
JavaScript was designed as a "glue" language — a way to stick together components
written in other languages. It was not meant for large-scale application development. Early
JavaScript programs were typically just a few dozen lines that added small interactive
touches to web pages: form validation, pop-up alerts, simple animations.
1.2 The Browser Wars and Early Chaos
When Microsoft introduced Internet Explorer and released their own version of JavaScript
(called JScript), a period known as the 'browser wars' began. Each browser implemented the
language slightly differently. Web developers had to write multiple versions of the same code
— one for Netscape, one for Internet Explorer — and constantly check 'does this work in that
browser?'
This era left lasting scars on the language. JavaScript inherited several design quirks that
persist today:
• Silent failures: JavaScript would often fail without telling you why. An error might
produce undefined instead of crashing, making bugs very hard to find.
• Loose type system: JavaScript would automatically convert values from one type to
another (called 'coercion'), sometimes in very surprising ways. For example, the string '5'
plus the number 3 gives '53', not 8.
• Global scope by default: Variables declared without a keyword became global —
visible everywhere, easily overwritten by accident.
Modern Web Development — A Complete Beginner's Guide Page 4
• Inconsistent behaviour: Things like how 'this' worked, how closures behaved, and how
dates were parsed all had browser-specific quirks.
1.3 Standardization: ECMAScript
To end the chaos, Netscape submitted JavaScript to ECMA International (European
Computer Manufacturers Association) for standardization in 1996. The resulting standard
was named ECMAScript (abbreviated ES) to avoid trademark disputes with Sun
Microsystems, who owned the Java trademark.
In practice, the standard is called ECMAScript and the implementation you write and run is
called JavaScript. The two words are often used interchangeably. Major versions you will
hear about:
<b>Version</b> <b>Year</b> <b>Key additions</b>
ES3 1999 Regular expressions, try/catch, many string methods
ES5 2009 Strict mode, JSON support, [Link]/map/filter
ES6 2015 let/const, arrow functions, classes, modules, Promises, template literals
ES2017 2017 async/await
ES2020+ 2020+ Optional chaining (?.), nullish coalescing (??), and more
ES6 (2015) was a turning point — it modernized the language so dramatically that many
developers consider pre-ES6 and post-ES6 JavaScript to be almost different languages. This
book focuses on ES6 and later.
1.4 The Ajax Revolution (2005)
For a decade, JavaScript remained a curiosity — useful for minor touches but not taken
seriously. That changed in 2005 when Google shipped Google Maps and Google Suggest.
These applications did something revolutionary: they loaded data without refreshing the
entire page. Previously, every user action that needed new data required a full page reload
— the browser would go blank, request a new HTML document, and redraw everything.
Google Maps let you pan around a map by dragging, smoothly loading new map tiles in the
background.
The technique was named Ajax — Asynchronous JavaScript and XML — by Jesse James
Garrett in a 2005 essay. Ajax turned JavaScript from a toy into a serious platform for building
applications that felt as responsive as desktop software. The era of 'web applications' had
begun.
1.5 The [Link] Era
Modern Web Development — A Complete Beginner's Guide Page 5
In 2009, Ryan Dahl released [Link]: a runtime that lets you execute JavaScript outside the
browser, directly on your operating system like Python or Ruby. This opened JavaScript to
server-side development, build tools, command-line utilities, and much more.
[Link] is important to us for several reasons. First, it ships with npm (Node Package
Manager), the world's largest software registry. Second, many frontend build tools (Vue CLI,
Vite, Webpack) run on [Link]. Third, you can now write both your frontend and backend in
the same language.
1.6 JavaScript's Quirks — and How to Work with
Them
JavaScript was designed with ease of use as a priority. This means it fails silently rather
than crashing loudly. While this sounds nice, it makes bugs notoriously hard to find. Here are
the main quirks every beginner must know:
Automatic Semicolon Insertion (ASI)
JavaScript can automatically add semicolons at the end of lines, so technically you don't
need to type them. However, ASI has surprising edge cases. Best practice is to always write
your semicolons explicitly.
Type Coercion
JavaScript will automatically convert values between types when you use operators like +,
==, or if(). For example:
// Surprising coercions
"5" + 3 // "53" (number becomes string)
"5" - 3 // 2 (string becomes number)
"5" == 5 // true (loose equality coerces)
"5" === 5 // false (strict equality — no coercion)
null == undefined // true
null === undefined // false
Note: Always use === (triple equals) for comparisons in JavaScript. The double-equals ==
version performs type coercion and can give deeply confusing results.
Strict Mode
You can opt into a stricter, more predictable version of JavaScript by writing 'use strict'; at the
top of a file or function. Strict mode disables several dangerous features (like creating global
variables by accident) and makes debugging easier.
"use strict";
Modern Web Development — A Complete Beginner's Guide Page 6
// Without strict mode, this silently creates a global variable:
// x = 5;
// With strict mode, this throws a ReferenceError:
x = 5; // ReferenceError: x is not defined
Modern Web Development — A Complete Beginner's Guide Page 7
Chapter 2
JavaScript Syntax — The Building
Blocks
2.1 Writing and Running JavaScript
There are several ways to run JavaScript code. As a beginner, the easiest is your browser's
developer console:
• Chrome/Edge: Press F12, then click the 'Console' tab.
• Firefox: Press F12, then click 'Console'.
• [Link]: Install from [Link], then run `node` in your terminal.
When JavaScript is embedded in a web page, it lives inside <script> tags:
<!DOCTYPE html>
<html>
<body>
<p id="greeting"></p>
<script>
// This JavaScript runs when the page loads
[Link]("greeting").textContent = "Hello, World!";
</script>
</body>
</html>
Alternatively — and this is the preferred modern approach — you link an external .js file:
<script src="[Link]"></script>
2.2 Comments
Comments are notes in your code that JavaScript ignores. They are essential for explaining
your intentions to future readers (including yourself).
// This is a single-line comment
/*
This is a multi-line comment.
Everything between /* and */ is ignored by JavaScript.
Modern Web Development — A Complete Beginner's Guide Page 8
*/
let x = 5; // Inline comment after code
2.3 Statements and Expressions
Understanding the difference between a statement and an expression is fundamental.
Expressions
An expression is any piece of code that produces a value. You can use an expression
anywhere a value is expected:
5 + 3 // produces 8
"Hello" // produces the string "Hello"
x * 2 // produces double the value of x
[Link](16) // produces 4
true // produces the boolean true
Statements
A statement is a complete instruction that does something — declares a variable, runs a
loop, calls a function, etc.
let name = "Alice"; // variable declaration statement
if (name === "Alice") { } // conditional statement
for (let i = 0; i < 5; i++) { } // loop statement
[Link]("Hello"); // expression statement
2.4 Data Types
JavaScript has seven primitive data types and one compound type (Object).
Primitive Types
<b>Type</b> <b>Example</b> <b>Description</b>
number 42, 3.14, -7 All numbers — integers and decimals — are the same type
string "hello", 'world' Text, enclosed in quotes (single, double, or backtick)
boolean true, false Logical true/false
null null Intentionally empty value
undefined undefined Value not yet assigned
bigint 9007199254740993n Very large integers (ES2020)
Modern Web Development — A Complete Beginner's Guide Page 9
symbol Symbol('id') Unique identifier (advanced — rarely needed by beginners)
The Number Type
Unlike most languages, JavaScript has only one number type for both integers and
floating-point numbers. This is convenient but has one important limitation: large integers
cannot be represented precisely.
let age = 25;
let price = 19.99;
let temperature = -3.5;
// Special number values
Infinity // e.g. 1/0
-Infinity // e.g. -1/0
NaN // "Not a Number" e.g. 0/0 or parseInt("hello")
// Check for NaN
isNaN(NaN) // true
[Link](NaN) // true (safer version)
Strings
Strings represent text. You can use single quotes, double quotes, or backticks (template
literals). Template literals are the most powerful because they let you embed expressions
directly inside the string:
let single = 'Hello';
let double = "World";
// Template literals (backtick strings)
let name = "Alice";
let age = 30;
let greeting = `Hello, ${name}! You are ${age} years old.`;
// greeting = "Hello, Alice! You are 30 years old."
// Multi-line template literal
let poem = `Roses are red,
Violets are blue,
JavaScript is interesting,
And so are you.`;
null vs undefined
Both null and undefined represent the absence of a value, but with different intentions:
Modern Web Development — A Complete Beginner's Guide Page 10
• undefined: The variable exists but has not been given a value yet. JavaScript sets this
automatically.
• null: You as the programmer are explicitly saying: "this variable intentionally has no
value".
let x; // x is undefined (declared but not initialized)
let y = null; // y is explicitly set to "nothing"
[Link](x); // undefined
[Link](y); // null
2.5 Variables and Scoping
Variables are named containers for storing data. In modern JavaScript we use two keywords:
let and const. You may also see the old keyword var in older code — avoid it in new code.
let — Mutable Variables
let score = 0;
score = score + 10; // OK — let allows reassignment
score += 10; // Same thing, shorter syntax
[Link](score); // 20
const — Immutable Bindings
const PI = 3.14159;
// PI = 3; // ERROR — cannot reassign a const
// IMPORTANT: const for objects/arrays means the binding is fixed,
// but the contents CAN still change:
const person = { name: 'Alice', age: 30 };
[Link] = 31; // OK! We're changing the object, not rebinding 'person'
// person = {}; // ERROR — cannot rebind
Scope — Where Variables Live
A variable's scope is the region of code where it is visible and can be accessed. JavaScript
uses block scope for let and const, meaning a variable declared inside a pair of {} curly
braces is only accessible inside those braces:
if (true) {
let blockVar = 'I live inside the if block';
[Link](blockVar); // works fine
}
// [Link](blockVar); // ERROR: blockVar is not defined
Modern Web Development — A Complete Beginner's Guide Page 11
for (let i = 0; i < 3; i++) {
[Link](i); // 0, 1, 2
}
// [Link](i); // ERROR: i is not defined outside the loop
Warning: The old 'var' keyword uses function scope, not block scope. This means a var
declared inside an if block is accessible outside it. This was a common source of bugs. Always
use let or const.
2.6 Operators
Arithmetic Operators
let a = 10, b = 3;
a + b // 13 (addition)
a - b // 7 (subtraction)
a * b // 30 (multiplication)
a / b // 3.333... (division)
a % b // 1 (modulo — remainder after division)
a ** b // 1000 (exponentiation: 10 to the power 3)
// Increment and decrement
let count = 5;
count++; // count is now 6
count--; // count is now 5 again
count += 3; // count is now 8
Comparison Operators
// Always prefer === over ==
5 === 5 // true
5 === '5' // false (different types)
5 !== 5 // false
5 !== '5' // true
5 > 3 // true
5 >= 5 // true
3 < 5 // true
3 <= 3 // true
Logical Operators
true && true // true (AND: both must be true)
Modern Web Development — A Complete Beginner's Guide Page 12
true && false // false
true || false // true (OR: at least one must be true)
false || false // false
!true // false (NOT: reverses the boolean)
!false // true
2.7 Control Flow
if / else if / else
Conditional statements let you run different code depending on a condition:
let temperature = 22;
if (temperature < 10) {
[Link]('Wear a coat!');
} else if (temperature < 20) {
[Link]('Bring a jacket.');
} else {
[Link]('Nice weather!');
}
// Output: 'Nice weather!'
for Loop
// Classic for loop
for (let i = 0; i < 5; i++) {
[Link](i); // prints 0, 1, 2, 3, 4
}
// for...of: iterate over values in an array
const fruits = ['apple', 'banana', 'cherry'];
for (const fruit of fruits) {
[Link](fruit); // apple, banana, cherry
}
// for...in: iterate over keys of an object
const person = { name: 'Alice', age: 30 };
for (const key in person) {
[Link](key, person[key]); // name Alice, age 30
}
while Loop
Modern Web Development — A Complete Beginner's Guide Page 13
let count = 0;
while (count < 5) {
[Link](count);
count++;
}
// do...while: always runs at least once
let x = 10;
do {
[Link](x); // prints 10, even though condition is false
x++;
} while (x < 5);
switch
let day = 'Monday';
switch (day) {
case 'Monday':
[Link]('Start of the work week');
break;
case 'Friday':
[Link]('End of the work week');
break;
case 'Saturday':
case 'Sunday':
[Link]('Weekend!');
break;
default:
[Link]('Mid-week');
}
Note: The 'break' keyword is essential in switch statements. Without it, JavaScript will 'fall
through' and execute all subsequent cases, which is almost never what you want.
Modern Web Development — A Complete Beginner's Guide Page 14
Chapter 3
Functions — Reusable Blocks of
Logic
3.1 What Is a Function?
A function is a named, reusable block of code. Instead of writing the same logic repeatedly,
you write it once as a function and call it whenever needed. Functions are the primary
mechanism for organizing and reusing code.
Functions in JavaScript are first-class values — they can be stored in variables, passed as
arguments to other functions, and returned from functions. This is profoundly important and
distinguishes JavaScript from older languages.
3.2 Function Declarations
The traditional way to define a function uses the function keyword followed by a name:
function greet(name) {
return 'Hello, ' + name + '!';
}
// Calling the function
let message = greet('Alice');
[Link](message); // 'Hello, Alice!'
// You can call it multiple times with different arguments
[Link](greet('Bob')); // 'Hello, Bob!'
[Link](greet('World')); // 'Hello, World!'
The return statement sends a value back to wherever the function was called from. If there is
no return statement (or return is used without a value), the function returns undefined.
Function Hoisting
Function declarations are 'hoisted' — JavaScript moves them to the top of their scope before
executing. This means you can call a declared function before it appears in the code:
// This works! Function declarations are hoisted.
[Link](add(2, 3)); // 5
function add(x, y) {
Modern Web Development — A Complete Beginner's Guide Page 15
return x + y;
}
3.3 Function Expressions
A function can also be assigned to a variable as a function expression. These are NOT
hoisted:
let multiply = function(x, y) {
return x * y;
};
[Link](multiply(4, 5)); // 20
// The function can be anonymous (no name) when assigned to a variable
let greet = function(name) {
return `Hi, ${name}!`;
};
3.4 Arrow Functions
Introduced in ES6, arrow functions provide a shorter syntax for writing function expressions.
They are especially popular for short, one-line functions and callbacks.
// Traditional function expression
let double = function(x) {
return x * 2;
};
// Arrow function — equivalent
let double = (x) => {
return x * 2;
};
// Even shorter: if one parameter, no parentheses needed
let double = x => x * 2;
// Arrow function with multiple parameters
let add = (a, b) => a + b;
// Arrow function with multiple lines still uses {} and return
let complexCalc = (x, y) => {
let sum = x + y;
let product = x * y;
return sum + product;
Modern Web Development — A Complete Beginner's Guide Page 16
};
Note: Arrow functions and regular functions behave slightly differently in how they handle the
'this' keyword. For now, just know that both exist. The difference matters inside classes and
objects — we will cover it later.
3.5 Parameters and Arguments
Parameters are the variables listed in the function definition. Arguments are the actual
values passed when calling the function.
// parameters
// ↓↓↓
function greet(firstName, lastName) {
return `${firstName} ${lastName}`;
}
// arguments
// ↓↓↓
greet('Alice', 'Smith'); // 'Alice Smith'
Default Parameters (ES6)
function greet(name = 'World') {
return `Hello, ${name}!`;
}
greet('Alice'); // 'Hello, Alice!'
greet(); // 'Hello, World!' — uses default
Rest Parameters
When you don't know how many arguments a function will receive, use the rest parameter
syntax (...):
function sum(...numbers) {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
Modern Web Development — A Complete Beginner's Guide Page 17
3.6 Callbacks — Functions Passed to Functions
Because functions are first-class values in JavaScript, you can pass a function as an
argument to another function. The function that is passed in is called a callback function.
Callbacks are everywhere in JavaScript and are central to how asynchronous programming
works.
// A function that takes a callback
function doTwice(action) {
action();
action();
}
function sayHello() {
[Link]('Hello!');
}
doTwice(sayHello);
// Output:
// Hello!
// Hello!
// More commonly, callbacks are arrow functions passed inline:
doTwice(() => [Link]('Hi!'));
// Hi!
// Hi!
Array methods like map, filter, and forEach all use callbacks. We will explore these in depth in
the next chapter.
3.7 Closures
A closure is a function that remembers the variables from its enclosing scope even after that
scope has finished executing. This is one of JavaScript's most powerful and initially confusing
features.
function makeCounter() {
let count = 0; // this variable is 'closed over'
return function() {
count++;
return count;
};
}
Modern Web Development — A Complete Beginner's Guide Page 18
let counter = makeCounter();
[Link](counter()); // 1
[Link](counter()); // 2
[Link](counter()); // 3
// Each call to makeCounter() creates a SEPARATE count:
let counter2 = makeCounter();
[Link](counter2()); // 1 (its own count, starting from 0)
[Link](counter()); // 4 (continues from where it left off)
Closures are the mechanism behind module patterns, data privacy, and factory functions.
You will see them used extensively in [Link] internals.
Modern Web Development — A Complete Beginner's Guide Page 19
Chapter 4
The DOM — JavaScript Meets the
Browser
4.1 What Is the DOM?
When your browser loads an HTML page, it parses the HTML and creates an internal
tree-shaped representation of the document. This representation is called the Document
Object Model or DOM.
Each HTML element (like <div>, <p>, <button>) becomes a node in this tree. JavaScript can
read and modify these nodes — changing text, colours, styles, adding or removing elements,
responding to clicks — all without reloading the page. This is the foundation of all interactive
web pages.
<!-- Example HTML -->
<div id='app'>
<h1 id='title'>Hello</h1>
<p class='intro'>Welcome to my page.</p>
</div>
The DOM tree for this HTML looks like:
document
■■■ html
■■■ body
■■■ div#app
■■■ h1#title ('Hello')
■■■ [Link] ('Welcome to my page.')
4.2 Selecting Elements
// By ID — returns a single element (or null if not found)
const title = [Link]('title');
// By CSS selector — returns the FIRST matching element
const title = [Link]('#title');
const intro = [Link]('.intro');
const firstParagraph = [Link]('p');
Modern Web Development — A Complete Beginner's Guide Page 20
// By CSS selector — returns ALL matching elements as a NodeList
const allParagraphs = [Link]('p');
[Link](p => [Link]([Link]));
4.3 Reading and Changing Content
const title = [Link]('#title');
// Read the text content
[Link]([Link]); // 'Hello'
// Change the text
[Link] = 'Goodbye!';
// innerHTML lets you use HTML tags inside a string
[Link] = 'Hello World!';
// Warning: never use innerHTML with user-provided content
// (security risk — XSS attacks)
4.4 Changing Styles and Classes
const box = [Link]('.box');
// Direct style changes
[Link] = 'red';
[Link] = '24px';
[Link] = 'none'; // hide the element
[Link] = 'block'; // show it again
// Better: add/remove CSS classes
[Link]('highlighted');
[Link]('highlighted');
[Link]('highlighted'); // adds if absent, removes if present
[Link]('highlighted'); // true or false
4.5 Event Handling
Events are things that happen in the browser: a user clicks a button, presses a key, moves
the mouse, the page finishes loading, an AJAX request completes. You can write code that
responds to these events.
const button = [Link]('#myButton');
// Add an event listener
Modern Web Development — A Complete Beginner's Guide Page 21
[Link]('click', function() {
[Link]('Button was clicked!');
});
// With arrow function
[Link]('click', () => {
alert('You clicked me!');
});
// The event object carries information about what happened
[Link]('click', (event) => {
[Link]('Click position:', [Link], [Link]);
[Link]('Clicked element:', [Link]);
});
Common Events
<b>Event</b> <b>When it fires</b>
click User clicks an element
dblclick User double-clicks
mouseover Mouse cursor enters the element
mouseout Mouse cursor leaves the element
keydown A key is pressed down
keyup A key is released
input Value of <input> or <textarea> changes
change Value of <select> changes (or checkbox toggled)
submit A <form> is submitted
load Page or resource finishes loading
DOMContentLoaded HTML is parsed and DOM is ready (before images)
4.6 Creating and Removing Elements
// Create a new element
const newParagraph = [Link]('p');
[Link] = 'I was created by JavaScript!';
[Link]('dynamic-content');
// Append it to an existing element
const container = [Link]('#app');
Modern Web Development — A Complete Beginner's Guide Page 22
[Link](newParagraph);
// Insert before a specific element
const reference = [Link]('#title');
[Link](newParagraph, reference);
// Remove an element
[Link]();
// Remove a child element
[Link](reference);
Note: Modern frameworks like [Link] manage the DOM for you — you rarely write direct DOM
manipulation code when using Vue. However, understanding the DOM is essential for
understanding why frameworks exist and what they are doing under the hood.
Modern Web Development — A Complete Beginner's Guide Page 23
Chapter 5
Collections — Arrays, Objects,
Maps, and Sets
5.1 Arrays
An array is an ordered list of values. In JavaScript, arrays are flexible: they can hold values of
any type, and their length is not fixed.
// Creating arrays
let empty = [];
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, 'two', true, null, { name: 'Alice' }];
// Accessing elements (zero-indexed)
numbers[0] // 1 (first element)
numbers[2] // 3 (third element)
numbers[[Link] - 1] // 5 (last element)
// Modifying elements
numbers[0] = 10; // [10, 2, 3, 4, 5]
// Length
[Link] // 5
Essential Array Methods
let fruits = ['apple', 'banana', 'cherry'];
// Adding / removing
[Link]('date'); // add to end → ['apple','banana','cherry','date'
]
[Link](); // remove from end → returns 'date'
[Link]('avocado'); // add to start
[Link](); // remove from start
// Finding
[Link]('banana'); // 1 (index of element, or -1 if not found)
[Link]('cherry'); // true
[Link](f => [Link]('b')); // 'banana'
[Link](f => f === 'cherry'); // 2
Modern Web Development — A Complete Beginner's Guide Page 24
// Slicing and splicing
[Link](1, 3); // ['banana', 'cherry'] (non-destructive)
[Link](1, 1); // removes 1 element at index 1 (destructive)
// Joining and splitting
[Link](', '); // 'apple, banana, cherry'
'a,b,c'.split(','); // ['a', 'b', 'c']
5.2 Functional Array Methods
These methods take a callback function and apply it to every element. They are the
cornerstone of modern JavaScript programming.
map — Transform Every Element
map creates a new array by applying a function to every element:
const numbers = [1, 2, 3, 4, 5];
const doubled = [Link](n => n * 2);
// [2, 4, 6, 8, 10]
const names = ['alice', 'bob', 'charlie'];
const upperNames = [Link](name => [Link]());
// ['ALICE', 'BOB', 'CHARLIE']
// With objects:
const people = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
];
const names = [Link](person => [Link]);
// ['Alice', 'Bob']
filter — Keep Only Matching Elements
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evens = [Link](n => n % 2 === 0);
// [2, 4, 6, 8, 10]
const people = [
{ name: 'Alice', age: 17 },
{ name: 'Bob', age: 21 },
{ name: 'Carol', age: 30 },
];
Modern Web Development — A Complete Beginner's Guide Page 25
const adults = [Link](p => [Link] >= 18);
// [{ name: 'Bob', age: 21 }, { name: 'Carol', age: 30 }]
reduce — Combine All Elements into One Value
const numbers = [1, 2, 3, 4, 5];
// Sum all numbers
const total = [Link]((accumulator, current) => {
return accumulator + current;
}, 0); // 0 is the starting value
// total = 15
// Shorter:
const total = [Link]((acc, curr) => acc + curr, 0);
forEach — Do Something with Each Element
const fruits = ['apple', 'banana', 'cherry'];
[Link]((fruit, index) => {
[Link](`${index + 1}. ${fruit}`);
});
// 1. apple
// 2. banana
// 3. cherry
// Note: forEach does NOT return a new array
// Use map if you need a transformed array
Chaining Methods
Because map and filter return new arrays, you can chain them together:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(n => n % 2 === 0) // [2, 4, 6, 8, 10]
.map(n => n * n) // [4, 16, 36, 64, 100]
.reduce((a, b) => a + b, 0); // 220
[Link](result); // 220
5.3 Destructuring
Destructuring is a shorthand syntax for extracting values from arrays or objects into variables:
Modern Web Development — A Complete Beginner's Guide Page 26
Array Destructuring
const coordinates = [10, 20, 30];
// Without destructuring:
const x = coordinates[0];
const y = coordinates[1];
const z = coordinates[2];
// With destructuring (much cleaner):
const [x, y, z] = coordinates;
[Link](x, y, z); // 10 20 30
// Skip elements with commas:
const [first, , third] = coordinates; // x=10, z=30
// Rest operator collects remaining elements:
const [head, ...tail] = [1, 2, 3, 4, 5];
// head = 1, tail = [2, 3, 4, 5]
Object Destructuring
const person = { name: 'Alice', age: 30, city: 'Paris' };
// Without destructuring:
const name = [Link];
const age = [Link];
// With destructuring:
const { name, age } = person;
[Link](name, age); // Alice 30
// Rename while destructuring:
const { name: fullName, age: years } = person;
// fullName = 'Alice', years = 30
// In function parameters (very common in [Link]):
function greet({ name, age }) {
return `${name} is ${age} years old`;
}
greet(person); // 'Alice is 30 years old'
5.4 Spread and Rest Operators
The ... operator serves two purposes depending on context: spreading an iterable into
individual elements, or resting individual elements into an array.
// SPREAD: expand array into individual items
Modern Web Development — A Complete Beginner's Guide Page 27
const a = [1, 2, 3];
const b = [4, 5, 6];
const combined = [...a, ...b]; // [1, 2, 3, 4, 5, 6]
// Copy an array (avoids shared reference):
const copy = [...a]; // [1, 2, 3]
// Spread into function arguments:
[Link](...a); // 3 (same as [Link](1, 2, 3))
// SPREAD on objects (ES2018):
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3 }
// Override a property:
const updated = { ...obj1, b: 99 }; // { a: 1, b: 99 }
5.5 Objects — JavaScript's Workhorse
Objects are collections of key-value pairs. They represent real-world entities and data
structures. Nearly everything in JavaScript is (or behaves like) an object.
// Object literal syntax
const person = {
name: 'Alice',
age: 30,
isStudent: false,
address: { // nested object
city: 'Paris',
country: 'France'
},
greet() { // method (function as property)
return `Hi, I'm ${[Link]}`;
}
};
// Accessing properties
[Link] // 'Alice' (dot notation)
person['age'] // 30 (bracket notation)
[Link] // 'Paris'
[Link]() // 'Hi, I'm Alice'
// Adding and deleting properties
[Link] = 'alice@[Link]';
Modern Web Development — A Complete Beginner's Guide Page 28
delete [Link];
// Get all keys, values, or entries
[Link](person) // ['name', 'age', 'address', 'greet', 'email']
[Link](person) // ['Alice', 30, {...}, fn, 'alice@...']
[Link](person) // [['name','Alice'], ['age',30], ...]
5.6 Maps and Sets
Map — A True Dictionary
While objects can be used as dictionaries, the Map type is designed specifically for this
purpose. Maps allow any type as a key (not just strings), and they maintain insertion order
reliably.
const map = new Map();
[Link]('name', 'Alice');
[Link](42, 'the answer');
[Link]({ id: 1 }, 'an object as key');
[Link]('name'); // 'Alice'
[Link]('name'); // true
[Link]; // 3
[Link]('name');
// Iterating
for (const [key, value] of map) {
[Link](key, '->', value);
}
Set — A Collection of Unique Values
const set = new Set([1, 2, 3, 2, 1]); // duplicates are removed
[Link]([...set]); // [1, 2, 3]
[Link](4);
[Link](3); // true
[Link](2);
[Link]; // 3
// Remove duplicates from an array:
const arr = [1, 1, 2, 3, 3, 4];
const unique = [...new Set(arr)]; // [1, 2, 3, 4]
Modern Web Development — A Complete Beginner's Guide Page 29
Chapter 6
Modules, npm, and Asynchronous
JavaScript
6.1 Why Modules?
As applications grow larger, putting all your JavaScript in one file becomes unmanageable.
Modules let you split code into separate files, each with its own scope. This prevents naming
conflicts and makes code easier to maintain and test.
Imagine building a house: you don't mix plumbing and electrical in the same blueprint.
Modules are your blueprints — each one handles one concern.
6.2 A Brief History of JavaScript Modules
Because JavaScript was originally just a browser scripting language, it had no module
system. Over time, several approaches emerged:
• Script tags: Simply include multiple <script> tags in HTML. All files share the same
global scope — everything can see everything else. This does not scale.
• CommonJS (2009): Introduced require() and [Link] for server-side JavaScript
([Link]). Loads modules synchronously.
• AMD (Asynchronous Module Definition): Designed for browsers, loads modules
asynchronously using define() callbacks. Never became mainstream.
• ES6 Modules (2015): The official standard, using import and export keywords. Works in
both browsers and [Link]. This is what you should use today.
6.3 ES6 Modules — import and export
Named Exports
// [Link] — exporting named items
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
Modern Web Development — A Complete Beginner's Guide Page 30
// [Link] — importing named items
import { PI, add, multiply } from './[Link]';
[Link](PI); // 3.14159
[Link](add(2, 3)); // 5
[Link](multiply(4, 5)); // 20
// Import with alias
import { add as sum } from './[Link]';
sum(2, 3); // 5
// Import everything under a namespace
import * as math from './[Link]';
[Link](2, 3); // 5
Default Exports
Each module can have one default export. Default exports are useful when a module has a
single primary thing to export:
// [Link]
export default function greet(name) {
return `Hello, ${name}!`;
}
// [Link]
import greet from './[Link]'; // no curly braces for default
greet('Alice'); // 'Hello, Alice!'
// You can name it anything:
import sayHello from './[Link]';
sayHello('Bob');
6.4 npm — The Node Package Manager
npm is the world's largest software registry. It hosts over two million packages — reusable
pieces of code published by developers around the world. Using npm means you don't have
to write everything from scratch.
[Link] — Your Project's Manifest
Every npm project has a [Link] file that describes the project and lists its
dependencies:
{
Modern Web Development — A Complete Beginner's Guide Page 31
"name": "my-web-app",
"version": "1.0.0",
"description": "A simple web application",
"main": "[Link]",
"scripts": {
"start": "node [Link]",
"dev": "vite"
},
"dependencies": {
"vue": "^3.3.0"
},
"devDependencies": {
"vite": "^4.0.0"
}
}
Common npm Commands
<b>Command</b> <b>What it does</b>
npm init Create a new [Link] interactively
npm install Install all dependencies listed in [Link]
npm install vue Install the 'vue' package and add to dependencies
npm install -D viteInstall 'vite' as a dev dependency
npm run dev Run the 'dev' script from [Link]
npm update Update all packages to their latest allowed versions
npm uninstall vue Remove a package
6.5 Asynchronous JavaScript
One of JavaScript's most important features is its ability to handle long-running tasks
(network requests, file reads) without freezing the browser. This is called asynchronous
programming.
The Event Loop
JavaScript is single-threaded — it can only do one thing at a time. But browsers have
additional threads for things like network requests. When a network request completes, the
result is placed in a task queue. The event loop watches the call stack: when it becomes
empty, it picks the next task from the queue.
Modern Web Development — A Complete Beginner's Guide Page 32
This means: while waiting for a network response, JavaScript can continue running other
code. When the response arrives, it processes it.
Callbacks (Old-Style Async)
// Simulating async with setTimeout
[Link]('Start');
setTimeout(() => {
[Link]('This runs after 2 seconds');
}, 2000); // 2000 milliseconds = 2 seconds
[Link]('End');
// Output order:
// Start
// End
// (2 seconds later) This runs after 2 seconds
Callbacks work, but nesting them leads to 'callback hell' — deeply nested code that is hard to
read and debug:
// Callback hell — hard to read and maintain
fetchUser(userId, (user) => {
fetchPosts([Link], (posts) => {
fetchComments(posts[0].id, (comments) => {
fetchLikes(comments[0].id, (likes) => {
// We are 4 levels deep already...
});
});
});
});
Promises (Modern Async)
A Promise represents a value that will be available in the future. It can be in one of three
states: pending, fulfilled, or rejected.
// Creating a Promise
const fetchData = new Promise((resolve, reject) => {
// Simulate a network request
setTimeout(() => {
const success = true;
if (success) {
resolve({ name: 'Alice', age: 30 }); // fulfilled
Modern Web Development — A Complete Beginner's Guide Page 33
} else {
reject(new Error('Network failed')); // rejected
}
}, 1000);
});
// Consuming a Promise with .then() and .catch()
fetchData
.then(data => {
[Link]('Got data:', [Link]);
})
.catch(error => {
[Link]('Error:', [Link]);
});
async/await (Best Modern Approach)
The async/await syntax lets you write asynchronous code that looks like synchronous
code. It is built on Promises but is much easier to read and write:
// Mark a function as async
async function loadUserData(userId) {
try {
// 'await' pauses execution until the Promise resolves
const user = await fetch(`/api/users/${userId}`);
const userData = await [Link]();
const posts = await fetch(`/api/posts?userId=${userId}`);
const postsData = await [Link]();
return { user: userData, posts: postsData };
} catch (error) {
[Link]('Failed to load data:', error);
}
}
// Call an async function
loadUserData(123).then(data => [Link](data));
// Or inside another async function:
async function main() {
const data = await loadUserData(123);
[Link](data);
}
Modern Web Development — A Complete Beginner's Guide Page 34
6.6 JSON
JSON (JavaScript Object Notation) is a text format for representing structured data. It is the
standard format for sending data between a client and server in modern web applications.
JSON looks almost identical to JavaScript object literals, but with a few rules:
• Keys must be in double quotes
• Values can be: strings (double-quoted), numbers, booleans, null, arrays, or objects
• No functions, no undefined, no comments
// Valid JSON
{
"name": "Alice",
"age": 30,
"isStudent": false,
"scores": [95, 87, 92],
"address": {
"city": "Paris",
"country": "France"
}
}
// Converting JavaScript to JSON string
const person = { name: 'Alice', age: 30 };
const jsonString = [Link](person);
// '{"name":"Alice","age":30}'
// Pretty-printed:
[Link](person, null, 2);
// {
// "name": "Alice",
// "age": 30
// }
// Parsing JSON string back to JavaScript object
const jsonStr = '{"name":"Alice","age":30}';
const obj = [Link](jsonStr);
[Link]; // 'Alice'
Modern Web Development — A Complete Beginner's Guide Page 35
Chapter 7
Frontend Architecture — State,
Reactivity, and the Declarative
Model
7.1 What Exactly Is the Frontend?
The frontend is the part of an application that a user sees and interacts with. In a web
application, the frontend runs in the user's browser. It is responsible for:
• Displaying data in a way that is readable and attractive
• Collecting input from the user (forms, clicks, gestures)
• Sending requests to the backend API and displaying responses
• Managing what the user sees as they interact with the app
Crucially, the frontend should:
• Avoid complex business logic — this belongs in the backend. For example, the
frontend should not calculate prices or validate business rules.
• Not store permanent data — data lives in the backend database. The frontend only
holds data temporarily to display it.
• Be responsive — no long pauses or lags. If data is loading, show a spinner, not a blank
screen.
• Be adaptive — work on mobile screens, tablets, and desktops.
7.2 Imperative vs Declarative Programming
There are two fundamentally different ways to think about building UIs:
Imperative Style — How to do it
Imperative programming means describing step-by-step instructions for achieving a result.
jQuery and vanilla DOM manipulation are imperative:
// Imperative: manually update every element that needs to change
function loginUser(user) {
[Link]('username').textContent = [Link];
[Link]('loginBtn').[Link] = 'none';
[Link]('logoutBtn').[Link] = 'block';
Modern Web Development — A Complete Beginner's Guide Page 36
[Link]('greeting').textContent = `Hello, ${[Link]}!`;
[Link]('.course-list').innerHTML = generateCourseHTML(user
.courses);
[Link]('body').className = [Link];
// ... and so on
}
This works, but it is fragile and verbose. Every change to the app requires you to carefully
track every element that might need updating.
Declarative Style — What to show
Declarative programming means describing what the UI should look like given the current
state. The framework figures out how to make it so:
// Declarative: just describe what the UI should look like
// The framework updates the DOM automatically
// In [Link]:
// <span>{{ [Link] }}</span>
// <button v-if="!loggedIn">Login</button>
// <button v-if="loggedIn">Logout</button>
// <li v-for="course in [Link]">{{ [Link] }}</li>
// When user data changes, the UI updates automatically.
// You never manually touch the DOM.
The key insight from the Flutter documentation (quoted in the course notes) is: UI = f(state).
The UI is a function of the application state. Change the state, and the UI updates to reflect it.
7.3 Understanding State
State is the information that determines what is currently displayed. It is the 'memory' of your
application. Without state, every page would look identical for every user.
There are three levels of state in a web application:
System State — The Whole Database
The complete state of all data in the system. For an e-commerce site, this is every product,
every user account, every order ever placed. This lives in the backend database and is
entirely separate from the frontend.
Application State — The User's Session
The state specific to one user's current session. Examples: what is in the shopping cart,
whether the user is logged in, their theme preference, which notifications they have seen.
This is shared between the frontend and backend.
Modern Web Development — A Complete Beginner's Guide Page 37
UI State (Ephemeral State) — What You See Right Now
The temporary state of the interface itself. Which tab is currently selected? Is this accordion
expanded or collapsed? Is the modal open? This state is typically managed entirely on the
frontend and discarded when the page is refreshed.
Note: Understanding these three levels helps you make good architectural decisions. A
common beginner mistake is storing UI state in the backend database — for example, saving
'which tab the user has open' to the server. This wastes bandwidth and overcomplicates the
backend.
7.4 The Problem with Stateless HTTP
HTTP — the protocol of the web — is stateless. This means every HTTP request is
completely independent. The server does not remember who you are between requests.
This was a deliberate design choice: it makes servers simpler and more scalable. But it
creates a challenge: how do you build an application that remembers who is logged in, what
is in the cart, or where in a multi-step form the user is?
Solutions include:
• Sessions: The server creates a session ID and sends it to the browser as a cookie. On
each request, the browser sends the cookie back. The server looks up the session ID to
find the user's state.
• JWT Tokens: A signed token is sent to the client and attached to every request. The
server verifies the signature rather than looking up a session.
• Client-side state management: Frameworks like [Link] (with Pinia or Vuex) manage
state entirely on the client, syncing with the server only when necessary.
Modern Web Development — A Complete Beginner's Guide Page 38
Chapter 8
Introduction to [Link]
8.1 What Is [Link]?
[Link] (pronounced 'view') is a progressive JavaScript framework for building user interfaces.
It was created by Evan You (a former Google engineer) and first released in 2014. Vue is
known for being:
• Approachable: You can add Vue to an existing HTML page with a single script tag. You
don't need to learn a build system to get started.
• Progressive: Start small and add features as needed. Vue does not force you into a
specific architecture.
• Performant: Vue's virtual DOM and reactivity system are highly optimised. Applications
stay fast even as they grow.
• Versatile: Vue can power a simple interactive widget on a static page, or a full-featured
Single Page Application (SPA) with routing and state management.
8.2 The MVVM Pattern
Vue is inspired by the Model-View-ViewModel (MVVM) architectural pattern. Understanding
this pattern helps you understand how Vue thinks about code.
Model
The data of your application — the 'truth'. In Vue, this is the data you define in your
component (name, age, list of items, etc.).
View
The HTML template that the user sees. In Vue, this is your template section with HTML tags
and Vue directives.
ViewModel
The glue between Model and View. It exposes the data to the view and handles interactions.
In Vue, the component instance itself is the ViewModel. It includes:
• The data properties (the model)
• Computed properties (derived data)
• Methods (responding to user actions)
• The template (the view)
Modern Web Development — A Complete Beginner's Guide Page 39
The key feature of MVVM is data binding: when the model changes, the view automatically
updates. When the user interacts with the view, the model automatically updates. This
two-way connection eliminates the need for manual DOM manipulation.
Warning: Vue's documentation notes: 'Although not strictly associated with the MVVM pattern,
Vue's design was partly inspired by it.' Vue adds features beyond pure MVVM, including
computed properties, watchers, and component lifecycle hooks.
8.3 MVC vs MVVM — Not Either/Or
The course notes make an important point: MVC and MVVM are not competing alternatives.
They operate at different levels:
• MVC (Model-View-Controller) is typically a backend pattern. The controller receives
HTTP requests, fetches data from the model, and calls the appropriate view.
• MVVM is a frontend pattern. It operates within the view layer of MVC, managing data
binding and UI updates.
In a Vue application, you might have a Flask backend following MVC architecture, which
provides a REST API. The Vue frontend then implements MVVM to consume that API and
manage the UI.
8.4 Your First Vue Application
Here is a minimal Vue 3 application. Notice how the component structure separates the
template (view) from the data (model):
<!DOCTYPE html>
<html>
<head>
<script src='[Link]
</head>
<body>
<div id='app'>
<h1>{{ title }}</h1>
<p>Hello, {{ name }}! You have {{ count }} messages.</p>
<button @click='count++'>Add message</button>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
Modern Web Development — A Complete Beginner's Guide Page 40
return {
title: 'My Vue App',
name: 'Alice',
count: 0
};
}
}).mount('#app');
</script>
</body>
</html>
When you click the button, count increases — and the display automatically updates to
show the new count. No [Link], no textContent =. Vue handles it all.
8.5 Template Syntax — The {{ }} Mustache
Vue uses double curly braces {{ }} to insert data values into the template. This is called
'mustache syntax' or 'text interpolation':
<!-- Template -->
<p>{{ message }}</p>
<p>{{ 2 + 2 }}</p>
<p>{{ [Link]() }}</p>
<p>{{ isLoggedIn ? 'Welcome!' : 'Please log in' }}</p>
// Component data
data() {
return {
message: 'Hello Vue!',
isLoggedIn: true
};
}
// Rendered HTML:
// <p>Hello Vue!</p>
// <p>4</p>
// <p>HELLO VUE!</p>
// <p>Welcome!</p>
Note: {{ }} only works for text content — it cannot be used to set HTML attributes. For
attributes, use the v-bind directive (covered in the next chapter). Also, {{ }} will escape HTML
tags for security — it won't render <b>bold</b> as bold text.
Modern Web Development — A Complete Beginner's Guide Page 41
Chapter 9
Vue Directives — Bringing
Templates to Life
9.1 What Are Directives?
Directives are special attributes you add to HTML elements in a Vue template. They all start
with the prefix v- and tell Vue to do something to that element. Directives are Vue's main
mechanism for connecting data to the DOM.
9.2 v-bind — Binding Attributes
The {{ }} mustache syntax works for text content, but what about HTML attributes like href,
src, class, disabled? That is what v-bind is for:
<!-- v-bind:attribute='value' -->
<a v-bind:href="url">Click here</a>
<img v-bind:src="imageUrl" v-bind:alt="imageDescription">
<button v-bind:disabled="isLoading">Submit</button>
<!-- Shorthand: just use : -->
<a :href="url">Click here</a>
<img :src="imageUrl" :alt="imageDescription">
<button :disabled="isLoading">Submit</button>
data() {
return {
url: '[Link]
imageUrl: '/images/[Link]',
imageDescription: 'Company Logo',
isLoading: false
};
}
Class Binding
v-bind has special support for the class attribute. You can pass an object where keys are
class names and values are booleans — the class is applied if the value is true:
<!-- Object syntax -->
Modern Web Development — A Complete Beginner's Guide Page 42
<div :class="{ active: isActive, error: hasError, loading: isLoading }">
Content here
</div>
data() {
return {
isActive: true, // 'active' class will be applied
hasError: false, // 'error' class will NOT be applied
isLoading: false // 'loading' class will NOT be applied
};
}
// Rendered: <div class='active'>Content here</div>
<!-- Array syntax — always apply these classes -->
<div :class="['base-style', isActive ? 'active' : '']"></div>
Style Binding
<!-- Object syntax for inline styles -->
<div :style="{ color: textColor, fontSize: fontSize + 'px' }">
Styled text
</div>
data() {
return {
textColor: 'blue',
fontSize: 18
};
}
// Rendered: <div style='color: blue; font-size: 18px;'>
9.3 v-on — Event Handling
v-on attaches event listeners to elements. When the event fires, a method or expression is
executed:
<!-- v-on:event='handler' -->
<button v-on:click="greetUser">Greet</button>
<!-- Shorthand: @ -->
<button @click="greetUser">Greet</button>
<input @input="handleInput">
<form @[Link]="handleSubmit">
Modern Web Development — A Complete Beginner's Guide Page 43
methods: {
greetUser() {
alert(`Hello, ${[Link]}!`);
},
handleInput(event) {
[Link] = [Link];
},
handleSubmit() {
// .prevent modifier calls [Link]() automatically
[Link]('Form submitted');
}
}
Event Modifiers
Vue provides convenient modifiers appended to v-on with a dot:
<b>Modifier</b> <b>Effect</b>
.prevent Calls [Link]() — prevents default browser behaviour
.stop Calls [Link]() — stops event from bubbling up
.once Listener is called at most once, then removed
.self Only triggers if the event target is the element itself
.key modifiers @[Link], @[Link] — filter keyboard events
9.4 v-model — Two-Way Binding
While v-bind is one-way (data → view), v-model creates two-way binding: data flows both
from the component to the input AND from the input back to the component. This is perfect
for forms:
<input v-model="username" placeholder="Enter your name">
<p>Hello, {{ username }}!</p>
data() {
return {
username: ''
};
}
// As you type in the input, the paragraph updates in real time.
// No event handler needed!
Modern Web Development — A Complete Beginner's Guide Page 44
<!-- v-model on checkboxes -->
<input type="checkbox" v-model="agreeToTerms">
// agreeToTerms is true when checked, false when unchecked
<!-- v-model on multiple checkboxes (binds to array) -->
<input type="checkbox" value="Vue" v-model="selectedTechs"> Vue
<input type="checkbox" value="React" v-model="selectedTechs"> React
data() { return { selectedTechs: [] }; }
// selectedTechs = ['Vue'] when Vue is checked, ['Vue', 'React'] when both
<!-- v-model on select -->
<select v-model="selectedCity">
<option value="paris">Paris</option>
<option value="london">London</option>
</select>
9.5 v-if, v-else-if, v-else — Conditional Rendering
These directives show or hide elements based on conditions. With v-if, the element is actually
added to or removed from the DOM:
<div v-if="userRole === 'admin'">
<h2>Admin Dashboard</h2>
<button>Delete All Users</button>
</div>
<div v-else-if="userRole === 'editor'">
<h2>Editor Panel</h2>
</div>
<div v-else>
<h2>Welcome, regular user!</h2>
</div>
data() {
return {
userRole: 'admin' // 'admin', 'editor', or anything else
};
}
9.6 v-show — Toggle Visibility
v-show is similar to v-if but works differently under the hood: instead of adding/removing the
element from the DOM, it toggles the CSS display property. The element is always in the
Modern Web Development — A Complete Beginner's Guide Page 45
DOM.
<div v-show="isMenuOpen">
<!-- Menu contents -->
</div>
<!-- When isMenuOpen = false, renders as: -->
<div style="display: none;">
<!-- Menu contents (still in DOM) -->
</div>
<b></b> <b>v-if</b> <b>v-show</b>
DOM behaviour Adds/removes element Always in DOM, toggles CSS
Performance (toggle)Higher cost (recreates) Lower cost (just CSS)
Performance (initial) Lower cost if false at start Always renders initially
Use when Condition rarely changes Condition changes often
Example User role checks Dropdown menus, tooltips
9.7 v-for — Rendering Lists
The v-for directive iterates over arrays or objects and renders an element for each item. This
is how you render dynamic lists in Vue:
<!-- Iterating over an array -->
<ul>
<li v-for="fruit in fruits" :key="fruit">
{{ fruit }}
</li>
</ul>
data() {
return {
fruits: ['Apple', 'Banana', 'Cherry']
};
}
// Renders:
// <ul>
// <li>Apple</li>
// <li>Banana</li>
// <li>Cherry</li>
// </ul>
Modern Web Development — A Complete Beginner's Guide Page 46
<!-- With index -->
<li v-for="(fruit, index) in fruits" :key="index">
{{ index + 1 }}. {{ fruit }}
</li>
<!-- Iterating over an array of objects -->
<div v-for="student in students" :key="[Link]">
<h3>{{ [Link] }}</h3>
<p>Score: {{ [Link] }}</p>
</div>
<!-- Iterating over an object's properties -->
<li v-for="(value, key) in userProfile" :key="key">
<strong>{{ key }}:</strong> {{ value }}
</li>
The :key Attribute — Why It Matters
Always provide a :key attribute when using v-for. Vue uses keys to track which items have
changed when the list updates. Without keys, Vue may make incorrect assumptions and
update the wrong elements.
The key must be unique for each item. Use an ID from your data when available. If no unique
ID exists, use the index — but be aware that index-based keys can cause issues if items are
reordered or removed.
<!-- Good: use a unique ID from the data -->
<li v-for="item in items" :key="[Link]">
{{ [Link] }}
</li>
<!-- Acceptable if no ID available -->
<li v-for="(item, index) in items" :key="index">
{{ [Link] }}
</li>
Modern Web Development — A Complete Beginner's Guide Page 47
Chapter 10
Vue Reactivity — The Magic
Explained
10.1 What Is Reactivity?
Reactivity is the feature that makes Vue feel magical. When you change a data property, the
template automatically updates to reflect the change. No manual DOM manipulation required.
But how does Vue know that data changed? How does it know which parts of the template to
update?
The course notes phrase this beautifully: Vue lets you focus on What instead of How. You
declare what the UI should look like for a given state, and Vue figures out how to update the
DOM to match.
10.2 The Problem Reactivity Solves
User interaction is fundamentally reactive. Consider a user logging in:
• The navigation bar needs to update — hide Login, show Logout
• The greeting message changes — 'Hello, Alice!'
• The course list needs to show the user's enrolled courses
• The colour scheme might change based on the user's theme preference
Without reactivity, you would manually update each of these DOM elements. With Vue's
reactivity, you just change the user data object, and everything updates automatically.
10.3 How Vue's Reactivity Works Under the Hood
Vue tracks reactivity using a JavaScript feature called [Link]() (in Vue 2) or
Proxy (in Vue 3). Understanding the [Link] approach (as shown in the course
notes) gives excellent intuition for how the whole system works.
[Link] — Intercepting Property Access
[Link] lets you define a property with custom getter and setter functions. Vue
uses this to intercept every read and write to your data:
// Without Vue — just raw data
const data = { count: 10 };
Modern Web Development — A Complete Beginner's Guide Page 48
// Vue-like: intercept reads and writes
const reactiveData = {};
[Link](reactiveData, 'count', {
get() {
[Link]('Someone READ count!');
return [Link];
},
set(newValue) {
[Link]('Someone WROTE count! New value:', newValue);
[Link] = newValue;
// Here Vue would also say: 'update the DOM!'
}
});
[Link]; // logs: 'Someone READ count!' → returns 10
[Link] = 20; // logs: 'Someone WROTE count! New value: 20'
[Link]; // logs: 'Someone READ count!' → returns 20
The Full Reactivity Cycle
When Vue initializes a component, it does something like this for every data property:
• 1. Track (getter): When the template is being rendered, Vue reads each data property.
The getter fires, and Vue records: 'the template depends on this property.'
• 2. Trigger (setter): When you change a data property, the setter fires, and Vue says:
'something that the template depends on has changed — schedule a re-render.'
• 3. Update the DOM: Vue re-renders the affected parts of the template using its virtual
DOM algorithm.
Here is a more complete simulation:
// Simulating Vue's reactivity (simplified)
let currentEffect = null; // the current 'watcher'
function track() {
if (currentEffect) {
[Link]('Dependency tracked: template now watches this property');
}
}
function trigger() {
[Link]('Property changed! Scheduling DOM update...');
// In real Vue, this re-runs the affected render functions
}
Modern Web Development — A Complete Beginner's Guide Page 49
const data = { count: 0 };
const reactive = {};
[Link](reactive, 'count', {
get() { track(); return [Link]; },
set(v) { [Link] = v; trigger(); }
});
[Link]; // 'Dependency tracked'
[Link] = 5; // 'Property changed! Scheduling DOM update...'
Vue 3: Proxy-based Reactivity
Vue 3 switched from [Link] to the ES6 Proxy API. Proxies are more powerful
because they can intercept all property accesses, including properties added after
initialization, array index assignments, and more. The concept is the same — intercept reads
and writes — but the implementation is more robust.
// Vue 3's approach uses Proxy
const data = { count: 0, name: 'Alice' };
const reactive = new Proxy(data, {
get(target, property) {
[Link](`Reading: ${property}`);
return target[property];
},
set(target, property, value) {
[Link](`Setting: ${property} = ${value}`);
target[property] = value;
// Vue would trigger DOM update here
return true;
}
});
[Link]; // 'Reading: count'
[Link] = 10; // 'Setting: count = 10'
[Link] = 5; // 'Setting: newProp = 5' — works with new properties
too!
10.4 Computed Properties
Often you need data that is derived from other data. For example, if you have firstName and
lastName, you might want a fullName. Computed properties are reactive properties derived
from other reactive data. They update automatically when their dependencies change.
Modern Web Development — A Complete Beginner's Guide Page 50
const app = createApp({
data() {
return {
firstName: 'Alice',
lastName: 'Smith',
prices: [10, 20, 30, 40]
};
},
computed: {
// Automatically updates when firstName or lastName changes
fullName() {
return `${[Link]} ${[Link]}`;
},
// Automatically updates when prices array changes
totalPrice() {
return [Link]((sum, price) => sum + price, 0);
},
averagePrice() {
return [Link] / [Link];
}
}
});
// Template: {{ fullName }} → "Alice Smith"
// Template: {{ totalPrice }} → 100
// Template: {{ averagePrice }} → 25
Computed vs Methods
You could achieve the same result with methods, but computed properties have an important
advantage: caching. A computed property only re-evaluates when its dependencies change.
A method re-runs every time the template is re-rendered.
// This computed property is cached
computed: {
expensiveCalculation() {
// Only runs again if 'numbers' changes
return [Link]((a, b) => a + b, 0);
}
}
// This method re-runs on EVERY render, even if numbers hasn't changed
Modern Web Development — A Complete Beginner's Guide Page 51
methods: {
expensiveCalculationMethod() {
return [Link]((a, b) => a + b, 0);
}
}
Note: Rule of thumb: use computed properties when deriving a value from existing data. Use
methods when performing an action (like an API call or modifying data). Never call methods
from templates just to get a value — use computed properties instead.
10.5 Watchers — Reacting to Changes
Imperatively
While computed properties are declarative (define what the value is), watchers are
imperative (define what to do when a value changes). Use watchers when you need to
perform side effects in response to data changes: making API calls, logging, triggering
animations.
const app = createApp({
data() {
return {
searchQuery: '',
searchResults: [],
userId: null
};
},
watch: {
// Called every time searchQuery changes
searchQuery(newValue, oldValue) {
[Link](`Search changed from '${oldValue}' to '${newValue}'`);
[Link](newValue);
},
// Watch a nested property
'[Link]'(newEmail) {
[Link](newEmail);
}
},
methods: {
async fetchResults(query) {
if ([Link] > 2) {
Modern Web Development — A Complete Beginner's Guide Page 52
const res = await fetch(`/api/search?q=${query}`);
[Link] = await [Link]();
}
}
}
});
Warning: The course notes emphasise: when possible, use computed properties instead of
watchers. Computed properties are more declarative, more readable, and benefit from
caching. Watchers are best reserved for async operations and situations where computed
properties are insufficient.
Modern Web Development — A Complete Beginner's Guide Page 53
Chapter 11
Vue Components — Building
Reusable UI
11.1 The DRY Principle and Components
The DRY principle — Don't Repeat Yourself — is one of the most important principles in
software engineering. If you find yourself writing the same code in multiple places, it is time to
refactor.
In UI development, the same pattern appears constantly:
• A list of news items on a homepage — each item has the same structure
• 'People also bought' cards on Amazon — same layout, different data
• Social media posts — same structure repeated for each post in a feed
Vue components are the solution. A component is a reusable, self-contained piece of UI
with its own template, data, and logic. You define it once and use it as many times as you
need.
11.2 What Is a Component?
Every Vue application is a tree of components. At the root is the App component. Inside it are
other components: Navbar, Sidebar, ContentArea. Inside ContentArea might be PostList,
which contains multiple PostCard components.
App
■■■ Navbar
■ ■■■ NavLink
■ ■■■ UserMenu
■■■ Sidebar
■ ■■■ CategoryList
■■■ ContentArea
■■■ SearchBar
■■■ PostList
■■■ PostCard
■■■ PostCard
■■■ PostCard
Modern Web Development — A Complete Beginner's Guide Page 54
Each component encapsulates:
• Template: How to render the component's HTML
• Data: The component's own private reactive data
• Props: Values passed in from the parent component (like function parameters)
• Methods: Functions the component can call
• Computed properties: Derived reactive data
• Watchers: Reactions to data changes
11.3 Defining and Using a Component
Simple Component Definition
// Define a component
const PostCard = {
template: `
<div class='post-card'>
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<span class='author'>By {{ author }}</span>
<button @click='likePost'>
Like ({{ likeCount }})
</button>
</div>
`,
props: ['title', 'body', 'author'],
data() {
return {
likeCount: 0
};
},
methods: {
likePost() {
[Link]++;
}
}
};
// Register and use the component
const app = createApp({
components: { PostCard },
Modern Web Development — A Complete Beginner's Guide Page 55
data() {
return {
posts: [
{ id: 1, title: 'Vue is Great', body: 'Here is why...', author: 'Ali
ce' },
{ id: 2, title: 'JavaScript Tips', body: 'These tips will...', autho
r: 'Bob' },
]
};
},
template: `
<div>
<PostCard
v-for='post in posts'
:key='[Link]'
:title='[Link]'
:body='[Link]'
:author='[Link]'
/>
</div>
`
});
11.4 Props — Data Flowing Down
Props (short for properties) are the way parent components pass data to child components.
They are like function parameters for components. Props flow in one direction only: parent →
child.
// Child component with typed props
const UserAvatar = {
props: {
name: {
type: String,
required: true
},
size: {
type: Number,
default: 40 // Default value if parent doesn't provide it
},
Modern Web Development — A Complete Beginner's Guide Page 56
imageUrl: {
type: String,
default: '/[Link]'
}
},
template: `
<img
:src='imageUrl'
:alt='name'
:width='size'
:height='size'
/>
`
};
<!-- Parent using the component -->
<UserAvatar
name='Alice'
:size='60'
:imageUrl='[Link]'
/>
Warning: Never modify a prop directly inside a child component — this violates Vue's
one-way data flow principle. If you need to modify the value, copy it to the component's own
data first, or emit an event to ask the parent to change it.
11.5 Emitting Events — Data Flowing Up
Props flow down. But what if a child component needs to tell its parent about something? For
example, the user clicked 'Delete' on a post card — the PostCard component needs to tell the
parent to remove that post from the list. This is done via custom events:
// Child component emits events
const PostCard = {
props: ['post'],
emits: ['delete', 'like'], // declare events this component emits
template: `
<div class='post-card'>
<h2>{{ [Link] }}</h2>
<button @click='$emit("delete", [Link])'>Delete</button>
<button @click='$emit("like", [Link])'>Like</button>
Modern Web Development — A Complete Beginner's Guide Page 57
</div>
`
};
// Parent listens for events
const App = {
components: { PostCard },
data() { return { posts: [...] }; },
methods: {
removePost(postId) {
[Link] = [Link](p => [Link] !== postId);
},
likePost(postId) {
const post = [Link](p => [Link] === postId);
[Link]++;
}
},
template: `
<PostCard
v-for='post in posts'
:key='[Link]'
:post='post'
@delete='removePost'
@like='likePost'
/>
`
};
11.6 Slots — Composable Content
Sometimes you want a component that provides structure but lets the parent fill in the
content. Slots are Vue's mechanism for this:
// A Card component with a slot
const Card = {
template: `
<div class='card'>
<div class='card-header'>
<slot name='header'>Default Header</slot>
</div>
<div class='card-body'>
Modern Web Development — A Complete Beginner's Guide Page 58
<slot></slot> <!-- default slot -->
</div>
<div class='card-footer'>
<slot name='footer'></slot>
</div>
</div>
`
};
<!-- Using the Card component with slot content -->
<Card>
<template #header>
<h2>My Amazing Post</h2>
</template>
<p>This is the main content of the post.</p>
<img src='[Link]' alt='Post image'>
<template #footer>
<button>Read More</button>
</template>
</Card>
11.7 Single File Components (.vue files)
When building larger applications with a build tool (like Vite), Vue supports Single File
Components (.vue files). These elegantly combine the template, script, and styles for one
component in a single file:
<!-- [Link] -->
<template>
<div class='post-card'>
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<button @click='likeCount++'>
♥ {{ likeCount }}
</button>
</div>
</template>
<script>
export default {
name: 'PostCard',
Modern Web Development — A Complete Beginner's Guide Page 59
props: {
title: String,
body: String
},
data() {
return { likeCount: 0 };
}
};
</script>
<style scoped>
.post-card {
border: 1px solid #ddd;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
}
/* 'scoped' means these styles only apply to this component */
</style>
Note: The 'scoped' attribute on the style tag is powerful — it means the CSS rules only apply
to elements in THIS component. You can safely use simple class names like '.button' without
worrying about clashing with other components. Vue adds a unique attribute to elements at
build time to achieve this.
Modern Web Development — A Complete Beginner's Guide Page 60
Chapter 12
Putting It All Together — A
Complete Example
12.1 Building a Simple Task Manager
Let's apply everything from this book by building a simple task manager application. It will
demonstrate reactivity, v-for, v-model, v-if, computed properties, and component
communication.
12.2 Application Structure
my-task-app/
■■■ [Link]
■■■ [Link]
■■■ components/
■ ■■■ [Link]
■ ■■■ [Link]
■ ■■■ [Link]
■■■ [Link]
12.3 [Link] — The Root Component
<template>
<div class='app'>
<h1>My Tasks ({{ completedCount }}/{{ [Link] }} done)</h1>
<TaskInput @add-task='addTask' />
<div class='filters'>
<button @click='filter = "all"'>All</button>
<button @click='filter = "active"'>Active</button>
<button @click='filter = "done"'>Done</button>
</div>
<TaskList
:tasks='filteredTasks'
@toggle='toggleTask'
Modern Web Development — A Complete Beginner's Guide Page 61
@delete='deleteTask'
/>
<p v-if='[Link] === 0'>
No tasks yet. Add one above!
</p>
</div>
</template>
<script>
import TaskInput from './components/[Link]';
import TaskList from './components/[Link]';
export default {
components: { TaskInput, TaskList },
data() {
return {
tasks: [
{ id: 1, text: 'Learn JavaScript', done: true },
{ id: 2, text: 'Learn [Link]', done: false },
{ id: 3, text: 'Build an app', done: false },
],
filter: 'all',
nextId: 4
};
},
computed: {
completedCount() {
return [Link](t => [Link]).length;
},
filteredTasks() {
if ([Link] === 'active') return [Link](t => ![Link]);
if ([Link] === 'done') return [Link](t => [Link]);
return [Link];
}
},
methods: {
addTask(text) {
[Link]({ id: [Link]++, text, done: false });
},
toggleTask(id) {
const task = [Link](t => [Link] === id);
Modern Web Development — A Complete Beginner's Guide Page 62
if (task) [Link] = ![Link];
},
deleteTask(id) {
[Link] = [Link](t => [Link] !== id);
}
}
};
</script>
12.4 [Link] — Collecting User Input
<template>
<div class='task-input'>
<input
v-model='newTask'
@[Link]='submit'
placeholder='Add a new task...'
/>
<button @click='submit' :disabled='![Link]()'>
Add
</button>
</div>
</template>
<script>
export default {
emits: ['add-task'],
data() {
return { newTask: '' };
},
methods: {
submit() {
if ([Link]()) {
this.$emit('add-task', [Link]());
[Link] = ''; // clear the input
}
}
}
};
</script>
Modern Web Development — A Complete Beginner's Guide Page 63
12.5 [Link] — Individual Task Display
<template>
<li :class='{ done: [Link] }' class='task-item'>
<input
type='checkbox'
:checked='[Link]'
@change='$emit("toggle", [Link])'
/>
<span>{{ [Link] }}</span>
<button
class='delete-btn'
@click='$emit("delete", [Link])'
>
×
</button>
</li>
</template>
<script>
export default {
props: {
task: { type: Object, required: true }
},
emits: ['toggle', 'delete']
};
</script>
<style scoped>
.task-item { display: flex; align-items: center; gap: 8px; padding: 8px; }
.done span { text-decoration: line-through; color: #888; }
.delete-btn { margin-left: auto; cursor: pointer; }
</style>
12.6 What This Example Demonstrates
This task manager — though simple — demonstrates every major concept from this book
working together:
• v-model: The TaskInput uses v-model to bind the text input to a data property. As you
type, the data updates. When you clear it after submitting, the input clears too.
Modern Web Development — A Complete Beginner's Guide Page 64
• Computed properties: completedCount and filteredTasks are both computed from the
tasks array. They update automatically whenever tasks changes.
• v-for with :key: TaskList renders a TaskItem for each task, using [Link] as the key for
efficient updates.
• v-if: The 'No tasks yet' message only appears when the tasks array is empty.
• :class binding: TaskItem adds the 'done' class when [Link] is true, triggering the
strikethrough style.
• Props and events: Data flows down through props (tasks, task). User actions flow up
through emitted events (add-task, toggle, delete).
• Reactivity: Everything updates automatically. No manual DOM manipulation anywhere.
Modern Web Development — A Complete Beginner's Guide Page 65
Chapter A
Quick Reference — Vue Directives
and Concepts
Vue Directives Summary
<b>Directive</b>
<b>Purpose</b> <b>Example</b>
v-bind / : Bind attribute to data :href='url'
v-on / @ Attach event listener @click='doSomething'
v-model Two-way data binding v-model='username'
v-if Conditionally add to DOM v-if='isLoggedIn'
v-else-if Else-if branch v-else-if='isPending'
v-else Else branch v-else
v-show Toggle CSS display v-show='isMenuOpen'
v-for Render list from array v-for='item in items'
v-html Render raw HTML string v-html='htmlContent'
v-text Set text content v-text='message'
v-once Render only once v-once
v-pre Skip compilation for subtreev-pre
v-cloak Hide until Vue is ready v-cloak
Component Options Summary
<b>Option</b> <b>Purpose</b>
data() Returns the component's reactive data object
props Declares properties passed from parent
emits Declares custom events the component emits
computed Cached, derived reactive properties
watch Side-effect reactions to data changes
methods Functions callable from template or other code
components Child components used in this component's template
template The component's HTML template (or use .vue file)
Modern Web Development — A Complete Beginner's Guide Page 66
setup() Vue 3 Composition API entry point
mounted() Lifecycle hook: called after component is inserted into DOM
created() Lifecycle hook: called after component instance is created
beforeUnmount() Lifecycle hook: called before component is destroyed
Recommended Learning Resources
[Link] Official Documentation ([Link]): Comprehensive, well-written docs with
interactive examples
JavaScript for Impatient Programmers ([Link]): Deep, modern JS reference by
Dr. Axel Rauschmayer
MDN Web Docs ([Link]): The definitive browser API and web standards
reference
The Odin Project ([Link]): Free full-stack curriculum — excellent project-based
learning
freeCodeCamp ([Link]): Free interactive coding challenges, projects, and
certifications
[Link] ([Link]): Modern JavaScript tutorial — very thorough, great for
beginners