四、Vue.js 指令

在编写 Vue 应用时,我们可以访问各种强大的指令,这些指令允许我们塑造内容在屏幕上的显示方式。这使我们能够通过添加 HTML 模板来打造高度交互式的用户体验。本章将详细介绍这些指令中的每一条,以及允许我们改进工作流程的任何快捷方式和模式。

本章结束时,您将拥有:

  • 使用属性绑定有条件地更改元素行为
  • 研究了与v-model的双向结合
  • v-ifv-elsev-if-else的条件显示信息
  • 使用v-for迭代集合中的项
  • 使用v-on收听事件(如键盘/输入)
  • 使用事件修饰符更改指令的绑定
  • 使用筛选器更改绑定的视图数据
  • 我们研究了如何使用速记语法来节省时间和更具声明性

模型

任何业务应用最常见的需求之一是文本输入。Vue 通过v-model指令帮助我们满足这一需求。它允许我们在表单输入事件上创建反应式双向数据绑定,从而使表单的使用更加容易。这是一个受欢迎的抽象概念,它描述了获取表单值和输入事件的繁琐方式。为了探索这一点,我们可以创建一个新的 Vue 项目:

# Create a new Vue project
$ vue init webpack-simple vue-model

# Navigate to directory
$ cd vue-model

# Install dependencies
$ npm install

# Run application
$ npm run dev

我们可以转到根App.vue文件,删除模板中的所有内容,而是添加一个包含labelform输入的新div

<template>
 <div id="app">
  <label>Name:</label>
  <input type="text">
 </div>
</template>

这使我们能够将文本添加到输入元素中,即提示用户输入他们的名称。为了演示,我想捕获这个值并将其显示在 name 元素下面。为了做到这一点,我们需要向输入元素添加v-model指令;这将允许我们捕获用户输入事件并将值放入变量中。我们将调用此变量name,然后将其添加到 Vue 实例中的data对象中。由于该值现在被捕获为一个变量,因此我们可以使用插值绑定在模板中显示该值:

<template>
  <div id="app">
    <label>Name:</label>
    <input type="text" v-model="name">
    <p>{{name}}</p>
  </div>
</template>

<script>
export default {
  data () {
    return {
     name: ''
    }
  }
}
</script>

结果可以在以下屏幕截图中看到:

使用v-model时,我们不仅限于使用文本输入,还可以在选中时捕获单选按钮或复选框。以下示例显示了这一点:

 <input type="checkbox" v-model="checked">
 <span>Am I checked? {{checked ? 'Yes' : 'No' }}</span>

这将显示在我们的浏览器中,如下所示:

v-model的优点在于它对各种表单控件具有高度适应性,使我们能够对 HTML 模板进行声明。

v-for 迭代

如果我们有想要重复一定数量的内容,我们可以使用v-for。这通常用于用数据集填充模板。例如,假设我们有一个杂货清单,我们想在屏幕上显示这个清单;我们可以用v-for来实现这一点。我们可以创建一个新项目,以查看这一点:

# Create a new Vue project
$ vue init webpack-simple vue-for

# Navigate to directory
$ cd vue-for

# Install dependencies
$ npm install

# Run application
$ npm run dev

首先,让我们创建一个数组,其中包含可以显示在屏幕上的食品杂货列表。每个项目有一个idnamequantity

