Vue学习笔记(1) 根据MDN教程,创建一个To-Do List


在这里插入图片描述

主要学习与参考:

MDN Web Docs:开始使用 Vue
Vue2.x文档
github项目地址
运行中的实时版本

笔记只是对自己学习的一个记录,并没有完整记录下做这个To-Do List的全部过程。

安装Vue

为了让使用Vue构建应用程序更容易,有一个CLI来简化开发过程。

npm install --global @vue/cli

安装之后,要初始化一个新项目,可以在要创建项目的目录中打开一个终端,并运行

vue create <project-name>

CLI将会给你一个可以使用的项目配置列表。

初始化一个新项目

使用Vue脚手架工具去创建一个新的应用框架并在上面搭建我们的应用。

  1. 在终端,用cd命令进入你想要创建示例的文件夹,然后执行 vue create moz-todo-vue.
  2. 使用方向键然后 Enter 选择 “Manually select features(手动选择功能)” 选项.
  3. 你会看到的第一个菜单允许你选择你想要包含在你的项目中的功能。确保 “Babel” 和 “Linter / Formatter” 这两项是被选中的. 如果他们没有被选中,使用方向键切换,空格键选中,一旦他们被选中按下 Enter 继续进行。
  4. 接下来你要为linter / formatter选择一个配置。切换选中"Eslint with error prevention only"然后再次按下 Enter 。这样辅助我们捕获常见的并且不自以为是的错误。
  5. 然后你会被询问需要那种自动化的 lint,选择 “Lint on save”,这样我们在项目中保存文件的时候就会自动检查错误。按下 Enter 继续。
  6. 接着你将需要选择把配置文件放在哪里。“In dedicated config files” 这个选项会把你的配置文件比如 ESLint 单独放在一个文件里。另一个选项 “In package.json” 则会把配置放进项目的 package.json 文件里。选择 “In dedicated config files” 然后使劲敲下 Enter。
  7. 最后会问你,是否选择把本次的选择作为将来的一个预设配置(Save this as a preset for future projects?),这个就安全由你自己决定了。如果你想把本次的配置作为一个预设配置并且以后想再次使用的话,按下 y , 否则按下 n。

项目结构

在这里插入图片描述
列举一些比较重要的:(因为我创建项目的时候一路回车,有些文件没有生成)

  • .eslintrc.js: 这个是 eslint 的配置文件,可以通过它来管理你的校验规则。
  • babel.config.js: 这个是 Babel 的配置文件,可以在开发中使用 JavaScript 的新特性,并且将其转换为在生产环境中可以跨浏览器运行的旧语法代码。你也可以在这个里配置额外的 babel 插件。
  • .browserslistrc: 这个是 Browserslist 的配置文件,可以通过它来控制需要对哪些浏览器进行支持和优化。
    在这里插入图片描述
  • public: 这个目录包含一些在 Webpack 编译过程中没有加工处理过的文件(有一个例外:index.html 会有一些处理)。
    • favicon.ico: 这个是项目的图标,当前就是一个 Vue 的 logo。
    • index.html: 这是应用的模板文件,Vue 应用会通过这个 HTML 页面来运行,也可以通过 lodash 这种模板语法在这个文件里插值。
      注意:这个不是负责管理页面最终展示的模板,而是管理 Vue 应用之外的静态 HTML 文件,一般只有在用到一些高级功能的时候才会修改这个文件。
      在这里插入图片描述
  • src: 这个是 Vue 应用的核心代码目录
    • main.js:这是应用的入口文件。目前它会初始化 Vue 应用并且制定将应用挂载到 index.html 文件中的哪个 HTML 元素上。通常还会做一些注册全局组件或者添额外的 Vue 库的操作。
    • App.vue:这是 Vue 应用的根节点组件,往下看可以了解更多关注 Vue 组件的信息。
    • components:这是用来存放自定义组件的目录,目前里面会有一个示例组件。
    • assets:这个目录用来存放像 CSS 、图片这种静态资源,但是因为它们属于代码目录下,所以可以用 webpack 来操作和处理。意思就是你可以使用一些预处理比如 Sass/SCSS 或者 Stylus。
      注意:根据创建项目时候的一些配置项,可能会有一些其他的预设目录(例如,如果你选择了路由配置,会看到一个 views 的文件夹)

