1.项目结构

2.Vuex,什么是Vuex?

官方文档上的介绍是:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

https://vuex.vuejs.org/zh/

我的理解是Vuex类似于一个 '全局变量' 管理器,你可以在这个 '管理器' 中对全局变量进行监听、修改、取值、赋值等操作;

基础用法:

2.1在Vue项目初始化时添加Vuex的安装,项目初始化后会生成 store 文件夹,index.js文件是Vuex的入口文件:

2.1.1.namespaced:true,vuex中的store是模块管理,在store的index.js中引入各个模块时为了解决不同模块命名冲突的问题,将 namespace置为 true可以加上模块名后调用不同模块的mutations、actions等属性;

2.1.2 state,当前模块管理的‘状态‘ ,可以理解为index模块中管理的变量;

2.1.3 mutations,当前模块的提交方法,所有对state中的 '状态' 的改动都需要通过mutations来进行,只接受两个参数,一个是默认参数,当前模块的state对象,另一个是传入的数据,传入多个数据只接受第1个,在同一模块下调用其他mutation要使用 this.otherMuation来进行调用;

2.1.4 actions,类似于mutations但是actions是用来提交mutations,他并不像mutations那样直接更改管理的 '状态',actions可以包含异步操作,比如在可以actions调用一个接口,在接口的回调用再提交mutations;

2.1.5 modules,引入的模块;

2.2 新建一个todolist模块,新建todo.js文件,unfinishedList和finishedList没有用到可以忽略;

2.3 调用一次mutations来更改 '状态':

 1 let body = {
2 title:'测试数据',
3 date:'2020-03-01',
4 content:'测试内容测试内容'
5 };
6 // 提交todo模块中的pushStuff,传入的数据是一个body对象;数据将会被push到todo模块的list中
7 this.$store.commit("todo/pushStuff", body);
8
9 // 如果不加模块名,则默认提交index文件中的mutation
10 // this.$store.commit("someMutaion", body);

2.4 获取一次 '状态':

 1 // 在Vue项目的任何 .vue文件中使用 下列语句进行 '状态' 的获取
2
3 ...
4 mounted(){
5 // this.$store.state.(模块名).(模块state中的变量);
6 // 获取的是todo模块中的 '状态'
7
8 // this.$store.state.(index中state中的变量);
9 // 获取的是index模块中的状态
10
11 let list = this.$store.state.todo.list;
12 }

2.5 到这一步,已经完成一次Vuex的基础使用;

3.localStorage

3.1 localStorage是window对象中的一个存储对象属性,将传入的数据以键值对(Key/Value)的形式存储起来;Vuex中管理的 '状态' 在页面刷新时会丢失,配合localStorage本地存储数据实现数据持久化;

3.2 基础用法:

从localStorage取出数据:

 1 ...
2 mounted(){
3 if (window.localStorage) {
4 let storage = window.localStorage;
5 // localStorage存有内容
6 if (storage.length > 0) {
7 /*
8 * 取出 key为 'staffList' 的值,取出来的值是JSON字符串需要
9 * 用JSON.parse()转成对象,将取出来的值存入Vuex
10 */
11 if (localList && localList.length > 0) {
12 this.$store.commit("todo/concatStaff", JSON.parse(localList));
13 }
14 }
15 }else{
16 console.log('浏览器不支持 localStorage');
17 }
18 }
19 ...

向localStorage存储数据:

1 let body={title:"标题",date:"2020-01-01",content:"内容"}; 2 // 将数据转换成JSON字符串再处存入 3 window.localStorage.setItem('body',JSON.stringify(body));

4.项目代码:

App.vue

 1 <template>
