概要:VUE2+Compositon API,完成todo list的DEMO。
背景:笔者工作中在大佬的带动下,在VUE2的项目中使用Compositon API进行开发,为了方便学习,笔者这里通过一个简单的待办清单项目,还没有用过 Compositon API 的小伙伴们,请跟我一起一步步地揭开它神秘的面纱;
1、使用 vue-cli 创建项目(过程略)。
2、安装 Compositon API:
npm install @vue/composition-api --save
// 或
yarn install @vue/composition-api --save
3、在 main.js 文件引用 Compositon API
import Vue from 'vue'
import App from './App.vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
4、新建 untils > storage.ts ,将待办事件的状态存储在 storage 中:
interface TodoItem {
text: string
hasDone: boolean
}
export default {
getItem(key: string) {
return JSON.parse(localStorage.getItem(key) || '[]')
},
setItem(key: string, value: Array<TodoItem>) {
localStorage.setItem(key, JSON.stringify(value))
},
removeItem(key: string) {
localStorage.removeItem(key)
},
}
5、编写组件UI代码:
<template>
<div class="hello">
<header>
<section>
<form @submit.prevent>
<label for="title">ToDoList</label>
<input
type="text"
name="title"
placeholder="添加ToDo"
required="required"
autocomplete="off"
v-model="todoItem"
@keyup.enter="addItem"
/>
</form>
</section>
</header>
<section>
<h2>
正在进行<span>{{ todoCount }}</span>
</h2>
<ol class="demo-box">
<li v-for="item in todoList" :key="item.text">
<input
type="checkbox"
v-model="item.hasDone"
@change="update(item.text, item.hasDone)"
/>
<p>{{ item.text }}</p>
<a href="javascript:" @click.prevent="remove(item.text)">—</a>
</li>
</ol>
<h2>
已经完成<span>{{ doneCount }}</span>
</h2>
<ul>
<li v-for="item in doneList" :key="item.text">
<input
type="checkbox"
v-model="item.hasDone"
@change="update(item.text, item.hasDone)"
/>
<p>{{ item.text }}</p>
<a href="javascript:" @click.prevent="remove(item.text)">—</a>
</li>
</ul>
</section>
<footer>
<a href="javascript:" @click.prevent="clear">清空已完成的任务</a>
</footer>
</div>
</template>
<style scoped lang="less">
body{margin: 0; padding: 0; font-size: 16px; background: #cdcdcd;}
header{height: 50px; background: #333; background: rgba(47, 47, 47, 0.98);}
section{width: 600px; margin: 0 auto;}
label{float: left; width: 100px; line-height: 50px; color: #ddd; font-size: 24px; cursor: pointer; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}
header input{float: right; width: 60%; height: 24px; margin-top: 12px; text-indent: 10px; border-radius: 5px; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.24), 0 1px 6px rgba(0, 0, 0, 0.45) inset; border: none;}
input:focus{outline-width: 0;}
h2{position: relative; text-align: left;}
span{position: absolute; top: 2px; right: 5px; display: inline-block; padding: 0 5px; height: 20px; border-radius: 20px; background: #e6e6fa; line-height: 22px; text-align: center; color: #666; font-size: 14px;}
ol,ul{padding: 0; list-style: none;}
li input{position: absolute; top: 2px; left: 10px; width: 22px; height: 22px; cursor: pointer;}
p{margin: 0; text-align: left;}
li p input{top: 3px; left: 40px; width: 70%; height: 20px; line-height: 14px; text-indent: 5px; font-size: 14px;}
li{height: 32px; line-height: 32px; background: #fff; position: relative; margin-bottom: 10px; padding: 0 45px; border-radius: 3px; border-left: 5px solid #629a9c; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07);}
ol li{cursor: move;}
ul li{border-left: 5px solid #999; opacity: 0.5;}
li a{position: absolute; top: 2px; right: 5px; display: inline-block; width: 18px; height: 18px; border-radius: 50%; line-height: 18px; text-align: center; color: #fff; font-weight: bold; font-size: 12px; cursor: pointer; background-color: crimson; text-decoration: none;}
footer{padding-top: 50px; text-align: center;}
footer a{text-decoration: none; font-size: 16px; border: 1px solid #ccc; display: inline-block; padding: 4px 12px; border-radius: 3px; color: #333;}
footer a:hover{background-color: #eefbff;}
</style>
6、定义方法:
import { ref, reactive, computed, onMounted } from '@vue/composition-api'
import storage from '../until/storage'
interface TodoItem {
text: string
hasDone: boolean
}
export default {
name: 'ToDoList',
setup() {
const todoItem = ref('')
const todo: {
list: Array<TodoItem>
} = reactive({
list: [],
})
// 通过计算获取待办列表内容是列表中尚未完成的项
const todoList = computed(() => todo.list.filter(item => !item.hasDone))
// 通过计算获取待办的项的个数
const todoCount = computed(
() => todo.list.filter(item => !item.hasDone).length,
)
// 通过计算获取完成列表内容是列表中已完成的项
const doneList = computed(() => todo.list.filter(item => item.hasDone))
// 通过计算完成的项的个数
const doneCount = computed(
() => todo.list.filter(item => item.hasDone).length,
)
// 待办事项添加方法
const addItem = () => {
const text = todoItem.value
for (let i = 0, len = todo.list.length; i < len; i++) {
if (todo.list[i].text == text) {
alert('重复了,请换一个~')
return
}
}
todo.list.push({
text: text,
hasDone: false,
})
storage.setItem('todolist', todo.list)
todoItem.value = ''
}
// 复选框勾选出发待办事项完成的方法
const update = (text: string, hasDone: boolean) => {
todo.list.forEach(item => {
if (item.text == text) {
item.hasDone = hasDone
}
})
storage.setItem('todolst', todo.list)
}
// 移除一个待办事项的方法
const remove = (text: string) => {
todo.list = todo.list.filter(item => item.text == text)
storage.setItem('todolist', todo.list)
}
// 清空已完成列表的方法
const clear = () => {
todo.list = todo.list.filter(item => !item.hasDone)
storage.removeItem('todoList')
}
onMounted(() => {
const list = storage.getItem('todolist')
if (Array.isArray(list)) {
todo.list = list
} else {
storage.setItem('todolist', todo.list)
}
})
return {
todoItem,
todo,
todoList,
todoCount,
doneList,
doneCount,
addItem,
update,
remove,
clear,
}
},
}
7、查看页面效果:
以上就是利用 Compositon API 完成的一个简单的待办事项功能的DEMO,本文的侧重点在于展示 Compositon API 在使用中,其灵活的组件逻辑、编写的代码易读性好,与敏捷开发更匹配。