打开main.js

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
//render: h => h(App) 是下面内容的缩写:
render: function (createElement) {
    return createElement(App);
}
//进一步缩写为(ES6 语法):
render (createElement) {
    return createElement(App);
}
//再进一步缩写为:
render (h){
    return h(App);
}
//按照 ES6 箭头函数的写法,就得到了:
render: h => h(App);

打开index.html我们就知道挂载到了哪里

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

.vue 文件(单文件组件)

组件是构建 Vue 应用中非常重要的一部分。组件可以把一个很大的应用程序拆分为独立创建和管理的不相关区块,然后彼此按需传递数据,这些小的代码块可以方便更容易的理解和测试。

Vue 把模板、相关脚本和 CSS 一起整合放在 .vue 结尾的一个单文件中。这些文件最终会通过 JS 打包工具(例如 Webpack)处理。

App.vue

打开 App.vue 文件,可以看到有三部分组成 <template>, <script>, and <style>,分别包含了组件的模板、脚本和样式相关的内容。所有的单文件组件都是这种类似的基本结构。

  • <template> 包含了所有的标记结构和组件的展示逻辑。template 可以包含任何合法的 HTML,以及一些 Vue 特定的语法。

通过设置 <template>标签的 lang 属性,例如可以通过设置 <template lang="pug"> 就可以在使用 Pub 模板来替代标准 HTML。

  • <script> 包含组件中所有的非显示逻辑,最重要的是, <script>标签需要默认导出一个 JS 对象。该对象是您在本地注册组件、定义属性、处理本地状态、定义方法等的地方。在构建阶段这个对象会被处理和转换(包含 template 模板)成为一个有 render() 函数的 Vue 组件。

对于 App.vue,我们的默认导出将组件的名称设置为 App ,并通过将组件添加到 components 属性中来注册它。以这种方式注册组件时,就是在本地注册
本地注册的组件只能在注册它们的组件内部使用,因此您需要将其导入并注册到使用它们的每个组件文件中。这对于拆包 / tree shaking(译者注:一种减小包体积优化方式)很有用,因为并不是应用程序中的每个页面都需要每个组件。
App.vue中用到的子组件为ToDoItem.vue和ToDoForm.vue

<script>
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm";
import uniqueId from "lodash.uniqueid";

export default {
  name: "App",
  components: {
    ToDoItem,
    ToDoForm,
  },
</script>

组件的 CSS 应该写在 <style>标签里,如果你添加了 scoped 属性,形如 <style scoped> ,Vue 会把样式的范围限制到单文件组件的内容里。这个类似 CSS-in-JS 的解决方案,不过允许你书写文本格式的 CSS了。

本地运行程序

Vue CLI 带有内置的开发服务器。
CLI 会以 npm 脚本的形式将 serve 命令添加到项目的 package.json 文件中。
在这里插入图片描述
运行 npm run serve或者运行vue ui进入可视化项目管理工具再运行程序。

正式开始做To-Do-List

1. 创建第一个Vue组件

**目标:**创建一个Vue组件,将其渲染到另一个组件中,使用props将数据传递到组件中,并保存其状态。
创建第一个组件,它将显示一个单一的待办事项。我们将用它来建立我们的待办事项列表。

在moz-todo-vue/src/components目录下,创建一个ToDoItem.vue的新文件。
打开该文件,通过在文件顶部添加<template></template>来创建组件的模板部分。
在你的模板部分下面创建一个<script></script>部分。在<script>标签内,添加一个默认导出对象export default {},这是你的组件对象。

<template> </template>
<script>
  export default {};
</script>

Vue模板目前只允许一个根元素–一个元素需要包裹模板内的所有内容(Vue 3 发布后会改变这种情况)。我们将为该根元素使用一个<div>

在你的组件模板中添加一个空的<div>
在那个<div>里面,让我们添加一个checkbox和一个对应的label。给复选框添加一个id,并添加一个for属性,将复选框映射到标签上,如下图所示。

<template>
  <div>
    <input type="checkbox" id="todo-item" checked="false" />
    <label for="todo-item">My Todo Item</label>
  </div>
</template>

1.1 在应用程序中使用TodoItem组件

在APP.vue中引入组件并注册

import ToDoItem from './components/ToDoItem.vue';

export default {
  name: 'app',
  components: {
    ToDoItem
  }
};

要在应用程序中实际展示ToDoItem组件,你需要在<template>模板内添加一个<to-do-item>/to-do-item>元素。

<div id="app">
  <h1>To-Do List</h1>
  <ul>
    <li>
      <to-do-item></to-do-item>
    </li>
  </ul>
</div>

请注意,组件文件名及其在JavaScript中的表示方式总是用大驼峰命名(例如ToDoList),而等价的自定义元素总是用连字符小写(例如<to-do-list>)。

1.2 使用props使组件变得动态

通过 Prop 向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
在ToDoItem.vue中添加props

<script>
  export default {
    props: {
      label: { required: true, type: String },
      done: { default: false, type: Boolean }
    }
  };
</script>

通过在组件对象中定义这些道具,我们现在可以在模板中使用这些变量值。

<template>
  <div>
    <input type="checkbox" id="todo-item" checked="false" />
    <label for="todo-item">{{label}}</label>
  </div>
</template>

{}是 Vue 中的一种特殊模板语法,它允许我们在模板中打印类中定义的 JavaScript 表达式的结果,包括值和方法。重要的是要知道{}中的内容是以文本而不是 HTML 的形式显示的。

在 App.vue 文件中,向< to-do-item > </to-do-item > 组件添加一个 label prop,就像一个常规的 HTML 属性:

<to-do-item label="My ToDo Item"></to-do-item>

data是你可以在组件中管理本地状态的地方,并且具有以下结构:

data() {
  return {
    key: value
  }
}

data 属性是一个函数而不是一个对象
这是为了在运行时保持组件的每个实例的数据值的唯一性——函数是为每个组件实例分别调用的。如果仅将数据声明为对象,则该组件的所有实例将共享相同的值。这是 Vue 注册组件的副作用,也是我们不想要的。

通过 this 从内部数据访问组件的props和其他属性

向 ToDoItem 组件添加一个数据属性。
我们将其命名为 isDone,其值为 This.done。

export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean }
  },
  data() {
    return {
      isDone: this.done
    };
  }
};

