찾다
웹 프론트엔드HTML 튜토리얼用 Vuex 构建一个笔记应用_html/css_WEB-ITnose

原文: Learn Vuex by Building a Notes App ,有删改。

本文假设读者熟悉 Vuex 文档 的内容。如果不熟悉,you definitely should!

在这个教程里面,我们会通过构建一个笔记应用来学习怎么用 Vuex。我会简单地介绍一下 Vuex 的基础内容, 什么时候该用它以及用 Vuex 的时候该怎么组织代码,然后我会一步一步地把这些概念应用到这个笔记应用里面。

这个是我们要构建的笔记应用的截图:

你可以从 Github Repo 下载源码,这里是 demo 的地址。

Vuex 概述

Vuex 是一个主要应用在中大型单页应用的类似于 Flux 的数据管理架构。它主要帮我们更好地组织代码,以及把应用内的的状态保持在可维护、可理解的状态。

如果你不太理解 Vue.js 应用里的状态是什么意思的话,你可以想象一下你此前写的 Vue 组件里面的 data 字段。Vuex 把状态分成 组件内部状态应用级别状态

  • 组件内部状态:仅在一个组件内使用的状态(data 字段)

  • 应用级别状态:多个组件共用的状态

举个例子:比如说有一个父组件,它有两个子组件。这个父组件可以用 props 向子组件传递数据,这条数据通道很好理解。

那如果这两个子组件相互之间需要共享数据呢?或者子组件需要向父组件传递数据呢?这两个问题在应用体量较小的时候都好解决,只要用 自定义事件 即可。

但是随着应用规模的扩大:

  • 追踪这些事件越来越难了。这个事件是哪个组件触发的?谁在监听它?

  • 业务逻辑遍布各个组件,导致各种意想不到的问题。

  • 由于要显式地分发和监听事件,父组件和子组件强耦合。

Vuex 要解决的就是这些问题,Vuex 背后有四个核心的概念:

  • 状态树: 包含所有应用级别状态的对象

  • Getters: 在组件内部获取 store 中状态的函数

  • Mutations: 修改状态的事件回调函数

  • Actions: 组件内部用来分发 mutations 事件的函数

下面这张图完美地解释了一个 Vuex 应用内部的数据流动:

这张图的重点:

  • 数据流动是单向的

  • 组件可以调用 actions

  • Actions 是用来分发 mutations 的

  • 只有 mutations 可以修改状态

  • store 是反应式的,即,状态的变化会在组件内部得到反映

搭建项目

项目结构是这样的:

  • components/包含所有的组件

  • vuex/包含 Vuex 相关的文件 (store, actions)

  • 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']  }}

然后在 package.json 里面配置一下 npm script:

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

后面测试和生产的时候直接运行 npm run dev 和 npm run build 就行了。

创建 Vuex Store

