使用Vuex实现一个笔记应用的方法


Posted in Javascript onMarch 13, 2018

最近开始着手学习 Vue,先大略的过了一遍官方文档,跟着敲过一部分官方文档中的 DEMO,然而还是不甚了了。在网上找到了一个入门的笔记应用,即便是入门级的应用,在学习途中依旧困难重重。特将学习作此笔记,方便今后回看,也希望能够帮到刚开始学习 Vue 的女同学

附原作者 Github 链接:https://github.com/lichenbuliren/vuex-notes-app2

预期目标

笔记具备如下基本功能
1.新增
2.删除
3.收藏
4.在全部笔记和收藏笔记间切换
5.在当前列表中进行搜索

使用Vuex实现一个笔记应用的方法

卖家秀

使用Vuex实现一个笔记应用的方法

买家秀

准备工作

1.新建项目

选个文件夹存放项目,这里我用的是 Git Bush 执行语句($ 符号是 Git Bush 中自带的),你也可以使用命令行,一样的

使用Vuex实现一个笔记应用的方法

选择项目存放位置

2.查看模块(爱看不看)

查看一下全局安装的模块  npm list --depth=0 -global

使用Vuex实现一个笔记应用的方法

查看全局安装的模块

3.创建项目

在命令行输入 vue init webpack vuex-note 并做好设置,创建一个项目

使用Vuex实现一个笔记应用的方法

这都什么鬼

4.简单解释一下各个配置都是干嘛的

  1. vue init webpack vuex-note:初始化(init)一个使用 webpack 构建工具构建的 vue 项目,项目名为 vuex-note
  2. Project name:项目名
  3. Project description:项目描述
  4. Author:朕
  5. Vue build:构建方式,分为独立构建和运行时构建,具体说明见如下链接,这里选择独立构建 standalone https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
  6. Install vue-router:是否需要安装 vue-router ,跳转页面用的,这里用不着,我过会学
  7. Use ESLint to lint your code:ESLint 规范与法用的,可能你熟悉的写法都是不标准的,如果采用 ESLint 则可能报错,这里选择 n
  8. 剩下的都是测试用的,一路 n
  9. Should we run 'npm install' for you after the project has been created:是否需要直接替你安装(npm install)相关的依赖,回车就行,之后会替你安装各种玩意

5.安装完后会有提示,我们接着按照提示走

先是 cd vuex-note 进入刚刚创建的 vue 项目文件夹

使用Vuex实现一个笔记应用的方法

安装完成

再通过 npm run dev 跑起项目

使用Vuex实现一个笔记应用的方法

后续操作

6.访问页面

此时通过浏览器访问 localhost:8080 就可以打开一个新的 vue 页面

使用Vuex实现一个笔记应用的方法

崭新的 vue 页面

7.项目结构

截止目前的项目结构如图

使用Vuex实现一个笔记应用的方法

项目结构

由于是初学,为了先搞个东西出来,所以暂时先不管一些乱七八糟的配置,只挑跟这次相关的说(其实多了我也没学到...)

8.查看 Vuex

既然是使用 Vuex 来实现笔记应用,我们就应该先查看一下构建的项目是否包含 Vuex 模块。

node_modules 文件夹包含了现有的模块,然而里面并没有我们想要的 Vuex,不信自己去看

package.json 文件描述了项目包含的文件,项目如何运行等信息

使用Vuex实现一个笔记应用的方法

package.json

9.安装 Vuex

在命令行中输入 npm install vuex --save:--save 就是将安装信息写入 package.json

使用Vuex实现一个笔记应用的方法

已安装了 Vuex

至此,所有前期工作已经准备完成,遗漏的部分将在实现过程中逐一解释

搞起

零、思路

整个应用可拆分为三个组件

