前言

首先说明这并不是一个教程贴,而记事本应用是网上早有的案例,对于学习 vuex 非常有帮助。我的目的是探索 vuex 2.0 ,然后使用 vue 2.0 + vuex 2.0 重写这个应用,其中最大的问题是使用 vue-cli 构建应用时遇到的问题。通过这些问题深入探索 vue 以及 vuex 。

我对于框架的学习一直断断续续,最先接触的是 react,所以有一些先入为主的观念,喜欢 react 更多一点,尤其在应用的构建层面来说。之所以断断续续,是因为自己 JS 基础较弱,刚开始学习的时候,只是比着葫芦画瓢,虽然可以做出点东西,但对于其中的一些概念仍然云里雾里,不知所云,无法深入理解框架。所以我又临时放弃框架的学习,开始学习 JS 基础。事实证明打牢基础之后,学习框架以及理解框架是神速的。而学习 webgl 和 three.js 的过程与此类似。没有 webgl 的基础,学习 three.js 只会停留在初级阶段。

我在过去的半年参加了很多面试,几乎无一例外的都会被问框架的使用情况,但是其中很多公司属于随波逐流,使用框架比较盲目。甚至觉得使用框架是极其高大上的事情。虽然我学习过框架,但毕竟没有深入学习也没有拿得出手的项目,所以只是只言片语的说两句,大部分知识是懵懂的。然而面对面试官不屑的神情以及以此作为选拔的指标,心想这样的面试官太肤浅。当然很多公司的面试还是以基础为主。框架属于探索,互相学习的状态。我在这篇文章中强调一点,学习能力以及解决问题的能力更重要。

开始吧

言归正传,对于这个笔记本案例,大家可以直接百度搜 vue notes ,这是一篇英文教程,大家看到的都是翻译的。在刚开始 vue 资料稀缺的时候,这样的文章非常珍贵。demo 点这里。说白了,算是 todoMVC 案例的一个变体。当初觉得这个例子非常好,想跟着学一学,结果一拖半年过去了。这几天终于抽时间把这个例子敲了一遍。学习在于举一反三。如果大家按照网上教程来做,那么 NPM 包默认安装的都是最新版本,运行会报错。所以如果用 vuex 2 要怎么写呢?

以下是 notes-vuex-app 的源文件目录:

在使用 vue 2 重写这个 app 之前,我在想能不能不改变文件目录结构以及配置位置呢?就是用比较生硬的方式重写,或者说单纯的语法修改。事实是可行的,否则我就不会写这篇文章了。然而面对的问题非常多,但却因此深入的理解了 vue 以及 vuex。最大的问题是 webpack 的构建,如果使用 webpack 2.0+的话,坑比较多。本人是菜鸟,所以最终选择了 vue-cli 提供的两个 webpack 的模板,分别是 webpack-simple 和 webpack,我先使用 webpack-simple,它和原 app 的结构基本吻合。

使用 vue-cli 生成基本目录之后,再安装 vuex2 。

main.js

原示例 main.js 如下所示,但运行出错了,主要是 Vue 2 的根实例渲染稍有变化

import Vue from 'vue'
import store from './vuex/store'
import App from './components/App.vue'new Vue({store, // 注入到所有子组件el: 'body',components: { App }
})

改正之后:

import Vue from 'vue'
import store from './vuex/store'
import App from './components/App.vue'new Vue({store, // inject store to all childrenel: '#app',template: '<App/>',components: { App }
})

或者

import Vue from 'vue'
import store from './vuex/store'
import App from './components/App.vue'new Vue({store, // inject store to all childrenel: '#app',render: h => h(App)
})

vuex 2 的变化

这个应用改写的主要问题集中在 vuex 2 的变化上,这些变化确实会让人感到凌乱,我无数次抓耳挠腮的骂娘。不过通过官方给出的示例也可以看出一些端倪。

首先是 action.js,只需注意一点,所有的 dispatch 都要改成 commit

export const addNote = ({ commit }) => {commit('ADD_NOTE')
}export const editNote = ({ commit }, e) => {commit('EDIT_NOTE', e.target.value)
}export const deleteNote = ({ commit }) => {commit('DELETE_NOTE')
}export const updateActiveNote = ({ commit }, note) => {commit('SET_ACTIVE_NOTE', note)
}export const toggleFavorite = ({ commit }) => {commit('TOGGLE_FAVORITE')
}

