在上一篇 Vite + Vue3 初体验 —— Vite 篇 博客中,我感受到了 Vite 带来的运行时效率提升,这一期再来感受感受 Vue3
带来的新变化 —— 关注点分离。
Todo List 设计
这次体验 Vue3
,我想做一个能体验(部分) Vue3
新特性的功能模块。
想了想,用一个 Todo List
应该是比较合适的。
我们来规划一下它的功能清单吧。
- 输入
Todo
,按下回车即可添加一条新的Todo Item
。 - 以列表的形式显示所有的
Todo Item
。 - 可以将
Todo Item
标记为完成,标记完成后的Todo Item
会置灰,并且排序处于最下面。 - 可以将
Todo Item
删除,删除后在列表中不展示。 - 可以将
Todo Item
置顶,高亮显示,以提高优先级。
OK,接下来,我们先把基础页面搭建出来吧。
搭建基础 UI 界面
配置 UI 库
目前支持 Vue3
的 UI 框架有下面几种:
其中 ant-design
和 elementui
是从 Vue2
一路走来的老 UI 库了,我在体验 Vue3
的时候决定还是使用轻风格的 ant-design
。
先安装支持 Vue3
的 ant-design-vue
吧。
yarn add ant-design-vue@next
然后,再配置一下按需加载,这样的话,只有被使用到的组件才会被打包,可有效减小生产包的体积。
// vite.config.ts
import {
defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import {
AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [
AntDesignVueResolver(),
],
}),
]
});
最后,在 main.ts
中引入样式文件。
// main.ts
import 'ant-design-vue/dist/antd.css';
基础布局
现在,我们的布局需要一个输入框和一个列表,我们先在页面把这两个元素画出来吧。
在此之前,在
App.vue
中引入了我们的TodoList
组件。
// TodoList.vue
<script setup lang="ts">
import { DeleteOutlined, CheckOutlined, CheckCircleFilled } from '@ant-design/icons-vue';
import { Input } from "ant-design-vue";
</script>
<template>
<section class="todo-list-container">
<section class="todo-wrapper">
<Input class="todo-input" placeholder="请输入待办项" />
<section class="todo-list">
<section class="todo-item">
<span>Todo Item</span>
<div class="operator-list">
<DeleteOutlined />
<CheckOutlined />
</div>
</section>
<section class="todo-item">
<span>Todo Item</span>
<div class="operator-list">
<DeleteOutlined />
<CheckOutlined />
</div>
</section>
<section class="todo-item todo-checked">
<span>Todo Item</span>
<div class="operator-list">
<CheckCircleFilled />
</div>
</section>
</section>
</section>
</section>
</template>
<style scoped lang="less">
.todo-list-container {
display: flex;
justify-content: center;
width: 100vw;
height: 100vh;
box-sizing: border-box;
padding-top: 100px;
background: linear-gradient(rgba(93, 190, 129, .02), rgba(125, 185, 222, .02));
.todo-wrapper {
width: 60vw;
.todo-input {
width: 100%;
height: 50px;
font-size: 18px;
color: #F05E1C;
border: 2px solid rgba(255, 177, 27, 0.5);
border-radius: 5px;
}
.todo-input::placeholder {
color: #F05E1C;
opacity: .4;
}
.ant-input:hover, .ant-input:focus {
border-color: #FFB11B;
box-shadow: 0 0 0 2px rgb(255 177 27 / 20%);
}
.todo-list {
margin-top: 20px;
.todo-item {
box-sizing: border-box;
padding: 15px 10px;
cursor: pointer;
border-bottom: 2px solid rgba(255, 177, 27, 0.3);
color: #F05E1C;
margin-bottom: 5px;
font-size: 16px;
transition: all .5s;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 10px;
.operator-list {
display: flex;
justify-content: flex-start;
align-items: center;
:first-child {
margin-right: 10px;
}
}
}
.todo-checked {
color: rgba(199, 199, 199, 1);
border-bottom-color: rgba(199, 199, 199, .4);
transition: all .5s;
}
.todo-item:hover {
box-shadow: 0 0 5px 8px rgb(255 177 27 / 20%);
border-bottom: 2px solid transparent;
}
.todo-checked:hover {
box-shadow: none;
border-bottom-color: rgba(199, 199, 199, .4);
}
}
}
}
&