Use Vuex + Vue.js build single-page application

Given the large amount of the article read, reply to the students is also very much, specially taking the time to write a vuex use a vue2.0 under, Portal: Use Vuex + Vue.js build a new one-page application [articles]

-------------------- -------------------- gorgeous dividing line

Original Address: https://coligo.io/learn-vuex-by-building-notes-app/

Introduction: When a recent study Vue.js and saw a foreign article describes how to use Vue.js and Vuex to build a simple one-page application note. Harvest feeling a lot that he made some optimization on the basis of its case and of the custom functions in here and share learning experiences.

In this tutorial we will learn by building a application note how to use Vuex in our Vue project. We will probably go over what is Vuex.js, use it in the project when, and how to build our Vue applications.

Here to put a picture preview of our project:

Effect preview

Project Source: vuex-Notes-App ; needy students can download the source code view.

Main points

  • Vuex use state management mechanism
  • Vue.js basis api
  • Vue-cli installation and use of scaffolding
  • vur-router used
  • ES6 grammar, recommended here look Ruan Yifeng Tutorial

Vuex Overview

Before starting a project we can not wait, we'd better take a few minutes to understand the next Vuex core concepts.

Vuex is an application designed specifically for Vue.js centralized state management architecture. It draws Flux Redux and design ideas, but to simplify the concept, and uses for the realization of a better play Vue.js data response mechanism specifically designed.

stateThe concept for the first time when such contacts might feel a little fuzzy, simply, is to statebe seen as a collection we used in the project data. Then, Vuex local state such that the component (component local state) the state and the application level (application state) have some differences.

  • component local state: This state is shown only in the state of the internal components used, somewhat similar to the Vue incoming internal components by means of configuration options.
  • application level state: application level state, indicating the plurality of components is simultaneously shared state level.

Consider a scenario: We have a parent component, contains two subcomponents. Parent may easily by using propsto pass data to the sub-assembly properties.

But the problem comes when our two sub-assemblies and how to communicate with each other? Or how to pass data to the sub-assembly components of his father? In our project very young, the two problems are not too difficult, because we can distribute and monitor to complete the communication components and subcomponents of the Father through the event.

However, with the growth in our project:

  • It will become difficult to keep track of all the events. In the end which event which component is dispatched, which component which the event listener?
  • Project logic scattered in various components which can easily lead to confusion logic, is not conducive to the maintenance of our project.
  • It will become the parent component and sub-component coupling and more serious, because it requires a clear distribution and monitoring of certain events subcomponents.

This is Vuex used to solve the problem. Vuex four core concepts are:

  • The state tree: Vuex single state tree, use an object contains all of the application-level status. At this point it will exist as a "unique data source (SSOT)." This also means that each application will store contains only one instance. Single state tree allows us to directly locate any particular state fragment, can easily take a snapshot of the entire current application state in the debugging process.
  • Getters: Vue component used to obtain data from the store.
  • Mutators: event handler to drive change in state.
  • Actions: you can use components to function, in order to drive the event handler mutations

How do you not understand this yet four concepts, do not worry, we will explain in detail later in the project actual combat.

Below is a detailed explanation of the flow chart (Vuex official figure) Vuex application data

Data flow diagram Vuex

Simple explain:

Vuex provisions, is an application-level status can only be modified by Mutation in the method, and distribute Mutation events only through action.

Left to turn, starting from the assembly, the assembly call action, action at this level we can and back-office data exchange, such as access to initialize the data source, or filtering the intermediate data and so on. Then in the action to distribute Mutation. Mutation to change the trigger state, changing the state, it will trigger update view.

Precautions

  • Data streams are unidirectional
  • Components can call action
  • action to distribute Mutation
  • Only mutation can change the state
  • store is responsive, no matter what time state update component updates will be synchronized

Environment Installation

This application will use webpack do module package, handling and hot restart. Scaffolding Vue provided by the official VUE-cli .

Installation vue-cli

npm install -g vue-cli

NOTE: Node.js> = 4.x, 5.x best

Initializing the application

vue init webpack vue-notes-app
cd vue-notes-app
npm install // 安装依赖包
npm run dev // 启动服务

