在上一篇 Vite + Vue3 初体验 —— Vite 篇 博客中,我感受到了 Vite 带来的运行时效率提升,这一期再来感受感受 Vue3
带来的新变化 —— 关注点分离。
Todo List 设计
这次体验 Vue3
,我想做一个能体验(部分) Vue3
新特性的功能模块。
想了想,用一个 Todo List
应该是比较合适的。
我们来规划一下它的功能清单吧。
- 输入
Todo
,按下回车即可添加一条新的Todo Item
。 - 以列表的形式显示所有的
Todo Item
。 - 可以将
Todo Item
标记为完成,标记完成后的Todo Item
会置灰,并且排序处于最下面。 - 可以将
Todo Item
删除,删除后在列表中不展示。 - 可以将
Todo Item
置顶,高亮显示,以提高优先级。
搭建基础 UI 界面 配置 UI 库
目前支持
Vue3
的 UI 框架有下面几种:- Ant Design Vue
- Element Plus
- Ionic
- Native 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.vueTodo Item
Todo Item
Todo Item .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);
}
}
}
}
这次我选了一套黄橙配色,我们来看看界面的效果吧。
文章图片
处理业务逻辑 处理输入
现在,我们来处理一下我们的输入逻辑,在按下回车键时,将输入的结果收集起来添加到
Todo
数组中,并且将输入框清空。这里需要用到双向绑定,定义一个
引用
变量,与输入框进行绑定。
//...
现在打开本地开发界面,输入一个值,然后按下回车,输入框的值就被清空了 —— 将这一项添加到了
todoList
数组中!渲染列表
在处理好了输入之后,现在需要将列表渲染出来。
这里还是用经典的
v-for
语法,同时需要加上一些状态的判断。{{item.title}}
这个语法相信用过
vue2
的都清楚,就不做过多介绍了。有一说一,处理删除和完成逻辑vscode
+volar
对vue3 + ts
的支持是真不错,代码提示和错误提示都非常完善了。在开发过程中,简直是事半功倍。
最后,我们来处理一下删除和完成的逻辑吧。
//...
最后,来看看我们界面的效果吧。(如下图)
文章图片
加入置顶逻辑
我们需要先给数组元素添加一个字段
is_top
,用于判断该节点是否置顶。然后,再加入置顶函数的逻辑处理以及样式显示。(如下)
//...{{item.title}}
然后,我们来看看我们的界面效果吧!(如下图)
文章图片
这样一来,我们的
Todo List
就完成了!现在再来看看我们的代码,主要是有两块逻辑关注点:
todoList
相关逻辑,负责列表的渲染以及列表的相关操作(删除、置顶、完成)。todoText
相关逻辑,负责处理输入框的输入。
todoList
相关的代码即可;如果我想要调整输入相关的逻辑,我只需要关注和调整 todoText
相关的逻辑即可。如果这两块的逻辑后面随着业务发展而变得越来越复杂了,我可以选择将其拆分成更小块的业务逻辑来进行维护,还可以将这些逻辑都拆分到单文件中进行维护管理,这样对于后续的维护和升级都能够有更好的把控。
处理前后端交互逻辑 我们之前所有的逻辑都是在本地做的处理,现在我们来接入服务端的逻辑,将我们的所有数据及变更进行持久化。同时,我们也来看看在
Vue3
中,如何处理有前后端交互逻辑的场景。假设我们有下面这么几组接口(如下图)
文章图片
那么,基于这几组接口的后端交互逻辑,我们还是用经典的
axios
来做吧。
使用
yarn add axios
添加依赖。
这里,我们先在 src
目录下新建一个 service
,用于初始化我们用于网络请求的 service
。(如下)// src/service/index.ts
import axios from "axios";
const service = axios.create({
// 设置 baseURL,这个地址是我部署的后端服务
baseURL: "https://hacker.jt-gmall.com"
});
export default service;
用户身份信息
我们设计的
Todo List
是一个在线网页,我们希望每个用户进来看到的都是自己的 Todo List
。我们来看看后台的接口设计,他使用
key
来给 Todo Item
做分组,所以我们需要在进入页面时,为每一个用户生成一个独一无二的 user key
。我们先设计一个用来获取
key
的函数吧。这里使用uuid
来生成唯一的user key
。
// service/auth.ts
import { v4 as uuid } from "uuid";
const getUserKey = () => {
if (localStorage.getItem('user_key')) return localStorage.getItem('user_key');
const userKey = uuid();
localStorage.setItem('user_key', userKey);
return userKey;
}export {
getUserKey
}
获取 Todo List
然后,我们回到我们的
TodoList.vue
文件,我们先写一个获取远端 Todo
列表的逻辑。(如下)// TodoList.vue
import service from "@/service";
import { getUserKey } from '@/service/auth';
// 创建一个引用变量,用于绑定 Todo List 数据
const todoList = ref<{
title: string,
is_completed: boolean,
is_top: boolean
}[]>([]);
// 初始化 todo list
const getTodoList = async () => {
const reply = await service.get('/todo/get-todo-list', { params: { key: getUserKey() } });
todoList.value = https://www.it610.com/article/reply.data.data;
}
getTodoList();
这里加上网络请求后,页面也是不会有什么变化的,因为这个用户目前是没有数据的。
接下来,我们把剩下的几个逻辑都补全。
注意:这里使用到了alias
别名功能,需要在vite.config.ts
和tsconfig.json
中进行配置。
import path from 'path';
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
}
},
// ...
})
// tsconfig.json{
"compilerOptions": {
// ...
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"]
}
}
}
新增、置顶、完成、删除 Todo
由于用户进入
Todo List
查看的都是自己的数据,并且该数据只有自己可操作。所以,也是为了能有更好的用户体验,在我们所有的操作逻辑完成后,回显数据还是用原有的逻辑。
当然,新增数据时,还是需要重新获取列表数据,因为我们操作数据时需要用到每一项的
id
。综上所述,我们重构后的四个函数长这样。
// 删除、完成、置顶的逻辑都与 todoList 放在同一个地方,这样对于逻辑关注点就更加聚焦了
const onDeleteItem = async (index: number) => {
const id = todoList.value[index].id;
await service.post('/todo/delete', { id });
todoList.value.splice(index, 1);
}const onCompleteItem = async (index: number) => {
const id = todoList.value[index].id;
await service.post('/todo/complete', { id });
todoList.value[index].is_completed = true;
// 重新排序,将已经完成的项目往后排列
const todoItem = todoList.value.splice(index, 1);
todoList.value.push(todoItem[0]);
}const onTopItem = async (index: number) => {
const id = todoList.value[index].id;
await service.post('/todo/top', { id });
todoList.value[index].is_top = true;
// 重新排序,将已经完成的项目往前排列
const todoItem = todoList.value.splice(index, 1);
todoList.value.unshift(todoItem[0]);
}// 新增 Todo Item 的逻辑都放在一处
// 创建一个引用变量,用于绑定输入框
const todoText = ref('');
const addTodoItem = () => {
// 新增一个 TodoItem,请求新增接口
const todoItem = {
key: getUserKey(),
title: todoText.value
}
return service.post('/todo/add', todoItem);
}
const onTodoInputEnter = async () => {
if (todoText.value =https://www.it610.com/article/=='') return;
await addTodoItem();
await getTodoList();
// 添加成功后,清空 todoText 的值
todoText.valuehttps://www.it610.com/article/= '';
}
逻辑修改完成后,我们回到页面查看一下效果吧!我们做一些操作后,刷新页面查看一下。(如下图)
文章图片
刷新页面后,我们的数据依然是可以展示出来的,说明数据已经成功做了服务端持久化啦!
小结 这次,我们用
Vue3
来完成了一个简单的 Todo List
系统。可以看出,
Vue3
对 ts
的支持变得更友好了,而新的 vue
单文件语法和 组合式 API
给我的体验也有点接近 React
+ JSX
。 —— 我的意思是,给开发者的体验更好了。我们再来看看我们用
组合式 API
实现的逻辑部分(如下图)。文章图片
从上图可以看出,我们的逻辑关注点被分成了两大块,分别是列表相关逻辑(渲染、操作)和新增 Todo Item。
这种清晰的职责划分使得我们需要维护某一部分的功能时,与之相关的内容都被圈在了一个比较小的范围,能够让人更加聚焦到需要调整的功能上。
如果现在让我给
Vue3
和 Vue2
的(开发)体验打个分的话,我会分别给出 8分
和 6分
。好啦,我们这次的
Vue3
体验就到此为止了,Vue3
给我的体验还是非常不错的!最后附上本次体验的 Demo 地址。
最后一件事 如果您已经看到这里了,希望您还是点个赞再走吧~
您的点赞是对作者的最大鼓励,也可以让更多人看到本篇文章!
【Vite + Vue3 初体验 —— Vue3 篇】如果觉得本文对您有帮助,请帮忙在 github 上点亮
star
鼓励一下吧!推荐阅读
- 防御式CSS是什么(这几点属性重点防御!)
- vue移动端下拉刷新和上拉加载组件,体积小运行快
- Vue中将json数据导出为Excel表格
- [Vue] mounted阶段获取不到dom的原因及解决方法
- h5通过ping++集成几种支付
- VUE 巧用$attrs和inheritAttrs提高组件的可扩展性
- vuex在F5时数据丢失的两种办法
- 获取当前页面显示的el-dialog元素
- 滚动定位到指定位置