在 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 里的 state。

  • App, 根组件,就是最外面那个红色的盒子

  • Toolbar是左边的绿色竖条,包括三个按钮

  • NotesList是包含了笔记标题列表的紫色框。用户可以点击所有笔记(All Notes)或者收藏笔记(Favorites)

  • Editor是右边这个可以编辑笔记内容的黄色框

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 id="Notes-coligo">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 id="note-text-trim-substring">            {{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 文档里面有 详细的说明 。

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

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
공식 계정 웹 페이지의 캐싱 업데이트에 어려움 : 버전 업데이트 후 사용자 경험에 영향을 미치는 이전 캐시를 피하는 방법은 무엇입니까?공식 계정 웹 페이지의 캐싱 업데이트에 어려움 : 버전 업데이트 후 사용자 경험에 영향을 미치는 이전 캐시를 피하는 방법은 무엇입니까?Mar 04, 2025 pm 12:32 PM

공식 계정 웹 페이지 업데이트 캐시, 이것은 간단하고 간단하며 냄비를 마시기에 충분히 복잡합니다. 공식 계정 기사를 업데이트하기 위해 열심히 노력했지만 사용자는 여전히 기존 버전을 열었습니까? 이 기사에서는이 뒤에있는 비틀기와 회전을 살펴 보고이 문제를 우아하게 해결하는 방법을 살펴 보겠습니다. 읽은 후에는 다양한 캐싱 문제를 쉽게 처리 할 수있어 사용자가 항상 가장 신선한 콘텐츠를 경험할 수 있습니다. 기본 사항에 대해 먼저 이야기 해 봅시다. 액세스 속도를 향상시키기 위해 브라우저 또는 서버는 일부 정적 리소스 (예 : 그림, CSS, JS) 또는 페이지 컨텐츠를 저장합니다. 다음에 액세스 할 때 다시 다운로드하지 않고도 캐시에서 직접 검색 할 수 있으며 자연스럽게 빠릅니다. 그러나 이것은 또한 양날의 검입니다. 새 버전은 온라인입니다.

HTML5 양식 유효성 검사 속성을 사용하여 사용자 입력을 유효성있게하려면 어떻게합니까?HTML5 양식 유효성 검사 속성을 사용하여 사용자 입력을 유효성있게하려면 어떻게합니까?Mar 17, 2025 pm 12:27 PM

이 기사에서는 브라우저에서 직접 사용자 입력을 검증하기 위해 필요한, Pattern, Min, Max 및 Length 한계와 같은 HTML5 양식 검증 속성을 사용하는 것에 대해 설명합니다.

HTML5의 크로스 브라우저 호환성에 대한 모범 사례는 무엇입니까?HTML5의 크로스 브라우저 호환성에 대한 모범 사례는 무엇입니까?Mar 17, 2025 pm 12:20 PM

기사는 HTML5 크로스 브라우저 호환성을 보장하기위한 모범 사례에 대해 논의하고 기능 감지, 점진적 향상 및 테스트 방법에 중점을 둡니다.

웹 페이지의 PNG 이미지에 뇌졸중 효과를 효율적으로 추가하는 방법은 무엇입니까?웹 페이지의 PNG 이미지에 뇌졸중 효과를 효율적으로 추가하는 방법은 무엇입니까?Mar 04, 2025 pm 02:39 PM

이 기사는 CSS를 사용한 웹 페이지에 효율적인 PNG 테두리 추가를 보여줍니다. CSS는 JavaScript 또는 라이브러리에 비해 우수한 성능을 제공하며, 미묘하거나 눈에 띄는 효과를 위해 테두리 너비, 스타일 및 색상 조정 방법을 자세히 설명합니다.

& lt; datalist & gt의 목적은 무엇입니까? 요소?& lt; datalist & gt의 목적은 무엇입니까? 요소?Mar 21, 2025 pm 12:33 PM

이 기사는 HTML & LT; Datalist & GT에 대해 논의합니다. 자동 완성 제안을 제공하고, 사용자 경험을 향상시키고, 오류를 줄임으로써 양식을 향상시키는 요소. 문자 수 : 159

& lt; Progress & Gt의 목적은 무엇입니까? 요소?& lt; Progress & Gt의 목적은 무엇입니까? 요소?Mar 21, 2025 pm 12:34 PM

이 기사는 HTML & lt; Progress & Gt에 대해 설명합니다. 요소, 그 목적, 스타일 및 & lt; meter & gt의 차이; 요소. 주요 초점은 & lt; progress & gt; 작업 완료 및 & lt; meter & gt; Stati의 경우

html5 & lt; time & gt; 의미 적으로 날짜와 시간을 나타내는 요소?html5 & lt; time & gt; 의미 적으로 날짜와 시간을 나타내는 요소?Mar 12, 2025 pm 04:05 PM

이 기사는 html5 & lt; time & gt; 시맨틱 날짜/시간 표현 요소. 인간이 읽을 수있는 텍스트와 함께 기계 가독성 (ISO 8601 형식)에 대한 DateTime 속성의 중요성을 강조하여 Accessibilit를 향상시킵니다.

& lt; meter & gt의 목적은 무엇입니까? 요소?& lt; meter & gt의 목적은 무엇입니까? 요소?Mar 21, 2025 pm 12:35 PM

이 기사는 HTML & lt; meter & gt에 대해 설명합니다. 범위 내에 스칼라 또는 분수 값을 표시하는 데 사용되는 요소 및 웹 개발의 일반적인 응용 프로그램. & lt; meter & gt; & lt; Progress & Gt; 그리고 Ex

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

SecList

SecList

SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경