一个输入框Demo带你理解vue3新特性【组件抽离版本】(Vue3传值通信)

 1、Todos.vue

<template>
  <div>
    <!-- 新增todo -->
    <!-- v-model绑定的值是todo-title -->
    <EditTodo
      v-model:todo-title="newTodo"
      @keyup.enter="addTodo"
      autofocus
      placeholder="新增今日待办"
      autocomplete="off"
    >
    </EditTodo>

    <!-- todo列表 -->
    <ul>
      <TodoItem
        v-for="todo in filtedTodos"
        :key="todo.id"
        :todo="todo"
        v-model:edited-todo="editedTodo"
        @remove-todo="removeTodo"
      ></TodoItem>
    </ul>
    <!-- 过滤 -->
    <Filter :items="filterItems" v-model="visibility"></Filter>
  </div>
</template>

<script>
import { reactive, toRefs, computed, watchEffect } from "vue";
import TodoItem from './TodoItem.vue'
import Filter from './Filter.vue'
// 过滤器
const filters = {
  all(todos) {
    return todos;
  },
  active(todos) {
    return todos.filter((todo) => !todo.completed);
  },
  completed(todos) {
    return todos.filter((todo) => todo.completed);
  },
};

// 缓存操作
const todoStorage = {
  fetch() {
    let todos = JSON.parse(localStorage.getItem("vue3-todos") || "[]");
    todos.forEach((todo, index) => {
      todo.id = index + 1;
    });
    return todos;
  },
  save(todos) {
    localStorage.setItem("vue3-todos", JSON.stringify(todos));
  },
};

export default {
  components:{
    TodoItem,
    Filter
  },
  setup() {
    const state = reactive({
      newTodo: "",
      todos: todoStorage.fetch(), //获取是否有缓存数据
      editedTodo: null, //正在编辑的todo
      filterItems: [
        {
          label:'All',value:'all'
        },
        {
          label:'Active',value:'active'
        },
        {
          label:'Completed',value:'completed'
        },
      ],
      visibility: "all",
      filtedTodos: computed(() => {
        return filters[state.visibility](state.todos);
      }),
    });

    function addTodo() {
      state.todos.push({
        id: state.todos.length + 1,
        title: state.newTodo,
        completed: false,
      });
      state.newTodo = "";
    }

    function removeTodo(todo) {
      state.todos.splice(state.todos.indexOf(todo), 1);
    }

    
    watchEffect(() => {
      // 执行一些操作,其中必须含有响应式变量(state.todos是响应式变量)
      todoStorage.save(state.todos);
    });
    return {
      ...toRefs(state),
      addTodo,
      removeTodo,
    };
  },
  
};
</script>

<style scoped>

</style>

2、EditTodo.vue

<template>
    <!-- v-bind="$attrs" 承接所有的props -->
  <input
    type="text"
    :value="todoTitle"
    @input="onInputChange"
    v-bind="$attrs" 
  />
</template>

<script>
import { reactive, toRefs } from "vue";

export default {
  emits: {
    onInputChange: null,//click事件没有检验
  },
  props: {
    todoTitle: {
      type: String,
      default: "",
    },
  },
  setup(props,{emit}) {
    const state = reactive({
      count: 0,
    });
    function onInputChange(e){
        emit('update:todoTitle',e.target.value)
    }

    return {
      ...toRefs(state),
      onInputChange,
    };
  },
};
</script>

<style lang="scss" scoped></style>

3、TodoItem.vue

<template>
  <li :class="{ completed: todo.completed, editing: todo === editedTodo }">
    <!-- 绑定完成状态 -->
    <div class="view">
      <input type="checkbox" v-model="todo.completed" />
      <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
      <button @click="removeTodo(todo)">X</button>
    </div>
    <!-- 编辑待办 -->
    <EditTodo
      class="edit"
      v-model:todo-title="todo.title"
      v-todo-focus="todo === editedTodo"
      @blur="doneEdit(todo)"
      @keyup.enter="doneEdit(todo)"
      @keyup.escape="cancelEdit(todo)"
    >
    </EditTodo>
  </li>
</template>

<script>
import { reactive, toRefs } from "vue";

export default {
  props: {
    todo: {
      type: Object,
      required: true,
    },
    editedTodo: {
      type: Object,
    },
  },
  emits:["remove-todo","update:edited-todo"],
  setup(props, { emit }) {
    const state = reactive({
      beforeEiditCache: "", // 缓存编辑前的title
    });

    function removeTodo(todo) {
      emit("remove-todo", todo);
    }

    function editTodo(todo) {
      state.beforeEiditCache = todo.title;
      emit("update:edited-todo", todo);
    }

    function cancelEdit(todo) {
      todo.title = state.beforeEiditCache;
      emit("update:edited-todo", null);
    }

    function doneEdit(todo) {
      emit("update:edited-todo", null);
    }

    return {
      ...toRefs(state),
      editTodo,
      cancelEdit,
      doneEdit,
      removeTodo,
    };
  },
  //   自定义指令
  directives: {
    "todo-focus": (el, { value }) => {
      if (value) {
        el.focus();
      }
    },
  },
};
</script>

<style scoped>
.completed label {
  text-decoration: line-through;
}
.edit,
.editing .view {
  display: none;
}
.view,
.editing .edit {
  display: block;
}
</style>

4、Filter.vue

<template>
  <p class="filters">
    <span
      v-for="item in items"
      :key="item.value"
      @click="$emit('update:modelValue', item.value)"
      :class="{ seleted: modelValue === item.value }"
      >{{ item.label }}</span
    >
  </p>
</template>

<script>
import { reactive, toRefs } from "vue";

export default {
  props: ["items", "modelValue"],
  emits: ["update:modelValue"],
};
</script>

<style scoped>
.filters > span {
  padding: 2px 4px;
  margin-right: 4px;
  border: 1px solid transparent;
}
.filters > span.seleted {
  border-color: rgba(173, 47, 47, 0.2);
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值