Tutorials on Vue 3

Learn about Vue 3 from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

Building a GraphQL Application with Vue 3 and Apollo

RESTful APIs adhere to a reliable architectural standard for transferring data statelessly over the HTTP protocol. Every endpoint of an API semantically describes how a resource should be created ( POST ), read ( GET ), updated ( PUT / PATCH ), deleted ( DELETE ), etc. Large, data-driven applications consume data from multiple third-party/in-house sources, and each one exposes a unique set of endpoints to manage different resources. Adapting these applications to support a wide range of platforms and device sizes (commonly mobile, desktop and web) may present several problems: Using Facebook's GraphQL query language, the client specifies its exact data requirements to the server via a single endpoint. Establishing a schema (written with the syntax of the GraphQL Schema Definition Language) creates a contract between the client and server that defines what data can be read from and written to the data graph by the client. This data graph centralizes all of the APIs consumed by your application by mapping each field to a resolver that populates it with a value retrieved from an endpoint of one of these APIs, a database, etc. A client can fetch data from a GraphQL server via plain HTTP and then manually update the UI accordingly. However, GraphQL clients such as Apollo Client abstract away the low-level implementation details of these features underneath a declarative API. Built by the Apollo GraphQL team, Apollo Client is an open-source GraphQL client that provides a lot of out-of-the-box functionality for communicating with a GraphQL server: To integrate Apollo Client into an application using another JavaScript library/framework besides React, which Apollo Client already has built-in support for, there exists view integration libraries within the Apollo ecosystem that provide bindings for Vue , Svelte , Angular , Ember and Web Components . The Vue Apollo library integrates Apollo Client into a Vue application. Different versions of Vue Apollo are compatible with different versions of Vue: Although Vue Apollo v4 is still in active development (alpha phase), it offers support for Vue 3's Composition API , which collocates the methods corresponding to component options ( watch , computed , etc.) and lifecycle hook registrations ( onMounted , onUnmounted , etc.) within a single component option, setup . Using Vue Apollo v4 methods, such as useQuery and useMutation , the data requirements are also placed within the setup method. This approach makes it much easier to reason about a component's code compared to the Vue Apollo Options API approach, which places the data requirements within an apollo component option (independent of the other code placed within the remaining component options). Below, I'm going to show you: Using Vue 3's Composition API and the Vue Apollo (v4) library, we will be building the following GitHub search client: To start, download this project from GitHub: This repository is based on a custom Vue CLI project template that runs Vue 3 and includes support for TypeScript, ESLint and Prettier. If you want to learn how to manually set up the base structure of this project, then proceed to the next section (" Installation "). Otherwise, you may skip the " Installation " section, download the already prepared project structure and proceed directly to the " GitHub GraphQL API " section. To generate a new Vue CLI project, run the following command in the terminal: When prompted with "Please pick a preset," select the "Manually select features" option: This project will support TypeScript. Press "Space" to select "TypeScript." When prompted with "Choose a version of Vue.js that you want to start the project with," select the "3.x (Preview)" option: Vue components will not be written with the class syntax. The Class API was officially dropped . This project will use Babel alongside TypeScript. For linting and code formatting, select the "ESLint + Prettier" option: Anytime changes to a file are saved, run the linter. For this project, let's place the Babel, ESLint, etc. configurations within their own dedicated files to avoid increasing the size of package.json . These answers are only for this project. In the future, you may want to try out different sets of project configurations to determine what specific tools make you more productive. To integrate type definitions from the schema of GitHub's GraphQL API , install @octokit/graphql-schema : Several type definitions are assigned nullable types , which will cause ESLint to raise the following error within your IDE. Inside of the .eslintrc.js file, turn off the rule @typescript-eslint/no-non-null-assertion . ( .eslintrc.js ) In 2017, GitHub publicly released its GraphQL API . GitHub's GraphQL API exposes a public schema for interacting with GitHub itself, whether fetching commit data or starring a repository, all accessible from a single endpoint. To send requests to GitHub's GraphQL API, generate an access token. This access token must be set to each request's Authorization header. When setting permissions for the access token, enable repository privileges. Create a .env file at the root of the project directory. Copy the 40 character-long access token to your clipboard. Set the environment variable VUE_APP_GITHUB_ACCESS_TOKEN to this access token. ( .env ) Note : Environment variables prefixed with VUE_APP_ can be accessed via process.env within Vue applications. Without this prefix, environment variables are undefined . First, install graphql , @apollo/client and @vue/apollo-composable as dependencies: To rapidly style the UI interface, we will be using the Tailwind CSS framework. Install tailwindcss , postcss and autoprefixer as dependencies: Then, create a minimal Tailwind configuration file ( tailwind.config.js ) at the root of the project directory. Note : The -p flag creates a minimal PostCSS configuration file ( postcss.config.js ) at the root of the project directory, alongside the generated tailwind.config.js . Inside of tailwind.config.js , set the purge option to a list of filenames/globs for PurgeCSS to analyze and remove unused CSS. ( tailwind.config.js ) Inside of public/index.html , add these two CSS classes to the <body /> element: ( public/index.html ) When you run the application, you will encounter the following error: Although the latest version of the tailwindcss PostCSS plugin (v2) is compatible with latest version PostCSS (v8), other tools within the PostCSS ecosystem may not yet be compatible with this version of PostCSS. To resolve this error, uninstall tailwindcss , postcss and autoprefixer , and then reinstall these dependencies with the PostCSS (v7) compatibility build. Inside of main.ts (the entry point of the Vue 3 application), create an ApolloClient instance. Let's pass an object containing configuration options to the Apollo Client: For this application, we will define two links: To inject the Apollo Client into the application and allow child components to access the Apollo Client, call the provide method within the createApp 's setup method to "provide" this client to the application and its children components. Since this application only interacts with a single GraphQL API, set this client as the default client. Putting it altogether... ( main.ts ) Our application requires three child components: By default, the reactive searchOptions object, which represents the arguments passed to the GitHub GraphQL API's search query, is dynamically assigned to the prop search-options of the <RepositoryList /> component. Any changes to searchOptions , particularly to query , which corresponds to the value of the search bar's input, will cause the <RepositoryList /> component to retrieve a new list of repositories. The value of query is changed whenever the search event is emitted from the <SearchBar /> component, which occurs on changes to the value of its input. ( src/App.vue ) Typing a query emits a "search" event with the current query and triggers the search function in the <App /> component. This search function sets the value of the query field in the reactive searchOptions object. Debounce the handleInputChange event handler to avoid sending the search event on every single input change. ( src/components/SearchBar.vue ) When an event is fired, this debounce function starts a timer and waits for a specific time period to elapse before calling its corresponding event handler. If another event is fired during this time period, then the previous event is ignored. The timer resets and must wait for the specific time period (now reset) to elapse before calling the new event's corresponding event handler. This debounce function invokes the event handler on the trailing edge of the timeout. ( src/utils.ts ) Store the queries and mutations within a single file. This application requires only one query and two mutations: If you decide to add more queries/mutations that return a repository/repositories, and you request for the same repository fields for those queries/mutations, then use the repo fragment to keep your code DRY. ( src/graphql/documents.ts ) Inside of the <RepositoryList /> component, fetch a list of repositories based on the searchOptions passed from the <App /> parent component. To fetch this list of repositories, the component executes the composition function useQuery , which accepts a GraphQL document ( SEARCH_REPOS ) as the first argument and query arguments ( searchOptions ) as the second argument. This function is compatible with the setup function of Vue 3's Composition API. useQuery returns an object that contains several Ref values: Sometimes, a query may return multiple top-level objects. To pick a single object from the result object returned by useQuery , use the useResult composition function. Instead of referencing the repositories from the result object as result.search.edges in the component's template, it can just be referenced as repositories . Plus, the default value assigned to repositories is the second argument passed to useResult (in this case, an empty array). ( src/components/RepositoryList.vue ) Inside of the <Repository /> component, there is a button for starring/unstarring a repository, depending on whether or not you have starred the repository previously. If you have not yet starred the repository, then clicking the button will star the repository, and vice-versa. The event handler calls either the unstarRepo or starRepo functions to unstar or star a repository respectively. Each of these functions execute the composition function useMutation , which accepts a GraphQL document ( ADD_STAR or REMOVE_STAR ) as the first argument and options (an object containing mutation arguments via the variables property, etc.) as the second argument. Similar to useQuery , this function is compatible with the setup function of Vue 3's Composition API. When a repository is starred/unstarred, we must update the cache to reflect this mutation. To understand why this is important, let's walkthrough an example. Imagine you typed the query "facebook" into the search bar's input. This will fetch all repositories relevant to "facebook" from GitHub's GraphQL API. Suppose you have already starred the facebook/react repository, and you decide to unstar it. After you unstar it, you decide to type the query "google" into the search bar's input. This will fetch all repositories relevant to "google" from GitHub's GraphQL API. If you again type the query "facebook" into the search bar's input, then this will fetch all repositories relevant to "facebook" from the Apollo Client's cache . What was cached previously for this query was a list of repositories relevant to "facebook," including the facebook/react repository. However, this repository was cached when it was still starred. Therefore, we must modify this repository in the cache to reflect that it was recently unstarred. To update the cache, set the update property in the options object to a function that provides an instance of the cache and the data returned from a mutation. This function will call the overrideMutationStarCache , which will read the already cached data (via the cache 's readQuery method) and write the result of the mutation to the appropriate repository entity (via the cache 's writeQuery method). Don't forget to also increment/decrement the stargazers count! Putting it altogether... ( src/components/Repository.vue ) Run the application locally: Visit localhost:8080 in a browser to interact with the application. In this blog post, we only explored a very small subset of the GitHub GraphQL API. Try experimenting with other aspects of the public schema of the GitHub GraphQL API. For a more difficult challenge, try connecting an existing Vue 3 application to a custom GraphQL server. If you want to learn more about Vue 3, then check out Fullstack Vue :

Thumbnail Image of Tutorial Building a GraphQL Application with Vue 3 and Apollo

Vue 3 - The Composition API - Reusability (Part 2)

Vue's Options API constrains the reusability of logic across multiple components. Patterns involving mixins and higher-order components (HOCs) have been established to create reusable blocks of code and consolidate repeated state and functionality. However, each pattern has a drawback: With Vue 3's Composition API , component logic can be extracted, shared and reused amongst multiple components. Exposing Vue's core capabilities (reactivity and lifecycle hooks) as standalone, globally available functions allows developers to create custom hooks that are decoupled from any UI, but yet, can be consumed by any component. This can greatly improve the maintainability and flexibility of a large application composed of hundreds of components. Let's walkthrough an example demo to demonstrate the simplicity of writing reusable code with the Composition API. In this CodeSandbox demo , clicking the "Open Modal" button pops open a basic accessible modal that displays a list of links to sections in the official Vue.js documentation site. The <Modal /> component instance contains a setup option that houses all of its data, methods, lifecycle hooks, etc. ( src/components/Modal.vue ) Here's a brief overview of the code within the setup option: In a web application, a modal acts as a subordinate window to the browser window. Because modals can be used to communicate information to a user, they can be categorized as dialogs . Another component that behaves similarly to modals is toast notifications , which briefly notify users of certain events before disappearing. If you are not familiar with toast notifications, then have a look at an easy-to-use toast notifications library, toastr . Using the Composition API, we can implement a toast notification component that uses the same accessibility and closing logic as our modal component. Let's refactor this logic into a separate hook that can be used in both components. First, create a new directory, hooks , within the src directory. Then, create a new file useDialog.js within this new directory. Inside of src/hooks/useDialog.js , create a function called useDialog that declares a ref, isDialogOpened , and has two methods responsible for updating this ref's value, openDialog and closeDialog . To allow a component to access these functions and this ref, return them in an object. ( src/hooks/useDialog.js ) The useDialog function is created outside of the context of components as a standalone piece of functionality. Notice how this logic resembles the logic within the setup option of the <App /> component: Let's refactor this logic by replacing it with a call to the useDialog function. ( src/App.vue ) Since isDialogOpened is reactive, any updates to this value will be reflected wherever it is used in the component template. Examining at the <Modal /> component, we should extract out the methods and lifecycle hooks for re-use in a <ToastNotification /> component. Because the close method relies on the context object, the handleKeydown method relies on the modalRef ref and the onBeforeMount lifecycle hook relies on the prevRef ref, all of these values need to passed to the useDialog method when we migrate these methods and lifecycle hooks. ( src/hooks/useDialog.js ) Because the call to the useDialog function within the <App /> component does not need to pass prevRef , dialogRef (formerly modalRef ) and context , we should isolate this logic within a function initDialog , which will be exposed to the <Modal /> component and accept these arguments when called. ( src/hooks/useDialog.js ) We will also need to slightly adjust the <Modal /> component's template to call emitClose when the <button class="modal__close-btn" /> element is clicked. ( src/components/Modal.vue ) Because modalRef and prevRef are reactive, their updated values will be available to the methods and lifecycle hooks within useDialog whenever they are called. Try out these changes! When you click on the "Open Modal" button, the modal pops open, and focus is trapped within it. Once the modal is closed, the focus resumes back to the "Open Modal" button. Let's create a simple toast notification component. Add a new file to the src/components directory named ToastNotification.vue . Plug-in the same logic that is now used in the setup option of the <Modal /> component. ( src/components/ToastNotification.vue ) Let's add an example toast notification and a button that pops open this notification when clicked to the <App /> component. ( src/App.vue ) And with little effort (after the initial refactoring of functionality to useDialog ), we have successfully introduced a new component to our application. Additionally, these same steps can be repeated for other dialog-like components. If you would like to view the final version of this CodeSandbox demo, then click here . Composing and isolating functionality within a separate function makes it incredibly easy to integrate and share this logic amongst existing/new components. The Composition API's flexibility and compositional nature allows us to structure and group our logic based on high-level component features rather than low-level component details (in the case of the Options API). Try rewriting React functional components that use hooks with Vue 3's Composition API. If you want to learn more about Vue 3, then check out Fullstack Vue :

Thumbnail Image of Tutorial Vue 3 - The Composition API - Reusability (Part 2)

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Vue 3 - The Composition API (Part 1)

As the most starred open source, JavaScript library/framework repository on GitHub, Vue.js has become a top three front-end technology alongside React.js and Angular in terms of popularity, usage, ecosystem activity and developer satisfaction. Compared to React.js and Angular, Vue.js is incrementally adoptable and provides a declarative API that resonates with AngularJS developers. Evan You , the author of Vue.js, explained the original concept of Vue.js as extracting the best parts of AngularJS, such as directives (i.e., v-if and v-show ), and building a lightweight, flexible alternative. Building large Vue.js applications requires composing components together with state management (i.e., Vuex ) and routing (i.e., Vue Router ) libraries. In September 2020, the Vue.js team officially released Vue 3, which welcomed a number of improvements and new features: Particularly, the Composition API of Vue 3 has been met with controversy due to its resemblance to React hooks and its philosophical divergence from the Options API , which emphasizes separation of concerns by defining component logic within specific options ( data , props , computed , methods , etc.). For those who prefer the Options API, unlike other major version upgrades that introduce incompatible changes, the Composition API will not break existing Vue components using Options API (" purely additive "). Although it offers similar logic composition capabilities as React hooks, the Composition API leverages Vue's reactivity system to circumvent several of React hooks' issues. To understand why the Composition API's new approach for creating components allows developers to better reason about and maintain large components, we will need to... A single file component consists of HTML-based markup ( <template /> ), CSS-based styles ( <style /> ) and JavaScript-based logic ( <script /> ), all self-contained within a single .vue file. Example : The following component will display "Hello World!" as red text. Note : The scoped attribute applies to CSS only to the elements within the current component. In the above example, the data function returns an object that represents the component's data. Vue converts each data property to a Proxy , which makes the state reactive by allowing for proper this bindings and tracking/triggering changes. The object returned by data contains a single property, greeting , which has a value of "Hello World!" and is rendered within the <p /> element's mustaches ( {{ ... }} ). For a one-time interpolation, use the v-once directive. Note : In previous versions of Vue, the reactivity system used Object.defineProperty , not Proxy . Now that you are familiar with the base structure of a component, let's explore a slightly more complex component. Open the CodeSandbox demo here . In this demo, let's look at a component that represents a simple accessible modal. When the modal is opened, the focus should be trapped within it. Elements within the modal should be "tabbable." When the modal is closed, the focus should return back to the element responsible for opening the modal. ( src/components/Modal.vue ) ( src/App.vue ) Here, the <Modal /> component is defined using the Options API , which organizes component logic into specific options. The initialization of internal state ( data ), the passed props ( props ), the emitted custom events ( emits ), the available instance methods ( methods ) and lifecycle hooks ( beforeMount , mounted and unmounted ) are clearly separated from one another. For an option to access values/methods defined within other options, those values/methods must be referenced via the this keyword, which refers to the component instance itself. Below is a list of commonly used options: Note : Do not use arrow functions when defining a watcher or methods / computed method. Otherwise, the this keyword will not reference the component instance. As a component grows larger with more new features, it increasingly becomes more difficult to maintain and reason about the component, especially when the logic of each feature is fragmented over the individual options. The RFC for the Composition API provides a side-by-side comparison of how logic related to a feature (also called a logical concern in the official documentation ) is distributed throughout a component's code. Each logical concern is identified by a unique color. Notice how neatly grouped each feature's logic is when using the Composition API. By restricting component logic to specific options, it adds a mental tax by requiring us to constantly switch from one block of code to another to understand/work on any single feature of a component. Also, it limits our ability to extract and reuse common logic between components. This is the primary motivation behind the Composition API. By exposing Vue's core capabilities, such as reactivity and lifecycle hooks, as standalone functions, the Composition API can use these functions within the setup component option, which is executed before the component is created (formerly the created lifecycle hook option) and serves as the component's main entry point. Let's revisit the simple accessible modal, but this time, create the component using the Composition API. Open the CodeSandbox demo here . ( src/components/Modal.vue ) ( src/App.vue ) All of the logic that was previously handled in multiple options of a Vue component is now contained within a single option, setup . Functions that behave similar to those options can be imported as named exports from vue and called anywhere within the setup function. For example, the lifecycle hook options beforeMount , mounted and unmounted can be replaced using the onBeforeMount , onMounted and onUnmounted methods in setup respectively. This allows component logic to be structured and arranged flexibly. The reactive data returned by the data option can be replaced using the ref and reactive methods in setup . The ref method accepts a default value and returns a reactive ref object that can be safely passed throughout the entire application. Because this object is mutable, the value can be directly modified. To reference this value, access this object's .value property. ref is commonly used for establishing reactivity for a single primitive value. To establish reactivity for an entire object, use the reactive method. setup exposes two arguments, props and context . props contains the props passed from a parent component. They are reactive and automatically updated whenever new props are passed in. To properly destructure props , which means still preserving each props reactivity after destructuring, use the toRefs method. context contains three properties: Within the context of the <Modal /> component: Although the component's methods are defined within the setup option, the component's template can only access these methods when they placed within the object returned by setup . This also applies to ref and reactive values, which are automatically unwrapped to allow the template to access their values without having to reference .value . The close method and modalRef ref are made available to the <Modal /> template. The close method is set to the @click shortcut directive, so when the user clicks this button, the "close" event will be emitted by the component, which will trigger the function set to @close on the <Modal /> component within the <App /> parent component (the closeModal method). When set to the <div class="model" /> element's ref , modalRef will reference this DOM element. A component built with the Composition API has its setup function only called once during its entire lifetime, regardless of how many times it is re-rendered. A React functional component using hooks is called whenever a re-render occurs as a result of a state/prop change, which pressures the browser's garbage collector. React provides useCallback to prevent inline functions in functional components from being garbage collected on subsequent re-renders via a referential equality check. Additionally, these re-renders may cause unnecessary re-renders, which happens when the function representing a child functional component is called when its parent component is re-rendered, even if it is completely stateless. Example : When the count increases/decreases upon clicking any one of the <Counter /> component's button, the Child function is called. However, its DOM is unaffected by this state change. This is an unnecessary re-render. If this component was larger with more inline handlers, calls to hooks, etc., then any unnecessary re-render would cause these inline handlers, etc. to be garbage collected. React provides a number of memoization-related methods ( useMemo , memo , etc.) to address this. Of course, you should use your browser's profiling tool first to identify your application's actual bottlenecks before chucking in useMemo , memo and other memoization techniques. Each developer reasons about code differently. Whether you find it easier to reason about code by its low-level (Options API, which is better suited for grouping logic by a low-level attributes of a component) or high-level (Composition API, which is better suited for grouping logic by a component's high-level features) implementation, you must always evaluate and make trade-offs that best serves your needs. You should definitely read this RFC that outlines the Vue team's rationale and decisions for the Composition API, along with addressing some of the common criticisms echoed within the Vue community. Many of these new features, such as portals and fragments, in Vue 3 are already present in React v16+. Try converting your React functional components to Vue 3 components using the Composition API. Please read Part 2 of this blog post to learn about the main benefit of the Composition API: the capability to extract and reuse shared logic . If you want to learn more about Vue 3, then check out Fullstack Vue :

Thumbnail Image of Tutorial Vue 3 - The Composition API (Part 1)