Initiate a project called vue-notes-appapplication, and select Use webpack are packaged. At the command line to select the initial configuration items follow the prompts. In selecting which JSLint check when the recommended choice AirBNB specification.

Use your favorite editor to open our newly erected projects, construction projects probably below:

Program structure.

  • components / folder used to store our components Vue
  • vuex / folders stored and Vuex store is something related to (state object, actions, mutators)
  • build / compile the package file is webpack profile
  • config / folder is stored in a number of configuration items, such as our port configuration server access, etc.
  • dist / folder the beginning does not exist, we will build the project after output
  • App.vue root component, all subcomponents will be quoted here
  • Entrance index.html file of the entire project, we will refer to the root component App.vue
  • js main.js file entry logic, after webpack packing will be injected into the index.html

functional module

  • Add notes, add a note, edit the content area displays an empty note
  • Delete a note, then delete a note, edit area shows the current first class notes
  • List of notes handover, all the notes are divided into two kinds of notes and collection, after the handover, the first note editing area display the current list
  • Collection notes, to the currently active collection of notes marked with labels

Project components division

In this project, we will use a total of four components: the root component App.vue, action bar assembly Toolbar.vue, do not watch components NotesList.vue, notes editing component Editor.vue.

FIG components into

Creating Vuex Store

According to the above function module we listed, we have established a store.js file in Vuex / below.

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

Vue.use(Vuex);

// 需要维护的状态
const state = {
  notes: [],
  activeNote: {},
  show: ''
};

const mutations = {
  // 初始化 state
  INIT_STORE(state, data) {
    state.notes = data.notes,
    state.show = data.show;
    state.activeNote = data.activeNote;
  },
  // 新增笔记
  NEW_NOTE(state) {
    var newNote = {
      id: +new Date(),
      title: '',
      content: '',
      favorite: false
    };
    state.notes.push(newNote);
    state.activeNote = newNote;
  },
  // 修改笔记
  EDIT_NOTE(state, note) {
    state.activeNote = note;
    // 修改原始数据
    for (var i = 0; i < state.notes.length; i++) {
      if(state.notes[i].id === note.id){
        state.notes[i] = note;
        break;
      }
    };
  },
  // 删除笔记
  DELETE_NOTE(state) {
    state.notes.$remove(state.activeNote);
    state.activeNote = state.notes[0] || {};
  },
  // 切换笔记的收藏与取消收藏
  TOGGLE_FAVORITE(state) {
    state.activeNote.favorite = !state.activeNote.favorite;
  },
  // 切换显示数据列表类型:全部 or 收藏
  SET_SHOW_ALL(state, show){
    state.show = show;
    // 切换数据展示,需要同步更新 activeNote
    if(show === 'favorite'){
      state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
    }else{
      state.activeNote = state.notes[0] || {};
    }
  },
  // 设置当前激活的笔记
  SET_ACTIVE_NOTE(state, note) {
    state.activeNote = note;
  }
};

export default new Vuex.Store({
  state,
  mutations
});

Creating Vuex Actions

Establishing a action.js in Vuex / below, is used to the function component.

function makeAction(type) {
  return ({ dispatch }, ...args) => dispatch(type, ...args);
};

const initNote = {
  id: +new Date(),
  title: '我的笔记',
  content: '第一篇笔记内容',
  favorite: false
};

// 模拟初始化数据
const initData = {
  show: 'all',
  notes: [initNote],
  activeNote: initNote
};

export const initStore = ({ dispatch }) => {
  dispatch('INIT_STORE', initData);
};
// 更新当前activeNote对象
export const updateActiveNote = makeAction('SET_ACTIVE_NOTE');

// 添加一个note对象
export const newNote = makeAction('NEW_NOTE');

// 删除一个note对象
export const deleteNote = makeAction('DELETE_NOTE');
export const toggleFavorite = makeAction('TOGGLE_FAVORITE');
export const editNote = makeAction('EDIT_NOTE');

// 更新列表展示
export const updateShow = makeAction('SET_SHOW_ALL');

Creating Vuex Getters

Establish a getter.js file in vuex / below, used to obtain data from the store.

// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
export const filteredNotes = (state) => {
  if(state.show === 'all'){
    return state.notes || {};
  }else if(state.show === 'favorite'){
    return state.notes.filter(note => note.favorite) || {};
  }
};


