本篇继续学习vuex,还是以实例为主;我们以一步一步学Vue(四)中讲述的例子为基础,对其改造,基于vuex重构一遍,这是原始的代码:

todolist.js

; (function () {
var list = [];
var Todo = (function () {
var id = 1;
return function (title, desc) {
this.title = title;
this.desc = desc;
this.id = id++;
}
})();
/**
* 搜索组件
*/
var SearchBar = {
template: `
<div class="row toolbar">
<div class="col-md-8">
keyword:
<input type="text" v-model="keyword" />
<input type="button" @click="search()" value="search" class="btn btn-primary" />
</div>
</div>
`,
data: function () {
return {
keyword: ''
}
},
methods: {
search: function () {
this.$emit('onsearch', this.keyword);
}
} }
/**
* 表单组件
*/
var TodoForm = {
template: `
<div class="col-md-6">
<div class="form-inline">
<label for="title" class="control-label col-md-4">title:</label>
<input type="hidden" v-bind:value="todo.id" />
<input type="text" v-model="todo.title" class="form-control col-md-8">
</div>
<div class="form-inline">
<label for="desc" class="control-label col-md-4">desc</label>
<input type="text" v-model="todo.desc" class="form-control col-md-8">
</div>
<div class="form-inline">
<input type="button" value="OK" v-on:click="ok()" class="btn btn-primary offset-md-10" />
</div>
</div>
`,
props: ['initItem'], computed: {
todo: function () {
return { id: this.initItem.id, title: this.initItem.title, desc: this.initItem.desc };
}
}, methods: {
ok: function () {
this.$emit('onsave', this.todo); }
} }
/**
* 列表项组件
*/
var TodoItem = {
template: `
<tr>
<th>{{todo.id}}</th>
<td>{{todo.title}}</td>
<td>{{todo.desc}}</td>
<td>
<input type="button" value="remove" @click="remove()" class="btn btn-danger" />
<input type="button" value="edit" @click="edit()" class="btn btn-info" />
</td>
</tr>
`,
props: ['todo'],
methods: {
edit: function () {
console.log(this.todo);
this.$emit('onedit', this.todo.id);
},
remove: function () {
this.$emit('onremove', this.todo.id);
}
}
}
/**
* 列表组件
*/
var TodoList = {
template: `
<div class="col-md-6">
<table class="table table-bordered">
<tr>
<th></th>
<th>title</th>
<th>desc</th>
<th></th>
</tr>
<todo-item v-for="item in items" :todo="item" :key="item.id" @onedit="edit($event)" @onremove="remove($event)"></todo-item>
</table>
</div>
`,
props: ['items'],
components: {
'todo-item': TodoItem
},
methods: {
edit: function ($e) {
this.$emit('onedit', $e);
},
remove: function ($e) {
this.$emit('onremove', $e);
}
}
}
/**
* 容器组件
* 说明:容器组件包括三个字组件
*/
var TodoContainer = {
template: `
<div class="container">
<search-bar @onsearch="search($event)"></search-bar>
<div class="row">
<todo-list :items="items" @onremove="remove($event)" @onedit="edit($event)"></todo-list>
<todo-form :init-item="initItem" @onsave="save($event)" ></todo-form>
</div>
</div>
`,
data: function () {
return {
/**
* Todolist数据列表
* 说明:通过props传入到Todolist组件中,让组件进行渲染
*/
items: [],
/**
* TodoForm初始化数据
* 说明:由于TodoForm包括两种操作:新增和编辑;新增操作无需处理,编辑操作需要进行数据绑定,这里通过传入initItem属性进行编辑时数据的初始化
* 如果传入的值为空,说明为新增操作,由initItem参数的Id是否为空,来确认是更新保存还是新增保存
*/
initItem: {
title: '',
desc: '',
id: ''
}
}
},
components: {
'search-bar': SearchBar,/**SearchBar组件注册 */
'todo-form': TodoForm,/**TodoForm组件注册 */
'todo-list': TodoList/**TodoList组件注册 */
},
methods: {
/**
* 模拟保存数据方法
* 辅助方法
*/
_mock_save: function (lst) {
list = lst;
},
/**
* 根据id查询对象
* 辅助方法
*/
findById: function (id) {
return this.items.filter(v => v.id === id)[0] || {};
},
/**
* 查询方法
* 由SearchBar组件触发
*/
search: function ($e) {
this.items = list.filter(v => v.title.indexOf($e) !== -1);
},
/**
* 保存方法
* 响应新增和更新操作,由TodoForm组件触发
*/
save: function ($e) {
//id存在则为编辑保存
if (this.initItem.id) {
var o = this.findById($e.id);
o.title = $e.title;
o.desc = $e.desc;
} else {
this.items.push(new Todo($e.title, $e.desc));
} this.initItem = { id: '', title: '', desc: '' }; this._mock_save(this.items);
},
/**
* 删除方法
* 响应删除按钮操作
* 由TodoItem组件触发
*/
remove: function ($e) {
this.items = this.items.filter(v => v.id !== $e);
this._mock_save(this.items);
},
/**
* 编辑按钮点击时,进行表单数据绑定
*/
edit: function ($e) {
this.initItem = this.findById($e);
}
}
} var app = new Vue({
el: '#app',
components: {
'todo-container': TodoContainer
}
}); })(); /**
*
*
* <div id="app">
* <todo-container></todo-container>
* </app>
*/