使用Vuex实现一个笔记应用的方法


  1. 每条笔记包括 编号(ID),标题(title),内容(content),是否已收藏(fav) 四种信息
  2. Vuex 中的 state 得有个地方存放 所有的笔记(notes)
  3. 而 收藏,删除 操作只能对 当前的笔记 进行操作,因此我还需要一个标识用来记录 当前的笔记(activeNote) 是哪个
  4. 包含 全部 和 收藏 两种切换方式,因此还需要有一个标识来进行区分,就叫 show 吧,all 代表 全部,fav 就代表 已收藏
  5. 组件 ==> actions.js ==> mutations.js = > state:通过组件调用 actions 中的方法(dispatch),通过 actions 中的方法调用 mutations 中的方法(commit),通过 mutations 中的方法去操作 state 中的笔记列表(notes),当前笔记(activeNote)等等

一、index.html

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>vuex-note</title>
 </head>
 <body>
  <div id="app"></div>
  <!-- built files will be auto injected -->
 </body>
</html>

这个没什么说的,注意 div 的 ID 就行

二、main.js

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

Vue.config.productionTip = false

new Vue({
 el: '#app',
 store,        
 components: { App },
 template: '<App/>'
})

1.在 import 时什么时候需要 ' ./ '?

从项目模块中导出,引入时不需要 ./,而从自己写的组件中引入时需要 ./

2.什么时候需要 import {aaa} from abc 这种加大括号的引入?什么时候不需要?

当 abc 中被导出的部分是 export aaa 时

当 import 的是被 export default 导出的部分时不加 {},并且可以起个别名

3.项目结构中并没有 store 文件,只有 store 文件夹,那 import store from './store' 是什么意思?

不知道,求指教

4. new Vue 中单独的 store 是什么意思?

ES6 的一种简写方式,缩写之前是 store:store,这句话的意思是为全局注入 Vuex,这样在各个组件中都可以通过 this.$store 去调用状态库,如果不在全局注入,则需要在每个组件中单独引入,多了会很麻烦

三、store 下的 index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'

Vue.use(Vuex)    

const defaultNote = {
  id: +new Date(),    
  title: '新建笔记' + new Date().getMilliseconds(),    // 加时间是为了做一下区分
  content: '笔记内容',
  fav: false
}

// 可以理解为一个状态的仓库  
const state = {
  notes: [defaultNote],      // 以数组方式存放所有的笔记
  activeNote: defaultNote,    // 用来记录当前笔记  
  show: 'all'           // 用于切换 全部 / 已收藏 两种不同列表的标识
}

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

1. Vue.use(Vuex) 是什么意思?

使用 Vuex,今后用 Vue-router 时也得来这么一出,只是得写在 route 文件夹下的 index.js 文件中

2. +new Date() 是什么意思?

获取时间戳的另一种写法,等同于 new Date().getTime()

3.state,getters,mutations,actions 之间的关系?

state:如上所言状态仓库

getters:state 的修饰,比如 state 中有 str:"abc" 这么个属性,而在很多组件中需要进行 str + "def" 的操作,如果在每个组件都进行 str + "def" 的操作未免太麻烦,于是可以在 getters 中增加:

strAdd(){
  return this.str + "abc"
}

今后在组件中使用 strAdd 就可以了

  1. mutations:简单讲就是用来修改 state 的,同步方法.常规调用 this.$store.commit
  2. actions:简单讲用来调用 mutations 的,异步方法.常规调用 this.$store.dispatch

四、tool.vue

<template>
  <div id="tool">
    <button class="add" @click="add_note">新增</button>
    <button class="fav" @click="fav_note">收藏</button>
    <button class="del" @click="del_note">删除</button>
  </div>
</template>
<script type="text/javascript">
  import { mapState, mapGetter, mapActions } from 'vuex'
  export default {
    name: 'tool',
    methods:{      
      ...mapActions(['add_note','del_note','fav_note'])
    }
  }
</script>
<style type="text/css" scoped>
  #tool {
    width: 200px;
    height: 600px;
    border: 2px solid #ccc;
    float: left;
  }
  
  button {
    width: 100%;
    height: calc(100% / 3);    
    font-size: 60px;
  }
</style>

1.mapState, mapGetter, mapActions 都是什么?

这里有个非常好的解释 http://www.imooc.com/article/14741

此外,当 methods 和 Vuex 的 actions 中具有同名的属性 A 时,可使用 mapActions(['A']) 这种方式简写

