一、写在前面+效果图
二、Vuex
2.1 Codepen:html+js+css
2.1.1 一个可能更加通顺的思路
2.2 本地:App.vue,components/component1.vue,store/index.js

写在前面+效果图

本次是探索性质,肯定有一定差错和不足,我会继续学习。

不做详细的vue和vuex语法、功能介绍,因为官方文档讲得比我清楚,我主要是提供撰写的顺序思路,以及完整的代码。

目录在上面,自取所需。

效果图:

alt text

Vuex

Codepen:html+js+css

如何在这个界面使用vuex?只需要在html里写上:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.js"></script>

codepen这里的意思就是不分文件层级,所有js都在一个js里,html、css也是,确实很简洁。

一个可能更加通顺的思路

对于js部分,首先把框架写出来,三个部分:new Vue、vue的组件、Vuex部分:

Const store = new Vuex.Store({
mutations:{
},
getters:{
},
actions:{
},
});

vue.component('c1',{
template:`
`,
data(){
},
computed(){
},
methods:{
},   
});

vue.component('c2',{

});

New Vue({
el: '#app',
store,
created(){
this.$store.commit("loadTodos");
}
});

然后,根据界面里有什么,构想各个component应该长什么样,譬如我认为,我需要一个header,还需要一个content。

html:对的,用组件就这么简单明了。

<div id="app">
<header-component></header-component>
<content-component></content-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.js"></script>

根据html设计思路,在Vue.component里填写template内容:譬如div、button,以及一些要用的变量,一开始只写个框,譬如<button>增加</button>,{{total}},先不考虑这个变量存不存在,以便于排除冗杂的信息;

接着,根据文字说明,具体地填写功能,譬如我要实现增加,我就要点这个按钮调用函数,所以我扩充为<button @click="addTodo">增加</button>