index.html

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo1</title>
<script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/vuex/2.3.1/vuex.js"></script> </head> <body class="container">
<div id="app">
<todo-container></todo-container>
</div>
<script src="./todolist.js"></script>
</body> </html>

注意要在html页面引入vuex的路径,我们这里直接使用cdn上的库,开始我们的重构

第一步:创建全局store

在vuex中,store是全局唯一的,我们在上一篇文章中也介绍了其基本创建方式,修改todolist.js,添加如下代码:

  var store=new Vuex.Store({
state:{
//TODO:状态
},
mutations:{
//TODO:改变状态的方法
}
}) var app = new Vue({
store:store,
el: '#app',
components: {
'todo-container': TodoContainer
}
});

上述代码表示store已经创建,并且注入到注册组件中了,在任何组件中都可以通过this.store来访问state和提交mutation,这里再简单说一下mutaiton,其实我们可以把mutation当成事件来理解,在store定义的时候,创建mutation,我们可以认为是mutation的注册,就如我们去注册普通的事件一样,内容都是key和value,其中key是事件的全局表示,value是事件的回调函数,类比mutation,定义是注册,模式还是func:function(){}的模式,在我们做commit(“mutation”)的时候相当于触发事件,这时候就会执行我们注册的回调函数。

第二步,我们把共享状态进行提取:

  var store=new Vuex.Store({
state:{
items:[] // todoContainer中items,
        
        initItem: {
          title: '',
          desc: '',
          id: ''
        },//初始化表单所用
        },
mutations:{
//TODO:改变状态的方法
}
})

就下来,我们把所有改变state的方法,都通过注册mutation的方式来重构,在vuex中,一定要通过mutation来改变状态:

mutations: {
search: function (state, payload) {
state.items = list.filter(v => v.title.indexOf(payload.title) !== -1);
},
save: function (state, payload) {
if (state.initItem.id) {
var o = list.filter(v => v.id === payload.id);
o.title = payload.title;
o.desc = payload.desc;
state.items=state.items.map(v=>{
if(v.id==payload.id){
return payload;
}
return v;
}); } else {
state.items.push(new Todo(payload.title, payload.desc));
} list = state.items;
},
remove: function (state, payload) {
state.items = state.items.filter(v => v.id !== payload.id);
},
edit: function (state, payload) {
state.initItem = state.items.filter(v => v.id === payload.id)[0];
}
}

我们添加的上述几个mutations,包括search、save、remove、edit,由于在每一个组件中都可以访问到this.$store,那么我们就不用对事件一层一层的传递啦,我们只需要在需要调用的地方,commit对应的mutation即可,比如search操作就是在searchbar组件中,那么我们没必要传递到父组件中来触发,基于此,我们修改SearchBar组件:

 /**
* 搜索组件
*/
var SearchBar = {
template: `
<div class="row toolbar">
<div class="col-md-8">
keyword:
<input type="text" v-model="keyword" />
<input type="button" @click="search()" value="search" class="btn btn-primary" />
</div>
</div>
`,
data: function () {
return {
keyword: ''
}
},
methods: {
search: function () {
this.$store.commit("search", {
title: this.keyword
});
}
} }