2 <div id="app">
3 <div id="nav">
4 <router-link to="/">Home</router-link>|
5 <router-link to="/about">About</router-link>
6 </div>
7 <router-view />
8 </div>
9 </template>
10 <script>
11 export default {
12 created() {
13 this.localStoreCheck();
14 // window绑定 '刷新前' 触发的事件,将数据存储在localStorage中
15 window.addEventListener("beforeunload", () => {
16 this.localStorageSet();
17 });
18 },
19 computed: {},
20 beforeMount() {
21 this.localStoreCheck();
22 },
23 methods: {
24 localStoreCheck() {
25 if (window.localStorage) {
26 let storage = window.localStorage;
27 if (storage.length > 0) {
28 let localList = storage.getItem("staffList");
29 if (localList && localList.length > 0) {
30 this.$store.commit("todo/concatStaff", JSON.parse(localList));
31 }
32 }
33 } else {
34 console.log("浏览器不支持 localStorage");
35 }
36 },
37 localStorageSet() {
38 let list = JSON.stringify(this.$store.state.todo.list);
39 console.log("---------", list);
40 localStorage.setItem("staffList", list);
41 },
42 },
43 };
44 </script>
45
46 <style>
47 #app {
48 font-family: Avenir, Helvetica, Arial, sans-serif;
49 -webkit-font-smoothing: antialiased;
50 -moz-osx-font-smoothing: grayscale;
51 text-align: center;
52 color: #2c3e50;
53 }
54
55 #nav {
56 padding: 30px;
57 }
58
59 #nav a {
60 font-weight: bold;
61 color: #2c3e50;
62 }
63
64 #nav a.router-link-exact-active {
65 color: #42b983;
66 }
67 </style>

Home.vue

 <template>
<div class="home">
<ToDoList></ToDoList>
</div>
</template> <script>
import ToDoList from "@/views/todolist/ToDoList";
export default {
name: "Home",
components: {
ToDoList,
},
mounted() {
},
data() {
return {};
},
methods: {},
};
</script>
<style lang="less" scoped>
</style>

ToDoList.vue

 <template>