// 获取列表展示状态 : all or favorite
export const show = (state) => {
  return state.show;
};

// 获取当前激活 note
export const activeNote = (state) => {
  return state.activeNote;
};

And that's all we Vuex logic, and after we set the function needs to be done, the next step is only required in the assembly to call action to implement the corresponding function.

Routing Configuration

Here we will use vue-router to do the routing, reference bootstrap style.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuex-notes-app</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

All entrance logic we will be prepared in main.js

main.js

import Vue from 'vue';
import App from './App';

import VueRouter from 'vue-router';
import VueResource from 'vue-resource';

// 路由模块和HTTP模块
Vue.use(VueResource);
Vue.use(VueRouter);

const router = new VueRouter();

router.map({
  '/index': {
    component: App
  }
});

router.redirect({
  '*': '/index'
});

router.start(App, '#app');

Root component App.vue

<template>
  <div id="app" class="app">
    <toolbar></toolbar>
    <notes-list></notes-list>
    <editor></editor>
  </div>
</template>

<style>
  html, #app {
    height: 100%;
  }

  body {
    margin: 0;
    padding: 0;
    border: 0;
    height: 100%;
    max-height: 100%;
    position: relative;
  }
</style>

<script>
  import Toolbar from './components/Toolbar';
  import NotesList from './components/NotesList';
  import Editor from './components/Editor';
  import store from './vuex/store';
  import { initStore } from './vuex/actions';

  export default {
    components: {
      Toolbar,
      NotesList,
      Editor
    },
    store,
    vuex: {
      actions: {
        initStore
      }
    },
    ready() {
      this.initStore()
    }
  }
</script>

It cited in the root subassembly three components: Toolbar.vue, NotesList.vue, Editor.vue.

Note: We join in the configuration inside vuexsuch an option here for our action methods defined inside to be exposed, we only do one thing in the root element, the analog data that is initialized, so we component lifecycle the stage ready to call the actions inside initStore to initialize our store inside the state

Toolbar.vue

<template>
  <div id="toolbar">
    <i class="glyphicon logo"><img src="../assets/logo.png" width="30" height="30"></i>
    <i @click="newNote" class="glyphicon glyphicon-plus"></i>
    <i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i>
    <i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
  </div>
</template>

<script>
import { newNote, deleteNote, toggleFavorite } from '../vuex/actions';
import { activeNote } from '../vuex/getters';

export default {
  vuex: {
    getters: {
      activeNote
    },
    actions: {
      newNote,
      deleteNote,
      toggleFavorite
    }
  }
}
</script>

<style lang="scss" scoped>
  #toolbar{
    float: left;
    width: 80px;
    height: 100%;
    background-color: #30414D;
    color: #767676;
    padding: 35px 25px 25px 25px;

    .starred {
      color: #F7AE4F;
    }

    i{
      font-size: 30px;
      margin-bottom: 35px;
      cursor: pointer;
      opacity: 0.8;
      transition: opacity 0.5s ease;

      &:hover{
        opacity: 1;
      }
    }
  }
</style>

Here, we use a case Vuex is that we need to know whether the current activation of note is the collection category, if it is, we need to highlight the Save button, then how do you know it? That is to get the current active objects through vuex notes inside getters, to determine whether its favorite is true.

Always keep in mind a concept, data vuex is one-way, can only get from the store, and we are in this example activeNote is always maintained in store.js in this way can the public to other components

// 需要维护的状态
const state = {
  notes: [],
  activeNote: {},
  show: ''
};

NotesList.vue

<template>
  <div id="notes-list">
    <div id="list-header">
      <h2>Notes | heavenru.com</h2>
      <div class="btn-group btn-group-justified" role="group">
        <!-- all -->
        <div class="btn-group" role="group">
          <button type="button" class="btn btn-default"
            @click="toggleShow('all')"
            :class="{active: show === 'all'}">All Notes</button>
        </div>

        <!-- favorites -->
        <div class="btn-group" role="group">
          <button type="button" class="btn btn-default"
            @click="toggleShow('favorite')"
            :class="{active: show === 'favorite'}">Favorites</button>
        </div>
      </div>
    </div>

    <!-- 渲染笔记列表 -->
    <div class="container">
      <div class="list-group">
        <a v-for="note in filteredNotes"
         class="list-group-item" href="#"
         :class="{active: activeNote === note}"
         @click="updateActiveNote(note)">
          <h4 class="list-group-item-heading">
            {{note.title.trim().substring(0,30)}}
          </h4>
        </a>
      </div>
    </div>
  </div>
