5. Chapter 4 - Introduction to [Vue.js]
5.1. Sources
This chapter is based on the official documentation for [Vue.js]: vuejs.org, and in particular its guide (vuejs.org/guide/introduction.html).
5.2. From AngularJS 1.x to [Vue.js]
The original document (2014) used AngularJS 1.x: a framework (not yet TypeScript) organized around $scope, controllers, directives (ng-repeat, ng-if…), and a change detection mechanism based on the “digestion loop ” ($digest) and variable monitoring ($watch).
[Vue.js] (created by Evan You in 2014, following an experience at Google working on AngularJS) is, like [Angular], a full-fledged framework: unlike [React] (a simple interface library), it provides its own component system, reactive state management, and—via its separate official packages, which are not used in this document—a router ([vue-router]) and a global state manager ([Pinia]). [Vue.js] describes itself as “progressive”: a core focused on the view layer (similar in concept to from the $scope/template of AngularJS 1.x), supplemented by its own component format—the [.vue] files (see below). This chapter presents these concepts one by one, with short examples independent of the case study—the following chapter will explore their application in the complete [RdvMedecins] client, in [Vue.js] 3.
5.3. The Concept Behind a [Vue.js] Application: the “Single Page Application”
A [Vue.js] application is, like a [Angular] or [React] application, a single-page application (SPA): the server delivers only a single HTML page to the browser, containing a single JavaScript file that embeds all of the application’s logic. Once this page is loaded, there is never another full page reload: every user interaction (changing views, submitting a form, etc.) is handled by the JavaScript code already present in the browser, which updates the display and communicates with the server in the background via asynchronous HTTP requests (most often using JSON). This was already the principle behind the original document with AngularJS and 1.x, as well as the [Angular] and [React] variants in this course—it hasn’t changed: only the framework that handles the display differs from one variant to another.
5.4. The components: files [.vue] and [Composition API]
As in [Angular] and [React], a component is the basic unit of a [Vue.js] application: it generates what is to be displayed on the screen based on its current state and the properties it receives. The difference lies in the form: a [Angular] component is a decorated class, associated with a separate (or inline) HTML template; a [React] component is a function that returns [JSX]; a [Vue.js] component is a “single-file” [.vue] file, which combines three distinct blocks into a single file: a [<template>] (the HTML, similar to that of [Angular]), a [<script setup>] (the logic, as in TypeScript), and, optionally, a [<style>] (the styles specific to this component).
Here is a very small component, independent of the case study, equivalent to [CompteurComponent] ([Angular]) and [Compteur.tsx] ([React]), which have already been presented in this course:
Let’s break down this code:
- Line 3: [import { ref } from 'vue';] — [ref] is the function [Vue.js], which creates a reactive value: a role comparable to that of a signal on the [Angular] side, or of [useState] on the [React] side;
- Line 5: [const valeur = ref(0);] — [ref(0)] creates a reactive "box" initialized to 0. Unlike [useState]() ([React]), which returns a PAIRE (the value and a setter), [ref()] returns a SEUL object, whose value is read and written via its [.value] property —a mechanism more similar, in concept, to a [signal()] [Angular] (which is read using [monSignal()] and written using [monSignal.set(...)]) than a [valeur, setValeur] pair;
- line 8: [valeur.value++;] — modifying [.value] updates the value of ET and automatically redraws everything that depends on it in [<template>] - exactly as with [monSignal.update(v => v + 1)] relative to [Angular]; unlike [React] (where state values are immutable), nothing here prevents [.value] from being incremented directly (++), without going through a separate update function;
- lines 12–13: [Valeur : {{ valeur }}] — [<template>] and EST are the template, just as HTML is separate from [Angular] - unlike [JSX] and [React], which are derived from JavaScript and are intermixed with the component function’s code. The double curly braces [{{ }}] insert an expression there—the same role as [{{ valeur() }}] relative to [Angular] ([Vue.js] does not require PAS to have call parentheses ([valeur], not [valeur()]): in a [<template>], a [ref] is automatically “unpacked”; there is never a need to write [.value]).
5.5. The local state: [ref]/[reactive] and the props
In AngularJS and 1.x, [Angular] detected that a piece of data had changed using a systematic check loop ($digest), which was replaced in the [Angular] variant of this course by signals (signal(), computed()). [React] takes a different approach: a component fully re-executes its function with every state change. [Vue.js] takes a third, hybrid approach: like [Angular], it precisely tracks the reactive values a component uses (thanks to the compiled [<template>] and the “proxy” system of [ref]/[reactive]), and updates only those parts of DOM that actually depend on them—without ever re-executing the entire [<script setup>], and without the cost of the virtual DOM used by [React] to achieve a comparable result.
Fonction | Rôle |
creates a local state at UNE with a single value; read/written via [.value] in [<script>] (never in [<template>]) | |
creates a local state in PLUSIEURS properties (an object); read/written directly, SANS [.value], property by property | |
derives a value from other [ref]/[reactive], which is automatically recalculated only when its dependencies change—the direct equivalent of [computed()] on the [Angular] side, and of [useMemo]() on the [React] side | |
re-executes [callback] whenever [source] changes—the equivalent of [useEffect](..., [dep]) on the [React] side, or of [effect()] on the [Angular] side |
Two mechanisms complement [ref]/[reactive] to enable a component to communicate with its parent—these are consistently found in the [RdvMedecins] client (next chapter), exactly where [input()]/[output()] were located on the [Angular] side, and the props/callback props on the [React] side:
- the props ([defineProps<...>()]): A [Vue.js] component declares the format of its inputs using [defineProps], a special function recognized by the compiler (it does not require any [import]) - the direct equivalent of [input.required<Medecin[]>()] on the [Angular] side, and of a destructured props object on the [React] side;
- events ([defineEmits<...>()]): rather than a simple function passed as a prop (the choice of [React]), [Vue.js] explicitly distinguishes the events that a component emits—the function [emit(‘nomEvenement’, valeur)] that it returns is the direct equivalent of [rechercher.emit(valeur)] on the [Angular] side ([output<...>()]).
5.6. No modules: each file is already self-contained
A AngularJS 1.x application was organized into modules (angular.module(...)). [Angular] first adopted this idea in a more rigid form (the NgModule files), before proposing standalone components as the default mode. Like [React], [Vue.js] never needed this concept: a [.vue] file implicitly exports its component (the [<script setup>] EST is the file’s default export, without even needing to specify the [export] keyword), and another file uses it with a simple [import]—the standard ECMAScript modules, without any additional framework-specific layers:
This is the style used throughout the [RdvMedecins] client (next chapter): no *.module.ts files, no [imports: [...]] decorators to maintain—a simplification identical to that already seen with [React], compared to AngularJS, 1.x, and [Angular] with NgModule.
5.7. Shared logic: composables and [provide]/[inject]
As in [NestJS] (Chapter 2), the [RdvMedecins] client requires logic shared among multiple components: communicating with the server, identifying the logged-in user, and switching languages. [Angular] addresses this need with injectable services ([@Injectable]({ providedIn: ‘root’ }) + inject(...)); [React] combines custom hooks with [Context]. [Vue.js] uses a combination similar to that of [React], but with its own vocabulary and mechanism:
- a composable—a function whose name, by convention, begins with [use] (like a hook [React]), which can itself call other functions [Vue.js] ([ref], [inject]…)—encapsulates reusable logic, just like a server-side service method [Angular];
- the pair [provide]/[inject] shares a value (state + functions) with TOUT in the component subtree, without having to manually pass it down, property by property, through each intermediate level—the role played by [providedIn: ‘root’] on the [Angular] side, and by [Context] on the [React] side. Unlike [React], which requires a [Provider] component to be visually present in the IMBRIQUER tree around the root component, [Vue.js] returns a value with a single function call, [app.provide(cle, valeur)], on the application instance (see main.ts, next chapter)—without any component nesting required.
Here is a short example, independent of the case study, that is conceptually equivalent to [MonService] ([Angular]) and [mon-contexte.tsx] ([React]), which have already been presented in this course:
Let’s break down this code:
- Line 8: [const MonStoreKey: InjectionKey<MonStore> = Symbol('MonStore');] — the injection key: a [Symbol] rather than a simple string, to avoid any accidental collision with another key of the same name - [InjectionKey<T>] is simply an alias for [Symbol] on the type side, which allows [inject()] to automatically determine the type of the returned value;
- Line 10: [export function creerMonStore(): MonStore {] — the factory, called UNE SEULE times (in [main.ts]; see the next chapter), AVANT that at least one component exists—which is why it receives any dependencies as ordinary function parameters, rather than via [inject()];
- line 11: [return reactive<MonStore>({] — [reactive(...)] wraps the object in a JavaScript proxy: any read of one of its properties in a [<template>] automatically registers a dependency; any write operation redraws everything that depends on it—unlike [ref()], the property ([store.saluer]) is read and written directly, without [.value];
- Lines 16–22: [export function useMonStore(): MonStore { …] — the composable that the components call ([const mon = useMonStore();]), just as they would have called [inject(MonService)] from [Angular], or [useMonContexte()] from [React]; the error raised if [inject(MonStoreKey)] returns [undefined] indicates a wiring omission (the corresponding [app.provide(...)] was not called) - exactly the same safety net as on the [React] side (Context is null by default).
5.8. Communicate with the server: [fetch]
[HttpClient] ([Angular]) was the direct successor to the $http service, which itself succeeded AngularJS and 1.x used by the original document. Like [React], [Vue.js] is merely an interface framework and, at its core, does not provide any HTTP client: the [RdvMedecins] client directly uses [fetch], the browser’s standard API (available without any additional dependencies):
An important difference from $http and [HttpClient]: [fetch] directly returns a Promise (the standard asynchronous mechanism of JavaScript), not an Observable—no RxJS library, no [.subscribe]({ next, error }), no [.pipe(map(...))]. It can be used with [.then(...)], or, for better readability, with async/await (as shown above). A network error or an error response is then handled with a simple try/catch block, rather than with the error branch of a RxJS subscription—exactly as on the [React] side.
The [useRdvService()] composable of the [RdvMedecins] client (next chapter) systematically encapsulates this [fetch] call followed by [.json()], for a specific reason explained at that point (the [Reponse]<T> wrapper common to all server responses).
5.9. Conditional rendering and lists: [v-if]/[v-else] and [v-for]
AngularJS and 1.x used structural directives (ng-if, ng-repeat) to display an element conditionally or repeat a template block for each element in a list. The [Angular] variant of this course uses the built-in syntax [@if]/[@for]; the [React] client expresses the same thing using the ternary operator and [.map(...)], in pure JavaScript. [Vue.js] is, on this specific point, closer to [Angular]: its [<template>] has its own “language” of directives, very similar in spirit to that of AngularJS and 1.x (ng-if becomes [v-if], ng-repeat becomes [v-for]):
Let’s break down this code:
- lines 1–2: [v-if="utilisateurConnecte" … v-else …] — [v-if]/[v-else] replace [@if]/[@else] ([Angular]) and ng-if (AngularJS 1.x): only the branch whose condition is true is MONTÉE within DOM—exactly the same behavior as the ternary operator [React] ({ condition ? ... : ... }), but expressed as an attribute HTML rather than as an expression JavaScript nested within [JSX];
- Line 5: [v-for="item in listeItems" :key="item.id"] — [v-for] replaces [@for] ([Angular]) and ng-repeat (AngularJS 1.x), and serves exactly the same purpose as [.map(...)] on the [React] side: it repeats the element for each entry in the array. [:key] (required, just like [key={item.id}] in [React] and track in [Angular]) tells [Vue.js] how to recognize an element that has already been displayed when it redraws the list.
5.10. Summary: mappings [Angular] -> [React] -> [Vue.js]
[Angular]/[React] (previous versions of this course) | [Vue.js] (2026) |
| Single-file components ([.vue]) |
| [ref]()/[reactive](), [defineProps]/[defineEmits], [computed]() |
| [watch]() |
| [<template>] + [<script setup>] combined into the same file [.vue] |
| [v-if]/[v-else], [v-for] |
| Composables + [provide]/[inject] |
| [fetch] (Native Promises, such as [React]) |
| No router (same conclusion, taken a step further) |
| [Vite] (with [@vitejs/plugin-vue] instead of [@vitejs/plugin-react]) |
These concepts are sufficient to tackle the [RdvMedecins] client in the next chapter: each new use will nevertheless be explained again there, as the code progresses, at the exact point where it appears.