<script>
export default {
  name: 'app',
  data () {
    return {
      groceries: [
        {
          id: 1,
          name: 'Pizza',
          quantity: 1
        },
        {
          id: 2,
          name: 'Hot Sauce',
          quantity: 5
        },
        {
          id: 3,
          name: 'Salad',
          quantity: 1
        },
        {
          id: 4,
          name: 'Water',
          quantity: 1
        },
        {
          id: 4,
          name: 'Yoghurt',
          quantity: 1
        }
      ]
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: block;
}

</style>

然后,我们可以遍历杂货列表中的每个项目,并修改 DOM 以在屏幕上显示它们:

<template>
  <div id="app">
    <h1>Shopping List</h1>
    <ul>
      <li v-for="item in groceries" v-bind:key="item.id">
        {{item.name}}
      </li>
    </ul>
  </div>
</template>

注意我们在li元素上有一个v-bind:key="item.id"。这使 Vue 能够更好地处理随时间变化的迭代,并应尽可能添加一个键:

绑定

在本节中,我们将了解如何在 Vue 应用中动态切换 CSS 类。我们将从研究v-bind指令开始,我们将了解如何将其应用于classstyle属性。这对于基于特定业务逻辑有条件地应用样式非常有用。让我们为此示例创建一个新的 Vue 项目:

# Create a new Vue project
$ vue init webpack-simple vue-bind

# Navigate to directory
$ cd vue-bind

# Install dependencies
$ npm install

# Run application
$ npm run dev

在我们的项目内部,我们可以创建代表应用不同状态的复选框。我们将从一个名为red的开始。您可以推断,通过选中此选项,我们可以将特定文本red变成彩色,然后通过取消选中将其变成黑色。

App.vue内创建一个名为reddata对象,其值为false

<script>
export default {
 data () {
  return {
   red: false
  }
 }
}
</script>

这表示复选框的值,我们可以使用v-model指令设置该值:

<template>
 <div id="app">
  <h1>Vue Bindings</h1>

  <input type="checkbox" v-model="red" >
  <span>Red</span>
 </div>
</template>

此时,我们可以为颜色创建一个新的 CSS 类:

<style>
.red {
 color: red;
}
</style>

正如您在浏览器中看到的,如果我们打开开发工具,我们可以看到文本的颜色当前设置为blue

最后,要根据red变量的上下文添加/删除类,我们需要向h1添加v-bind:class指令,如下所示:

<h1 v-bind:class="{ 'red': red }">Vue Bindings</h1>

现在在我们的浏览器中,我们可以看到,我们可以选中复选框,将文本设置为red,如下所示:

添加辅助属性

如果我们还想向类绑定中添加另一个属性,我们需要向data对象中添加另一个属性(如strikeThrough),如下所示:

data () {
 return {
  red: false,
  strikeThrough: false
 }
}

然后我们可以添加另一个checkbox

<input type="checkbox" v-model="strikeThrough">
<span>Strike Through</span>

使用适当的style

<style>
.red {
 color: red;
}

.strike-through {
 text-decoration: line-through;
}
</style>

最后,我们需要调整绑定以添加额外的类,如下所示:

<h1 v-bind:class="{ 'red': red, 'strike-through': strikeThrough }">Vue Bindings</h1>

以下是选中这两个框的结果:

样式绑定

我们可能希望在标题中添加各种样式,因此可以使用v-bind:style。通过在data对象内部创建一个名为headingStyles的新对象,我们可以看到这一点:

data () {
 return {
  headingStyles: {
   color: 'blue',
   fontSize: '20px',
   textAlign: 'center'
  }
 }
}

任何时候,如果我们添加 CSS 类的话,这些类本来应该是烤羊肉串大小写(例如,text-align),现在它们就变成了 JavaScript 中的驼峰大小写(textAlign)。

让我们将样式添加到模板内部的标题中:

<h1 v-bind:style="headingStyles">Vue Bindings</h1>

每次编译器看到一个v-bind:时,"中的内容都被视为带有隐式this的 JavaScript。

我们也可以将其拆分为一个单独的对象添加layoutStyles,例如:

data () {
 return {
  headingStyles: {
   color: 'blue',
   fontSize: '20px',
  },
  layoutStyles: {
   textAlign: 'center',
   padding: '10px'
  }
 }
}

所以我们现在要做的就是在template中的数组中添加styles,就像在<h1>标记中一样,使用v-bind

<template>
 <h1 v-bind:style="[headingStyles, layoutStyles]">Vue Bindings</h1>
</template>

您现在可以在屏幕上看到我们的样式设置结果。请注意,数组中任何进一步的项都将比首先声明的项具有样式首选项。

DOM 事件和 v-on

我们可以使用v-on在 Vue 中处理 DOM 事件。通过监听 DOM 事件,我们能够对用户输入做出反应,从按键事件(如点击进入按钮)到按钮点击事件等等。

让我们做一个游乐场项目,在我们自己的项目中尝试:

# Create a new Vue project
$ vue init webpack-simple vue-on

# Navigate to directory
$ cd vue-on

# Install dependencies
$ npm install

# Run application
$ npm run dev

让我们想象一个input框,当我们点击 Add 按钮或Enter键时,输入被添加到一个数组中:

<template>
 <div id="app">
  <ul>
   <li v-for="(p, index) in person" :key="index">
    {{p}}
   </li>
  </ul>
  <input type="text" v-model="person" v-on:keyup.enter="addPerson" />
  <button v-on:click="addPerson">Add {{ person}} </button>
 </div>
</template>

<script>
export default {
 name: 'app',
 data () {
  return {
   person: '',
   people: []
  }
 },
 methods: {
  addPerson() {
   this.people = this.people.concat(
    {id: this.people.length, name: this.person}
   );
  this.person = '';
  }
 }
}
</script>

您必须先复制对象,然后才能将其推入。

这里到底发生了什么?在用户的输入中使用[person]指令的输入值;然后我们监听keyup.enterv-on:click事件,这两个事件都调用addPerson函数,然后将person添加到数组中。之后,使用v-for指令,我们可以将以下人员列表输出到页面:

关键修改器

我们不仅限于简单地使用enter修饰符,我们还可以使用各种速记修饰符,例如使用@符号和用@符号替换来缩短v-on:event.name``v-on:。其他缩短方法包括:

  • @v-on:相同
  • @keyup.13@keyup.enter相同
  • @key*可以排队,如@keyup.ctrl.alt.delete

下表中显示了其他修改器:

| 名称 | 说明 | 代码示例 | | .enter | 每当点击回车键时。 | <input v-on:keyup.enter="myFunction" /> | | .tab | 每当点击Tab键时。 | <input v-on:keyup.tab="myFunction" /> | | .delete | 每当点击删除退格键时。 | <input v-on:keyup.delete="myFunction" /> | | .esc | 每当点击Esc键时。 | <input v-on:keyup.esc="myFunction" /> | | .up | 每当点击向上箭头键时。 | <input v-on:keyup.up="myFunction" /> | | .down | 每当按下向下箭头键时。 | <input v-on:keyup.down="myFunction" /> | | .left | 每当点击左箭头键时。 | <input v-on:keyup.left="myFunction" /> | | .right | 只要按下向右箭头键。 | <input v-on:keyup.right="myFunction" /> |

事件修饰符

通常,当我们在 JavaScript 中处理事件时,我们会修改事件本身的功能。这意味着我们需要在处理程序中添加event.preventDefault()event.stopPropagation()。Vue 通过使用事件修饰符在模板内部处理这些调用来帮助我们抽象这些调用。

最好用一个form示例来说明这一点。让我们以前面的人员示例为例,将其修改为包含一个form元素:

<template>
  <div id="app">
    <ul>
      <li v-for="p in people" v-bind:key="p.id" >
        {{p}}
      </li>
    </ul>

    <form v-on:submit="addPerson">
      <input type="text" v-model="person" />
      <button>Add {{ person}} </button>
    </form>
  </div>
</template>

如果您尝试运行这个示例,您会注意到当我们单击 Add 按钮时,页面会刷新。这是因为这是form提交事件的默认行为。由于此时我们没有将数据发布到服务器,因此需要将.prevent修饰符添加到submit事件中:

 <form v-on:submit.prevent="addPerson">
  <input type="text" v-model="person" />
  <button>Add {{ person}} </button>
 </form>

现在,当我们选择按钮时,addPerson函数被调用,而不刷新页面。

有条件地显示 DOM 元素

在创建业务应用时,如果某个条件为truefalse,那么很多时候您只想显示特定的元素。这可能包括用户的年龄、用户是否已登录、是否是管理员或您可以想到的任何其他业务逻辑。

为此,我们有多种条件指令,如v-showv-ifv-elsev-else-if,它们的作用方式相似但不同。让我们通过创建一个新的游乐场项目来更详细地了解这个问题:

# Create a new Vue project
$ vue init webpack-simple vue-conditionals

# Navigate to directory
$ cd vue-conditionals

# Install dependencies
$ npm install

# Run application
$ npm run dev

电视节目

如果我们想隐藏视图中的元素,但它们仍在 DOM 中(实际上是display:none,我们可以使用v-show

<template>
<div id="app">
 <article v-show="admin">
  <header>Protected Content</header>
 <section class="main">
  <h1>If you can see this, you're an admin!</h1>
 </section>
</article>

 <button @click="admin = !admin">Flip permissions</button>
</div>
</template>

<script>
export default{
name: 'app',
 data (){
  return{
   admin: true
    }
  }
}
</script>

例如,如果我们有一个数据变量,允许我们确定某人是否是管理员,那么我们就可以使用v-show只向适当的用户显示受保护的内容:

请注意,在上图中,当admin设置为false时,display: none样式是如何添加到元素的。乍一看,这似乎正是我们想要的,我们的物品已经不见了!在某些情况下是这样,但在其他情况下,使用v-if可能更好。

v-show不会从 DOM 中删除元素,这意味着所有内容最初都是加载的,如果不使用,则会被隐藏。我们的页面将不得不呈现此内容,这可能会导致性能问题时,使用错误的方式;因此,在使用v-show之前,请询问以下问题:

Do I need to show this component again? If so, will be showing it often?

如果这个问题的答案是,那么在这种情况下v-show可能会更好。否则,如果回答为否,则在本用例中v-if可能更好。

v-if

如果我们想有条件地从 DOM 中删除元素,我们可以使用v-if。让我们用v-if替换之前的v-show指令:

 <article v-if="admin">
  <header>Protected Content</header>
  <section class="main">
   <h1>If you can see this, you're an admin!</h1>
  </section>
 </article>

请注意,现在当我们查看 DOM 时,元素被完全删除:

v-else

显示或隐藏元素时的常见模式是显示不同的内容。虽然我们可以多次使用v-ifv-show,但我们也可以访问v-else指令,该指令可以在显示或隐藏元素后直接使用。

让我们更详细地看一下这个问题:

<article v-if="admin">
  <header>Protected Content</header>
  <section class="main">
    <h1>If you can see this, you're an admin!</h1>
  </section>
</article>
<article v-else>
  <header>You're not an admin!</header>
  <section class="main">
    <h1>Perhaps you shouldn't be here.</h1>
  </section>
</article>

通过将v-else指令添加到第二个<article>,我们告诉 Vue,我们希望在第一个条件隐藏时显示这个 DOM 元素。由于其工作方式,我们不必将值传递给v-else,因为 Vue 专门在前面的元素中查找结构指令。

重要的是要认识到,如果我们在v-ifv-else指令之间有一个元素,这将不起作用,例如:

<article v-if="admin">
  <header>Protected Content</header>
  <section class="main">
    <h1>If you can see this, you're an admin!</h1>
  </section>
</article>
<h1>The v-else will be ignored.</h1>
<article v-else>
  <header>You're not an admin!</header>
  <section class="main">
    <h1>Perhaps you shouldn't be here.</h1>
  </section>
</article>

v-else-if

虽然v-else在标准中运行良好,但如果不是 A那么B场景,您可能需要测试多个值并显示不同的模板。与v-else类似,我们可以使用v-else-if来更改应用的行为。在本例中,我们将通过使用 ES2015 中引入的生成器获得乐趣。

要使用发电机,我们需要安装babel-polyfill包;这也允许我们使用asyncawait等更好的承诺处理:

$ npm install babel-polyfill --save-dev

安装后,我们可以修改我们的网页配置(webpack.config.js,将其包含在我们的输入文件中:

module.exports = {
 entry: ['babel-polyfill', './src/main.js'],
 output: {
  path: path.resolve(__dirname, './dist'),
  publicPath: '/dist/',
  filename: 'build.js',
 },
 // Omitted

如果我们没有安装适当的 polyfill,我们将无法在项目中使用生成器功能。让我们创建一个名为returnRole()的新方法,它在调用时为我们提供三个用户“角色”中的一个:

export default {
 name: 'app',
 data() {
  return {
   role: '',
  }
 },
  methods: {
   *returnRole() {
    yield 'guest';
    yield 'user';
    yield 'admin';
  }
 }
};

如果您以前从未见过生成器函数,您可能想知道我们在函数名前面加了什么星号(*),以及yield关键字。这本质上允许我们通过捕获函数的一个实例来逐步完成该函数。例如,让我们创建一个返回迭代器的数据值,我们可以对其调用next()

 data() {
  return {
   role: '',
   roleList: this.returnRole()
  }
 },
 methods: {
  getRole() {
   /**
    * Calling this.roleList.next() gives us an Iterator object with the interface of:
    * { value: string, done: boolean}
    * We can therefore check to see whether this was the >last< yielded value with done, or get the result by calling .value
    */

    this.role = this.roleList.next().value;
 },

因此,我们可以通过根据用户角色显示不同的消息来制作一个利用v-if-else的模板:

<template>
 <div id="app">
  <article v-if="role === 'admin'">
   <header>You're an admin!</header>
   <section class="main">
    <h1>If you can see this, you're an admin!</h1>
   </section>
  </article>
  <article v-else-if="role === 'user'">
   <header>You're a user!</header>
   <section class="main">
    <h1>Enjoy your stay!</h1>
   </section>
  </article>
 <article v-else-if="role === 'guest'">
  <header>You're a guest!</header>
  <section class="main">
   <h1>Maybe you should make an account.</h1>
  </section>
 </article>
 <h1 v-else>You have no role!</h1>
 <button @click="getRole()">Switch Role</button>
 </div>
</template>

根据用户角色的不同,屏幕上会显示不同的消息。如果用户没有角色,我们使用v-else来显示一条说明You have no role!的消息。这个例子展示了我们如何利用结构化指令,根据应用状态来真正更改 DOM。

过滤器

在本节中,我们将研究过滤器;您以前可能在 Angular(管道)等框架中遇到过过滤器。也许我们想创建一个过滤器,允许我们以可读的格式(DD/MM/YYYY)格式化日期。让我们创建一个游乐场项目来进一步研究这个问题:

# Create a new Vue project
$ vue init webpack-simple vue-filters

# Navigate to directory
$ cd vue-filters

# Install dependencies
$ npm install

# Run application
$ npm run dev

如果我们有一些测试人员,并使用v-for指令在屏幕上显示他们,我们将得到以下结果:

为了获得前面屏幕截图中显示的结果,我们通过v-for指令用适当的数据显示我们的测试人员,我们必须添加以下代码:

<template>
 <div id="app">
  <ul>
   <li v-for="person in people" v-bind:key="person.id">
    {{person.name}} {{person.dob}}
   </li>
  </ul>
 </div>
</template>

<script>
export default {
 name: 'app',
 data() {
  return {
   people: [
    {
     id: 1,
     name: 'Paul',
     dob: new Date(2000, 5, 29),
    },
    {
     id: 2,
     name: 'Terry',
     dob: new Date(1994, 10, 25),
    },
    {
     id: 3,
     name: 'Alex',
     dob: new Date(1973, 4, 15),
    },
    {
     id: 4,
     name: 'Deborah',
     dob: new Date(1954, 2, 5),
    },
   ],
  };
 },
};
</script>

我们可以自己做转换日期的工作,但如果可能的话,总是值得一看是否有一个可信的第三方组件可以做同样的事情。我们将使用力矩(https://momentjs.com )这样做。

让我们为我们的项目安装moment

$ npm install moment --save

然后我们可以将其添加到我们的App.vue

<script>
import moment from 'moment';

export default {
 // Omitted
}
</script>

本地注册过滤器

然后我们有一个选择:将过滤器本地添加到此 Vue 实例,或将其全局添加到整个项目。我们将首先了解如何在本地添加它:

首先,我们将创建一个函数,该函数接受一个值并使用moment将日期作为格式化日期返回:

const convertDateToString = value => moment(String(value)).format('MM/DD/YYYY');

然后,我们可以向 Vue 实例添加一个 filters 对象,并通过一个key引用它,例如date。当我们在模板内部调用date过滤器时,该值将传递给该过滤器,我们将在屏幕上显示转换的日期。这可以通过使用|键来完成,如以下代码所示:

 <ul>
  <li v-for="person in people" v-bind:key="person.id">
   {{person.name}} {{person.dob | date}}
  </li>
 </ul>

最后,要将其添加到本地 Vue 实例中,我们可以添加一个引用我们函数的filters对象:

export default {
 filters: {
  date: convertDateToString,
 },

结果显示了预期的日期:

全局注册过滤器

如果我们想在其他地方使用这个过滤器,我们可以将这个函数抽象到它自己的文件中并再次引用我们的过滤器,或者,我们可以在我们的应用中全局注册date过滤器。让我们将我们的convertDateToString函数抽象到它自己的文件src/filters/date/date.filter.js

import moment from 'moment';

export const convertDateToString = value =>
 moment(String(value)).format('MM/DD/YYYY');

之后我们可以通过以下接口定义我们main.js内部的过滤器:Vue.filter('filterName', filterFunction())。由于我们已经将函数抽象到它自己的文件中,我们可以导入它并这样定义它:

import Vue from 'vue';
import App from './App.vue';
import { convertDateToString } from './filters/date/date.filter';

Vue.filter('date', convertDateToString);

new Vue({
 el: '#app',
 render: h => h(App),
});

如果您再次检查我们的应用,您将看到我们得到与以前相同的结果。因此,重要的是要考虑过滤器在项目中使用的时间和次数。如果在特定组件/实例上使用它(一次),则应将其放置在本地;否则,请将其放在全局。

总结

在本章中,我们介绍了许多 Vue 指令及其用法。这使我们能够声明性地更改模板在屏幕上的显示方式,包括捕获用户输入、钩住事件、过滤视图数据等。当您希望在 Vue.js 应用中实现指令时,本章应作为参考。

基于组件的体系结构是一个重要概念,它允许我们构建从个人到企业的可扩展项目。在下一章中,我们将研究如何创建这些可重用组件来封装项目中的功能。