Vue.component("header-component", {
template: `
<div class="header">
<div class="filter">
<button @click="myFilter('all')">全部</button>
<button @click="myFilter('finished')">已完成</button>
<button @click="myFilter('not-finished')">未完成</button>
<span>总共{{ total }}条,已完成{{ completed }}条,剩余{{ remaining }}条</span>
<button @click="clearCompleted">清除已完成</button>
<button @click="checkAll">全部完成</button>
<button class="headerAdd" @click="addTodo">增加新的TODO</button>
</div>
</div>
`,
// 以下省略
}

既然写了一些想实现的方法,譬如addTodo,那么接下来就在各个component的methods里写上要让vuex调用数据驱动的方法的大概样子(有点像先写个抽象类,之后再完善),格式都是this.$store.commit(‘vuex里的方法名’,要传的变量);

变量也是如此,在computed里写。vuex里已有的变量用this.$store.state,要计算的变量用this.$store.getters(这个目前还不知道要不要计算,所以先写一个试试)

alt text

那么现在vue的部分完成了,要写vuex了。根据之前的这些commit、state、getters的内容,填充Vuex,先写mutation,再写getters,过程中会发现需要变量,就往state里加。大概长图片那样

  • mutations和getters:每个函数的第一个变量都是state,修改state{}里的数据必须用这个前缀;第二个开始的参数是传入的,根据之前写的来
  • 具体功能就在这里实现,这就是数据驱动的精妙之处,具体的操作都让vuex执行,不用vue的component之前传来传去的麻烦
  • getters需要return,之前的computed也要写return,别忘了就是。

alt text

最后,进行功能检查:getters和computed用对了吗,dispatch和commit用对了吗,其他各种检查。那么vuex的功能差不多也完成了。

codepen这种情况下的总体代码:

html里:

<div id="app">
<header-component></header-component>
<content-component></content-component>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.js"></script>

js里:

Vue.component("header-component", {
template: `
<div class="header">
<div class="filter">
<button @click="myFilter('all')">全部</button>
<button @click="myFilter('finished')">已完成</button>
<button @click="myFilter('not-finished')">未完成</button>
<span>总共{{ total }}条,已完成{{ completed }}条,剩余{{ remaining }}条</span>
<button @click="clearCompleted">清除已完成</button>
<button @click="checkAll">全部完成</button>
<button class="headerAdd" @click="addTodo">增加新的TODO</button>
</div>
</div>
`,
computed: {
total() {
return this.$store.state.todoList.length;
},
completed() {
return this.$store.getters.completed;
},
remaining() {
return this.$store.getters.remaining;
}
},
methods: {
myFilter(status) {
this.$store.commit("setFilterStatus", status);
},
clearCompleted() {
this.$store.commit("clearCompleted");
},
checkAll() {
this.$store.commit("checkAll");
},
addTodo() {
this.$store.commit("addTodo");
}
}
});

Vue.component("content-component", {
template: `
<div class="content">
<div class="todo-item" v-for="(item, index) in filteredTodoList" :key="item.id" transition="zj">
<input class="checkbox" type="checkbox" @click="check(index, item.id)" v-model="item.isChecked">
<input class="item-input" v-model="item.text" type="text" :disabled="item.isChecked" @blur="handleBlur()">
<input type="datetime-local" v-model="item.time" @blur="handleBlur">
<button @click="removeTodo(index, item.id)">删除</button>
</div>
</div>
`,
computed: {
filteredTodoList() {
return this.$store.getters.filteredTodoList;
}
},
methods: {
check(index, id) {
this.$store.commit("check", { index, id });
},
removeTodo(index, id) {
this.$store.commit("removeTodo", { index, id });
},
handleBlur() {
this.$store.commit("saveTodos");
}
}
});

const store = new Vuex.Store({
state: {
todoList: [],
filterStatus: "all"
},
getters: {
filteredTodoList(state) {
if (state.filterStatus === "finished") {
return state.todoList.filter((item) => item.isChecked);
} else if (state.filterStatus === "not-finished") {
return state.todoList.filter((item) => !item.isChecked);
} else {
return state.todoList;
}
},
total(state) {
return state.todoList.length;
},
completed(state) {
return state.todoList.filter((item) => item.isChecked).length;
},
remaining(state) {
return state.todoList.filter((item) => !item.isChecked).length;
}
},
mutations: {
setFilterStatus(state, status) {
state.filterStatus = status;
},
addTodo(state) {
state.todoList.unshift({
id: Number(Math.random().toString().substr(2, 10) + Date.now()).toString(
10
),
isChecked: false,
text: "",
time: new Date().toISOString().slice(0, 16)
});
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
removeTodo(state, { index, id }) {
if (state.todoList[index].id === id) {
state.todoList.splice(index, 1);
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
}
},
check(state, { index, id }) {
if (state.todoList[index].id === id) {
state.todoList[index].isChecked = !state.todoList[index].isChecked;
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
}
},
checkAll(state) {
state.todoList.forEach((item) => {
item.isChecked = true;
});
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
clearCompleted(state) {
state.todoList = state.todoList.filter((item) => !item.isChecked);
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
saveTodos(state) {
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
loadTodos(state) {
const tmp = localStorage.getItem("todoList1");
if (tmp) {
state.todoList = JSON.parse(tmp);
} else {
state.todoList = [
{
id: Number(Math.random().toString().substr(2, 10) + Date.now()).toString(
10
),
text: "请点击上方的添加按钮添加事件",
isChecked: false,
time: new Date().toISOString().slice(0, 16)
}
];
}
}
}
});

new Vue({
el: "#app",
store,
created() {
this.$store.commit("loadTodos");
}
});

css部分请自由发挥。

本地:App.vue,components/component1.vue,store/index.js,main.js

内容还是基本一样的,本地的问题主要是看东西放在哪个文件夹。

首先你得装了vue;然后在一个文件夹启动环境;而且要在同一个文件夹装vuex。

这样的话,你将会看到的结构:(TEST_VUE是根目录;store文件夹本来没有,自己建一个。)

App.vue同级还需要一个main.js:

alt text

请看各个文件

App.vue几乎完全一样:

<template>

<div id="app">
<header-component></header-component>
<content-component></content-component>
</div>

</template>

<style>

</style>

store/index.js:放的是vuex的东西,记得import和Vue.use(vuex)

注意export default new Vuex.Store代替了之前的new store = Vuex.Store,不改的话会报错:We‘re sorry but doesn’t work properly without JavaScript enabled. Please enable it to continue

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)


export default new Vuex.Store({
state: {
todoList: [],
filterStatus: "all"
},
getters: {
filteredTodoList(state) {
if (state.filterStatus === "finished") {
return state.todoList.filter((item) => item.isChecked);
} else if (state.filterStatus === "not-finished") {
return state.todoList.filter((item) => !item.isChecked);
} else {
return state.todoList;
}
},
total(state) {
return state.todoList.length;
},
completed(state) {
return state.todoList.filter((item) => item.isChecked).length;
},
remaining(state) {
return state.todoList.filter((item) => !item.isChecked).length;
}
},
mutations: {
setFilterStatus(state, status) {
state.filterStatus = status;
},
addTodo(state) {
state.todoList.unshift({
id: Number(Math.random().toString().substr(2, 10) + Date.now()).toString(
10
),
isChecked: false,
text: "",
time: new Date().toISOString().slice(0, 16)
});
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
removeTodo(state, { index, id }) {
if (state.todoList[index].id === id) {
state.todoList.splice(index, 1);
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
}
},
check(state, { index, id }) {
if (state.todoList[index].id === id) {
state.todoList[index].isChecked = !state.todoList[index].isChecked;
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
}
},
checkAll(state) {
state.todoList.forEach((item) => {
item.isChecked = true;
});
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
clearCompleted(state) {
state.todoList = state.todoList.filter((item) => !item.isChecked);
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
saveTodos(state) {
localStorage.setItem("todoList1", JSON.stringify(state.todoList));
},
loadTodos(state) {
const tmp = localStorage.getItem("todoList1");
if (tmp) {
state.todoList = JSON.parse(tmp);
} else {
state.todoList = [
{
id: Number(Math.random().toString().substr(2, 10) + Date.now()).toString(
10
),
text: "请点击上方的添加按钮添加事件",
isChecked: false,
time: new Date().toISOString().slice(0, 16)
}
];
}
}
}
});

main.js:注意下面的东西路径正确,注意header-component是最终使用在html的名字,不能写错;注意这里用了mount,和之前不一样了

import Vue from 'vue';
import store from './store/index.js';
import app from './App.vue';
import HeaderComponent from './components/header.vue';
import ContentComponent from './components/content.vue';

Vue.component('header-component', HeaderComponent);
Vue.component('content-component', ContentComponent);

new Vue({
store,
render: (h) => h(app)
}).$mount('#app');

components/content.vue:注意export default的变化。header那个也类似地调整一下。

<template>
<div class="content">
<div class="todo-item" v-for="(item, index) in filteredTodoList" :key="item.id" transition="zj">
<input class="todo-item__checkbox" type="checkbox" @click="check(index, item.id)" v-model="item.isChecked">
<input class="todo-item__item-input" v-model="item.text" type="text" :disabled="item.isChecked" @blur="handleBlur()">
<input type="todo-item__datetime-local" v-model="item.time" @blur="handleBlur">
<button @click="removeTodo(index, item.id)">删除</button>
</div>
</div>
</template>

<script>
export default {
computed: {
filteredTodoList() {
return this.$store.getters.filteredTodoList;
}
},
methods: {
check(index, id) {
this.$store.commit("check", { index, id });
},
removeTodo(index, id) {
this.$store.commit("removeTodo", { index, id });
},
handleBlur() {
this.$store.commit("saveTodos");
}
}
};
</script>

<style>

</style>

差不多就完成了。

其他的具体配置我也不懂了,主要还是跟着其他人的教程进行安装,有问题就找攻略修。希望大家都写得顺利!