Vue直接将所有的props(以及其它的属性,data,methods,computed等等)绑定到了组件实例,所以我们不需要用this.props.done来访问。
接下来把isDone属性附到组件中。
v-bind:bind JavaScript expressions to HTML elements and components
v-bind:attribute="expression"or 简写形式:attribute="expression"

在我们的ToDoItem组件的复选框中,使用 v-bind 将 isDone 属性映射到 < input > 元素的 checked 属性。
以下两个写法是等价的:

<input type="checkbox" id="todo-item" v-bind:checked="isDone" />
<input type="checkbox" id="todo-item" :checked="isDone" />

更常用的是简写语法
向 App.vue 中的 ToDoItem 调用传递: done = “ true”
这里也需要使用v-bind语法,否则 true 将作为字符串传递。

<template>
  <div id="app">
    <h1>My To-Do List</h1>
    <ul>
      <li>
        <to-do-item label="My ToDo Item" :done="true"></to-do-item>
      </li>
    </ul>
  </div>
</template>

这样就实现了从App.vue向子组件传递数据,子组件中的props负责接受,再用子组件中的data属性控制html元素的属性。

1.3 unique id

目前只能向页面中添加一个 ToDoList 组件,因为 id 是硬编码的。
在组件的data中,通过编程方式设置id。
可以使用 lodash 包的 uniqueid ()方法来帮助保持索引的唯一性。
这个包导出一个函数,该函数接受一个字符串,并在前缀的末尾追加一个唯一的整数。
这将足以保持组件 id 的唯一性。
在shell中安装

npm install --save lodash.uniqueid

Add the following line at the top of ToDoItem.vue’s < script > element:

import uniqueId from 'lodash.uniqueid';

接下来,在数据属性中添加一个 id 字段,这样组件对象看起来就像这样

import uniqueId from 'lodash.uniqueid';
export default {
  props: {
    label: { required: true, type: String },
    done: { default: false, type: Boolean }
  },
  data() {
    return {
      isDone: this.done,
      id: uniqueId('todo-')
    };
  }
};

(uniqueId ()返回指定的前缀 "todo-"加上一个惟一的字符串)

2. 利用v-for指令渲染待办事项列表

首先做一个代码的重构
在ToDoItem的script部分添加一个新的prop id 到 ToDoItem 组件。
把id作为ToDoItem的一个prop,而不是在每个checkbox里生成它。

export default {
    props: {
        label: {required: true, type: String},
        done: {default: false, type: Boolean},
        id: {required: true, type: String}
    },
    data() {
        return {
           isDone : this.done,
        }
    },
}

v-for类似js中的for…in循环,v-for=“item in items”
iterms是你要迭代的列表, item 是数组中当前元素的引用。
APP.vue中用<li>的形式展示每一个待办事项,通过每个待办事项传递数据到其对应的ToDoItem组件。
在 App.vue 组件中将 item.id 作为一个prop传递给 ToDoItem 组件。

<template>
  <div id="app">
    <h1>My To-Do List</h1>
    <ul>
      <li v-for="item in ToDoItems" :key="item.id">
        <to-do-item :label="item.label" :done="item.done" :id="item.id"></to-do-item>
      </li>
    </ul>
  </div>
</template>

key属性和v-for一起使用,用来帮助Vue标识列表中的元素。
添加 id 字段到 ToDoItems 数组的每一个元素中, 并且将他们赋值为 uniqueId(‘todo-’),这样就为每个待办事项数据生成了独特的key。

import ToDoItem from './components/ToDoItem.vue';
import uniqueId from 'lodash.uniqueid'

export default {
  name: 'app',
  components: {
    ToDoItem
  },
  data() {
    return {
      ToDoItems: [
        { id: uniqueId('todo-'), label: 'Learn Vue', done: false },
        { id: uniqueId('todo-'), label: 'Create a Vue project with the CLI', done: true },
        { id: uniqueId('todo-'), label: 'Have fun', done: true },
        { id: uniqueId('todo-'), label: 'Create a to-do list', done: false }
      ]
    };
  }
};

3. 在子组件中通过表单获取待办事项数据,发到App.vue

完成1,2两节后,待办事项列表可以展示,但是不能更新。只能在APP.vue中手动修改每个待办事项数据的label和完成情况。

新建文件 ToDoForm.vue,用来添加新的待办项。

代码如下:

<template>
  <form @submit.prevent="onSubmit">
    <h2 class="label-wrapper">
      <label for="new-todo-input" class="label__lg">
        What needs to be done?
      </label>
    </h2>
    <input
      type="text"
      id="new-todo-input"
      name="new-todo"
      autocomplete="off"
      v-model.lazy.trim="label"
      class="input__lg"
    />
    <button type="submit" class="btn btn__primary btn__lg">
      Add
    </button>
  </form>
</template>

<script>
export default {
  methods: {
    // 让 ToDoForm 发出一个传递数据的自定义事件,并让 App 侦听数据
    onSubmit() {
      if (this.label === "") {
        return;
      }
      this.$emit("todo-added", this.label); //  发送自定义的todo-added 事件
      this.label = "";
    },
  },
  data() {
    return {
      label: "",
    };
  },
};
</script>

在这里插入图片描述
如果填写并点击“添加”按钮,页面将把表单发送回服务器,但这不是我们真正想要的。
我们真正想做的是在submit事件上运行一个方法,它将把新的待办事项添加到定义在App内部的ToDoItem数据列表中。要做到这一点,我们需要向组件实例添加一个方法,即上面代码中的onSubmit()方法
通过v-on语法实现这一点
v-on:event="method"也可以简写为@event="method"
通过简写语法,为form的submit事件绑定上onSubmit()

运行此操作时,应用程序仍会将数据发布到服务器,从而导致刷新。由于本例中我们在客户机上进行所有处理,因此没有服务器来处理postback。我们也会在页面刷新时丢失所有的本地状态。为了防止浏览器发布到服务器,我们需要阻止事件的默认操作(在原版JavaScript中使用event.preventDefault())。

Vue有一种称为event modifiers的特殊语法,可以在模板中为我们处理这个问题。

使用的格式为@event.modifier

  • .stop: Stops the event from propagating. Equivalent to Event.stopPropagation() in regular JavaScript events.
  • .prevent: Prevents the event’s default behavior. Equivalent to Event.preventDefault().
  • .self: Triggers the handler only if the event was dispatched from this exact element.
  • {.key}: Triggers the event handler only via the specified key. MDN has a list of valid key - values; multi-word keys just need to be converted to kebab case (e.g. page-down).
  • .native: Listens for a native event on the root (outer-most wrapping) element on your component.
  • .once: Listens for the event until it’s been triggered once, and then no more.
  • .left: Only triggers the handler via the left mouse button event.
  • .right: Only triggers the handler via the right mouse button event.
  • .middle: Only triggers the handler via the middle mouse button event.
  • .passive: Equivalent to using the { passive: true } parameter when creating an event listener in vanilla JavaScript using addEventListener().
    本例中使用.prevent去阻止浏览器默认的提交动作。
<form @submit.prevent="onSubmit">

为了获取表单的输入,需要使用v-model在表单控件或者组件上创建双向绑定。
v-model works across all the various input types, including check boxes, radios, and select inputs.

修饰符:
.lazy - 取代 input 监听 change 事件
.number - 输入字符串转为有效的数字
.trim - 输入首尾空格过滤

v-model.lazy.trim="label"

对于文本输入,此同步使用输入事件进行。通常,这意味着Vue在每次输入后都会同步数据。.lazy修饰符使v-model改用change事件。(This means that Vue will only sync data when the input loses focus or the form is submitted.)只需要最终的数据是更为合理的。

接下来我们需要做的是将新创建的待办事项传递给我们的App组件。为此,我们可以让ToDoForm发出一个传递数据的自定义事件,并让App侦听它。子组件可以发出一个事件,该事件可以通过v-on进行监听。

在onSubmit()方法中

this.$emit("todo-added", this.label); //  发送自定义的todo-added 事件

It’s important to know that event handlers are case sensitive and cannot include spaces. Vue templates also get converted to lowercase, which means Vue templates cannot listen for events named with capital letters.

App.vue中负责监听的代码如下:

<to-do-form @todo-added="addToDo"></to-do-form>

该监听器在事件触发时调用addToDo()方法

methods: {
  addToDo(toDoLabel) { //参数即为接收的数据
    this.ToDoItems.push({id:uniqueId('todo-'), label: toDoLabel, done: false});
  }
}

4. 使用CSS

此部分略过

5. 使用computed属性,计算待办事项完成情况

计算一个字符串,展示待办事项的完成状况。

  computed: {
    listSummary() {
      const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
        .length;
      return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
    },
  }

但是这不能实时追踪页面上的变化
在ToDoItem.vue中使用一个inline的$emit()

<input type="checkbox" class="checkbox" :id="id" :checked="isDone"
       @change="$emit('checkbox-changed')" />

在App.vue中增加相应的事件监听器checkbox-changed
(剩下两个事件监听器会在下一小节中解释其作用)

   <to-do-item
     :label="item.label" 
     :done="item.done"
     :id="item.id"
     @checkbox-changed="updateDoneStatus(item.id)"
     @item-deleted="deleteToDo(item.id)"
     @item-edited="editToDo(item.id, $event)"
   >

同时在App.vue中增加对应要调用的方法

updateDoneStatus(toDoId) {
  const toDoToUpdate = this.ToDoItems.find(item => item.id === toDoId)
  toDoToUpdate.done = !toDoToUpdate.done
}

这个方法中使用了arr.find方法
语法如下:

let result = arr.find(function(item, index, array) {
  // 如果返回 true,则返回 item 并停止迭代
  // 对于假值(falsy)的情况,则返回 undefined
});

依次对数组中的每个元素调用该函数:

  • item 是元素。
  • index 是它的索引。
  • array 是数组本身。
    如果它返回 true,则搜索停止,并返回 item。如果没有搜索到,则返回 undefined。

现在计数功能就可以正确工作了。

6. 对现有的待办事项进行更新与删除

这一节学习如何在Vue中进行条件渲染。
点击Edit,可以编辑现有待办事项的内容。
在这里插入图片描述
点击Delete,可以将其删除。

创建一个ToDoItemEditForm.vue
这个组件会被引入ToDoItem.vue中使用

<template>
  <form class="stack-small" @submit.prevent="onSubmit">
    <div>
      <label class="edit-label">Edit Name for &quot;{{ label }}&quot;</label>
      <input
        :id="id"
        type="text"
        autocomplete="off"
        v-model.lazy.trim="newLabel"
      />
    </div>
    <div class="btn-group">
      <button type="button" class="btn" @click="onCancel">
        Cancel
        <span class="visually-hidden">editing {{ label }}</span>
      </button>
      <button type="submit" class="btn btn__primary">
        Save
        <span class="visually-hidden">edit for {{ label }}</span>
      </button>
    </div>
  </form>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      required: true,
    },
    id: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      newLabel: this.label,
    };
  },
  methods: {
    onSubmit() {
      if (this.newLabel && this.newLabel !== this.label) {
        this.$emit("item-edited", this.newLabel);
      }
    },
    onCancel() {
      this.$emit("edit-cancelled");
    },
  },
};
</script>

接着对ToDoItem.vue进行更新
加入了一个v-if和v-else结构

  • v-if directive will only render a block if the value passed to it is truthy.
  • v-else和v-else-if块必须是v-if/v-else-if块的第一个同级,否则Vue将无法识别它们。
    点击Edit按钮,根据页面上的监听器,触发toggleToItemEditForm()方法,将.isEditing设置为true,将切换显示ToDoItemEditForm组件
    单击“Delete”按钮时,将触发deleteToDo方法删除todo项。在此处理程序中,我们将向父组件(App.vue)发出item-deleted事件,以便更新列表。
<template>
  <div class="stack-small" v-if="!isEditing">
    <div class="custom-checkbox">
      <input
        type="checkbox"
        class="checkbox"
        :id="id"
        :checked="isDone"
        @change="$emit('checkbox-changed')"
      />
      <label :for="id" class="checkbox-label">{{ label }}</label>
    </div>
    <div class="btn-group">
      <button type="button" class="btn" @click="toggleToItemEditForm">
        Edit <span class="visually-hidden">{{ label }}</span>
      </button>
      <button type="button" class="btn btn__danger" @click="deleteToDo">
        Delete <span class="visually-hidden">{{ label }}</span>
      </button>
    </div>
  </div>
  <to-do-item-edit-form
    v-else
    :id="id"
    :label="label"
    @item-edited="itemEdited" 
    @edit-cancelled="editCancelled"
  >
  </to-do-item-edit-form>
</template>

methods定义如下:

  methods: {
    deleteToDo() {
      this.$emit("item-deleted");
    },
    toggleToItemEditForm() {
      this.isEditing = true;
    },
    itemEdited(newLabel) {
      this.$emit("item-edited", newLabel);
      this.isEditing = false;
    },
    editCancelled() {
      this.isEditing = false;
    },
  },

当我们点击Edit进入编辑状态

  1. 当按下"Cancel" ,在ToDoItemEditForm组件中触发onCancel()方法,向其父组件(ToDoItem.vue)发送事件edit-cancelled,父组件监听edit-cancelled事件,触发editCancelled方法,将.isEditing变为false。根据v-if逻辑判断,ToDoItemEditForm组件将不再显示。
  2. 当按下"Save" ,同理,ToDoItemEditForm组件发送item-edited事件(包含数据new label)。ToDoItem组件监听此事件,触发itemEdited(newLabel),向其父组件(App.vue)发送item-edited事件。在主组件中,我们设置了如下监听器
<to-do-item
          :label="item.label" 
          :done="item.done"
          :id="item.id"
          @checkbox-changed="updateDoneStatus(item.id)"
          @item-deleted="deleteToDo(item.id)"
          @item-edited="editToDo(item.id, $event)"
        >

触发的方法如下,分别对ToDoItems数组中的对象进行删除和更新

deleteToDo(toDoId) {
  const itemIndex = this.ToDoItems.findIndex((item) => item.id === toDoId);
  this.ToDoItems.splice(itemIndex, 1);
},
editToDo(toDoId, newLabel) {
  const toDoToEdit = this.ToDoItems.find((item) => item.id === toDoId);
  toDoToEdit.label = newLabel;
},

最后修复一个小bug
在ToDoItem.vue中,将isDone()变为computed属性

  computed: {
    isDone() {
      return this.done;
    },
  },

计算属性的另一个优点是它们保持了反应性,这意味着(除其他外)当模板像我们现在做的(点击Edit再Cancel)时,它们的状态被保存。
这一部分阅读Vue文档:深入响应式原理

7. 总结整个程序的监听与交互

让我们自顶向下捋一捋整个程序
具体流程已经在上面描述了,画张图。
一图胜千言。

App.vue

在这里插入图片描述

ToDoForm.vue

< form >监听submit事件。
调用onSubmit(),发送todo-added event (上图中App.vue监听的)

ToDoItem.vue

在这里插入图片描述

ToDoItemEditForm.vue

< form >侦听submit事件。
结果:调用onSubmit方法,检查新标签值是否为空,是否与旧标签值不同,发出item-edited事件(然后在ToDoItem.vue中侦听,见上图)。
“Cancel”< button >侦听click事件。
结果:调用onCancel方法,发出edit-cancelled事件(然后在ToDoItem.vue中侦听该事件,见上图)。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值