注意:1、中括号不能省略;2、中括号内是字符串;3、展开运算符...不能省略

也可以取个别名,写法如下,注意 [] 变成了 {}:

...map({
 本组件的属性 : Vuex 中 actions 中的属性
})

需要传入参数时,前提是 actions 中的属性(方法)能接收参数:

methods:{
 ...mapActions(['abc'])
 // 自定义一个方法,通过触发这个方法调用之前重名的方法并传入参数
 tragger_abc(参数){
  this.abc(参数)
 }
}

2.scoped

对当前组件生效的 CSS

3.calc

使用时记得在运算符前后各加一个空格

五、list.vue

<template>
  <div id="list">
    <div class="switch">
      <button class="all" @click='get_switch_note("all")'>全部</button><button class="fav" @click='get_switch_note("fav")'>已收藏</button>
    </div>
    <div class="search">
      <input type="text" placeholder="在这里搜索" v-model="search" />
    </div>
    <div class="noteList">
      <div class="note" v-for="note in search_filteredNote" :class="{favColor:note.fav===true,active:note.id===activeNote.id}" @click='get_select_note(note)'>
        <div class="title">
          <p>{{note.title}}</p>
        </div>
        <div class="content">
          <p>{{note.content}}</p>
        </div>
      </div>
    </div>
  </div>
</template>
<script type="text/javascript">
  import { mapState, mapGetters, mapActions } from 'vuex'
  export default {
    name: 'list',
    data: function() {
      return {
        search: "" 
      }
    },
    computed: {
      ...mapState(['notes', 'activeNote']),
      ...mapGetters(['filteredNote']),
      // 二次过滤:在当前列表(全部 或 已收藏)中进行筛选,返回值被用在组件的 v-for 中 
      search_filteredNote() {
        if(this.search.length > 0) {    // 如果输入框有值,返回二次过滤的结果并加载
          return this.filteredNote.filter(note => {
            if(note.title.indexOf(this.search) > 0) {
              return note
            }
          })
        } else {      // 输入框没值,不过滤,直接拿来加载
          return this.filteredNote
        }
      }
    },
    methods: {
      ...mapActions(['select_note', 'switch_note']), 
      get_select_note(note) {
        this.select_note(note)
      },
      get_switch_note(type) {
        this.switch_note(type)
      }
    }
  }
</script>
<style type="text/css" scoped="scoped">
  #list {
    width: 300px;
    height: 600px;
    border: 2px solid #ccc;
    float: left;
    margin-left: 10px;
    display: flex;
    flex-direction: column;
  }
  
  p {
    margin: 0;
  }
  
  .switch {}
  
  .switch button {
    height: 60px;
    width: 50%;
    font-size: 40px;
  }
  
  .search {
    border: 1px solid #CCCCCC
  }
  
  input {
    width: 100%;
    box-sizing: border-box;
    height: 50px;
    line-height: 50px;
    padding: 10px;
    outline: none;
    font-size: 20px;
    border: none;
  }
  
  .noteList {
    flex-grow: 1;
    overflow: auto;
  }
  
  .note {
    border: 1px solid #CCCCCC;
  }
  
  .favColor {
    background: pink;
  }
  
  .active {
    background: lightblue
  }
</style>

1.data 中的 search 是干嘛的?可不可以写在 computed 中?

用来与搜索框进行关联。可以写在 computed 中,但 computed 中的属性默认都是 getter ,就是只能获取值,如果想修改,需要设置 setter ,详见官方文档

六、edit.vue

<template>
  <div id="edit">
    <div class="title">
      <input type="text" placeholder="在这里输入标题" v-model="activeNote.title"/>
    </div>
    <div class="content">
      <textarea name="" placeholder="在这里吐槽" v-model="activeNote.content"></textarea>
    </div>
  </div>
</template>
<script type="text/javascript">
  import { mapState, mapGetter, mapActions } from 'vuex'
  export default {
    name: 'edit',
    computed:{
      ...mapState(['activeNote'])   // 当本组件中 computed 中的属性名与 Vuex 中的 state 属性名相同时,就可以在 mapState() 中简写
    }
  }
