Learn Vuex by Building a Notes App.

This article assumes that the reader is familiar with the content of the Vuex documentation. If not, you definitely should!

In this tutorial, we will learn how to use Vuex by building a note-taking application. I will briefly cover the basics of Vuex, when to use it and how to organize code while using Vuex, and then I will step by step apply these concepts to the note-taking application.

Here’s a screenshot of the note-taking app we’re building:

You can download the source code from the Github Repo, where the demo is located.

Vuex overview

Vuex is a Flux-like data management architecture for medium to large single-page applications. It helps us organize our code better and keep the state of the application in a maintainable and understandable state.

If you don’t understand what state means in a vue.js application, you can imagine the data field in the Vue component you wrote earlier. Vuex divides state into component internal state and application level state:

  • Component internal state: State used only within a component (data field)

  • Application level state: State shared by multiple components

For example, let’s say we have a parent component that has two children. The parent component can use props to pass data to the child component, which is a well-understood data channel.

What if the two child components need to share data with each other? Or does the child need to pass data to the parent? Both of these problems are easy to solve when the application size is small, just use custom events.

But as applications scale up:

  • It’s getting harder and harder to keep track of these events. Which component fires this event? Who’s listening to it?

  • Business logic pervades the components, causing all sorts of unexpected problems.

  • Because events are explicitly distributed and listened for, the parent and child components are strongly coupled.

Vuex aims to solve these problems. There are four core concepts behind Vuex:

  • State tree: Objects that contain all application-level state

  • Getters: A function inside the component to get the state in the store

  • Mutations: The event callback function that changes the status

  • Actions: The function inside the component that distributes the Mutations event

The following diagram perfectly illustrates the flow of data within a Vuex application:

The highlights of this chart:

  • The flow of data is one-way

  • Components can call actions

  • Actions is used to distribute mutations

  • Only mutations can change the status

  • Store is reactive, that is, changes in state are reflected within the component

Set up the project

The project structure looks like this:

  • Components/Contains all components

  • Vuex/Contains vuex related files (Store, actions)

  • Build.js is the file that Webpack will output

  • Index.html is the page to render

  • Main.js is the entry point to the application and contains the root instance

  • style.css

  • webpack.config.js

New project:

mkdir vuex-notes-app && cd vuex-note-app
npm init -yCopy the code

Install dependencies:

npm install\
  webpack webpack-dev-server\
  vue-loader vue-html-loader css-loader vue-style-loader vue-hot-reload-api\
  babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015\
  babel-runtime@5\
  --save-dev

npm install vue vuex --saveCopy the code

Then configure Webpack:

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    path: __dirname,
    filename: 'build.js'
  },
  module: {
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue'
      },
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/
      }
    ]
  },
  babel: {
    presets: ['es2015'],
    plugins: ['transform-runtime']
  }
}Copy the code

Then configure NPM script in package.json:

"scripts": {
  "dev": "webpack-dev-server --inline --hot",
  "build": "webpack -p"
}Copy the code

Run NPM run dev and NPM run build directly for later tests and production.

Create Vuex Store

Create a store.js file under vuex/ :

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  notes: [],
  activeNote: {}
}

const mutations = { ... }

export default new Vuex.Store({
  state,
  mutations
})Copy the code

Now I use the following diagram to break the application into components and map the data needed within the components to state in store.js.

  • The App, the root component, is the outer red box

  • The Toolbar is the green bar on the left and contains three buttons

  • NotesList is a purple box that contains a list of note titles. The user can click on All Notes or Favorites

  • Editor is the yellow box on the right for editing notes

The state object in store.js contains all the application-level states that each component needs to share.

The Notes list (Notes: []) contains notes objects to be rendered by the NodesList component. The current note (activeNote: {}) contains the currently selected note object, which is required by multiple components:

  • The Toolbar component’s Favorites and delete buttons correspond to this object

  • The NotesList component highlights this object with CSS

  • The Editor component displays and edits the contents of the note object.

After talking about state, let’s look at mutations, and the mutation method we want to realize includes:

  • Add notes to array (state.notes)

  • Set the selected note to current Note (state.activenote)

  • Delete the current note

  • Edit current Note

  • Bookmark/unbookmark current notes

First, to add a new note, what we need to do is:

  • Create a new object

  • Initialize properties

  • Push into state.notes

  • Set the new note to the current note (activeNote)

ADD_NOTE (state) {
  const new Note = {
    text: 'New note',
    favorite: fals
  }
  state.notes.push(newNote)
  state.activeNote=  newNote
}Copy the code

Then, editing the note takes the note content text as an argument:

EDIT_NOTE (state, text) {
  state.activeNote.text = text
}Copy the code

The rest of these mutations are easy and I won’t go over them again. The entire vuex/store.js looks like this:

