As a web developer that is fairly accustomed to using Angular, the introduction of Typescript and class-based components to Vue, was enough reason to check out the framework. With these changes, it's easy to see that there are a lot of similarities in the project and code structure between the two frameworks and in turn should make the transition quite simple.
Assuming that you already have Node.js installed, we can start by getting the Vue CLI installed globally. For reference, I am developing on macOS and my code editor of choice is Visual Studio Code which you can find here
Run the following in the terminal/command line of your choice.
$ npm install -g @vue/cli
Next, using the CLI we can create our project.
$ vue create <project-name>
Once you have done this, you will be prompted to select a preset. We want to choose Manually select features and then select the following :
Please note that some of these are optional, depending on your preference. If like me, you like to use SASS or another CSS pre-processor, make sure you select the option here. As with the Linter/Formatter, Unit and E2E testing options, while they are not required, its usually good to get them set up now, in case you need them later. In particular, using TSlint can always help with your code quality. You will then be asked if you want to use class-style component syntax, choose yes. For reference, here are my selections for the remaining config :
Once the command has finished, you should be able to open the project in your code editor and we can finish the setup for the project.
To help with the development, we will be using the component framework, https://vuetifyjs.com/en/. In short, its a material component framework for Vue. Now, this is not 100% required, if you would prefer to create your own UI elements. To get started, let's add it to the project :
$ npm install vuetify --save
Now that we have Vuetify installed, we can navigate to the applications entry point — main.ts and add the following:
Finally, we need to add the Material Icons to the project, as some of the Vuetify components use them. Simply copy the following link into your index.html.
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
Quite a crucial part of using Firebase for authentication is to have a Firebase account already and if you don't just create one here https://firebase.google.com/. Once you are logged in, head to the console and create your project. I won't go into too much detail here, as there is plenty of articles and documentation covering Firebase.
Next, head to over the Authentication tab, in the side menu and click on Set up a sign-in method. Then from the top of the list, select Email/Password then Enable and finally Save.
Next, we can go back to the Project Overview, and add an app to the project. Obviously, select to add a web app and register the app with an appropriate nickname. Once completed, you will be shown a script, simply copy the contents of the <script> tags, head back into your code editor and open your main.ts. Paste the contents into this document. It should look something like this:
We can add the Firebase SDK to the project. Run the following command :
npm install firebase --save
Finally, we need to add the following import for Firebase to our main.ts :
import * as firebase from 'firebase'
Please note, that for production apps, we should NOT store the firebase config information in the main.ts and as stated by the documentation, we should only import the modules that we need to use, rather than the whole SDK. The resulting code for the main.ts should be similar to the following:
Now to run the local development server, we can use :
npm run serve
This will host the application locally, on port 8080.
Project Structure and Clean Up
Now, this next part is optional and the only goal is to tidy up the project structure a bit and remove some files we won't be needing before we dive in further. If you do skip this, don't forget that the code shown from now will reflect some of the changes we do here.
So first, remove the HelloWorld component from the components folder. Then head to Home.Vue and remove the highlighted code, that references the component we just removed.
Next, from the views directory, remove About.Vue . We then also need to remove the page from the router. Remove the following from router.ts :
Next, create two new directories under the src folder, the first will be for the router and the second for the store. Then move the respective files into their new folders. So move router.ts to the router directory and store.ts to the store directory. Now you may notice that the app will no longer compile, this is because we also need to update the imports in the main.ts for the two files we just moved and update the import for the home view. Update your main.ts imports for the files to the following :
And finally we need to update the import for the home view in router/router.ts to the following:
Note that I will assume you have a basic understanding of Vue!
To navigate the various pages of the site, we will create a navbar. Create a new component in the components directory called NavBar.vue , and copy the following :
To reduce the amount of markup that is written for the menu items, the buttons are created dynamically using two arrays. The menu items are split into two sections; one for when the user is authenticated and one for when the user is not authenticated. The menu items are simply an icon, link and a title. There is also a static logout button, which we will hook up later on.
As this navbar will be present on all of our views, we can add the component to the main App.vue file. Here is the updated file :
And once the app has recompiled you should see the navbar in your browser!
Register and Login
Now we can create the views for the login and register pages. Start with creating Register.vue in the views directory. We then need to add it to our router with the following :
If you are not familiar with the router you can check out the docs and if like me, you’re coming from an Angular background, you will find this very similar to how routing works in Angular too. So your router should now look something like this.
Now, in our new Vue component copy the following:
The markup for this page is straightforward to understand. It is just a simple form including some basic validation for the inputs. Along with some self-explanatory methods; A register method that ensures that the form is valid before calling the method that will register a user with Firebase and a method that clears the current content of the form. Firebase authentication also covers various types of social logins such as Google or Facebook if that is required.
Next, we have to do the same for the login page. These pages are almost identical in terms of the content with a few minor differences. Usually, it would be better to have re-unusable components, rather than repeating code however we can save that for another time. As with the register page, create the new component (view), in the views directory and add the page to the router. Here is the gist for the login :
As I have mentioned, this is very similar to the register page. With the only differences being in the form and register functionality replaced with the login methods. Next, we need to test these two views are working before we continue. The links in the navbar should already be working, as long as the route names in the navbar component, match what you have in the router. So navigate to the registration page and complete the form, if successful an alert should show. Now refresh your browser page and navigate to the login page and again, if successful another alert should show.
State Management with VueX
The next step is to integrate state management into our app using Vuex, which is a state management pattern and library for Vue.js applications. You can check out the docs here. In short, Vuex provides us with stores, in which we can store data that can be shared across the whole application, allowing us to manage a common state.
This process can be broken down into a few stages. First we have the state, in which we have our data, for example, let’s say a counter for cars. This data can be changed by a mutation. For example, a mutation that would increment the value of our car counter by 1. Now to mutate our data, we need to dispatch an action which would commit a mutation. Then we can retrieve our data from the store with the user of a getter. All of this is then encapsulated within stores.
So for our application, we can break down what we need in our store quite easily. First, the states that we want to store are the user, any errors we need to show to the user and finally the current status of the application. For the mutations, we need to be able to set the values of the user, errors and the status and remove the value for the user. For the actions, we will be moving the Firebase functionality in our views into the store, so we will need actions that allow the user to register an account as well as being able to log in and logout. For the getters, we just need to return the values that are stored in the store.
Now we can create the store, here is the completed version. There should already be a store.ts in your project which you can use.
Next, we need to update the register and login components, as we have moved the firebase functionality into the store rather than in the component. So in the methods for registration and login, instead of completing any business logic in the component itself we simply are going to dispatch an action to the store, where the login logic will take place. Update the createUser and loginUser methods to the following :
The next component to update is the navigation, in order to change which buttons should be shown depending on the state of the user. In our navigation we have two sets of v-toolbar-items which are for the two states of authentication of the user. This state can be retrieved from the store which we can use to change the items are being shown. Update the navigation component to the following:
So in this update, we add a computed property for the user state, userLoggedIn which gets the user object from the store. This property is then used in conjunction with an if-else statement to change which toolbar items are shown. Finally, the logout button some logic bound to it, to call the logout function from the store.
As another example of taking data from the store, we can display some of the information in the user object on the home view:
During development, you may have noticed that the authentication session does not persist when the page updates due to code changes or if simply refreshed. The reason for this is that when the Vue app is started, the current authentication state is unknown and we do not check for it. So a quick update in the main.ts can resolve this:
Finally, we can add a couple of router guards to control what views are accessible depending on the authentication state. Router guards provide a means to guard navigations either by redirecting or cancelling it. There are a number of options for this, but we will be using Global Before Guards. The two guards we will add will be requiresAuth and requiresLogout. Guards are added to the meta section when defining a route and the logic is run before the change in route. For the register and login pages, we don’t want our logged-in users to be able to go to these pages and in turn, the home page is only for logged in users. Update your router.ts with the following:
Now if you try to navigate to the home page before logging in you will be redirected to the login page and if you try to navigate to either the login or register pages while logged in, you will get redirected to the home page.
While this is a good start, it’s by no means ready for production. There are plenty more features that would need to be implemented such as handling the errors that are returned from firebase properly. However, it does provide a good base to work from. If you are more familiar with class-based components from working with Angular, for example, this class-style implementation of Vue can definitely make the transition easier.