</script>
<style type="text/css" scoped="scoped">
  #edit {
    width: 300px;
    height: 600px;
    border: 2px solid #ccc;
    float: left;
    margin-left: 10px;
    display: flex;
    flex-direction: column;
  }
  
  .title {
    border: 1px solid #CCCCCC;
  }
  
  input {
    width: 100%;
    box-sizing: border-box;
    height: 50px;
    line-height: 50px;
    padding: 10px;
    outline: none;
    font-size: 20px;
    border: none;
  }
  
  .content {
    flex-grow: 1;
    background: orange;
    display: flex;
    flex-direction: column;
  }
  
  textarea {
    width: 100%;
    box-sizing: border-box;
    flex-grow: 1;
    resize: none;
    padding: 10px;
    font-size: 20px;
    outline: none;
    font-family: inherit;
  }
</style>

七、actions.js

export default {
  add_note({commit}) {
    commit('ADD_NOTE')
  },
  select_note({commit}, note) {
    commit("SELECT_NOTE", note)
  },
  del_note({commit}) {
    commit("DEL_NOTE")
  },
  fav_note({commit}) {
    commit("FAV_NOTE")
  },
  switch_note({commit}, type) {
    commit("SWITCH_NOTE", type)
  }
}

1.这是干什么?

这里的每个方法实际上是通过 commit 调用 mutations.js 中的方法;

举个栗子:tool.vue 的 新增 按钮上绑了一个 add_note 自定义方法,在 actions.js 中也定义一个同名的方法,这样就可以在 tool.vue 中的 mapActions 中简写,就是下面这句:

# tool.vue
...mapActions(['add_note','del_note','fav_note'])

而 actions.js 中的 add_note 去调用 mutations.js 中写好的 ADD_NOTE 方法,而实际的添加操作也是在 ADD_NOTE 中,组件也好,actions 也好,最终只是调用 ADD_NOTE 。之所以这么做是因为 mutations 中的方法都是同步的,而 actions 中的方法是异步的,不过在本例里没啥区别

八、getters.js

export default {
  filteredNote: (state) => {
    if(state.show === 'all') {
      return state.notes
    } else {
      return state.notes.filter((note) => {
        if(note.fav === true) {
          return note
        }
      })
    }
  }
}

实现一个过滤,根据 show 来判断展示 全部笔记 还是 已收藏笔记

九、mutations.js

import { SWITCH_NOTE, ADD_NOTE, SELECT_NOTE, DEL_NOTE, FAV_NOTE } from './mutation-types'

export default {
  [ADD_NOTE](state, note = {
    id: +new Date(),
    title: '新建笔记' + new Date().getMilliseconds(),
    content: '笔记内容',
    fav: false
  }) {
    state.notes.push(note)
    state.activeNote = note
  },
  [SELECT_NOTE](state, note) {
    state.activeNote = note
  },
  [DEL_NOTE](state) {
    for(let i = 0; i < state.notes.length; i++) {
      if(state.notes[i].id === state.activeNote.id) {
        state.notes.splice(i, 1)
        state.activeNote = state.notes[i] || state.notes[i - 1] || {}
        return
      }
    }
  },
  [FAV_NOTE](state) {
    state.activeNote.fav = !state.activeNote.fav
  },
  [SWITCH_NOTE](state, type) {
    state.show = type
  }
}

1.export default 那里看着好熟悉

ES6 函数的一种写法,中括号 + 常量 作为函数名,这里常量从其它文件引入

十、mutation-types.js

export const ADD_NOTE = "ADD_NOTE"
export const SELECT_NOTE = "SELECT_NOTE"
export const DEL_NOTE = "DEL_NOTE"
export const FAV_NOTE = "FAV_NOTE"
export const SWITCH_NOTE = "SWITCH_NOTE"

抛出常量,mutations.js 中的函数常量就是这里抛出的,查资料说是这么做便于一目了然都有那些方法。