import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const state = { note: [], activeNote: {} } const mutations = { ADD_NOTE (state) { const newNote = { text: 'New Note', favorite: false } state.notes.push(newNote) state.activeNote = newNote }, EDIT_NOTE (state, text) { state.activeNote.text = text }, DELETE_NOTE (state) { state.notes.$remove(state.activeNote) state.activeNote = state.notes[0] }, TOGGLE_FAVORITE (state) { state.activeNote.favorite = ! state.activeNote.favorite }, SET_ACTIVE_NOTE (state, note) { state.activeNote = note } } export default new Vuex.Store({ state, mutations })Copy the code

Next, we’ll talk about Actions, which is the function inside the component that’s used to distribute mutations. They accept store as the first argument. For example, when a user clicks the Add button on the Toolbar component, we want to invoke an action that distributes ADD_NOTE mutation. Now let’s create an actions.js file under vuex/ and put addNote in it:

// actions.js
export const addNote = ({ dispatch }) => {
  dispatch('ADD_NOTE')
}Copy the code

The rest of the actions are similar to this:

export const addNote = ({ dispatch }) => {
  dispatch('ADD_NOTE')
}

export const editNote = ({ dispatch }, e) => {
  dispatch('EDIT_NOTE', e.target.value)
}

export const deleteNote = ({ dispatch }) => {
  dispatch('DELETE_NOTE')
}

export const updateActiveNote = ({ dispatch }, note) => {
  dispatch('SET_ACTIVE_NOTE', note)
}

export const toggleFavorite = ({ dispatch }) => {
  dispatch('TOGGLE_FAVORITE')
}Copy the code

This completes all the code you need to write in the Vuex folder. These include state and mutations in Store. js, and actions that are used to distribute mutations in actions.js.

Build Vue components

In this final summary, we will implement four components (App, Toolbar, NoteList, and Editor) and learn how to retrieve data from the Vuex Store and invoke Actions in these components.

Create the root instance-main.js

Main. js is the entry file of the application, which contains the root instance. We will add the Vuex Store to this root instance, and then inject it into all its children:

import Vue from 'vue' import store from './vuex/store' import App from './components/App.vue' new Vue({ store, // Inject into all child components el: 'body', components: {App}})Copy the code

App-root component

The root component App imports the remaining three components: Toolbar, NotesList, and Editor:



Copy the code

Put the App components in index.html, use BootStrap to provide the basic styles, and write the component-related styles in style.css:





  
    
    Notes | coligo.io
    
    
  
  
    
    
  
Copy the code

Toolbar

The Toolbar component gives the user three buttons: Create a new note, bookmark the currently selected note, and delete the currently selected note.

This is an excellent use case for Vuex because the Toolbar component needs to know which “currently selected note” is so that we can delete, bookmark/unbookmark it. We can see the need to share data by saying that the “currently selected notes” are required by each component and should not exist in any single component.

Every time the user clicks on one of the notes in the list, The NodeList component distributes SET_ACTIVE_NOTE mutation by calling the updateActiveNote() action, which sets the currently selected note to activeNote.

That is, the Toolbar component needs to get the activeNote attribute from state:

vuex: {
  getters: {
    activeNote: state => state.activeNote
  }
}Copy the code

We also need to bring in the actions for these three buttons, so tool. vue looks like this:




Copy the code

Notice that when activenote. favorite === true, the favorite button also has a starred-class name that highlights the favorite button.

NotesList

The NotesList component has three main functions:

  1. Render the list of notes

  2. Allows the user to select “All notes” or display only “favorite notes”

  3. When the user clicks on an item, the updateActiveNoteaction is called to update the activeNote in the store

Obviously, in NoteLists you need notes Array and activeNote in store:

vuex: {
  getters: {
    notes: state => state.notes,
    activeNote: state => state.activeNote
  }
}Copy the code

When the user clicks on a note, set it to the current note:

import { updateActiveNote } from '.. /vuex/actions' export default { vuex: { getters: { // as shown above }, actions: { updateActiveNote } } }Copy the code

Next, the filtered list is displayed depending on whether the user clicked on “All notes” or “Favorites notes” :

import { updateActiveNote } from '.. /vuex/actions' export default { data () { return { show: 'all' } }, vuex: { // as shown above }, computed: { filteredNotes () { if (this.show === 'all'){ return this.notes } else if (this.show === 'favorites') { return this.notes.filter(note => note.favorite) } } } }Copy the code

Here the show property within the component appears as an internal component state, which is obviously only present within the NoteList component.

Here is the complete Noteslist.vue:



Copy the code

A few key points of this component:

  • Use the first 30 characters as the title of the note

  • When the user clicks on a note, it becomes the currently selected note

  • Choosing between “all” and “favorite” is essentially setting the show property

  • Set the style with :class=””

Editor

The Editor component is the simplest and does only two things:

  • Get the current note activeNote from store and display its contents in textarea

  • When the user updates notes, the editNote() action is called

Here’s the full editor. vue:



Copy the code

The reason why the Textarea here does not use the V-Model is explained in detail in the VUEX documentation.

At this point, the application code is finished, do not understand the place can look at the source code, and then practice again.