store.js 变化也不大,但是要注意几个地方:

import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'Vue.use(Vuex)const state = {notes: [],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.splice(state.notes.indexOf(state.activeNote),1)state.activeNote = state.notes[0] || {}},TOGGLE_FAVORITE (state) {state.activeNote.favorite = !state.activeNote.favorite},SET_ACTIVE_NOTE (state, note) {state.activeNote = note}
}const getters = {notes: state => state.notes,activeNote: state => state.activeNote,activeNoteText: state => state.activeNote.text
}export default new Vuex.Store({state,mutations,actions,getters
})

原示例文件中没有将 getters 写到 store.js 中,而是直接在组件中定义的。为了更清晰,我仿照官方示例也提取出来写在了 store.js 中,这样在组件中调用时比较方便。其次也引入了 action.js,并作为 actions 对象传递给 Vuex.store(),这算是 vuex 的标准写法吧,对于后面在组件中调用比较有利。

其中要注意 DELETE_NOTE (state){} 这个方法,原示例使用了 vue1 提供的 remove 方法,但是 vue2 中去掉了这个方法。仔细想想就会明白,这个函数的作用就是删除 notes 数组中的元素。可以使用原生的 splice方法。如果 JS 基础扎实的话,这里应该很好理解,没有什么大问题。其次相比原示例,添加一个删除后操作的判断。

我之前一直不太理解 flux 的概念,感觉像是新东西,完全不知道它的目的及作用。换成 Vuex,还是有点稀里糊涂。但是通过修改这个示例,基本算是开窍了。这些东西本身并没有玄机奥妙,想一想,如果我们不用框架,而是自己手写一个 todoMVC 时要怎么做?应该也是这样的思路,定义一个 notes 数组变量以及 activeNote 的变量。然后在创建一些改变状态的方法。我在面试中遇到过一个情况,面试官反复再问我为什么需要使用框架,用 jQuery 不是也可以实现吗?这样说确实没错,用比较原始的方法当然可以做,只是代码结构会冗余或者凌乱,缺少小而美的特点。框架以及设计模式对代码做了整合封装,对于一个 CURD 应用比较友好,实现起来更方便更简单。我对于 Vuex 的理解就是,它是一个对象,封装了与状态相关的方法和属性。而所谓的状态就是点击、按键等操作之后的变化。

组件中使用 vuex

先看一下 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 { mapGetters, mapActions } from 'vuex'export default {computed: mapGetters(['activeNote']),methods: {...mapActions(['addNote','deleteNote','toggleFavorite'])}
}
</script>

通过和原示例代码对比,这里的区别一目了然。我通过在控制台打印 Vue 实例,折腾很长时间才大体明白怎么回事。vuex 1 在组件中使用时会直接将 getters 以及 actions 挂到 vuex 这个属性上,没有提供 mapGetters 及 mapActions 等一些方法。而 vuex 使用 mapGetters 及 mapActions 等一些方法将 actions 的方法挂到 Vue 实例上。总的来说,都是把 actions 的方法挂到 Vue 实例上。我们从这个层面上谈谈 Vue 实例,Vue 2 的变化就是其属性的变化。比如 Vue1 中在 methods 中添加的方法可以在 vue 实例的 $options 属性中查看,而 vue2 中这些方法可以直接在第一级属性中查找。在 vue1 中可以查看 vuex 这个属性,但是 vue2 中移除了,被 store 取代。至于其它的不同,大家可以自己对比,通过这种方式,可以深入理解 vue 的设计思想。

假设其它组件都以这种方式改好了,就在我们满心欢喜地运行示例时,又报错了。问题出在扩展运算符 ...上,webpack-simple 这个模板无法解析 ES6 的 ...。为此,我又折腾了很久,想试着修改 webpack  的配置文件,但改动太大。我妥协了,决定抛弃扩展运算符,手写这个方法。当然如果使用 webpack 的模板就没有问题,这个比较简单,我们最后再说。

