使用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作用域和作用域链
Oct 21 Javascript
node.js中的querystring.parse方法使用说明
Dec 10 Javascript
js字符串操作方法实例分析
May 06 Javascript
学习JavaScript设计模式之装饰者模式
Jan 19 Javascript
jQuery属性选择器用法示例
Sep 09 Javascript
Jquery Easyui分割按钮组件SplitButton使用详解(17)
Dec 18 Javascript
jQuery模拟12306城市选择框功能简单实现方法示例
Aug 13 jQuery
JavaScript中关于base64的一些事
May 06 Javascript
通过javascript实现段落的收缩与展开
Jun 26 Javascript
Element的el-tree控件后台数据结构的生成以及方法的抽取
Mar 05 Javascript
在react中使用vue的状态管理的方法示例
May 02 Javascript
在vant中使用时间选择器和popup弹出层的操作
Nov 04 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启动windows应用程序、执行bat批处理、执行cmd命令的方法(exec、system函数详解)
2014/10/20 PHP
php通过curl模拟登陆DZ论坛
2015/05/11 PHP
php中的登陆login实例代码
2016/06/20 PHP
php判断目录存在的简单方法
2019/09/26 PHP
jQuery 关于伪类选择符的使用说明
2013/04/24 Javascript
基于JQuery的列表拖动排序实现代码
2013/10/01 Javascript
js实现的简单radio背景颜色选择器代码
2015/08/18 Javascript
微信小程序 表单Form实例详解(附源码)
2016/12/22 Javascript
JavaScript简单拖拽效果(1)
2017/05/17 Javascript
JS FormData上传文件的设置方法
2017/07/05 Javascript
使用vue的v-for生成table并给table加上序号的实例代码
2017/10/27 Javascript
vue axios 给生产环境和发布环境配置不同的接口地址(推荐)
2018/05/08 Javascript
JavaScript设计模式之观察者模式实例详解
2019/01/16 Javascript
JavaScript实现移动端弹窗后禁止滚动
2020/05/25 Javascript
Python的Scrapy爬虫框架简单学习笔记
2016/01/20 Python
利用 Monkey 命令操作屏幕快速滑动
2016/12/07 Python
11月编程语言排行榜 Python逆袭C#上升到第4
2017/11/15 Python
python如何通过twisted实现数据库异步插入
2018/03/20 Python
Python实现的微信好友数据分析功能示例
2018/06/21 Python
Django框架模板注入操作示例【变量传递到模板】
2018/12/19 Python
Python删除n行后的其他行方法
2019/01/28 Python
Python类的继承用法示例
2019/01/31 Python
详解python3安装pillow后报错没有pillow模块以及没有PIL模块问题解决
2019/04/17 Python
python 实现将小图片放到另一个较大的白色或黑色背景图片中
2019/12/12 Python
关于TensorFlow新旧版本函数接口变化详解
2020/02/10 Python
Python任务调度模块APScheduler使用
2020/04/15 Python
Python如何给你的程序做性能测试
2020/07/29 Python
Python Opencv轮廓常用操作代码实例解析
2020/09/01 Python
如何用 Python 制作一个迷宫游戏
2021/02/25 Python
波兰品牌内衣及泳装网上商店:Astratex.pl
2017/02/03 全球购物
Sport-Thieme荷兰:购买体育用品
2019/08/25 全球购物
QA工程师岗位职责
2013/11/20 职场文书
省级优秀班集体申报材料
2014/05/25 职场文书
本科毕业答辩开场白
2015/05/27 职场文书
2017新年晚会开幕词
2016/03/03 职场文书
Mac M1安装mnmp (Mac+Nginx+MySQL+PHP) 开发环境
2021/03/29 PHP