当然,根据个人习惯,你也可以不这么写

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript对数组的常用操作代码 数组方法总汇
Jan 27 Javascript
对setInterval在火狐和chrome切换标签产生奇怪的效果之探索,与解决方案!
Oct 29 Javascript
js如何判断访问是来自搜索引擎(蜘蛛人)还是直接访问
Sep 14 Javascript
终于实现了!精彩的jquery弹幕效果
Jul 18 Javascript
JavaScript结合Bootstrap仿微信后台多图文界面管理
Jul 22 Javascript
AngularJs  Creating Services详解及示例代码
Sep 02 Javascript
React组件之间的通信的实例代码
Jun 27 Javascript
vue嵌套路由与404重定向实现方法分析
May 04 Javascript
如何让node运行es6模块文件及其原理详解
Dec 11 Javascript
微信小程序之左右布局的实现代码
Dec 13 Javascript
修改vue源码实现动态路由缓存的方法
Jan 21 Javascript
vue操作dom元素的3种方法示例
Sep 20 Javascript
基于Axios 常用的请求方法别名(详解)
Mar 13 #Javascript
Bootstrap 中data-[*] 属性的整理
Mar 13 #Javascript
JS实现的集合去重,交集,并集,差集功能示例
Mar 13 #Javascript
setTimeout时间设置为0详细解析
Mar 13 #Javascript
vue-cli脚手架config目录下index.js配置文件的方法
Mar 13 #Javascript
用vue写一个仿简书的轮播图的示例代码
Mar 13 #Javascript
vue-cli脚手架引入图片的几种方法总结
Mar 13 #Javascript
You might like
浅谈电磁辐射对健康的影响
2021/03/01 无线电
php中在PDO中使用事务(Transaction)
2011/05/14 PHP
php中随机显示图片的函数代码
2011/06/23 PHP
如何使用PHP获取指定日期所在月的开始日期与结束日期
2013/08/01 PHP
php 后端实现JWT认证方法示例
2018/09/04 PHP
PHP从零开始打造自己的MVC框架之路由类实现方法分析
2019/06/03 PHP
Span元素的width属性无效果原因及解决方案
2010/01/15 Javascript
Jquery validation remote 验证的缓存问题解决方法
2014/03/25 Javascript
Javascript基础知识(二)事件
2014/09/29 Javascript
JavaScript动态修改弹出窗口大小的方法
2015/04/06 Javascript
Bootstrap基本插件学习笔记之Tooltip提示工具(18)
2016/12/08 Javascript
React简单介绍
2017/05/24 Javascript
jfinal与bootstrap的登出实战详解
2017/11/27 Javascript
jQuery实现模糊查询的方法分析
2018/05/10 jQuery
vue axios请求频繁时取消上一次请求的方法
2018/11/10 Javascript
vue代码分割的实现(codesplit)
2018/11/13 Javascript
Node.js 获取微信JS-SDK CONFIG的方法示例
2019/05/21 Javascript
[02:32]DOTA2亚洲邀请赛 C9战队出场宣传片
2015/02/07 DOTA
Python发送Email方法实例
2014/08/21 Python
在Python中封装GObject模块进行图形化程序编程的教程
2015/04/14 Python
python通过定义一个类实例作为ftp回调方法
2015/05/04 Python
python3实现基于用户的协同过滤
2018/05/31 Python
Python绘制的二项分布概率图示例
2018/08/22 Python
kafka-python批量发送数据的实例
2018/12/27 Python
win10安装tensorflow-gpu1.8.0详细完整步骤
2020/01/20 Python
python selenium 获取接口数据的实现
2020/12/07 Python
python Autopep8实现按PEP8风格自动排版Python代码
2021/03/02 Python
用JAVA SOCKET编程,读服务器几个字符,再写入本地显示
2012/11/25 面试题
财务主管自我鉴定
2014/01/17 职场文书
求职信模板怎么做
2014/01/26 职场文书
汉语言文学专业自荐信
2014/06/11 职场文书
2015年社区文体活动总结
2015/03/25 职场文书
实验心得体会范文
2016/01/25 职场文书
Javascript设计模式之原型模式详细
2021/10/05 Javascript
HTML中实现音乐或视频自动播放案例详解
2022/05/30 HTML / CSS
Nginx文件已经存在全局反向代理问题排查记录
2022/07/15 Servers