手写扩展运算符 ... 之前,我们先看一下 mapActions 这个方法。对于 mapGetters 以及 mapActions 这两个函数,最简单的理解办法就是查看 vuex 的源码,最终返回的是一个对象。也就是根据需要获取 store.js中 actions 对象的某些方法。然后通过扩展运算符把返回的对象拆开然后挂到 Vue 实例上。举例来说(以下只是扩展运算符的用法之一,别的用法可以参考其它的文章):

var obj = {a: 1,b: 2,
}var methods = {...obj
}// console.log(methods)
{a: 1,b: 2
}

明白扩展运算符的用法之后就好办了。为了简单一点,我直接使用 babel 官网的在线解析器,查看扩展运算符的 ES5 写法。

// ES5 实现扩展运算符...
var _extends = Object.assign || function(target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } }return target;
};

完整的 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><i @click="_test" class="glyphicon glyphicon-remove"></i></div>
</template><script>import { mapGetters, mapActions } from 'vuex'// ES5 实现扩展运算符...
var _extends = Object.assign || function(target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } }return target;
};var actions = mapActions(['addNote','deleteNote','toggleFavorite'
]);var methodsObj = _extends({},actions)export default {computed: mapGetters(['activeNote']),methods:methodsObj
}
</script>

其余两个子组件类似,相信大家已经明白了我的思路,具体代码如下:

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 { mapGetters, mapActions } from 'vuex'// ES5 实现扩展运算符...
var _extends = Object.assign || function(target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } }return target;
};var getters = mapGetters(['activeNote'
]);var filters = {filteredNotes: function () {if (this.show === 'all'){return this.$store.state.notes} else if (this.show === 'favorites') {return this.$store.state.notes.filter(note => note.favorite)}}
}var actions = mapActions(['updateActiveNote'])var computedObj = _extends({},getters,filters);var methodsObj = _extends({},actions);export default {data () {return {show: 'all'}},computed:computedObj,methods:methodsObj
}
</script>

Editor.vue

<template><div id="note-editor"><textarea:value="activeNoteText"@input="editNote"class="form-control"></textarea></div>
</template><script>import { mapGetters, mapActions } from 'vuex'export default {computed:mapGetters(['activeNoteText']),methods:mapActions(['editNote'])
}
</script>

webpack 模板

直接使用 vue-cli 的 webpack 模板就会简单很多,可以直接解析扩展运算符,代码也会比较简洁。我就不多说了,直接贴上 github 的地址,大家有不懂的可以看一下:https://github.com/nzbin/notes-app-vuejs2-vuex2

总结

终于写完了这个教程,感慨颇多。这个例子比较典型,学习的人很多,可能我并不是第一个重写这个案例的人,我只是与大家分享我的一些心得。顺便提一句,为了重写这个示例并解决遇到的这些小问题,我们可能要使用很多资源,比如 github、npm 官网、babel 官网、stackoverflow、vuejs 官网、vuex 官网、博客等等。回头再想想 Vue 到底是什么,一个对象,没错,一个集合了很多属性和方法的对象。为什么要强调面向对象的重要性,可能这就是最好的阐释,包括 jQuery、react、其它框架等等。一旦遇到问题,在控制台打印 Vue 实例,反复查看其属性可能很有帮助。

最后发个预告,下一篇文章我想探讨一下面向对象的 CSS,分析几个优秀的 UI 框架,我相信每个人都可以书写属于自己的 CSS 框架。

