使用 Vue.js 2 + Vuex 创建 Notes App

作者: 与蟒唯舞 | 来源:发表于2017-06-23 23:25 被阅读262次

    Vuex 是什么?

    Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.

    Here is a vuex store definition:

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      state: {
      },
      actions: {
      },
      mutations: {
      },
      getters: {
      },  
      modules: {  
      }
    })
    
    export default store
    

    There are 5 objects that make up a store:

    • state:
      This is where you define the data structure of your application. You can also set default or initial state here.

    • actions:
      Actions are where you define the calls that will commit changes to your store. A common example of this would be a call to an api to retrieve data, once it completes you call store.commit() to invoke a mutation to actually update the data in the store. Actions are called in your components via dispatch call.

    • mutations:
      The mutations calls are the only place that the store can be updated.

    • getters:
      Getters are a way to grab computed data from the store.

    • modules:
      The modules object provides a way to split your store in multiple stores, but allow them to all remain part of the store tree. As your application grows this is a good way to organize your codebase.

    更多详情参看 中文文档

    几点说明:

    • 数据是单向流动的
    • 组件可以调用 actions
    • actions 用来调度/提交 mutations
    • 只有 mutations 可以修改 state,组件不能也不应该直接修改 state
    • 无论 state 何时更新,组件都将反映这些变化

    开始项目

    项目结构
    初始化项目
    vue init webpack vuex-notes-app
    cd vuex-notes-app
    npm install
    
    npm install vuex --save
    
    npm run dev
    
    创建 Vuex Store
    // store.js
    
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    const state = {
      notes: [],
      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) {
        if (state.notes.length > 0) {
          state.notes.splice(state.notes.indexOf(state.activeNote), 1)
          state.activeNote = state.notes[0]
        }
      },
      TOGGLE_FAVORITE(state) {
        if (state.activeNote) {
          state.activeNote.favorite = !state.activeNote.favorite
        }
      },
      SET_ACTIVE_NOTE(state, note) {
        state.activeNote = note
      }
    }
    
    const getters = {
      activeNoteText: state => {
        if (state.activeNote) {
          return state.activeNote.text
        }
      },
      activeNoteFavorite: state => {
        if (state.activeNote) {
          return state.activeNote.favorite
        }
      },
    }
    
    const actions = {
      addNote({ commit }) {
        commit('ADD_NOTE')
      },
      editNote({ commit }, e) {
        commit('EDIT_NOTE', e.target.value)
      },
      deleteNote({ commit }) {
        commit('DELETE_NOTE')
      },
      updateActiveNote({ commit }, note) {
        commit('SET_ACTIVE_NOTE', note)
      },
      toggleFavorite({ commit }) {
        commit('TOGGLE_FAVORITE')
      }
    }
    
    export default new Vuex.Store({
      state,
      mutations,
      getters,
      actions
    })
    
    导入
    // main.js
    // import the store and add it to the root vue object
    
    import Vue from 'vue'
    import App from './App'
    import store from './vuex/store'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      template: '<App/>',
      components: {
        App
      },
      store,
    })
    
    Toolbar.vue
    <template>
        <div id="toolbar">
            <i @click="addNote" class="glyphicon glyphicon-plus"></i>
            <i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNoteFavorite}"></i>
            <i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
        </div>
    </template>
    
    <script>
    import { mapActions, mapGetters } from 'vuex'
    
    export default {
        methods: {
            ...mapActions(['addNote', 'deleteNote', 'toggleFavorite'])
        },
        computed: {
            ...mapGetters(['activeNoteFavorite'])
        }
    }
    </script>
    
    NotesList.vue
    <template>
      <div id="notes-list">
        <div id="list-header">
          <h2>Notes | Awesome</h2>
          <div class="btn-group btn-group-justified" role="group">
            <!-- All Notes button -->
            <div class="btn-group" role="group">
              <button type="button" class="btn btn-default" @click="show = 'all'" :class="{active: show === 'all'}">
                All Notes
              </button>
            </div>
            <!-- Favorites Button -->
            <div class="btn-group" role="group">
              <button type="button" class="btn btn-default" @click="show = 'favorites'" :class="{active: show === 'favorites'}">
                Favorites
              </button>
            </div>
          </div>
        </div>
        <!-- render notes in a list -->
        <div class="container">
          <div class="list-group">
            <a v-for="note in filteredNotes" :key="note" class="list-group-item" href="#" :class="{active: activeNote === note}" @click="updateActiveNote(note)">
              <h4 class="list-group-item-heading">
                {{note.text.trim().substring(0, 30)}}
              </h4>
            </a>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { mapActions, mapState } from 'vuex'
    export default {
      data() {
        return {
          show: 'all'
        }
      },
      methods: {
        ...mapActions(['updateActiveNote'])
      },
      computed: {
        ...mapState(['notes', 'activeNote']),
        filteredNotes() {
          if (this.notes) {
            if (this.show === 'all') {
              return this.notes
            } else if (this.show === 'favorites') {
              return this.notes.filter(note => note.favorite)
            }
          }
        }
      }
    }
    </script>
    
    Editor.vue
    <template>
      <div id="note-editor">
        <textarea :value="activeNoteText" @input="editNote" class="form-control">
        </textarea>
      </div>
    </template>
    
    <script>
    import { mapActions, mapGetters } from 'vuex'
    export default {
      computed: {
        ...mapGetters(['activeNoteText'])
      },
      methods: {
        ...mapActions(['editNote'])
      }
    }
    </script>
    
    App.vue
    <template>
      <div id="app">
        <Toolbar></Toolbar>
        <NotesList></NotesList>
        <Editor></Editor>
      </div>
    </template>
    
    <script>
    import Toolbar from './components/Toolbar'
    import NotesList from './components/NotesList'
    import Editor from './components/Editor'
    
    export default {
      name: 'app',
      components: {
        Toolbar,
        NotesList,
        Editor
      }
    }
    </script>
    
    styles.css
    @import url(https://fonts.googleapis.com/css?family=Raleway:400,300);
    
    html, #app {
      height: 100%;
    }
    
    body {
      margin: 0;
      padding: 0;
      border: 0;
      height: 100%;
      max-height: 100%;
      position: relative;
    }
    
    #toolbar {
      float: left;
      width: 80px;
      height: 100%;
      background-color: #30414D;
      color: #767676;
      padding: 35px 25px 25px 25px;
    }
    
    #notes-list {
      float: left;
      width: 300px;
      height: 100%;
      background-color: #F5F5F5;
      font-family: 'Raleway', sans-serif;
      font-weight: 400;
    }
    
    #list-header {
      padding: 5px 25px 25px 25px;
    }
    
    #list-header h2 {
      font-weight: 300;
      text-transform: uppercase;
      text-align: center;
      font-size: 22px;
      padding-bottom: 8px;
    }
    
    #notes-list .container {
      height: calc(100% - 137px);
        max-height: calc(100% - 137px);
        overflow: auto;
      width: 100%;
      padding: 0;
    }
    
    #notes-list .container .list-group-item {
      border: 0;
      border-radius: 0;
    }
    
    .list-group-item-heading {
      font-weight: 300;
      font-size: 15px;
    }
    
    #note-editor {
      height: 100%;
      margin-left: 380px;
    }
    
    #note-editor textarea {
      height: 100%;
      border: 0;
      border-radius: 0;
    }
    
    #toolbar i {
      font-size: 30px;
      margin-bottom: 35px;
      cursor: pointer;
      opacity: 0.8;
      transition: opacity 0.5s ease;
    }
    
    #toolbar i:hover {
      opacity: 1;
    }
    
    .starred {
      color: #F7AE4F;
    }
    
    index.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Notes</title>
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
        <link rel="stylesheet" href="static/css/styles.css">
      </head>
      <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    

    效果截图

    相关文章

      网友评论

        本文标题:使用 Vue.js 2 + Vuex 创建 Notes App

        本文链接:https://www.haomeiwen.com/subject/wykfcxtx.html