Vuex_html/css_WEB-ITnose を使用してメモを取るアプリケーションを構築する

WBOY
リリース: 2016-06-24 11:19:37
オリジナル
1453 人が閲覧しました

原文: Learn Vuex by Building a Notes App, with deletions.

この記事は、読者が Vuex ドキュメントの内容に精通していることを前提としています。まだ知らない方はぜひ!

このチュートリアルでは、メモを取るアプリケーションを構築することによって Vuex の使用方法を学びます。 Vuex の基本、いつ使用するか、Vuex を使用する際のコードの編成方法を簡単に紹介します。その後、これらの概念をこのメモ作成アプリケーションに段階的に適用していきます。

これはこれから構築するメモ作成アプリケーションのスクリーンショットです:

ソース コードは Github Repo からダウンロードできます。デモのアドレスはここにあります。

Vuex の概要

Vuex は、主に中規模および大規模のシングルページ アプリケーションで使用される Flux のようなデータ管理アーキテクチャです。これは主に、コードをより適切に整理し、アプリケーション内の状態を保守可能で理解可能な状態に保つのに役立ちます。

Vue.js アプリケーションの状態が何を意味するのかよく理解できない場合は、前に作成した Vue コンポーネントのデータ フィールドを想像してください。 Vuex は、状態をコンポーネントの内部状態アプリケーションレベルの状態に分割します。

  • コンポーネントの内部状態: 1つのコンポーネント(データフィールド)内でのみ使用される状態

  • アプリケーションレベルの状態: 複数のコンポーネントによって共有される状態

例: 2 つの子コンポーネントを持つ親コンポーネントがあるとします。この親コンポーネントは props を使用してデータを子コンポーネントに渡すことができます。このデータ チャネルは理解しやすいです。

それでは、2 つのサブコンポーネントが相互にデータを共有する必要がある場合はどうすればよいでしょうか? あるいは、サブコンポーネントがデータを親コンポーネントに渡す必要がある場合はどうすればよいでしょうか? これら 2 つの問題は、アプリケーションが小さい場合には簡単に解決できます。カスタム コンポーネントを使用するだけです。イベント。

しかし、アプリが拡大するにつれて、

  • これらのイベントを追跡することがますます困難になります。このイベントをトリガーしたコンポーネントはどれですか?誰がそれを聞いているのでしょうか?

  • ビジネス ロジックはさまざまなコンポーネントに分散されており、さまざまな予期しない問題を引き起こします。

  • イベントの明示的な配布とリスニングにより、親コンポーネントと子コンポーネントは強く結合されます。

Vuex はこれらの問題を解決したいと考えています。Vuex の背後には 4 つの中心的な概念があります:

  • 状態ツリー: すべてのアプリケーション レベルの状態を含むオブジェクト

  • ゲッター: 内部のストア内の状態を取得する関数コンポーネント

  • ミューテーション: 状態を変更するイベント コールバック関数

  • アクション: コンポーネント内でミューテーション イベントを分散するために使用される関数

次の図は、Vuex アプリケーション内のデータ フローを完全に説明しています:

この図の重要なポイント:

  • データフローは一方向です

  • コンポーネントはアクションを呼び出すことができます

  • アクションはミューテーションを配布するために使用されます

  • ミューテーションのみが状態を変更できます

  • store これはリアクティブです。つまり、状態の変更はコンポーネント内に反映されます

プロジェクトをビルドします

プロジェクトの構造は次のようになります:

  • コンポーネント/すべてのコンポーネントが含まれます

  • vuex/ Vuex 関連ファイル (ストア、アクション) が含まれています

  • build.js は webpack が出力するファイルです

  • Index.html はレンダリングされるページです

  • main.js はアプリケーションのエントリ ポイントです、ルート インスタンスを含む

  • style.css

  • webpack.config.js

新しいプロジェクト:

mkdir vuex-notes-app && cd vuex-note-appnpm init -y
ログイン後にコピー

依存関係のインストール:

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-devnpm install vue vuex --save
ログイン後にコピー

次に Webpack を構成します:
// webpack.config.jsmodule.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']  }}
ログイン後にコピー

次に、npm スクリプトの設定をパックしますage.json:

"scripts": {  "dev": "webpack-dev-server --inline --hot",  "build": "webpack -p"}
ログイン後にコピー