<div class="container">
<div class="lt-form">
<form class="form">
<div class="title-label">
<label for="title">{{TITLE}}</label>
<input
:disabled="status!=='SUBMIT'?false:true"
autocomplete="off"
type="text"
id="title"
v-model="form.title"
data-rule="title:required"
/>
</div> <div class="date-label">
<label for="date">{{DATE}}</label>
<input
:disabled="status!=='SUBMIT'?false:true"
autocomplete="off"
type="date"
id="date"
v-model="form.date"
/>
</div> <div class="content-label">
<label for="content">{{CONTENT}}</label>
<textarea
:disabled="status!=='SUBMIT'?false:true"
id="content"
rows="5"
v-model="form.content"
></textarea>
</div>
<div class="btns" v-if="status==='ADD'">
<div class="btn btn-submit">
<input type="button" value="提交" @click="submit" />
</div>
<div class="btn btn-reset">
<input type="button" value="清空" @click="reset" />
</div>
<div class="btn btn-cancle">
<input type="button" value="取消" @click="cancle" />
</div>
</div>
<div class="btns" v-else-if="status==='EDIT'">
<div class="btn btn-save">
<input type="button" value="保存" @click="save" />
</div>
<div class="btn btn-cancle">
<input type="button" value="取消" @click="cancle" />
</div>
</div>
<div class="btns" v-else>
<div class="btn btn-new">
<input type="button" value="新增" @click="newItem" />
</div>
<div class="btn btn-delete">
<input type="button" value="删除" @click="deleteItem" />
</div>
<div class="btn btn-edit">
<input type="button" value="修改" @click="modifyItem" />
</div>
</div>
</form>
</div>
<div class="rt-part">
<div class="rt-nav">
<div class="rt-nav-block">
<div
:class="'nav-item item-'+index "
@click="clickHandler(index,item)"
v-for="(item,index) in arr "
:key="index"
>
<span>{{item.title|doSlice}}</span>
<span>{{item.date}}</span>
<span>{{item.content|doSlice}}</span>
</div>
</div>
</div>
</div>
</div>
</template> <script>
export default {
name: "Home",
components: {
// HelloWorld
},
mounted() {
this.renderNav();
this.$nextTick(() => {
// console.log(document.querySelector(".item-0"));
if (this.list.length > 0) {
this.clickHandler("0");
}
}); // setTimeout(() => {}, 0);
}, computed: {
list() {
// this.renderNav();
return this.$store.state.todo.list;
},
},
data() {
return {
form: {
title: "",
date: "",
content: "",
},
TITLE: "标题",
DATE: "日期",
CONTENT: "事件",
FORMSTATUS: {
ADD: "ADD",
EDIT: "EDIT",
SUBMIT: "SUBMIT",
},
arr: [],
currTarget: "",
lastIndex: "",
status: "ADD", // ADD EDIT SUBMIT
};
},
filters: {
doSlice: function (value) {
let newVal = value.length > 5 ? value.slice(0, 5) + "..." : value;
return newVal;
},
},
methods: {
submit() {
let objKey;
let flag = Object.keys(this.form).some((key) => {
if (this.form[key] === "") {
objKey = key;
return true;
}
});
if (flag) {
alert(`${this[objKey.toUpperCase()]} 不能为空`);
return false;
}
let body = this.$clone(this.form);
this.$store.commit("todo/pushStuff", body);
this.arr.push(body); // 等到DOM渲染完成后调用的钩子函数
this.$nextTick(() => {
this.clickHandler(this.arr.length - 1);
});
this.changeFormStatus(this.FORMSTATUS.SUBMIT);
},
reset() {
Object.keys(this.form).forEach((key) => {
this.form[key] = "";
});
},
renderNav() {
this.arr = this.$clone(this.list);
if (this.arr.length > 0) {
this.status = true;
let temp = this.$clone(this.list[0]);
this.form = temp;
}
},
clickHandler(index) {
// console.log(index);
// console.log(this.arr);
// this.$store.commit("todo/deleteStuff");
if (this.status === "ADD" || this.status === "EDIT") {
this.changeFormStatus(this.FORMSTATUS.SUBMIT);
}
this.changeClassByClass(index);
this.lastIndex = index;
let temp = this.$clone(this.list[index]);
this.form = temp;
},
changeClassByClass(index) {
if (this.lastIndex === "") {
let currClass = document.querySelector(".item-" + index);
currClass.className = currClass.className + " active";
} else {
// let lastClass = document.querySelector(".item-" + this.lastIndex);
// lastClass.className = lastClass.className.replace(/active/g, " ");
this.clearActiveClass(this.lastIndex);
let currClass = document.querySelector(".item-" + index);
currClass.className = currClass.className + " active";
}
},
changeBtnByClass() {
document.querySelector;
},
newItem() {
this.reset();
// this.status = true;
this.changeFormStatus(this.FORMSTATUS.ADD);
this.clearActiveClass(this.lastIndex);
},
clearActiveClass(index) {
let dom = document.querySelector(".item-" + index);
dom.className = dom.className.replace(/active/g, " ");
},
modifyItem() {
if (this.arr.length > 0) {
this.changeFormStatus(this.FORMSTATUS.EDIT);
} else {
return;
}
},
deleteItem() {
// console.log();
let operation = {
index: this.lastIndex,
num: 1,
};
this.$store.commit("todo/deleteStuff", operation);
this.arr.splice(operation.index, operation.num);
this.reset();
// if(this.){}
this.setSelectedAfterDelete();
},
cancle() {
// this.status = "EDITING";
this.changeFormStatus(this.FORMSTATUS.SUBMIT);
},
changeFormStatus(status) {
this.status = status;
},
setSelectedAfterDelete() {
let lastIndex = parseInt(this.lastIndex);
let length = this.arr.length; // lastIndex是 被删掉的最后一个数据的索引
if (lastIndex == length && length > 0) {
// this.lastIndex = lastIndex - 1;
this.clickHandler(lastIndex - 1);
} else if (length > 0) {
this.clickHandler(this.lastIndex);
} else {
return;
}
},
save() {
let body = this.$clone(this.form);
this.$set(this.arr, this.lastIndex, body);
this.changeFormStatus(this.FORMSTATUS.SUBMIT);
},
},
};
</script>
<style lang="less" scoped>
* {
padding: 0;
margin: 0;
}
.container {
padding: 15px;
min-height: 300px;
display: flex;
justify-content: center;
align-items: stretch;
.lt-form {
height: 200px;
margin: 0;
width: 500px;
height: 100%;
border: 1px solid;
padding: 15px;
margin: 0 15px;
.form {
.title-label,
.date-label {
width: 50%;
display: inline-block;
text-align: left;
label {
display: inline-block;
width: 15%;
}
input {
width: 80%;
font-size: 16px;
// margin-right: -15px;
}
}
.content-label {
text-align: left;
display: block;
width: 100%;
label {
display: inline-block;
}
textarea {
font-size: 16px;
resize: none;
width: 100%;
}
}
.btns {
display: flex;
justify-content: space-between;
.btn-submit,
.btn-reset,
.btn-delete,
.btn-new,
.btn-edit,
.btn-cancle,
.btn-save {
// width: 50%;
flex: 1;
// display: inline-block;
// text-align: start;
input {
width: 50%;
// line-height: 10px;
padding: 5px;
font-size: 16px;
// background: rgb(123, 321, 313);
// border-radius: 5px;
margin: 10px 0;
}
}
// .btn.btn-submit,
// .btn.btn-edit {
// text-align: start;
// }
// .btn-new {
// text-align: center;
// }
// .btn.btn.btn-reset,
// .btn.btn-delete {
// text-align: end;
// }
}
}
}
.rt-part {
display: flex;
flex-direction: column;
.rt-nav {
width: 100%;
height: 200px;
min-height: 230px;
border: 1px solid;
margin: 0 15px;
// position: relative;
.rt-nav-block {
flex: 1;
height: 100%;
overflow: auto;
.nav-item {
display: flex;
align-items: center;
justify-content: space-around;
// position: relative;
flex-wrap: nowrap;
span {
flex: 1;
overflow: hidden;
}
}
}
}
}
}
.active {
background: #4f4f4f;
color: white;
}
</style>