这里看起来没有简单好多,但是我们至少不用把我们的事件往上级送了,可以对比最初代码,同理对我们的其它组件都进行重构:

 /**
* 表单组件
*/
var TodoForm = {
template: `
<div class="col-md-6">
<div class="form-inline">
<label for="title" class="control-label col-md-4">title:</label>
<input type="hidden" v-bind:value="todo.id" />
<input type="text" v-model="todo.title" class="form-control col-md-8">
</div>
<div class="form-inline">
<label for="desc" class="control-label col-md-4">desc</label>
<input type="text" v-model="todo.desc" class="form-control col-md-8">
</div>
<div class="form-inline">
<input type="button" value="OK" v-on:click="ok()" class="btn btn-primary offset-md-10" />
</div>
</div>
`,
props: ['initItem'], computed: {
todo: function () {
return { id: this.initItem.id, title: this.initItem.title, desc: this.initItem.desc };
}
}, methods: {
ok: function () {
this.$store.commit("save",this.todo);
}
} }
/**
* 列表项组件
*/
var TodoItem = {
template: `
<tr>
<th>{{todo.id}}</th>
<td>{{todo.title}}</td>
<td>{{todo.desc}}</td>
<td>
<input type="button" value="remove" @click="remove()" class="btn btn-danger" />
<input type="button" value="edit" @click="edit()" class="btn btn-info" />
</td>
</tr>
`,
props: ['todo'],
methods: {
edit: function () {
this.$store.commit('edit',this.todo);
},
remove: function () {
this.$store.commit('remove',{id:this.todo.id});
}
}
}
/**
* 列表组件
*/
var TodoList = {
template: `
<div class="col-md-6">
<table class="table table-bordered">
<tr>
<th></th>
<th>title</th>
<th>desc</th>
<th></th>
</tr>
<todo-item v-for="item in items" :todo="item" :key="item.id" ></todo-item>
</table>
</div>
`,
props: ['items'],
components: {
'todo-item': TodoItem
} }
/**
* 容器组件
* 说明:容器组件包括三个字组件
*/
var TodoContainer = {
template: `
<div class="container">
<search-bar></search-bar>
<div class="row">
<todo-list :items="items" ></todo-list>
<todo-form :init-item="initItem" ></todo-form>
</div>
</div>
`, components: {
'search-bar': SearchBar,/**SearchBar组件注册 */
'todo-form': TodoForm,/**TodoForm组件注册 */
'todo-list': TodoList/**TodoList组件注册 */
},
computed: {
initItem: function () {
return this.$store.state.initItem;
},
items: function () {
return this.$store.state.items;
}
}
}

首先看一下我们的TodoContainer组件,里面已经清爽了好多,原来所有的逻辑,所有的属性,都汇集在这里,现在每个组件的逻辑都是它自己负责,表单组件负责保存操作,所以在其中提交commit(“save”);todo组件负责编辑和删除,所以在其方法中封装了remove和edit的mutaiton的访问。至此,我们的代码可以正常运行,由于只是对前文demo的重构,这里不再贴出运行效果图。

小结,在store中定义的状态,是响应式的,对其中状态的改变会导致view的重新渲染,改变状态只能通过提交mutation。由于其状态的响应式,所以我们在访问时一般定义成计算属性,如TodoContainer组件中的initItem和items;一般来说,不是所有状态都要定义到vuex的store中,每个组件都会有自己私有状态,只有全局或者共享状态才适合定义在store中,所以在实际开发中,需要好好斟酌;本篇就到此为止,其实算是上篇的一个延伸,下一篇介绍Actions,会继续在本篇demo的基础上进行延伸,敬请期待。

一步一步学习Vue(十一)的更多相关文章

  1. 一步一步学习Vue(六)

    本篇继续介绍vue-router,我们需要要完成这样个demo:<分页显示文章列表>:这里我们以博客园首页列表为例简化处理: 按照上图框选所示,简单分为蓝色部分文章组件(ArticleIt ...

  2. 一步一步学Vue(四)

    接上篇.上篇中给出了代码框架,没有具体实现,这一篇会对上篇定义的几个组件进行分别介绍和完善: 1.TodoContainer组件 TodoContainer组件,用来组织其它组件,这是react中推荐 ...

  3. 一步步带你做vue后台管理框架(二)——上手使用

    系列教程<一步步带你做vue后台管理框架>第二课 github地址:vue-framework-wz 线上体验地址:立即体验 闲扯再多不会用也没白搭,这节课我来带大家直接上手框架,体验到简 ...

  4. Vue双向绑定原理,教你一步一步实现双向绑定

    当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...

  5. 一步一步学Vue(六)

    本篇继续介绍vue-router,我们需要要完成这样个demo:<分页显示文章列表>:这里我们以博客园首页列表为例简化处理: 按照上图框选所示,简单分为蓝色部分文章组件(ArticleIt ...

  6. 12.Linux软件安装 (一步一步学习大数据系列之 Linux)

    1.如何上传安装包到服务器 有三种方式: 1.1使用图形化工具,如: filezilla 如何使用FileZilla上传和下载文件 1.2使用 sftp 工具: 在 windows下使用CRT 软件 ...

  7. (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...

  8. (转) 一步一步学习ASP.NET 5 (二)- 通过命令行和sublime创建项目

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助. 注:昨天转发之后很多朋友指出了vNext的命名问题,原文作者已经做出了修改,后面的标题都适用 asp.net 5这个名称. 编者语 : 昨天发了第 ...

  9. 一步一步学习SignalR进行实时通信_1_简单介绍

    一步一步学习SignalR进行实时通信\_1_简单介绍 SignalR 一步一步学习SignalR进行实时通信_1_简单介绍 前言 SignalR介绍 支持的平台 相关说明 OWIN 结束语 参考文献 ...

随机推荐

  1. AngularJS高级程序设计读书笔记 -- 服务篇

    服务是提供在整个应用程序中所使用的任何功能的单例对象. 单例 : 只用一个对象实例会被 AngularJS 创建出来, 并被程序需要服务的各个不同部分所共享. 1. 内置服务 一些关键方法也被 Ang ...

  2. ES6的开发环境搭建

    在搭建es6开发环境之前,先简单介绍一下es6. ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了.它的目标,是使得 Java ...

  3. ReactiveCocoa源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  4. Swift基础语法

    简介 特点 (1)优于OC,快速,安全 (2)取消了预编译指令包括宏定义(OC用的太多了) (3)取消了OC指针和不安全访问的使用(看不到星星了) (4)舍弃 Objective-C 早期应用 Sma ...

  5. Linux文件管理浅析(一) _磁盘管理基础

    本文主要讨论一些磁盘管理相关的基本概念,同时也是这一系列文章的第一篇,就是下图中的最下一层的一部分. 在Linux中,SATA/USB/SCSI接口都是使用SCSI模块实现的,所以使用这些接口的硬盘在 ...

  6. H5个性三级联动日期插件(一)

    1. 先看效果:如图 2.如果跟你的需求一样的话,那就抓紧down(当)起来吧! 首先你的页面可能需要很多的开发需求文件: jquery,mobiscroll 等js框架插件等 自己参照官方的demo ...

  7. workerman启动失败解决

    提示stream_socket_server(): unable to connect to tcp://0.0.0.0:2120 (Address already in use)php xxx.ph ...

  8. 修改MySQL数据库密码

    在mysql数据库里面有一个默认安装的数据库是mysql,里面有一个user表.里面的字段Host是运行登录的ip地址,User 是登录的账号Password是密码. use mysql;//使用my ...

  9. 关于在eclipse上部署Tomcat时出现8080等端口被占用问题的解决方法

    问题描述: 在eclipse中部署Tomcat时,出现如下错误. 解决方法如下: 方法一: 1.开始->cmd->输入命令netstat -ano出现下图所示(注意下边显示有些错位,最后一 ...

  10. mysql常见的优化方法

    1.选取适当的字段属性.例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任 ...