後でテスト中や運用中に npm run dev と npm run build を直接実行するだけです。

Vuex ストアを作成する

vuex/ フォルダーに store.js を作成します:

import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)const state = {  notes: [],  activeNote: {}}const mutations = { ... }export default new Vuex.Store({  state,  mutations})
ログイン後にコピー

次に、下の図を使用してアプリケーションを複数のコンポーネントに分解し、コンポーネント内で必要なデータを store.js の状態にマッピングします。

アプリのルート コンポーネントは、一番外側の赤いボックスです
  • ツールバーは、3 つのボタンを含む左側の緑色の垂直バーです
  • NotesList は、メモのタイトルのリストが含まれる紫色のボックスです。ユーザーは、[すべてのメモ] または [お気に入り] をクリックできます
  • エディターは、メモの内容を編集できる右側の黄色のボックスです

store.js 里面的状态对象会包含所有应用级别的状态,也就是各个组件需要共享的状态。

笔记列表( notes: [] )包含了 NodesList 组件要渲染的 notes 对象。当前笔记(activeNote: {})则包含当前选中的笔记对象,多个组件都需要这个对象:

  • Toolbar 组件的收藏和删除按钮都对应这个对象

  • NotesList 组件通过 CSS 高亮显示这个对象

  • Editor 组件展示及编辑这个笔记对象的内容。

聊完了状态(state),我们来看看 mutations, 我们要实现的 mutation 方法包括:

  • 添加笔记到数组里 (state.notes)

  • 把选中的笔记设置为「当前笔记」(state.activeNote)

  • 删掉当前笔记

  • 编辑当前笔记

  • 收藏/取消收藏当前笔记

首先,要添加一条新笔记,我们需要做的是:

  • 新建一个对象

  • 初始化属性

  • push 到 state.notes 里去

  • 把新建的这条笔记设为当前笔记(activeNote)

ADD_NOTE (state) {  const new Note = {    text: 'New note',    favorite: fals  }  state.notes.push(newNote)  state.activeNote=  newNote}
ログイン後にコピー

然后,编辑笔记需要用笔记内容 text 作参数:

EDIT_NOTE (state, text) {  state.activeNote.text = text}
ログイン後にコピー

剩下的这些 mutations 很简单就不一一赘述了。整个 vuex/store.js 是这个样子的:

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})
ログイン後にコピー

接下来聊 actions, actions 是组件内用来分发 mutations 的函数。它们接收 store 作为第一个参数。比方说,当用户点击 Toolbar 组件的添加按钮时,我们想要调用一个能分发 ADD_NOTE mutation 的 action。现在我们在 vuex/文件夹下创建一个 actions.js 并在里面写上 addNote 函数:

// actions.jsexport const addNote = ({ dispatch }) => {  dispatch('ADD_NOTE')}
ログイン後にコピー

剩下的这些 actions 都跟这个差不多:

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')}
ログイン後にコピー

这样,在 vuex 文件夹里面要写的代码就都写完了。这里面包括了 store.js 里的 state 和 mutations,以及 actions.js 里面用来分发 mutations 的 actions。

构建 Vue 组件

最后这个小结,我们来实现四个组件 (App, Toolbar, NoteList 和 Editor) 并学习怎么在这些组件里面获取 Vuex store 里的数据以及调用 actions。

创建根实例 - main.js

main.js是应用的入口文件,里面有根实例,我们要把 Vuex store 加到到这个根实例里面,进而注入到它所有的子组件里面:

import Vue from 'vue'import store from './vuex/store'import App from './components/App.vue'new Vue({  store, // 注入到所有子组件  el: 'body',  components: { App }})
ログイン後にコピー

App - 根组件

根组件 App 会 import 其余三个组件:Toolbar, NotesList 和 Editor:

<template>  <div id="app">    <toolbar></toolbar>    <notes-list></notes-list>    <editor></editor>  </div></template><script>import Toolbar from './Toolbar.vue'import NotesList from './NotesList.vue'import Editor from './Editor.vue'export default {  components: {    Toolbar,    NotesList,    Editor  }}</script>
ログイン後にコピー

把 App 组件放到 index.html 里面,用 BootStrap 提供基本样式,在 style.css 里写组件相关的样式:

<!-- index.html --><!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8">    <title>Notes | coligo.io</title>    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">    <link rel="stylesheet" href="styles.css">  </head>  <body>    <app></app>    <script src="build.js"></script>  </body></html>
ログイン後にコピー

Toolbar

Toolbar 组件提供给用户三个按钮:创建新笔记,收藏当前选中的笔记和删除当前选中的笔记。

这对于 Vuex 来说是个绝佳的用例,因为 Toolbar 组件需要知道「当前选中的笔记」是哪一条,这样我们才能删除、收藏/取消收藏它。前面说了「当前选中的笔记」是各个组件都需要的,不应该单独存在于任何一个组件里面,这时候我们就能发现共享数据的必要性了。

每当用户点击笔记列表中的某一条时,NodeList 组件会调用 updateActiveNote() action 来分发 SET_ACTIVE_NOTE mutation, 这个 mutation 会把当前选中的笔记设为 activeNote 。

也就是说,Toolbar 组件需要从 state 获取 activeNote 属性:

vuex: {  getters: {    activeNote: state => state.activeNote  }}
ログイン後にコピー

我们也需要把这三个按钮所对应的 actions 引进来,因此 Toolbar.vue 就是这样的:

<template>  <div id="toolbar">    <i @click="addNote" 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 { addNote, deleteNote, toggleFavorite } from '../vuex/actions'export default {  vuex: {    getters: {      activeNote: state => state.activeNote    },    actions: {      addNote,      deleteNote,      toggleFavorite    }  }}</script>
ログイン後にコピー

注意到当 activeNote.favorite === true 的时候,收藏按钮还有一个 starred 的类名,这个类的作用是对收藏按钮提供高亮显示。

NotesList

NotesList 组件主要有三个功能:

  1. 把笔记列表渲染出来

  2. 允许用户选择"所有笔记"或者只显示"收藏的笔记"

  3. 当用户点击某一条时,调用 updateActiveNote action 来更新 store 里的 activeNote

显然,在 NoteLists 里需要 store 里的 notes array 和 activeNote :

vuex: {  getters: {    notes: state => state.notes,    activeNote: state => state.activeNote  }}
ログイン後にコピー

当用户点击某一条笔记时,把它设为当前笔记:

import { updateActiveNote } from '../vuex/actions'export default {  vuex: {    getters: {      // as shown above    },    actions: {      updateActiveNote    }  }}
ログイン後にコピー

接下来,根据用户点击的是"所有笔记"还是"收藏笔记"来展示过滤后的列表:

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)      }    }  }}
ログイン後にコピー

在这里组件内的 show 属性是作为组件内部状态出现的,很明显,它只在 NoteList 组件内出现。

以下是完整的 NotesList.vue:

<template>  <div id="notes-list">    <div id="list-header">      <h2>Notes | coligo</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"          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 { updateActiveNote } from '../vuex/actions'export default {  data () {    return {      show: 'all'    }  },  vuex: {    getters: {      notes: state => state.notes,      activeNote: state => state.activeNote    },    actions: {      updateActiveNote    }  },  computed: {    filteredNotes () {      if (this.show === 'all'){        return this.notes      } else if (this.show === 'favorites') {        return this.notes.filter(note => note.favorite)      }    }  }}</script>
ログイン後にコピー

这个组件的几个要点:

  • 用前30个字符当作该笔记的标题

  • 当用户点击一条笔记,该笔记变成当前选中笔记

  • 在"all"和"favorite"之间选择实际上就是设置 show 属性

  • 通过 :class="" 设置样式

Editor

Editor 组件是最简单的,它只做两件事:

  • 从 store 获取当前笔记 activeNote ,把它的内容展示在 textarea

  • 在用户更新笔记的时候,调用 editNote() action

以下是完整的 Editor.vue:

<template>  <div id="note-editor">    <textarea      :value="activeNoteText"      @input="editNote"      class="form-control">    </textarea>  </div></template><script>import { editNote } from '../vuex/actions'export default {  vuex: {    getters: {      activeNoteText: state => state.activeNote.text    },    actions: {      editNote    }  }}</script>
ログイン後にコピー

这里的 textarea 不用 v-model 的原因在 vuex 文档里面有 详细的说明 。

至此,这个应用的代码就写完了,不明白的地方可以看 源代码 , 然后动手操练一遍。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート