vue入门2

46 阅读4分钟

这节我们用vue2来实现todoMVC,主要功能有:添加和删除事件、筛选、显示未完成数量、清空、全选等等。
因为是对vue的练习,主要是处理一些逻辑和渲染,就不写html和css了,可以在github主页里copy一下。

静态展示

我们的最终目标是实现全部的功能,待办事项都是自己添加的,需要数据的交互。在这里我们首先尝试一下静态展示写好的待办事项。
首先当然是编辑列表里要展示的事项啦,数据写在data里,每个事件都有他自己的标识(id)、内容(title)、完成情况(completed)。
其次是在template中找出展示这些事件的结构标签,和数据进行绑定。这里物品们先只处理展示部分,编辑和删除的部分下面再处理。

<script>
export default {
  data() {
    return {
      todos: [
        {
          id: 123,
          title: "测试内容1",
          completed: false
        },
        {
          id: 456,
          title: "测试内容2",
          completed: true
        }
      ]
    };
  }
}
</script>

      <ul class="todo-list">
        <!-- These are here just to show the structure of the list items -->
        <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
        <li 
		v-for="todo in todos" 
		:key="todo.id" 
		:class="{ completed: todo.completed }"
		>
          <!-- 上面配置基础的结构 -->
          <div class="view">
            <!-- 做切换操作的,使用v-moudle进行双向绑定 -->
            <input class="toggle" type="checkbox" v-model="todo.completed" />
            <!-- 任务标题,使用v-text进行绑定或者使用插值 -->
            <label v-text="todo.title"></label>
            <button class="destroy"></button>
          </div>
          <input class="edit" value="Create a TodoMVC template" />
        </li>
      </ul>

image.png

顶部操作

现在我们来处理顶部全选按钮,点击切换所有选项状态。这个很简单,找到对应的结构标签绑定点击事件,然后在methods里写一下这个方法就好了,注意要遍历所有事件。

methods: {
    toggleAll(e) {
      this.todos.forEach(todo => todo.completed = e.target.checked)
    }
  }
<input id="toggle-all" class="toggle-all" type="checkbox" @click="toggleAll" />

底部操作

下面我们来做一下底部的切换功能,展示已完成项/未完成项。
首先找到该功能的模块,也就是<ul class="filters">,可以看到三个按钮是对应三个不同的hash值的。当我们点击按钮时,hash值会随之更新,页面显示的事件选项也会更新(也就是根据hash值过滤要显示的事件)。
因此我们要对hash进行监听并根据不同的hash进行处理。在hash值发生改变时,及时更新visibility,并使用计算属性来进行过滤,筛选出符合条件的数据。
最后再加一点细节,比如点击按钮时显示类名选中、刷新页面后保持原始状态、显示待办事件个数

<script>
export default {
  data() {
    return {
      todos: [
        {
          id: 123,
          title: "测试内容1",
          completed: false
        },
        {
          id: 456,
          title: "测试内容2",
          completed: true
        },
      ],
      // 筛选标准,默认全部可见
      visibility: 'all'
    };
  },
  computed: { // 计算属性,在数据有改变时才触发
    filteredTodos () { // filter是过滤器,返回符合条件的元素
      switch(this.visibility) {
        case 'all':
          return this.todos
        case 'active':
          return this.todos.filter(todo => !todo.completed)
        case 'completed':
          return this.todos.filter(todo => todo.completed)
      }
    },
    // 待办任务数量
    remining () {
      return this.todos.filter(todo => !todo.completed).length
    }
  }
  ,
  methods: {
    toggleAll(e) {
      this.todos.forEach(todo => (todo.completed = e.target.checked));
    },
    //根据监听到的hash进行处理
	  onHashChange() {
      // 去掉hash中多余的符号
      const hash = window.location.hash.replace('#/', '')
      // 根据不同的hash进行不同的操作
      if(['all', 'active', 'completed'].includes(hash)) {
        this.visibility = hash
      } else {
        window.location.hash = ''
        this.visibility = 'all'
      }
	  }
  },
  // 在元素挂载完毕后,在mounted钩子中对hash进行监听并调用事件处理
  mounted() {
    window.addEventListener('hashchange', this.onHashChange)
  }
};
</script>

<template>
  <section class="todoapp">
    <header class="header">
      <h1>todos</h1>
      <input class="new-todo" placeholder="What needs to be done?" autofocus />
    </header>
    <!-- This section should be hidden by default and shown when there are todos -->
    <section class="main">
      <!-- 顶部按钮点击切换全部事件状态 -->
      <input id="toggle-all" class="toggle-all" type="checkbox" @click="toggleAll" />
      <label for="toggle-all">Mark all as complete</label>
      <ul class="todo-list">
        <!-- These are here just to show the structure of the list items -->
        <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
        <!-- 配置基础的结构,展示筛选后的列表 -->
        <li v-for="todo in filteredTodos" :key="todo.id" :class="{ completed: todo.completed }">
          <div class="view">
            <!-- 做切换操作的,使用v-moudle进行双向绑定 -->
            <input class="toggle" type="checkbox" v-model="todo.completed" />
            <!-- 任务标题,使用v-text进行绑定或者使用插值 -->
            <label v-text="todo.title"></label>
            <button class="destroy"></button>
          </div>
          <input class="edit" value="Create a TodoMVC template" />
        </li>
      </ul>
    </section>
    <!-- This footer should be hidden by default and shown when there are todos -->
    <footer class="footer">
      <!-- This should be `0 items left` by default -->
      <span class="todo-count">
        <!--  显示剩余任务数量 -->
        <strong>{{ remining }}</strong> item left
      </span>
      <!-- Remove this if you don't implement routing -->
      <!-- 过滤功能,需要根据hash来进行过滤 -->
      <ul class="filters">
        <li>
          <!-- 根据visiblity来设置选中状态 -->
          <a :class="{ selected: visibility === 'all' }" href="#/all">All</a>
        </li>
        <li>
          <a :class="{ selected: visibility === 'active' }" href="#/active">Active</a>
        </li>
        <li>
          <a :class="{ selected: visibility === 'completed' }" href="#/completed">Completed</a>
        </li>
      </ul>
      <!-- Hidden if no completed items are left ↓ -->
      <button class="clear-completed">Clear completed</button>
    </footer>
  </section>
</template>

添加和删除

添加任务就要在输入框绑定回车事件,并写好相应的函数,根据输入的内容生成新对象

<script>
export default {
  data() {
    return {
      todos: [
        {
          id: 123,
          title: "测试内容1",
          completed: false
        },
        {
          id: 456,
          title: "测试内容2",
          completed: true
        },
      ],
      // 筛选标准,默认全部可见
      visibility: 'all'
    };
  },
  computed: { // 计算属性,在数据有改变时才触发
    filteredTodos () { // filter是过滤器,返回符合条件的元素
      switch(this.visibility) {
        case 'all':
          return this.todos
        case 'active':
          return this.todos.filter(todo => !todo.completed)
        case 'completed':
          return this.todos.filter(todo => todo.completed)
      }
    },
    // 待办任务数量
    remining () {
      return this.todos.filter(todo => !todo.completed).length
    }
  },
  methods: {
    toggleAll(e) {
      this.todos.forEach(todo => (todo.completed = e.target.checked));
    },
    //根据监听到的hash进行处理
	  onHashChange() {
      // 去掉hash中多余的符号
      const hash = window.location.hash.replace('#/', '')
      // 根据不同的hash进行不同的操作
      if(['all', 'active', 'completed'].includes(hash)) {
        this.visibility = hash
      } else {
        window.location.hash = ''
        this.visibility = 'all'
      }
	  },
    addTodo (e) {
      const title = e.target.value.trim() // 获取输入框的内容并去空格
      if (!title) {
        return
      }
      this.todos.push({
        id: Date.now(), // 把时间戳作为id
        title,
        completed: false
      })
      e.target.value = '' // 清空输入框的内容
    },
    removeTodo (todo) {
      this.todos.splice(this.todos.indexOf(todo), 1) // 根据索引删除一个元素
    },
    clearCompleted () {
      this.todos = this.todos.filter(todo => !todo.completed) // 过滤出未完成的元素
    }
  },
  // 在元素挂载完毕后,在mounted钩子中对hash进行监听并调用事件处理
  mounted() {
    window.addEventListener('hashchange', this.onHashChange)
  }
};
</script>

<template>
  <section class="todoapp">
    <header class="header">
      <h1>todos</h1>
      <!-- 回车添加新任务 -->
      <input class="new-todo" @keyup.enter="addTodo" placeholder="What needs to be done?" autofocus />
    </header>
    <!-- This section should be hidden by default and shown when there are todos -->
    <section class="main">
      <!-- 点击顶部按钮切换全部事件状态 -->
      <input id="toggle-all" class="toggle-all" type="checkbox" @click="toggleAll" />
      <label for="toggle-all">Mark all as complete</label>
      <ul class="todo-list">
        <!-- These are here just to show the structure of the list items -->
        <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
        <!-- 配置基础的结构,展示筛选后的列表 -->
        <li v-for="todo in filteredTodos" :key="todo.id" :class="{ completed: todo.completed }">
          <div class="view">
            <!-- 做切换操作的,使用v-moudle进行双向绑定 -->
            <input class="toggle" type="checkbox" v-model="todo.completed" />
            <!-- 任务标题,使用v-text进行绑定或者使用插值 -->
            <label v-text="todo.title"></label>
            <!-- 删除按钮,点击触发,记得传入当前todo -->
            <button class="destroy" @click="removeTodo(todo)"></button>
          </div>
          <input class="edit" value="Create a TodoMVC template" />
        </li>
      </ul>
    </section>
    <!-- This footer should be hidden by default and shown when there are todos -->
    <footer class="footer" v-show="todos.length">
      <!-- This should be `0 items left` by default -->
      <span class="todo-count">
        <!--  显示剩余任务数量,注意复数细节 -->
        <strong>{{ remining }} {{ remining > 1 ? 'items' : 'item' }}</strong> left
      </span>
      <!-- Remove this if you don't implement routing -->
      <!-- 过滤功能,需要根据hash来进行过滤 -->
      <ul class="filters">
        <li>
          <!-- 根据visiblity来设置选中状态 -->
          <a :class="{ selected: visibility === 'all' }" href="#/all">All</a>
        </li>
        <li>
          <a :class="{ selected: visibility === 'active' }" href="#/active">Active</a>
        </li>
        <li>
          <a :class="{ selected: visibility === 'completed' }" href="#/completed">Completed</a>
        </li>
      </ul>
      <!-- Hidden if no completed items are left ↓ -->
      <button class="clear-completed" @click="clearCompleted">Clear completed</button>
    </footer>
  </section>
</template>

<style>
@import "https://unpkg.com/todomvc-app-css@2.4.1/index.css";
</style>

最后呈现出的结果就是这样啦:

image.png