探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用相关推荐

  1. 探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用23

    前言 首先说明这并不是一个教程贴,而记事本应用是网上早有的案例,对于学习 vuex 非常有帮助.我的目的是探索 vuex 2.0 ,然后使用 vuejs 2.0 + vuex 2.0 重写这个应用,其 ...

  2. vuejs中使用vuex的两种方案之一

    vue.js使用vuex之一: 文件目录如下: index.js文件demo import Vue from 'vue' import Vuex from 'vuex'import name from ...

  3. 用户表如何区分普通用户和管理员_Gate.io 比特百科:什么是ETH 2.0及普通用户如何参与ETH 2.0质押挖矿...

    ETH 2.0是什么 ETH 2.0是ETH发展四阶段(1.前沿Frontier 2.家园Homestead 3.大都会Homestead 4.宁静Serenity)中的最后一个阶段.前三个阶段是ET ...

  4. .Net 6.0中的新增特性_.Net 6.0中的新增功能

    .Net 6.0中的新增特性_.Net 6.0中的新增功能 一..Net 6 介绍 .NET 6 作为 LTS 长期支持版本,.NET 6 将会获得 3 年的技术支持. .NET 6 是首个原生支持 ...

  5. android最新1.0.5版,我的世界1.1.0.5最新版下载

    我的世界1.1.0.5最新版是我的世界1.1版本的第5个测试版,在这个版本中主要修复了游戏文字界面对齐的问题已经一些系统bug,大家可以下载最新版本来获得更棒的游戏体验. 我的世界1.1.0.5最新版 ...

  6. mysql5.7与8.0用那个_MySQL 5.7 vs 8.0,哪个性能更牛?

    背景 测试mysql5.7和mysql8.0 分别在读写.只读.只写模式下不同并发时的性能(tps,qps) 前提测试使用版本为mysql5.7.22和mysql8.0.15 sysbench测试前先 ...

  7. windows 如何安装oracle 补丁包,Windows Server 2003 上安装 Oracle10g(10.2.0.1)并升级 至补丁(10.2.0.4) 图解...

    Windows Server 2003 上安装 Oracle10g(10.2.0.1)并升级 至补丁(10.2.0.4) 图解 第一部分:安装 Oracle 10.2.0.1 1.选择安装方法 2.选 ...

  8. 「Web2.0」的创造者表示:为Web3.0喝彩还为时尚早

    题图 | Kelly Sullivan / Getty Images for LinkedIn 图/ADOBE STOCK 最近Web3.0这个概念真的太火了. 简单来说,Web3.0将是去中心化.去 ...

  9. DotNetTextBox V3.0 所见即所得编辑器控件 For Asp.Net2.0(ver 3.0.9Beta)

    英文名:DotNetTextBox V3.0 WYSWYG Web Control For Asp.Net2.0 中文名:DotNetTextBox V3.0 所见即所得编辑器控件 For Asp.N ...

最新文章

  1. 负二项分布学习[转载]
  2. 求表达式 f(n)结果末尾0的个数
  3. 苹果公司提出Mobile-ViT | 更小更轻精度更高,MobileNets或成为历史
  4. FastAdmin 极速后台管理框架(三周年)V1.0.0.20190410_beta
  5. linux查看最近修改过的文件
  6. mongodb远程连接windows
  7. python基础之字符串定义常见操作、列表定义进阶操作
  8. js中substr与substring的差别
  9. 从SDE库文件手工删除SDE图层(转载)
  10. dll模块化设计与编程_PLC模块、结构化编程到底强在哪里?
  11. 「ZJOI2019」麻将
  12. MySQL的NoSQL扩展插件HandlerSocket
  13. MySQL必知必会——数据库基础知识和MySQL基本使用
  14. Win10系统微信,QQ打语音听不见声音
  15. 【IoT】产品设计:用实际案例,手把手教你写MRD(市场需求文档)
  16. openwrt MT7620A编译dropbear
  17. C++ lock_guard 自动释放锁
  18. Android项目小结——硬解码(MediaCodec实现[MP4]转YUV420各种格式)
  19. SQL Server基础操作(此随笔仅作为本人学习进度记录七 !--函数)
  20. HTML中nbsp 和空格的区别?

热门文章

  1. MNE-Python教程汇总
  2. android自定义xml弹窗,Android自定义弹窗提醒控件使用详解
  3. 扫地机器人能有多硬核?好家伙自动驾驶、激光扫描、NLP这些硬科技全上了,科沃斯:技术创新才能打破行业内卷...
  4. 看了AI种草莓,我想回去当农民
  5. ISC 2020技术日丨 网络空间危机四伏,如何发现威胁的蛛丝马迹?
  6. 冠状病毒如何杀灭最高效?这里有一份几十年的实验汇总
  7. 一批美国名校被曝监控学生:14秒记录一次手机位置,想翘课更难了
  8. 禾多推出高精度定位和自动驾驶验证平台!倪凯:助力行业量产,定位中国向导...
  9. 马化腾发起的科学探索奖首次颁出,50名中国大陆青年学者获奖,每人获300万资助...
  10. 十一.python面向对象(接口)abstractmethod,ABCMeta