/store/todolist/todo.js:

 export default {
namespaced: true,
state: {
list: [{
title: "测试数据1",
date: "2020-02-02",
content: "测试数据1测试数据1测试数据1"
}],
unfinishedList: [],
finishedList: [],
},
mutations: {
pushStuff(state, value) {
state.list.push(value);
},
concatStaff(state, arr) {
state.list = [].concat(arr);
},
deleteStuff(state, opertation) {
// console.log(arguments);
state.list.splice(opertation.index, opertation.num);
}
},
actions: {},
modules: {}
}

  /js/utils.js:

 export default {
clone: function (body) {
return JSON.parse(JSON.stringify(body));
},
}

效果图:超过5个字符则只截取前5个字符加上 '...' 展示。

Vuex + localStorage + html实现简易todolist的更多相关文章

  1. 基于vue2.0+vuex+localStorage开发的本地记事本

    本文采用vue2.0+vuex+localStorage+sass+webpack,实现一个本地存储的记事本.兼容PC端和移动端.在线预览地址:DEMO github地址:https://github ...

  2. Vuejs 实现简易 todoList 功能 与 组件

    todoList 结合之前 Vuejs 基础与语法 使用 v-model 双向绑定 input 输入内容与数据 data 使用 @click 和 methods 关联事件 使用 v-for 进行数据循 ...

  3. vue 简易toDoList

    vue+bootstrap简易响应式任务管理表: <!DOCTYPE html> <html> <head> <meta charset="UTF- ...

  4. jQuery+ localStorage 实现一个简易的计时器

    原型 图片发自简书App 需求1.关闭浏览器时时间继续运行2.刷新时保持当前状态3.结束时间保存在客户端 <div class="wrapper"> <div c ...

  5. Vuex基础 -01 -实现简易计数器 -支持 加数/ 减数/ 奇数再加/ 异步加法(setTimeout 1000ms) -单组件演示语法

    Vuex 的结构图 工程组织 Vuex的核心管理程序 store.js /* vuex的核心管理程序 */ import Vue from 'vue' import Vuex from 'vuex' ...

  6. vue项目用户登录状态管理,vuex+localStorage实现

    安装vuex cnpm install vuex --save-dev

  7. react 做的简易todolist

    首先要有一定的react的基础,里面的一些不做解释(包括项目文件的用法及作用)   ### 1. 先安装react的插件 npm  install  create-react-app  -g      ...

  8. 详解vuex结合localstorage动态监听storage的变化

    这篇文章主要介绍了详解vuex结合localstorage动态监听storage的变化,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 需求:不同组件间共用同一数据,当一个 ...

  9. 页面刷新 vuex 数据重新被初始化

    1.原因 vuex里用来存储的也只是一个全局变量,当页面刷新,该全局变量自然不存在了. 2.解决 使用localStorage存储一份 (1)storage.js /** * vuex localSt ...

随机推荐

  1. Python Ethical Hacking - ARPSpoof_Detector

    ARPSPOOF_DETECTOR Watch value for gateway mac in the arp table Nice and simple, but will not detect ...

  2. 题解 洛谷 P3734 【[HAOI2017]方案数】

    可以先考虑没有障碍物的情况,设计状态\(f_{i,j,k}\),表示到达坐标 \((x,y,z)\)二进制下,\(x\)有\(i\)位,\(y\)有\(j\)位,\(z\)有\(k\)位的方案数. 得 ...

  3. Alink漫谈(十三) :在线学习算法FTRL 之 具体实现

    Alink漫谈(十三) :在线学习算法FTRL 之 具体实现 目录 Alink漫谈(十三) :在线学习算法FTRL 之 具体实现 0x00 摘要 0x01 回顾 0x02 在线训练 2.1 预置模型 ...

  4. Flutter 实现酷炫的3D效果

    老孟导读:此文讲解3个酷炫的3D动画效果. 下面是要实现的效果: Flutter 中3D效果是通过 Transform 组件实现的,没有变换效果的实现: class TransformDemo ext ...

  5. 如何理解Flutter中的asyc 和 await

    https://blog.csdn.net/xdhc304/article/details/90232723 Flutter的语法非常精简, 对于异步任务, 只要使用asyc和awai 配合就能实现, ...

  6. ES数据库重建索引——Reindex(数据迁移)

    应用背景: 1.当你的数据量过大,而你的索引最初创建的分片数量不足,导致数据入库较慢的情况,此时需要扩大分片的数量,此时可以尝试使用Reindex. 2.当数据的mapping需要修改,但是大量的数据 ...

  7. Python网络编程基础 PDF 完整超清版|网盘链接内附提取码下载|

    点此获取下载地址提取码:y9u5 Python网络编程最好新手入门书籍!175个详细案例,事实胜于雄辩,Sockets.DNS.Web Service.FTP.Email.SMTP.POP.IMAP. ...

  8. Javascript 模块化概述

    模块化的目的 当网站开发得越来越复杂,会经常遇到以下问题: 命名冲突 文件依赖 Sea.js 一个适合web前端的模块加载器,遵守 CMD (Common Module Definition)模块定义 ...

  9. 2-Numpy之hstack、vstack、concatenate区别

    concatenate与hstack.vstack的异同点: 都表示拼接数组,concatenate可以实现hstack和vstack的功能,只需要通过调整参数axis的值即可. 其中:v表示垂直(V ...

  10. 6.18 省选模拟赛 字符串 LCT SAM

    LINK:字符串 看起来很难做 考虑一种暴力 建立SAM后每次查询暴力扫儿子. 期望得分10分.实际得分10分. 另外一种发现每次扫儿子过于暴力 可以每次儿子向上做贡献 每次都暴力向上跳. 期望得分1 ...