导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


初始化项目

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

Untitled

Vuex

工作流

标准流程:

所需成员:

  1. actionTypes action 类型
  2. actions 调用 mutation 的方法
  3. mutations 更改 state 的方法
  4. state 中央数据管理池
  5. store出口 action、mutations、state 统一到仓库里进行管理

Untitled

文件集合

Untitled

组件划分

Untitled

vue文件注意点

  1. <script> 要加 <lang="ts">
  2. export default {}defineComponent 包一下,才有TS的类型推断和提示
    1. 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>

type vs interface

各种类型定义

// 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: []
}

Vuex 设置

定义actionTypes

actionTypes.ts

export const SET_TODO: string = 'SET_TODO';

定义 actions

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); // 没有返回值
  }
}

定义 mutations

import { IState, ITodo } from "@/typings";
import { SET_TODO } from "./actionTypes";

export default {
  [SET_TODO](state: IState, todo: ITodo): void {
    state.list.unshift(todo);
  }
}

编写编辑 todo 相关的 composition api (hooks)

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
}

TodoInput 中使用 hooks

<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>

在 hooks 中再写个 useLocalStorage 的 hooks

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
  }
}

保存todo-list列表

actionTypes 中定义类型

export const SET_TODO: string = 'SET_TODO';
export const SET_TODO_LIST: string = 'SET_TODO_LIST'; // 新增类型

hooks 中完成 setTodoList 方法

...

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
  }
}

actions 和 mutations

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;
  },
}

App.vue 中使用

<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>

TodoList/index.vue 定义组件

<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>

App中提供给 TodoList 组件数据

<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>

TodoItem 编写

<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>

完善 useTodo 的 removeTodo、setStatus、setDoing 方法

actionTypes.ts 定义类型

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';