</template>

<script>
  import { updateActiveNote, updateShow } from '../vuex/actions';
  import { show, filteredNotes, activeNote } from '../vuex/getters';

  export default {
    vuex: {
      getters: {
        show,
        filteredNotes,
        activeNote
      },
      actions: {
        updateActiveNote,
        updateShow
      }
    },
    methods: {
      toggleShow(show) {
        this.updateShow(show);
      }
    }
  }
</script>

Notes the list of components, there are three main operations

  • Render notes
  • Switching rendering notes
  • Click on the list of title, switch activeNote

We get a list of notes by filteredNotes method getters in

// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
export const filteredNotes = (state) => {
  if(state.show === 'all'){
    return state.notes || {};
  }else if(state.show === 'favorite'){
    return state.notes.filter(note => note.favorite) || {};
  }
};

You can see, the list we have obtained is dependent on the state.show this state. Our operation is exactly the call to switch lists actions which way to update state.show, this way, to achieve a dynamic refresh the data list, and we are operating on a tree by a method call actions to achieve.

We look at the time switch lists, we also need to dynamically update activeNote. We look at store.js is how to do:

// 切换显示数据列表类型:全部 or 收藏
SET_SHOW_ALL(state, show){
  state.show = show;
  // 切换数据展示,需要同步更新 activeNote
  if(show === 'favorite'){
    state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
  }else{
    state.activeNote = state.notes[0] || {};
  }
}

These operations are triggered our two buttons are bound to our custom function, passing different parameters to a function, which then calls the actions of methods to implement filtering of data, update.

Editor.vue

<template>
  <div id="note-editor">
    <div class="form-group">
      <input type="text" name="title"
        class="title form-control"
        placeholder="请输入标题"
        @input="updateNote"
        v-model="currentNote.title">
      <textarea
        v-model="currentNote.content" name="content"
        class="form-control" row="3" placeholder="请输入正文"
        @input="updateNote"></textarea>
    </div>
  </div>
</template>

<script>
  import { editNote } from '../vuex/actions';
  import { activeNote } from '../vuex/getters';

  export default {
    vuex: {
      getters: {
        activeNote
      },
      actions: {
        editNote
      }
    },
    computed: {
      // 通过计算属性得到的一个对象,这样子我们就能愉快的使用 v-model 了
      currentNote: activeNote
    },
    methods: {
      // 为什么这么做? 因为在严格模式中不允许直接在模板层面去修改 state 中的值
      updateNote() {
        this.editNote(this.currentNote);
      }
    }
  }
</script>

In Editor.vue assembly, we need to be able to update content in real time we are modifying the current activeNote notes object components and the corresponding list.

As we mentioned earlier, the component is not allowed to directly modify the values ​​in the state in which store.js, so here, we calculated by an attribute, the value assigned to store the state of the inside of an object, and then since updateNotes () method defined in the call to action, passing in currentNote object.

In store.js we are doing, find an object corresponding to the id of the re-assignment as mentioned earlier, our data are responsive in here for a change, the corresponding view will be refreshed to change, such an action would achieve real-time editing, real-time rendering of a function.

// 修改笔记
EDIT_NOTE(state, note) {
  state.activeNote = note;
  // 修改原始数据
  for (var i = 0; i < state.notes.length; i++) {
    if(state.notes[i].id === note.id){
      state.notes[i] = note;
      break;
    }
  };
},

Q&A

In this project, we have not introduced vue-resource plugins, but his simulated data part, interested students can go try.

Because our example is relatively simple and does not involve something very deep, deeper research we need to spend more time to practice.

Finally, say one in action which, in fact, we can do much more, such as access to your notes, etc. The ATM id dynamic, these students are interested can go try a little of this rich example.

This article is reproduced in: ape → 2048 https://www.mk2048.com/blog/blog.php?id=hia0ca0k1ib

Guess you like

Origin www.cnblogs.com/10manongit/p/12628879.html