Zenika VueJS Formation Slides PDF

Summary

These slides provide an overview of VueJS, a JavaScript framework for building user interfaces. The material covers various aspects of VueJS, including its core concepts and functionalities. Topics like routing, data fetching, tooling, and template syntax are also explored, offering insights into the framework's capabilities for modern web development.

Full Transcript

VUEJS © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 LOGISTIC Hours and Scheduling Lunch & breaks © Copyright 2025 Zenika. All rights reserved OVERVIEW What is VueJS Routing Instance...

VUEJS © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 LOGISTIC Hours and Scheduling Lunch & breaks © Copyright 2025 Zenika. All rights reserved OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability © Copyright 2025 Zenika. All rights reserved © Copyright 2025 Zenika. All rights reserved WHAT IS VUEJS 1 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 1-1 © Copyright 2025 Zenika. All rights reserved INTRODUCTION VueJS was created in February 2014 by Evan You. It has been maintained by himself & his team ever since (not by any members of the GAFAM family). Since February 2022, Vue 3 is the default version of the framework. 1-2 © Copyright 2025 Zenika. All rights reserved INTRODUCTION VueJS is among the most popular projects on Github. GitHub is a web-based hosting service for version control using git & commonly used to host open-source software projects. 1-3 © Copyright 2025 Zenika. All rights reserved INSTALLATION VueJS can be installed on your computer via npm. npm is a package manager for Node.js. All packages can be found on npmjs.org You may also add the library to your project via a CDN link by adding it in the script tag. 1-4 © Copyright 2025 Zenika. All rights reserved INSPIRATION VueJS is used to write sophisticated Single-Page Applications (SPA). It is also designed to be incrementally adoptable with other Javascript Libraries. In fact, a lot of concepts used by VueJS are directly inspired by other great libraries & frameworks such as AngularJS, React, Svelte or even jQuery. 1-5 © Copyright 2025 Zenika. All rights reserved ANGULARJS SHARED CONCEPTS Syntax (v-if vs ng-if) Two-way binding Directives Watchers 1-6 © Copyright 2025 Zenika. All rights reserved REACT SHARED CONCEPTS Virtual DOM Render Functions & JSX Support Focus on the View Layer Hooks (Composition API) Fragments, Portals (Teleport), Suspense 1-7 © Copyright 2025 Zenika. All rights reserved SVELTE SHARED CONCEPTS Single File Components syntax improvements 1-8 © Copyright 2025 Zenika. All rights reserved REFERENCE DOCUMENTATION & LEARNING MATERIALS VueJS official doc API VueJS Tutorial VueJS 1-9 © Copyright 2025 Zenika. All rights reserved 1 - 10 © Copyright 2025 Zenika. All rights reserved Lab 1 1 - 11 © Copyright 2025 Zenika. All rights reserved THE APPLICATION INSTANCE 2 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 2-1 © Copyright 2025 Zenika. All rights reserved OUR FIRST INSTANCE A Vue application consists of a root instance optionally organized into a tree of nested, reusable components. The application instance is declared using the Vue createApp function. const vm = Vue.createApp({ // options }) Each instance is declared with an options object that represents the root component of your application. 2-2 © Copyright 2025 Zenika. All rights reserved MOUNT A DOM ELEMENT To bind the Application Instance to a DOM element, we use the app.mount method and pass a selector as a param. Hello World! const app = Vue.createApp({}) app.mount('#app') As a convention, we often use the variable vm (short for ViewModel) or app to refer to our instance. 2-3 © Copyright 2025 Zenika. All rights reserved DATA PROPERTIES Each property found in the data object is reactive. VueJS uses Proxy and getter/setter properties to provide this reactivity. All properties of the data option are accessible by the template. {{ message }} const app = Vue.createApp({ data () { return { message: 'Hello World!' } } }).mount('#app') 2-4 © Copyright 2025 Zenika. All rights reserved Lab 2 2-5 © Copyright 2025 Zenika. All rights reserved 2-6 © Copyright 2025 Zenika. All rights reserved TOOLING 3 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 3-1 © Copyright 2025 Zenika. All rights reserved VUE-CLI Vue CLI was the official webpack-based toolchain for Vue and is now in maintenance mode. This build tool lets you easily create, scale, and configure a Vue project. Some features provided: Interactive project scaffolding (vue create). A dev server & module bundler (based on Webpack). Rich plugin ecosystem A full graphical user interface to manage your project (vue ui). 3-2 © Copyright 2025 Zenika. All rights reserved VITE Build tool that provide a faster and leaner development experience for modern web projects. It consists of two major parts: A dev server that provides rich feature enhancements over native ES modules (based on esbuild). A build command that bundles your code (based on Rollup). 3-3 © Copyright 2025 Zenika. All rights reserved VITE - CREATE A PROJECT create-vue is the official Vue project scaffolding tool. Create a project : $ npm init vue 3-4 © Copyright 2025 Zenika. All rights reserved VITE - CREATE A PROJECT Running the npm init vue command allows you to select the features you want to integrate into your application. 3-5 © Copyright 2025 Zenika. All rights reserved EXTEND DEFAULT CONFIGURATION Vite configuration can be modified from the vite.config.js file. Below is the default content of this file. // vite.config.js import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) 3-6 © Copyright 2025 Zenika. All rights reserved IMPORTANT COMMANDS TO KNOW Install dependencies: npm install Compile and Hot-Reload for Development: npm run dev Type-Check, Compile and Minify for Production: npm run build 3-7 © Copyright 2025 Zenika. All rights reserved ENV VARIABLES Vite supports env variables out of the box. They can be accessed on the special import.meta.env object. During production, these env variables are statically replaced. Only variables prefixed with VITE_ are accessible from your Vite-processed code. //.env VITE_APP_NAME=My TV shows APP_NAME=My TV shows // src/main.js console.log(import.meta.env.VITE_APP_NAME) // log "My TV shows" from VITE_APP_NAME console.log(import.meta.env.APP_NAME) // log undefined because the env variable APP_NAME is not exposed in your Vite app 💡 To use env variables inside the vite config, you can use the vite's loadEnv method. 3-8 © Copyright 2025 Zenika. All rights reserved SINGLE FILE COMPONENTS File ending with.vue (e.g: CardShow.vue) Groups up all elements of a Vue component template: html or pug script: javascript or typescript style: css, sass, scss, stylus Better readability Better developer experience 3-9 © Copyright 2025 Zenika. All rights reserved SINGLE FILE COMPONENTS - EXAMPLE 3 - 10 © Copyright 2025 Zenika. All rights reserved SINGLE FILE COMPONENTS - EXAMPLE 3 - 11 © Copyright 2025 Zenika. All rights reserved VUE DEVTOOLS A browser extension very useful for debugging Vue applications (Components, Events, Store, Routes). Available on Chrome, Firefox or standalone application. Chrome extension Firefox extension Edge extension 3 - 12 © Copyright 2025 Zenika. All rights reserved VETUR / VUE - OFFICIAL VS Code has many extensions to enrich the development experience. Extensions exist for Vue that will help the IDE handle the syntax of *.vue component files. Some features provided by these extensions : Syntax & Semantic highlighting Snippet Linting / Error Checking Formatting... Vue 2 project extension : Vetur Vue 3 project extension : Vue - Official Vue - Official (formerly Volar) has been created specifically for Vue 3 and has better performance than Vetur. 3 - 13 © Copyright 2025 Zenika. All rights reserved 3 - 14 © Copyright 2025 Zenika. All rights reserved Lab 3 3 - 15 © Copyright 2025 Zenika. All rights reserved TEMPLATE SYNTAX 4 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 4-1 © Copyright 2025 Zenika. All rights reserved INTERPOLATIONS - TEXT The most basic form of data binding is text interpolation using the "Mustache" syntax (double curly braces): Message: {{ msg }} The mustache tag will be replaced with the value of the msg property from the corresponding component instance. It will also be updated whenever the msg property changes. 4-2 © Copyright 2025 Zenika. All rights reserved INTERPOLATIONS - HTML The double mustaches interprets the data as plain text, not HTML. In order to output real HTML, you will need to use the v-html directive: Using mustaches: {{ rawHtml }} Using v-html directive: Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. SFC Playground 4-3 © Copyright 2025 Zenika. All rights reserved ATTRIBUTES Mustaches cannot be used inside HTML attributes. Instead, use a v-bind directive: 👎 Wrong way to interpolate attribute 👍 Right way to interpolate attribute If the bound value is null or undefined then the attribute will not be included on the rendered element. 4-4 © Copyright 2025 Zenika. All rights reserved ATTRIBUTES - BOOLEAN In the case of boolean attributes, where their mere existence implies true, v-bind works a little differently. For example: Button The disabled attribute will be included if isButtonDisabled has a truthy value. It will also be included if the value is an empty string, maintaining consistency with. For other falsy values the attribute will be omitted. 4-5 © Copyright 2025 Zenika. All rights reserved JAVASCRIPT EXPRESSIONS So far we've only bound simple property keys in our templates. But VueJS actually supports the full power of JavaScript expressions in all data bindings: {{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} These expressions will be evaluated as JavaScript in the data scope of the current active instance. 4-6 © Copyright 2025 Zenika. All rights reserved DIRECTIVES 4-7 © Copyright 2025 Zenika. All rights reserved BASICS v-directive=“js expression” Now you see me A directive allows Vue to add a side effect to the DOM Starts with a v- The value is a Javascript expression 4-8 © Copyright 2025 Zenika. All rights reserved ARGUMENTS v-directive:arguments Pass arguments to the directive Denoted by a : after the directive's name 4-9 © Copyright 2025 Zenika. All rights reserved MODIFIERS v-directive.modifier Binds the directive a certain way Denoted by a. after the directive's name 4 - 10 © Copyright 2025 Zenika. All rights reserved DIRECTIVE SYNTAX 4 - 11 © Copyright 2025 Zenika. All rights reserved CONDITIONAL RENDERING 4 - 12 © Copyright 2025 Zenika. All rights reserved V-IF / V-ELSE / V-ELSE-IF / V-SHOW Built-in directives used to add or remove DOM element v-if example Title Subtitle done loading... Nothing to display v-show example SFC Playground 4 - 13 © Copyright 2025 Zenika. All rights reserved V-IF / V-ELSE / V-ELSE-IF / V-SHOW Alert displayed Title Subtitle Logout Login export default { data() { return { display: false, userStatus: 'loggedOut' } } } 4 - 14 © Copyright 2025 Zenika. All rights reserved V-IF / V-ELSE / V-ELSE-IF / V-SHOW What will it render in the DOM ? Rendered DOM: Alert displayed Title Subtitle Login 4 - 15 © Copyright 2025 Zenika. All rights reserved LIST RENDERING 4 - 16 © Copyright 2025 Zenika. All rights reserved V-FOR Built in directives used to render a list v-for="item in someArray" v-for="(item, index) in someArray" v-for="(value, key, index) in someObject" 4 - 17 © Copyright 2025 Zenika. All rights reserved V-FOR Example: {{ route.name }} export default { data() { return { routes: [ { name: 'ShowList' }, { name: 'ShowDetails' } ] } } } See SFC Playground 4 - 18 © Copyright 2025 Zenika. All rights reserved V-FOR To help Vue render each element correctly and faster, use the v-bind:key attribute with a unique identifier: {{ todo.label }} export default { data() { return { todos: [ { id: 1, label: 'Learn VueJS' }, { id: 2, label: 'Write some code' }] } } } See SFC Playground 4 - 19 © Copyright 2025 Zenika. All rights reserved V-FOR What will it render in the DOM ? {{ n }} {{ user.name }} alias {{ user.username }} {{index}}# {{key}}: {{value}} export default { data() { return { users: [ { id: 1, name: 'Evan You', username: '@yyx990803' }, { id: 2, name: 'Edd Yerburgh', username: '@eddyerburgh' } ], auth: { type: 'jwt', exp: 1501585988266 } } } } 4 - 20 © Copyright 2025 Zenika. All rights reserved V-FOR What will it render in the DOM ? Rendered DOM: 1 2 3 4 5 Evan You alias @yyx990803 Edd Yerburgh alias @eddyerburgh 0# type: jwt 1# exp: 1501585988266 4 - 21 © Copyright 2025 Zenika. All rights reserved CLASS AND STYLE BINDINGS 4 - 22 © Copyright 2025 Zenika. All rights reserved V-BIND Built-in directive used to dynamically bind an HTML attribute to a JS expression export default { data() { return { dynamicId: 28 } } } lorem ipsum Render: lorem ipsum See SFC Playground 4 - 23 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS Conditionally bind a CSS class to an element using v-bind:class Using an object: some link export default { data () { return { isActive: true } } } 4 - 24 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS What will it render? Render: isActive === true some link isActive === false some link See SFC Playground 4 - 25 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS Using an array: my message export default { data() { return { alertLevel: 'info', position: 'top' } } } 4 - 26 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS What will it render? Render: my message See SFC Playground 4 - 27 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS Using a ternary operator to return string: some tag export default { data() { return { isFinished: true } } } 4 - 28 © Copyright 2025 Zenika. All rights reserved V-BIND:CLASS What will it render? Render: isFinished === true some tag isFinished === false some tag See SFC Playground 4 - 29 © Copyright 2025 Zenika. All rights reserved V-BIND:STYLE Update CSS properties dynamically using v-bind:style some message export default { data () { return { color: '#00FF00' } } } 4 - 30 © Copyright 2025 Zenika. All rights reserved V-BIND:STYLE What will it render? some message See SFC Playground 4 - 31 © Copyright 2025 Zenika. All rights reserved EVENT HANDLING 4 - 32 © Copyright 2025 Zenika. All rights reserved V-ON Built-in directive used to listen to DOM or component events Example: Login export default { methods: { myAlert(message) { alert(`message: ${message}`) } } } See SFC Playground 4 - 33 © Copyright 2025 Zenika. All rights reserved V-ON - EVENT MODIFIERS.stop: event.stopPropagation().prevent: event.preventDefault().capture: uses capture mode when adding the event listener.self: only from the first component (ignored for nested component).once: the event is captured only once Click me export default { methods: { onClick() { alert('button clicked') } } } 4 - 34 © Copyright 2025 Zenika. All rights reserved V-ON - KEY MODIFIERS.enter: captures "Enter" key.tab: captures "Tab" key.delete: captures both “Delete” and export default { “Backspace” keys methods: { submit() {.esc: captures "Escape" key alert('submitting after clicking on Enter').space: captures "Space" key } }.up: captures "Arrow up" key }.down: captures "Arrow down" key See SFC Playground.left: captures "Arrow left" key.right: captures "Arrow right" key 4 - 35 © Copyright 2025 Zenika. All rights reserved SHORTHANDS 4 - 36 © Copyright 2025 Zenika. All rights reserved V-BIND v-bind:attr can be shortened to :attr The following Link Is the same as Link 4 - 37 © Copyright 2025 Zenika. All rights reserved V-ON v-on:event can be shortened to @event The following Is the same as 4 - 38 © Copyright 2025 Zenika. All rights reserved FORM INPUT BINDINGS 4 - 39 © Copyright 2025 Zenika. All rights reserved NAIVE IMPLEMENTATION Using v-bind and v-on 👍 Works well with input text or custom component 👎 Limited when using radio, select or multi-checkbox 4 - 40 © Copyright 2025 Zenika. All rights reserved V-MODEL Built-in directive used to provide "two-way" data bindings Text Checkboxes {{ selected }} export default { data() { export default { return { message: '' } data() { } return { selected: false } } } } See SFC Playground See SFC Playground 4 - 41 © Copyright 2025 Zenika. All rights reserved V-MODEL Multi-line text Multiple checkboxes Red {{ value }} Blue data() { return { value: '' } } } export default { data () { return { selected: ['red'] } } See SFC Playground } See SFC Playground 4 - 42 © Copyright 2025 Zenika. All rights reserved V-MODEL Radio Select Male one Female android {{ phoneType }} export default { data() { export default { return { gender: 'male' } data() { return { phoneType: '' } } } } } See SFC Playground See SFC Playground 4 - 43 © Copyright 2025 Zenika. All rights reserved V-MODEL - MODIFIERS.lazy: update model on change event {{ value }}.number: typecast to Number.trim: auto trim the value export default { data() { return { value: '' } } } See SFC Playground 4 - 44 © Copyright 2025 Zenika. All rights reserved SLOTS 4 - 45 © Copyright 2025 Zenika. All rights reserved SLOTS With slots, we can pass a template fragment to a child component, and let the child component render the fragment within its own template. This is the Dialog Content This is a dialog title See SFC Playground 4 - 46 © Copyright 2025 Zenika. All rights reserved SLOTS It will produce: This is a dialog title This is the Dialog Content 4 - 47 © Copyright 2025 Zenika. All rights reserved SLOTS - DEFAULT CONTENT It's possible to specify a default content by simply putting it in the slot element: This is a dialog title Default content 4 - 48 © Copyright 2025 Zenika. All rights reserved NAMED SLOTS It's also possible to name slots : This is a dialog title This is the Dialog Content 4 - 49 © Copyright 2025 Zenika. All rights reserved NAMED SLOTS It will produce: This is a dialog title This is the Dialog Content 4 - 50 © Copyright 2025 Zenika. All rights reserved Lab 4 4 - 51 © Copyright 2025 Zenika. All rights reserved OPTIONS API 5 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 5-1 © Copyright 2025 Zenika. All rights reserved OVERVIEW Components are custom HTML elements. They allow encapsulating reusable code. Each component should have its own behavior. 5-2 © Copyright 2025 Zenika. All rights reserved GLOBAL REGISTRATION We can use app.component(tagName, options) to register a component at the application level. Components declared this way are globally registered. import { createApp } from 'vue' import BaseButton from './components/BaseButton.vue' const app = createApp({}) app.component(BaseButton) app.mount('#app') We can then use it as a custom element in an instance’s template. 5-3 © Copyright 2025 Zenika. All rights reserved LOCAL REGISTRATION In a Vue application, generated by Vite or Vue CLI, we usually import and register components locally in other.vue components. import ComponentA from './ComponentA.vue' export default { components: { ComponentA } } ComponentA is a shorthand syntax for ComponentA: ComponentA where the key and the symbol have the same name. See SFC Playground 5-4 © Copyright 2025 Zenika. All rights reserved SINGLE FILE COMPONENTS Vue's big strength is the SFC pattern, as we saw in the Tooling chapter. Single File Component syntax See SFC Playground {{ message }} export default { data() { return {message: 'Hello World!'} } } h1 { font-size: 1.5rem; } 5-5 © Copyright 2025 Zenika. All rights reserved DEFINE A COMPONENT In tags you can directly export an object to define a component: export default { // component options. For example a prop: props: ['message'] } However, the recommendation is rather to use the defineComponent helper method which provides better type inference (even for a component written in Javascript). import { defineComponent } from 'vue' export default defineComponent({ // component options. For example a prop: props: ['message'] }) 5-6 © Copyright 2025 Zenika. All rights reserved DATA Like the Application Instance, we can put a data method in our component. This way, each component will have its own instance of its data and won't share it with other components. Data values can be used in a component's template: export default { Which will render: data() { return { message: 'Hello Vue' Hello Vue } } } See SFC Playground {{ message }} 5-7 © Copyright 2025 Zenika. All rights reserved PROPS Data can be passed down to child components using props. Props must be explicitly declared with the props option element. Props can be used inside templates. // src/components/StrongMessage.vue import StrongMessage from export default { '@/components/StrongMessage.vue' props: ['message'] } export default { components: { StrongMessage } } {{ message }} See SFC Playground 5-8 © Copyright 2025 Zenika. All rights reserved PROPS To specify a prop type, you can list them as an object, where the properties' names and values contain the prop names and types: import { defineComponent } from 'vue' export default defineComponent({ props: { message: String }, }) 5-9 © Copyright 2025 Zenika. All rights reserved PROPS And even further: import { defineComponent } from 'vue' export default defineComponent({ props: { message: { type: String, required: false, default: 'Default message', validator: value => value.length > 0 } }, }) 5 - 10 © Copyright 2025 Zenika. All rights reserved PASSING PROPS WITH V-BIND We can give expression to component props with v-bind. import { defineComponent } from 'vue' export default defineComponent({ data() { return { description: 'Short show description', isFavorite: true, } } }) 5 - 11 © Copyright 2025 Zenika. All rights reserved METHODS Methods are functions declared in our component instance. They can be declared using the methods option element. They can only be used in the component’s scope. // src/components/LikesButton.vue import LikesButton from export default { '@/components/LikesButton.vue' data() { export default { return { likes: 0 } components: { }, LikesButton methods: { } addLike() { this.likes++ } } } } {{ likes }} See SFC Playground 5 - 12 © Copyright 2025 Zenika. All rights reserved EVENTS It is possible to emit and listen events. Emit the event from the child component In the parent component, intercept the by using $emit: event emitted by using v-on directive: {... emits: ['my-event'], methods: { or: validate() { this.$emit('my-event') } } } See SFC Playground 5 - 13 © Copyright 2025 Zenika. All rights reserved EVENTS - WITH PAYLOAD From the child component, emit the In the parent component: custom event and pass any argument you want in the 2nd parameter of the $emit function: {... { methods: {... myFunction(payload) { emits: ['my-event'] // payload === { data: 'some data' } methods: { } validate() { } this.$emit('my-event', { data: 'some } data' }) } } } See SFC Playground 5 - 14 © Copyright 2025 Zenika. All rights reserved VALIDATING EVENTS Similar to prop type validation, an emitted event can be validated if it is defined with the object syntax instead of the array syntax. export default { emits: { increment click: null, // Validate increment event increment: (value) => { See SFC Playground if (!value || typeof value !== 'number') { console.warn('Invalid increment event payload !') return false } return true } } } 5 - 15 © Copyright 2025 Zenika. All rights reserved COMPUTED PROPERTIES Computed properties are used to avoid complex expressions in templates. We can use them with the computed option element. A computed property will only re-evaluate when some of its dependencies have changed. export default { data () { {{ fullName }} return { firstName: 'Evan', lastName: 'You' } Renders: }, computed: { Evan You fullName () { return `${this.firstName} ${this.lastName}` See SFC Playground } } } 5 - 16 © Copyright 2025 Zenika. All rights reserved WATCHERS With computed properties we can handle most cases. But for some specific cases we can use watchers. Useful for modifying data based on asynchronous or expensive operations. 5 - 17 © Copyright 2025 Zenika. All rights reserved WATCHERS Example : export default { data () { return { count: 0 } }, watch: { count(newValue, oldValue) { console.log(newValue, oldValue) } } } {{count}} See SFC Playground 5 - 18 © Copyright 2025 Zenika. All rights reserved WATCHERS Only use a watcher when it's relevant. A computed is often a better idea. Below is a bad example as it should be a computed. export default { data() { return { firstName: '', reversedFirstName: '' } }, watch: { firstName(value) { this.reversedFirstName = value.split('').reverse().join('') } } } {{ reversedFirstName }} 5 - 19 © Copyright 2025 Zenika. All rights reserved LIFECYCLE HOOKS Lifecycle hooks gives the opportunity to add our own code at specific stages Lifecycle hooks are non blocking for the render export default { created() { // lifecycle hook appropriate for an API call console.log("I'm created"); }, mounted() { // interaction with the DOM possible console.log("I'm mounted"); }, beforeUnmount() { // can be used to clean up timers, listeners console.log("I'm beforeUnmount"); }, } See SFC Playground for more interactivity 5 - 20 © Copyright 2025 Zenika. All rights reserved LIFECYCLE DIAGRAM 5 - 21 © Copyright 2025 Zenika. All rights reserved V-MODEL - CUSTOM COMPONENT v-model can also be used on custom Now v-model directive works perfectly components. The custom component must with this component: define a modelValue prop and emit the event update:modelValue. import { defineComponent } from 'vue' import BaseInput from './BaseInput.vue' // BaseInput.vue export default defineComponent({ import { defineComponent } from 'vue' components: { BaseInput }, data () { export default defineComponent({ return { message: '' } props: ['modelValue'], } emits: ['update:modelValue'] }) }) See SFC Playground 5 - 22 © Copyright 2025 Zenika. All rights reserved Lab 5 5 - 23 © Copyright 2025 Zenika. All rights reserved 5 - 24 © Copyright 2025 Zenika. All rights reserved COMPOSITION API 6 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 6-1 © Copyright 2025 Zenika. All rights reserved API STYLES Vue components can be authored in two different API styles: Options API and Composition API. While both API styles are fully capable of covering common use cases, the Composition API offers some benefits over Options API: Better Logic Reuse More Flexible Code Organization Better Type Inference See Composition API FAQ for a high level overview 6-2 © Copyright 2025 Zenika. All rights reserved COMPOSITION API WITH SETUP LIFECYCLE HOOK 6-3 © Copyright 2025 Zenika. All rights reserved COMPOSITION API Until now, we used what is called the Options API. Coming with Vue 3, the Composition API and it's setup method is a new way to write our components. export default { props: { user: { type: String } }, setup(props) { console.log(props) // { user: '' } return {} // anything returned here will be available for the rest of the component },... } 6-4 © Copyright 2025 Zenika. All rights reserved COMPOSITION API The setup lifecycle hook is executed before the component is created, once the props are resolved but before getting access to this. It is the only place in a Vue component where we can use the reactivity API. Except for props, you won't be able to access any properties declared in the component – local state, computed properties or methods. Everything that we return from setup will be exposed to the rest of our component (computed properties, methods, lifecycle hooks and so on) as well as to the component's template. 6-5 © Copyright 2025 Zenika. All rights reserved JAVASCRIPT DEFAULT REACTIVITY JavaScript is executed line by line and changes aren't necessarily reflected in previous declarations. let val1 = 2 let val2 = 3 let sum = val1 + val2 console.log(sum) // 5 val1 = 3 console.log(sum) // sum is still 5... With the Composition API comes the Reactivity API. 6-6 © Copyright 2025 Zenika. All rights reserved VUE REACTIVITY API Both the Options API and the Composition API use the Reactivity API under the hood. Both can be used, even in the same component. The object returned from setup() can be used inside Options API, but not the other way around. import { ref } from 'vue' export default { setup() { // composition API const counter = ref(0) return { counter } }, mounted () { console.log("counter: ", this.counter) // logs 0 } } See SFC Playground 6-7 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: DATA To create a reactive state from a primitive value, we can use the ref method: counter: {{ counter }} import { ref } from 'vue' export default { setup() { const counter = ref(0) console.log(counter) // { value: 0 } console.log(counter.value) // 0 }, } ref creates a wrapper around our object, which means we have to use.value to access or modify its value. ref values are auto-unwrapped in template which means we don't have to do count.value 6-8 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: DATA To create a reactive state, like we did with the data object from Options API, we can use the reactive method: import { reactive } from 'vue' user status: export default { {{ state.userStatus }} setup() { const state = reactive({ display: false, userStatus: 'loggedOut' }) See SFC Playground state.display = true; console.log(state) // { display: true, userStatus: 'loggedOut' } return { state } } } 6-9 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: DATA For reactive values, we can combine the spread operator... and the toRefs method: import { reactive, toRefs } from 'vue' Using the spread operator without export default { toRefs breaks the reactivity as it setup() { creates a new classic JS object. const data = reactive({ display: true, userStatus: 'loggedOut' See SFC Playground }) return {...toRefs(data)} // ✅{ display, userStatus} // return { data } ✅ { data.display, data.userStatus} // return {...data } display, userStatus} ❌ breaks reactivity { } } 6 - 10 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: METHODS Just replace it by a simple vanilla function. Don't forget that this doesn't exist yet: hello Toggle display import { ref } from 'vue' export default { setup() { const display = ref(true) function toggleUserDisplay () { display.value = !display.value } return { display, toggleUserDisplay } } } See SFC Playground 6 - 11 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: COMPUTED Use the computed method: {{ fullName }} import { computed } from 'vue' export default { props: ['firstName', 'lastName'], setup(props) { const fullName = computed(() => `${props.firstName} ${props.lastName}`) return { fullName } } } Note that we are using the props parameter of the setup function. props are already exposed in the template and don't need to be returned. See SFC Playground 6 - 12 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: WATCHEFFECT The watchEffect method will execute a callback function automatically when one of the reactive dependency is updated: counter: {{ counter }} export default { setup() { const counter = ref(0) See SFC Playground watchEffect(() => { console.log('counter value:', counter.value) }) return { counter } } } If you want to separate dependency tracking from the side effect and have more control over when the callback should fire, you can use watch. 6 - 13 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: EVENTS If you want to emit an event, use the second parameter of the setup method: Emit an event export default { emits: ['my-event'], setup(props, { emit }) { const emitEvent = () => { emit('my-event', {data: 'some data'}) } return { emitEvent } } } The second parameter is a context that contains other non-reactive objects. It means we can destructure it safely, without losing reactivity. See SFC Playground 6 - 14 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: COMPONENT METADATA Some component properties can't be moved to the setup method: export default { name: 'ComponentName', components: { }, props: [], emits: [], setup() { //... } } This is our component metadata and should remain as is. 6 - 15 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: LIFECYCLE HOOKS Lifecycle hooks in the Composition API have the same name as their counterpart in the Options API but are prefixed with on: i.e. mounted becomes onMounted. These functions accept a callback that will be executed when the hook is called by the component. import { onMounted } from 'vue' export default { setup () { onMounted(() => { console.log('on mounted') }) } } See SFC Playground 6 - 16 © Copyright 2025 Zenika. All rights reserved Lab 6-1 6 - 17 © Copyright 2025 Zenika. All rights reserved COMPOSITION API WITH SCRIPT SETUP 6 - 18 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: SCRIPT SETUP script setup is a compile-time syntactic sugar for using Composition API inside Single File Components (SFCs). It is the recommended syntax if you are using both SFCs and Composition API. It provides a number of advantages over the normal syntax: More succinct code with less boilerplate Ability to declare props and emitted events using pure TypeScript Better runtime performance (the template is compiled into a render function in the same scope, without an intermediate proxy) Better IDE type-inference performance (less work for the language server to extract types from code) 6 - 19 © Copyright 2025 Zenika. All rights reserved COMPOSITION API: SCRIPT SETUP We can use all we saw in the setup lifecycle hook directly in a script setup section. One advantage over setup method is that you don't need to return anything. Any top-level bindings are directly usable in the template. import { ref, watch } from 'vue' const counter = ref(0) const increment = () => { counter.value++ } watch(counter, () => { console.log('counter updated', counter.value) }) {{ counter }} See SFC Playground 6 - 20 © Copyright 2025 Zenika. All rights reserved SCRIPT SETUP: REACTIVITY Use the Reactivity API, like the ref, reactive, computed methods. import { ref, computed } from 'vue' const counter = ref(2) const doubleCounter = computed(() => counter.value * 2) counter: {{ counter }} double counter: {{ doubleCounter }} See SFC Playground 6 - 21 © Copyright 2025 Zenika. All rights reserved SCRIPT SETUP: USING COMPONENTS Imported components can be used directly without registering them. import HelloWorld from '@/components/HelloWorld.vue' See SFC Playground 6 - 22 © Copyright 2025 Zenika. All rights reserved SCRIPT SETUP: DEFINING PROPS To define props, use the compiler macro defineProps that is usable only inside script setup. It does not need to be imported. defineProps accepts the same value as the props option from Options API. const props = defineProps({ message: { type: String, required: true }, }) console.log(props.message) See SFC Playground 6 - 23 © Copyright 2025 Zenika. All rights reserved SCRIPT SETUP: DEFINING EMITS To define emits, use the compiler macro defineEmits that is usable only inside script setup. It does not need to be imported. defineEmits accepts the same value as the emits option from Options API. const emits = defineEmits(['toggle-favorite']) emits('toggle-favorite') See SFC Playground 6 - 24 © Copyright 2025 Zenika. All rights reserved Lab 6-2 6 - 25 © Copyright 2025 Zenika. All rights reserved REUSABILITY 7 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 7-1 © Copyright 2025 Zenika. All rights reserved CUSTOM DIRECTIVE 7-2 © Copyright 2025 Zenika. All rights reserved CUSTOM DIRECTIVE In addition to the default set of directives, Vue also allows you to register your own custom directives. Register it globally from src/main.js: Register it locally: const app = createApp(App) export default {.directive('focus', { directives: { mounted (el) { focus: { el.focus() mounted(el) { } el.focus() }) } app.mount('#app') } } } See SFC Playground 7-3 © Copyright 2025 Zenika. All rights reserved DIRECTIVE HOOKS 7-4 © Copyright 2025 Zenika. All rights reserved DIRECTIVE HOOKS ARGUMENTS el: The element the directive is bound to. This can be used to directly manipulate the DOM. binding: An object containing some contextual properties, like the value, arguments or modifiers provided to the directive Apart from el, you should treat the binding argument as read-only and never modify its content. 7-5 © Copyright 2025 Zenika. All rights reserved FUNCTION SHORTHAND Most of the time you'll want the same behavior on mounted and updated, but won't care for other hooks. Vue provides a shorthand notation for this, by defining the directive as a function: app.directive('focus', (el, binding) => { el.focus() }) 7-6 © Copyright 2025 Zenika. All rights reserved PLUGINS 7-7 © Copyright 2025 Zenika. All rights reserved WHY PLUGINS ? Register one or more global components or custom directives with app.component() and app.directive(). Make a resource injectable throughout the app by calling app.provide(). Add some global instance properties or methods by attaching them to app.config.globalProperties. A library that needs to perform some combination of the above (e.g. vue-router). 7-8 © Copyright 2025 Zenika. All rights reserved USING A PLUGIN Just call app.use() import { createApp } from 'vue' import App from './App.vue' import myPlugin from './plugins/myPlugin' const app = createApp(App) app.use(myPlugin, { myPluginOption: true }) app.use() automatically prevents multiple initialization import myPlugin from './plugins/myPlugin' //... app.use(myPlugin, { config: 1 }) app.use(myPlugin, { config: 2 }) app.use(myPlugin, { config: 3 }) // myPlugin is initialized with {config: 1} 7-9 © Copyright 2025 Zenika. All rights reserved USING A PLUGIN awesome-vue, the list of Vue plugins 7 - 10 © Copyright 2025 Zenika. All rights reserved WRITING A PLUGIN A plugin is defined as either an object that exposes an install() method, or simply a function that acts as the install function itself. // src/plugins/myPlugin.js // src/plugins/myPlugin.js export default { export default function (app, options) { install: (app, options) => { } } } 7 - 11 © Copyright 2025 Zenika. All rights reserved WRITING A PLUGIN From a plugin, you can do everything you can do with the Application Instance, like; register global components with app.component() register custom directives with app.directive() provide data to every component with app.provide() export default { install: (app, options) => { app.component('my-component', { }) app.directive('focus', (el) => el.focus()) app.provide('message', 'hello vuejs') } } 7 - 12 © Copyright 2025 Zenika. All rights reserved WRITING A PLUGIN You can also add global properties, by using app.config.globalProperties. We usually prefix them by $ export default { install: (app, options) => { app.config.globalProperties.$msg = 'hello vuejs' } } You can now access $msg from every component export default { mounted() { console.log(this.$msg) } } 7 - 13 © Copyright 2025 Zenika. All rights reserved COMPOSABLES 7 - 14 © Copyright 2025 Zenika. All rights reserved COMPOSABLES (1/2) In the context of Vue applications, a composable is a function that leverages Vue's Composition API to encapsulate and reuse stateful logic. A simple example would be a composable that encapsulates some logic related to a counter: // src/composables/useCounter.js import { ref } from 'vue' export function useCounter (initialCounter = 0) { const counter = ref(initialCounter) function increment() { counter.value++ } return { counter, increment } } 7 - 15 © Copyright 2025 Zenika. All rights reserved COMPOSABLES (2/2) Let's use the useCounter composable created previously: {{ counter }} import { useCounter } from '@/composables/useCounter' const { counter, increment } = useCounter(4) See SFC Playground 7 - 16 © Copyright 2025 Zenika. All rights reserved LEVERAGE THE POWER OF VUEUSE VueUse is a rich collection of utility functions based on Composition API. If you have a specific need for your application, it is very likely that a method already exists on VueUse. 7 - 17 © Copyright 2025 Zenika. All rights reserved 7 - 18 © Copyright 2025 Zenika. All rights reserved Lab 7 7 - 19 © Copyright 2025 Zenika. All rights reserved ROUTING 8 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 8-1 © Copyright 2025 Zenika. All rights reserved VUE-ROUTER vue-router is the official routing library for Vue, allowing developers to create SPA-like navigation within Vue applications by mapping URL to components. It facilitates seamless transitions between different views and manages the application's navigation state. 8-2 © Copyright 2025 Zenika. All rights reserved INSTALLATION (1/4) If you use create-vue and select vue-router, then the library is properly installed. Otherwise, install it via npm : npm install vue-router 8-3 © Copyright 2025 Zenika. All rights reserved INSTALLATION (2/4) Create the router instance by defining some routes and provide an history mode. // src/router.js import Home from '@/views/Home.vue' import About from '@/views/About.vue' import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ // Provide the history implementation to use. history: createWebHistory(), // Define some routes. Each route should map to a component. routes: [ { path: '/', component: Home }, { path: '/about', component: About }, ], }) export default router 8-4 © Copyright 2025 Zenika. All rights reserved INSTALLATION (3/4) Make the whole app router-aware: // src/main.ts import { createApp } from 'vue' import App from './App.vue' import router from '@/router' const app = createApp(App) app.use(router) app.mount('#app') 8-5 © Copyright 2025 Zenika. All rights reserved INSTALLATION (4/4) Update the App.vue component to include the and components. The component serves as a placeholder for where the content of the current route will be rendered. Go to Home Go to About 8-6 © Copyright 2025 Zenika. All rights reserved DYNAMIC ROUTE MATCHING (1/2) Very often we will need to map routes with the given pattern to the same component. For example, we may have a ShowDetails component which should be rendered for all TV Shows but with different IDs. In Vue Router we can use a dynamic param in the path to achieve that. import { createRouter, createWebHistory } from 'vue-router' Show details id: {{ import ShowDetails from $route.params.id }} '@/views/ShowDetails.vue' const router = createRouter({ history: createWebHistory(), By doing this, we will be able to routes: [ navigate to shows/1 or shows/2 and { path: '/shows/:id', use the same ShowDetails component: ShowDetails component. } ] }) 8-7 © Copyright 2025 Zenika. All rights reserved DYNAMIC ROUTE MATCHING (2/2) We saw that our props are accessible in $route.params but this creates a tight coupling with the route which limits the flexibility of the component. You can instead pass props to the route components. import { createRouter, createWebHistory } from 'vue-router' Show details id: {{ id }} import ShowDetails from '@/views/ShowDetails.vue' const router = createRouter({ history: createWebHistory(), routes: [ defineProps(['id']) { path: '/shows/:id', component: ShowDetails // define props to true to pass dynamic params as props props: true } ] }) 8-8 © Copyright 2025 Zenika. All rights reserved NAMED ROUTES Each route can have a name in addition to a path. This can be specified in the route declaration. const routes = [ path: '/shows', TV Shows name: 'showList', component: ShowList }, Game of thrones component: ShowDetails } ] 8-9 © Copyright 2025 Zenika. All rights reserved REDIRECTION Vue router allows us to redirect path, for example redirect from /foo to /bar const routes = [ { path: '/foo', redirect: '/bar' } ] We can also use a function instead of passing a hard coded path : const routes = [ { path: '/foo', redirect: (to) => { // the function receives the target route as the argument // we return a redirect path/location here. } } ] 8 - 10 © Copyright 2025 Zenika. All rights reserved CATCH ALL / 404 NOT FOUND ROUTE We can catch all route and link it to a 404 route by adding a regexp inside parentheses right after the param. In the example below, if the user goes to a route different than the homepage, then he will be redirected to the NotFound component. const routes = [ {path: '/', name: 'homepage', component: Homepage}, // will match everything and put it under `$route.params.pathMatch` {path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound}, ] 8 - 11 © Copyright 2025 Zenika. All rights reserved LAZY-LOADING ROUTES When an application starts to grow, its bundle does too. A good solution to gain performance is to load on demand when the route is accessed: import Homepage from '@/views/HomePage.vue' const routes = [ { path: '/', component: Homepage }, { path: '/other', component: () => import('./routes/LazyLoadedPage.vue') } ] Thanks to () => import('...') our file LazyLoadedPage will only load when visited. 8 - 12 © Copyright 2025 Zenika. All rights reserved HOW TO RETRIEVE THE CURRENT ROUTE ? Use the $route variable inside a template. {{ $route }} Inside a script, use the useRoute helper method when authoring your component with Composition API: // Equivalent to using $route inside templates const route = useRoute() console.log(route) 8 - 13 © Copyright 2025 Zenika. All rights reserved HOW TO RETRIEVE THE ROUTER INSTANCE ? Use the $router variable inside a template. {{ $router }} Inside a script, use the useRouter helper method when authoring your component with Composition API: // Equivalent to using $router inside templates const router = useRouter() function goHome() { router.push('/') } 8 - 14 © Copyright 2025 Zenika. All rights reserved NAVIGATION GUARDS The navigation guards provided are primarily used to guard navigations either by redirecting it or canceling it. There are 3 ways to hook into the route navigation process: globally per-route in-component 8 - 15 © Copyright 2025 Zenika. All rights reserved GLOBAL GUARDS You can register global before guards using router.beforeEach or router.afterEach: const router = createRouter({ // etc... }) router.beforeEach(async (to, from) => { if (!canAccess()) { // redirect the user to the login page return '/login' } }) router.afterEach((to, from) => { sendToAnalytics(to.fullPath) }) 8 - 16 © Copyright 2025 Zenika. All rights reserved PER-ROUTE GUARDS You can also add guard on specific routes with the beforeEnter property: const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { }, }, ] 8 - 17 © Copyright 2025 Zenika. All rights reserved IN COMPONENT GUARDS Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration). import { onBeforeRouteLeave } from 'vue-router' onBeforeRouteLeave(() => { const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (!answer) return false }) 8 - 18 © Copyright 2025 Zenika. All rights reserved 8 - 19 © Copyright 2025 Zenika. All rights reserved Lab 8 8 - 20 © Copyright 2025 Zenika. All rights reserved DATA FETCHING 9 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 9-1 © Copyright 2025 Zenika. All rights reserved DATA FETCHING Most of the time we want to fetch data from a remote source or consume an API in our applications. Initially, vue-resource was used to fulfill this need but since the creator of Vuejs (Evan You) announced that the VueJS team is discontinuing vue-resource, Axios is recommended. 9-2 © Copyright 2025 Zenika. All rights reserved AXIOS Axios is one of the most popular HTTP client libraries. It uses promises by default and runs on both the client and the server (so we can combine with async/await). Moreover, it is universal, supports cancellation and has TypeScript definitions. Installation: npm install axios --save 9-3 © Copyright 2025 Zenika. All rights reserved FETCHING DATA Here is a basic example of how we can get some data from a URL : import { onBeforeMount, ref } from 'vue' import axios from "axios"; const todos = ref([]) onBeforeMount(async () => { const response = await axios.get( "https://jsonplaceholder.typicode.com/todos" ); todos.value = response.data; }) 9-4 © Copyright 2025 Zenika. All rights reserved POST DATA Example : Submit import axios from "axios"; async function onSubmit() { await axios.post("https://jsonplaceholder.typicode.com/todos", { title: "Become a ninja with Vue 3", completed: false }); } 9-5 © Copyright 2025 Zenika. All rights reserved CREATE A BASE INSTANCE Axios allows us to create a base instance to make it easier to configure. We could provide parameters such as base URL or headers. // src/api.js export const httpClient = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com', headers: { Accept: 'application/json' } }) import { onBeforeMount } from 'vue' import { httpClient } from '@/api' onBeforeMount(async () => { try { const response = await httpClient.get('/todos') console.log(response.data) } catch (error) { console.log(error) } }) 9-6 © Copyright 2025 Zenika. All rights reserved 9-7 © Copyright 2025 Zenika. All rights reserved Lab 9 9-8 © Copyright 2025 Zenika. All rights reserved STORE 10 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 10 - 1 © Copyright 2025 Zenika. All rights reserved STATE MANAGEMENT IN VUEJS When your application grows, it is often necessary to be able to share data between several components. Several options are possible to manage the state of your application: the Reactivity API (which we will discuss in the advanced part of this training) and the use of a dedicated library. Vuex has long been the official library but since version 3 is the default version of Vue, Pinia has become the recommended library for state management. 10 - 2 © Copyright 2025 Zenika. All rights reserved PINIA Pinia is a store library. It allows you to share a state across components/pages. It is similar in philosophy to Vuex, except that it doesn't differentiate between mutations and actions. There are only actions and they can be both synchronous and asynchronous and mutate the state directly without the need to call a mutation, making the use of the library simpler. The Pinia library integrates seamlessly with VueJs: Devtools support Hot module replacement Plugins to extend Pinia features and fit our needs TypeScript support and autocompletion for JS users Server Side Rendering support 10 - Last 3 but not least, Pinia has a dedicated playground © Copyright. rights reserved 2025 Zenika. All INSTALL PINIA Install the dependency npm install pinia Create a Pinia instance by invoking the createPinia method and pass it to the application instance: // src/main.js import { createPinia } from 'pinia' import { createApp } from 'vue' import App from '@/App.vue' const app = createApp(App) app.use(createPinia()) 10 - 4 © Copyright 2025 Zenika. All rights reserved DEFINE A STORE A store is an entity holding state and business logic. You can use the defineStore method to define a store. There is 2 possible syntaxes to define stores: an Options syntax, similar to the Options API a Composition syntax, similar to the Composition API 10 - 5 © Copyright 2025 Zenika. All rights reserved DEFINE A STORE WITH OPTIONS SYNTAX (1/4) The store has three concepts: the state, getters and actions. Basically they are the equivalent of data, computed and methods from Options API. // src/stores/index.js import { defineStore } from 'pinia' // the first argument is a unique id of the store across your application export const useStore = defineStore('main', { state () { return { } }, getters: { }, actions: { }, }) 10 - 6 © Copyright 2025 Zenika. All rights reserved DEFINE A STORE WITH OPTIONS SYNTAX (2/4) The state is defined as a function that returns the initial state. // src/stores/index.js import { defineStore } from 'pinia' // the first argument is a unique id of the store across your application export const useStore = defineStore('main', { state () { return { counter: 1 } }, }) 10 - 7 © Copyright 2025 Zenika. All rights reserved DEFINE A STORE WITH OPTIONS SYNTAX (3/4) Getters are the equivalent of computed values for the state of a Store. // src/stores/index.js import { defineStore } from 'pinia' export const useStore = defineStore('main', { state () { return { counter: 0, } }, getters: { // compute a value from the state doubleCount() { return this.counter * 2 }, // access other getter doubleCountPlusOne() { return this.doubleCount + 1 }, }, }) 10 - 8 © Copyright 2025 Zenika. All rights reserved DEFINE A STORE WITH OPTIONS SYNTAX (4/4) Actions are the equivalent of methods in components. They can be synchronous and asynchronous. Actions get access to the whole store instance through this. import { defineStore } from 'pinia' import axios from 'axios' export const useStore = defineStore('main', { state () { return { counter: 0 } }, actions: { increment() { this.counter++ }, async randomizeCounter() { const { data } = await axios.get('http://www.randomnumberapi.com/api/v1.0/random') if (data.count && data.count.length > 0) { this.counter = data.count } }, }, 10 - 9 © Copyright 2025 Zenika. All rights reserved }) DEFINE A STORE WITH COMPOSITION SYNTAX You can also leverage the Reactivity API to define your store. In that case, you can pass in a function that defines reactive properties and methods and returns an object with the properties and methods you want to expose. import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const useStore = defineStore('counter', () => { // ref()s become state properties const counter = ref(0) // computed()s become getters const doubleCounter = computed(() => counter.value * 2) // function()s become actions function increment () { counter.value++ } return { counter, increment, doubleCounter } }) 10 - 10 © Copyright 2025 Zenika. All rights reserved WHICH SYNTAX SHOULD YOU CHOOSE ? Pick the one that you feel the most comfortable with. As we studied Composition API more than Options API during this training, you can try Composition syntax first. Perhaps an important thing to keep in mind is to stay consistent within a single project. 10 - 11 © Copyright 2025 Zenika. All rights reserved USE THE STORE (OPTIONS API) If your Vue component is written with Options API, you can use the helpers Pinia provides to access the state, getters and actions. One of them is the mapStores() helper which allows to access the whole store. import { mapStores } from 'pinia' import { useStore } from '@/stores' export default { computed: {...mapStores(useStore) }, mounted() { console.log(this.mainStore.counter) this.mainStore.increment() }, } The consequence of spreading...mapStores inside computed is that it declares a new variable whose name is the concatenation of the store name (main in this case) and the suffix Store => this.mainStore. 10 - 12 © Copyright 2025 Zenika. All rights reserved USE THE STORE (COMPOSITION API) If you Vue Component is written with Composition API, this is straightforward. Import your store and invoke it. Then you have access to your state, getters and actions. import { useStore } from '@/stores' const store = useStore() console.log(store.counter) 10 - 13 © Copyright 2025 Zenika. All rights reserved 10 - 14 © Copyright 2025 Zenika. All rights reserved Lab 10 10 - 15 © Copyright 2025 Zenika. All rights reserved TESTING 11 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 11 - 1 © Copyright 2025 Zenika. All rights reserved WHY TEST Writing tests on an application allows you to: prevent regression increase our confidence in what we deliver in production encourages developers to better organize code into easy-to-test modules 11 - 2 © Copyright 2025 Zenika. All rights reserved TESTING TYPES unit: verifies that inputs to a given function, class, or composable produce the expected output or side effects component: verifies that your component mounts, displays, can interact with, and behaves as expected. e2e: verifies functionality of the application as a whole, possibly involving multiple pages and interaction with an API 11 - 3 © Copyright 2025 Zenika. All rights reserved UNIT TESTS 11 - 4 © Copyright 2025 Zenika. All rights reserved RECOMMENDATIONS Jest if your project was scaffolded with Vue CLI. Vitest if it's a Vite project 11 - 5 © Copyright 2025 Zenika. All rights reserved EXAMPLE export function toggleFavorite(show) { show?.user?.favorited = !show?.user?.favorited } import { toggleFavorite } from './toggle-favorite.js' describe('toggleFavorite', () => { test('toggles favorite status', () => { const show = { user: { favorited: true } } toggleFavorite(show) expect(show.user.favorited).toBe(false) toggleFavorite(show) expect(show.user.favorited).toBe(true) }) test('does not throw error if show is null', () => { expect(() => toggleFavorite(null)).not.toThrow() expect(() => toggleFavorite(undefined)).not.toThrow() }) }) 11 - 6 © Copyright 2025 Zenika. All rights reserved COMPONENT TESTING 11 - 7 © Copyright 2025 Zenika. All rights reserved RECOMMENDATIONS Like unit tests, use Jest (for Vue CLI) or Vitest (for Vite) Component testing often involves mounting the component being tested in isolation. You will therefore need a mounting library. There are two very popular mounting libraries: VUE TESTING LIBRARY Simple and complete testing utilities that encourage good testing practices VUE TEST UTILS (LOW LEVEL, USED BY VUE-TESTING-LIBRARY) As this library exposes some low level API, we can write fragile tests by testing implementation details. So be careful ! When testing, remember to test what a component does, not how it does it. 11 - 8 © Copyright 2025 Zenika. All rights reserved EXAMPLE We will show how to implement a test with both libraries by using a simple example. {{ title }} : {{ counter }} - + import { ref } from 'vue' const props = defineProps(['title']) const counter = ref(0) function increment () { counter.value++ } function decrement () { counter.value-- } See SFC Playground 11 - 9 © Copyright 2025 Zenika. All rights reserved WITH VUE-TESTING-LIBRARY Check the docs here import { render, fireEvent } from '@testing-library/vue' import Counter from '@/components/Counter.vue' describe('Counter', () => { it('should increment the counter', async () => { // The render method returns a collection of utilities to query your component. const { getByText } = render(Counter, { props: { title: 'My counter', } }) // getByText returns the first matching node for the provided text, and // throws an error if no elements match or if more than one match is found. getByText('My counter : 0') const incrementBtn = getByText('+') await fireEvent.click(incrementBtn) getByText('My counter : 1') }) }) 11 - 10 © Copyright 2025 Zenika. All rights reserved WITH VUE-TEST-UTILS Check the Documentation import { mount } from '@vue/test-utils' import Counter from '@/components/Counter.vue' describe('Counter', () => { it('should increment the counter', () => { // Create an instance of our component const wrapper = mount(Counter, { props: { title: 'My counter', } }) expect(wrapper.text()).toContain('My counter : 0') wrapper.find('#increment').trigger('click') wrapper.vm.increment() // => bad way to test your component expect(wrapper.text()).toContain('My counter : 2') expect(wrapper.vm.counter).toBe(2) // => bad way to test your component }) }) 11 - 11 © Copyright 2025 Zenika. All rights reserved E2E 11 - 12 © Copyright 2025 Zenika. All rights reserved RECOMMENDATIONS There are two very popular libraries: CYPRESS All-in-one testing framework, assertion library, with mocking and stubbing features. PLAYWRIGHT Like Cypress, it is also a valuable E2E testing solution with a wider range of browser support (mainly WebKit). 11 - 13 © Copyright 2025 Zenika. All rights reserved EXAMPLE WITH CYPRESS Check the Documentation describe('ShowList', () => { it('renders the list of shows', () => { cy.visit('/') cy.get('h1').contains('My TV shows') cy.get('[data-cy=card-show]').should('have.length', 9) }) }) Cypress runs as fast as your browser can render content. You can watch tests run in real time as you develop your applications. 11 - 14 © Copyright 2025 Zenika. All rights reserved 11 - 15 © Copyright 2025 Zenika. All rights reserved Lab 11 11 - 16 © Copyright 2025 Zenika. All rights reserved TYPESCRIPT 12 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 12 - 1 © Copyright 2025 Zenika. All rights reserved INTRODUCTION Typescript is a typed superset of JavaScript that compiles to plain JavaScript. With its type system, TypeScript can catch many common errors through static analysis at build time. This reduces the risk of runtime errors in production and also allows us to refactor code with more confidence in large-scale applications. Also TypeScript greatly improves developer experience through type-based autocompletion in IDEs. 12 - 2 © Copyright 2025 Zenika. All rights reserved INSTALLATION Vue is written in TypeScript itself and provides first-class TypeScript support. When you run the npm init vue, select Typescript and everything will be configured properly. Then add lang="ts" on the script and your component becomes TS compatible. import { ref, onMounted } from 'vue' const title = ref('Hello world') // Type will be Ref const array = ref(['Paul', 0]) // Type will be Ref onMounted(() => { title.value = 0 // This will throw a type error }) See Typescript Support in the doc for more detail. 12 - 3 © Copyright 2025 Zenika. All rights reserved TYPING COMPONENT PROPS (1/3) The defineProps() macro infers the props types based on its argument: const props = defineProps({ label: { type: String, required: true }, counter: Number }) props.label // string props.counter // number | undefined 12 - 4 © Copyright 2025 Zenika. All rights reserved TYPING COMPONENT PROPS (2/3) If you have some complex types, you can use the as keyword: interface Show { title: string seasons: number } const props = defineProps({ show: { type: Object as () => Show, required: true }, shows: { type: Array as () => Show[], default: [] }, }) props.show.title // string props.shows.map((show) => { show.title // string }); 12 - 5 © Copyright 2025 Zenika. All rights reserved TYPING COMPONENT PROPS (3/3) You can also define props with pure types via a generic type argument. To declare default values for the props, you can use the withDefaults compiler macro. interface Show { title: string seasons: number } const props = withDefaults(defineProps(), { shows: () => [] }) In version 3.2 and below, the generic type parameter for defineProps() were limited to a type literal or a reference to a local interface. 12 - 6 © Copyright 2025 Zenika. All rights reserved TYPING COMPONENT EMITS The emit function can be typed using type declaration. const emit = defineEmits() emit('update-show', 6) // OK emit('toggle-favorite') // OK emit('toggle-favorite', 'test') // Error because a payload is not allowed emit('toggle') // Error because the event does not exist 12 - 7 © Copyright 2025 Zenika. All rights reserved TYPING REF() ref() infer the type from the initial value. import { ref } from 'vue' // inferred type: Ref const counter = ref(0) If you have complex types, you can pass a generic to ref() or use the Ref utility type. import { ref, type Ref } from 'vue' interface Show { title: string; } const gameOfThrones = ref({ title: 'game of thrones'}) const breakingBad: Ref = ref({ title: 'breaking bad'}) 12 - 8 © Copyright 2025 Zenika. All rights reserved TYPING REACTIVE() reactive() implicitly infers the type from its argument. If you have a complex type, you can use an interface. import { reactive } from 'vue' const fruits = reactive(['apple', 'strawberry']) // string[] interface Show { title: string; } const show: Show = reactive({ title: 'the walking dead' }) 12 - 9 © Copyright 2025 Zenika. All rights reserved TYPING COMPUTED() computed() implicitly infers the type from the return's value. import { computed, ref } from 'vue' const counter = ref(1) const doubleCounter = computed(() => counter.value * 2) // infers number If you need to specify an explicit type, you can use a generic import { computed } from 'vue' interface User { firstName: string; lastName?: string; } const user = computed(() => ({ firstName: 'James' })) 12 - 10 © Copyright 2025 Zenika. All rights reserved 12 - 11 © Copyright 2025 Zenika. All rights reserved Lab 12 12 - 12 © Copyright 2025 Zenika. All rights reserved SSR 13 © Copyright 2025 Zenika. All rights reserved 2025-01-05#c8284f6 OVERVIEW What is VueJS Routing Instance Data fetching Tooling Store Template syntax Testing Options API Typescript Composition API SSR Reusability 13 - 1 © Copyright 2025 Zenika. All rights reserved SERVER SIDE VS CLIENT SIDE RENDERING Initially, web frameworks had views rendered on the server. Whenever you want to see a new web page, your browser makes a request to the server, and it returns the HTML content. But today, front-end frameworks have introduced that content is rendered directly in the browser using JavaScript. The main benefit is that the browser will not make another request to the server when you want to load more content. It will reload only the part we want to. The downside to using client-side rendering is that it can be bad for SEO. Many search engines are unable to properly crawl the content. Another point is that the initial load can be slow. The browser will first ask the server for the HTML content, then the content of our javascript application which contains our entire application. 13 - 2 © Copyright 2025 Zenika. All

Use Quizgecko on...
Browser
Browser