使用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 相关文章推荐
jQuery 1.4 15个你应该知道的新特性(译)
Jan 24 Javascript
jQuery+CSS 实现随滚动条增减的汽水瓶中的液体效果
Sep 26 Javascript
HTTP 304错误的详细讲解
Nov 13 Javascript
使用jQuery设置disabled属性与移除disabled属性
Aug 21 Javascript
使用pjax实现无刷新更改页面url
Feb 05 Javascript
js实现模拟计算器退格键删除文字效果的方法
May 07 Javascript
深入浅析react native es6语法
Dec 09 Javascript
angularjs表格分页功能详解
Jan 21 Javascript
Bootstrap中表单控件状态(验证状态)
Aug 04 Javascript
纯JS实现表单验证实例
Dec 24 Javascript
Angular实现的table表格排序功能完整示例
Dec 22 Javascript
vue实现员工信息录入功能
Jun 11 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
坏狼的PHP学习教程之第2天
2008/06/15 PHP
探讨PHP中OO之静态关键字以及类常量的详解
2013/06/07 PHP
ThinkPHP中的关联模型注意点
2014/06/16 PHP
php递归遍历多维数组的方法
2015/04/18 PHP
php利用云片网实现短信验证码功能的示例代码
2017/11/18 PHP
javascript Onunload与Onbeforeunload使用小结
2009/12/31 Javascript
JavaScript 一行代码,轻松搞定浮动快捷留言-V2升级版
2010/04/02 Javascript
js设置cookie过期当前时间减去一秒相当于立即过期
2014/09/04 Javascript
JavaScript实现在标题栏上显示当前日期的方法
2015/03/19 Javascript
初步认识JavaScript函数库jQuery
2015/06/18 Javascript
Webpack 实现 Node.js 代码热替换
2015/10/22 Javascript
node.js中cluster的使用教程
2017/06/09 Javascript
Vee-validate 父组件获取子组件表单校验结果的实例代码
2019/05/20 Javascript
vue.js实现备忘录demo
2019/06/26 Javascript
js中Function引用类型常见有用的方法和属性详解
2019/12/11 Javascript
javascript实现简易数码时钟
2020/03/30 Javascript
将Python代码嵌入C++程序进行编写的实例
2015/07/31 Python
Python三种遍历文件目录的方法实例代码
2018/01/19 Python
Python wxPython库使用wx.ListBox创建列表框示例
2018/09/03 Python
win10系统下Anaconda3安装配置方法图文教程
2018/09/19 Python
关于tf.reverse_sequence()简述
2020/01/20 Python
python入门之井字棋小游戏
2020/03/05 Python
python 数据库查询返回list或tuple实例
2020/05/15 Python
Python eval函数原理及用法解析
2020/11/14 Python
python利用xpath爬取网上数据并存储到django模型中
2021/02/26 Python
三维科技面试题
2013/07/27 面试题
P/Invoke是什么
2015/07/31 面试题
解释下列WebService名词:WSDL、SOAP、UDDI
2012/06/22 面试题
超市客服工作职责
2014/06/11 职场文书
旅游专业毕业生自荐书
2014/06/30 职场文书
电焊工岗位工作职责
2014/07/09 职场文书
连锁超市项目计划书
2014/09/15 职场文书
2015年国庆节标语大全
2015/07/30 职场文书
高一军训口号
2015/12/25 职场文书
导游词之泰山玉皇顶
2019/12/23 职场文书
mysql中整数数据类型tinyint详解
2021/12/06 MySQL