导航
vue create todo-list
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to inve
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to inve
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to inve
? Check the features needed for your project: Choose Vue version, Babel, TS, Vuex
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)?
Yes
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No

标准流程:
所需成员:



<script> 要加 <lang="ts">export default {} 用 defineComponent 包一下,才有TS的类型推断和提示
export default defineComponent({})<template>
<div class="wrapper">
<todo-input></todo-input>
<todo-list></todo-list>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import TodoInput from './components/TodoInput/index.vue';
import TodoList from './components/TodoList/index.vue';
export default defineComponent({
name: 'App',
components: {
TodoInput,
TodoList
}
});
</script>
<template>
<input
type="text"
v-model="todoValue"
@keyup="setTodoValue"
>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: 'TodoInput',
setup() {
const todoValue = ref<string>(''); // 类型注解 加泛型
const setTodoValue = (e: KeyboardEvent): void => {
if (e.keyCode == 13 && todoValue.value.trim().length) {
// 设置数据
todoValue.value = '';
}
}
return {
todoValue,
setTodoValue
}
}
});
</script>
interface A extends interface Bclass A implements interface Btypings/index.ts 文件// todo对象
interface ITodo {
id: number;
content: string;
status: TODO_STATUS;
}
// vuex state对象
interface IState {
list: ITodo[];
}
// todo status 状态
enum TODO_STATUS {
WILLDO = 'willdo',
DOING = 'doing',
FINISHED = 'finished'
}
export {
ITodo,
IState,
TODO_STATUS
}
import { IState } from "@/typings";
export default <IState> { // 泛型
list: []
}
actionTypes.ts
export const SET_TODO: string = 'SET_TODO';
import { IState, ITodo } from "@/typings";
import { Commit } from "vuex";
import { SET_TODO } from "./actionTypes";
interface ICtx {
commit: Commit, // Vuex定义的
state: IState
}
export default {
// action 名就是上边 actionTypes 中定义的 SET_TODO
[SET_TODO]({ commit }: ICtx, todo: ITodo): void {
// 调用的也是 mutations 中同名的 SET_TODO 方法
commit(SET_TODO, todo); // 没有返回值
}
}
import { IState, ITodo } from "@/typings";
import { SET_TODO } from "./actionTypes";
export default {
[SET_TODO](state: IState, todo: ITodo): void {
state.list.unshift(todo);
}
}
hooks/index.ts
import { SET_TODO } from "@/store/actionTypes"
import { ITodo, TODO_STATUS } from "@/typings"
import { Store, useStore } from "vuex";
// 定义 useTodo 函数的类型
export interface IUseTodo {
setTodo: (value: string) => void;
setTodoList: () => void;
removeTodo: () => void;
setStatus: () => void;
setDoing: () => void;
}
function useTodo(): IUseTodo {
// setup 中 必须使用 useStore 才行,不能像vue2中那样从 vuex 中直接引入
const store: Store<any> = useStore(); // 项目唯一一处要用到 any 的地方就是 Store
function setTodo(value: string) {
// 创建 todo
const todo: ITodo = {
id: new Date().getTime(),
content: value,
status: TODO_STATUS.WILLDO
}
// 调用 action: SET_TODO
store.dispatch(SET_TODO, todo);
}
function setTodoList() {
}
function removeTodo() {
}
function setStatus() {
}
function setDoing() {
}
return {
setTodo,
setTodoList,
removeTodo,
setStatus,
setDoing
}
}
export {
useTodo
}
<template>
<input
type="text"
v-model="todoValue"
@keyup="setTodoValue"
>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { IUseTodo, useTodo } from '../../hooks'; // 引入 hooks useTodo
export default defineComponent({
name: 'TodoInput',
setup() {
const todoValue = ref<string>(''); // 类型注解 加泛型
const { setTodo }: IUseTodo = useTodo(); // 从 useTodo 中解构出 setTodo
const setTodoValue = (e: KeyboardEvent): void => {
if (e.keyCode == 13 && todoValue.value.trim().length) {
// 设置数据
setTodo(todoValue.value);
todoValue.value = '';
}
}
return {
todoValue,
setTodoValue
}
}
});
</script>
import { SET_TODO } from "@/store/actionTypes"
import { ITodo, TODO_STATUS } from "@/typings"
import { Store, useStore } from "vuex";
// 定义 useTodo 函数的类型
export interface IUseTodo {
setTodo: (value: string) => void;
setTodoList: () => void;
removeTodo: () => void;
setStatus: () => void;
setDoing: () => void;
}
// 定义 useLocalStorage 函数的类型
interface IUseLocalStorage {
getLocalList: () => ITodo[];
setLocalList: (todoList: ITodo[]) => void;
}
function useTodo(): IUseTodo {
// setup 中 必须使用 useStore 才行,不能像vue2中那样从 vuex 中直接引入
const store: Store<any> = useStore(); // 项目唯一一处要用到 any 的地方就是 Store
const { getLocalList, setLocalList }: IUseLocalStorage = useLocalStorage();
function setTodo(value: string) {
// 创建 todo
const todo: ITodo = {
id: new Date().getTime(),
content: value,
status: TODO_STATUS.WILLDO
}
// 调用 action: SET_TODO
store.dispatch(SET_TODO, todo);
setLocalList(store.state.list);
}
...
}
export {
useTodo
}
function useLocalStorage(): IUseLocalStorage {
function getLocalList(): ITodo[] {
return JSON.parse(localStorage.getItem('todoList') || '[]');
}
function setLocalList(todoList: ITodo[]): void {
localStorage.setItem('todoList', JSON.stringify(todoList));
}
return {
getLocalList,
setLocalList
}
}
export const SET_TODO: string = 'SET_TODO';
export const SET_TODO_LIST: string = 'SET_TODO_LIST'; // 新增类型
...
function useTodo(): IUseTodo {
// setup 中 必须使用 useStore 才行,不能像vue2中那样从 vuex 中直接引入
const store: Store<any> = useStore(); // 项目唯一一处要用到 any 的地方就是 Store
const { getLocalList, setLocalList }: IUseLocalStorage = useLocalStorage();
const todoList: ITodo[] = getLocalList(); // 从 localStorage 中获取最新 todoList
function setTodo(value: string) {
...
}
function setTodoList() { // 读取 localStorage 最新 list 赋值给 vuex 的 todo-list
store.dispatch(SET_TODO_LIST, todoList);
}
function removeTodo() {
}
function setStatus() {
}
function setDoing() {
}
return {
setTodo,
setTodoList,
removeTodo,
setStatus,
setDoing
}
}
export {
useTodo
}
function useLocalStorage(): IUseLocalStorage {
function getLocalList(): ITodo[] {
return JSON.parse(localStorage.getItem('todoList') || '[]');
}
function setLocalList(todoList: ITodo[]): void {
localStorage.setItem('todoList', JSON.stringify(todoList));
}
return {
getLocalList,
setLocalList
}
}
import { IState, ITodo } from "@/typings";
import { Commit } from "vuex";
import { SET_TODO, SET_TODO_LIST } from "./actionTypes";
interface ICtx {
commit: Commit, // Vuex定义的
state: IState
}
export default {
...
[SET_TODO_LIST]({ commit }: ICtx, todoList: ITodo[]): void {
commit(SET_TODO_LIST, todoList);
},
}
import { IState, ITodo } from "@/typings";
import { SET_TODO, SET_TODO_LIST } from "./actionTypes";
export default {
...
[SET_TODO_LIST](state: IState, todoList: ITodo[]): void {
state.list = todoList;
},
}
<template>
<div class="wrapper">
<todo-input></todo-input>
<todo-list></todo-list>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted } from 'vue';
import TodoInput from './components/TodoInput/index.vue';
import TodoList from './components/TodoList/index.vue';
import { IUseTodo, useTodo } from './hooks';
export default defineComponent({
name: 'App',
components: {
TodoInput,
TodoList
},
setup() {
const { setTodoList }: IUseTodo = useTodo();
onMounted(() => { // 页面挂载时设置todo-list
setTodoList();
});
}
});
</script>
<style>
</style>
<template>
<div>
<todo-item
v-for="item of todoList"
:key="item.id"
:item="item"
@removeTodo="removeTodo"
@setStatus="setStatus"
@setDoing="setDoing"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import TodoItem from './Item.vue';
import { ITodo } from '../../typings';
import { IUseTodo, useTodo } from '../../hooks';
export default defineComponent({
name: 'TodoList',
props: {
// PropType: Vue 提供的 类型断言
todoList: Array as PropType<ITodo[]>
},
components: {
TodoItem
},
setup(props) {
const {
removeTodo,
setStatus,
setDoing
}: IUseTodo = useTodo();
return {
removeTodo,
setStatus,
setDoing
}
}
});
</script>
<template>
<div class="wrapper">
<todo-input></todo-input>
<todo-list
:todoList="todoList"
></todo-list>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted } from 'vue';
import { Store, useStore } from 'vuex';
import TodoInput from './components/TodoInput/index.vue';
import TodoList from './components/TodoList/index.vue';
import { IUseTodo, useTodo } from './hooks';
export default defineComponent({
name: 'App',
components: {
TodoInput,
TodoList
},
setup() {
const { setTodoList }: IUseTodo = useTodo();
const store: Store<any> = useStore();
onMounted(() => { // 页面挂载时设置todo-list
setTodoList();
});
return {
// computed 计算属性 包裹, 让 list 响应式
todoList: computed(() => store.state.list)
}
}
});
</script>
<template>
<div>
<input
type="checkbox"
:checked="item.status === FINISHED"
@click="setStatus(item.id)"
>
<span
:class="item.status === FINISHED ? 'line-through' : ''"
>
{{ item.content }}
</span>
<!-- 删除此todo -->
<button @click="removeTodo(item.id)">删除</button>
<!-- 切换未完成 todo 的状态为 正在做 or 马上做 -->
<button
v-if="item.status !== FINISHED"
@click="setDoing(item.id)"
:class="item.status === DOING ? 'doing' : 'willdo'"
>
{{ item.status === DOING ? '正在做...' : '马上做' }}
</button>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { ITodo, TODO_STATUS } from "../../typings";
interface IStatusState {
DOING: TODO_STATUS;
WILLDO: TODO_STATUS;
FINISHED: TODO_STATUS;
}
export default defineComponent({
name: 'TodoItem',
props: {
item: Object as PropType<ITodo>
},
setup(props, { emit }) {
// 接口 或者 泛型都行
// const statusState: IStatusState = {
// DOING: TODO_STATUS.DOING,
// WILLDO: TODO_STATUS.WILLDO,
// FINISHED: TODO_STATUS.FINISHED
// };
const statusState = <IStatusState> {
DOING: TODO_STATUS.DOING,
WILLDO: TODO_STATUS.WILLDO,
FINISHED: TODO_STATUS.FINISHED
};
const removeTodo = (id: number): void => {
emit('removeTodo', id);
}
const setStatus = (id: number): void => {
emit('setStatus', id);
}
const setDoing = (id: number): void => {
emit('setDoing', id);
}
return {
...statusState,
removeTodo,
setStatus,
setDoing
}
}
});
</script>
<style scoped>
.line-through {
text-decoration: line-through;
}
.doing {
background-color: #ededed;
color: #999;
}
.willdo {
background-color: orange;
color: #fff;
}
</style>
export const SET_TODO: string = 'SET_TODO';
export const SET_TODO_LIST: string = 'SET_TODO_LIST';
export const REMOVE_TODO: string = 'REMOVE_TODO';
export const SET_STATUS: string = 'SET_STATUS';
export const SET_DOING: string = 'SET_DOING';