Bases des composants


Répertoire


Exemple de base


Voici un exemple de composant Vue :

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

Un composant est une instance Vue réutilisable avec un nom : dans ce cas <button-counter>. Nous pouvons utiliser ce composant comme élément personnalisé dans une instance racine de Vue créée via new Vue : <button-counter>。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

1.gif

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。


组件的复用


你可以将组件进行任意次数的复用:

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

1.jpg

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。


data 必须是一个函数

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data

data: {
  count: 0
}
data: function () {
  return {
    count: 0
  }
}

1.gif🎜🎜Parce que les composants sont des instances de Vue réutilisables, ils sont les mêmes que lorsque nouvelle Vue< /code> reçoit la même chose options, telles que données, calculé, watch, méthodes et crochets de cycle de vie. Les seules exceptions sont les options spécifiques à l'instance racine comme el . 🎜🎜🎜🎜

🎜🎜Réutilisation des composants🎜🎜🎜🎜🎜Vous pouvez réutiliser les composants autant de fois que vous le souhaitez : 🎜
Vue.component('my-component-name', {
  // ... options ...
})
🎜1.jpg🎜🎜Notez que lorsqu'un bouton est cliqué, chaque composant le maintiendra indépendamment < code>count< /code>. Parce que chaque fois que vous utilisez un composant, une nouvelle instance de celui-ci est créée. 🎜🎜🎜🎜

🎜data🎜🎜🎜 Ce doit être une fonction🎜🎜🎜🎜Lorsque nous définissons ce composant <button-counter>, vous constaterez peut-être que ses data ne fournissent pas directement un objet comme celui-ci : 🎜

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

Au lieu de cela, l'option data d'un composant doit être une fonction, afin que chaque instance puisse conserver une copie indépendante de l'objet renvoyé : data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例:

2.gif


组件的组织


通常一个应用会以一棵嵌套的组件树的形式来组织:

1.png

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

到目前为止,关于组件注册你需要了解的就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把组件注册读完。


通过 Prop 向子组件传递数据


早些时候,我们提到了创建一个博文组件的事情。问题是如果你不能向这个组件传递某一篇博文的标题或内容之类的我们想展示的数据的话,它是没有办法使用的。这也正是 prop 的由来。

Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义特性传递进来:

<h3>{{ title }}</h3>

2.jpg

然而在一个典型的应用中,你可能在 data

<h3>{{ title }}</h3>
<div v-html="content"></div>

Si Vue ne respecte pas cette règle, cliquez sur un Le bouton peut affecter toutes les autres instances comme le code suivant :

2.gif🎜🎜
🎜

🎜 Organisation du composant< /h2>
🎜Habituellement, une application est organisée sous la forme d'une arborescence de composants imbriquée :
🎜🎜1.png🎜🎜Par exemple, vous pouvez avoir un en-tête et une barre latérale, une zone de contenu et d'autres composants, chaque composant contient d'autres composants tels que liens de navigation, articles de blog, etc. 🎜🎜Pour pouvoir être utilisés dans des modèles, ces composants doivent d'abord être enregistrés afin que Vue puisse les reconnaître. Il existe deux types d'enregistrement de composants : 🎜enregistrement global et 🎜enregistrement local. Jusqu'à présent, nos composants n'ont été enregistrés globalement que via Vue.component : 🎜
<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>
🎜Les composants enregistrés globalement peuvent être utilisés dans n'importe quoi après avoir été enregistrés (via new Vue). L'instance racine Vue nouvellement créée inclut également les modèles de tous les sous-composants dans son arborescence de composants. 🎜🎜Jusqu'à présent, c'est tout ce que vous devez savoir sur l'enregistrement des composants. Si vous avez fini de lire cette page et maîtrisez son contenu, nous vous recommanderons de revenir et de Enregistrement des composants Terminez la lecture. 🎜🎜
🎜

🎜Transmettre les données aux composants enfants via Prop


🎜Plus tôt, nous avons mentionné la création d'un composant d'article de blog. Le problème est que si vous ne pouvez pas transmettre les données que nous souhaitons afficher, comme le titre ou le contenu d'un article de blog, à ce composant, ils ne peuvent pas être utilisés. C'est de là que viennent les accessoires. 🎜🎜Les props sont des propriétés personnalisées que vous pouvez enregistrer sur le composant. Lorsqu'une valeur est transmise à un attribut prop, elle devient une propriété de cette instance de composant. Afin de transmettre un titre au composant article de blog, nous pouvons utiliser une option props pour l'inclure dans la liste des props acceptées par le composant : 🎜
<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
></blog-post>
🎜Un composant peut avoir n'importe quel nombre de props par par défaut, et n'importe quelle valeur Peut être transmise à n'importe quel accessoire. Dans le modèle ci-dessus, vous remarquerez que nous pouvons accéder à cette valeur dans l'instance du composant, tout comme pour accéder à la valeur dans les données. 🎜🎜Une fois qu'un accessoire est enregistré, vous pouvez transmettre les données en tant que fonctionnalité personnalisée comme ceci : 🎜
<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>
🎜2.jpg🎜🎜Cependant, dans une application typique, vous pouvez avoir un tableau d'articles de blog dans data : 🎜
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})
🎜 et souhaitez afficher un composant pour chaque article de blog : 🎜
new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
  }
})

Comme indiqué ci-dessus, vous constaterez que nous pouvons utiliser v-bind pour transmettre dynamiquement des accessoires. C'est lorsque vous ne connaissez pas le contenu spécifique à afficher au début, comme par exemple obtenir une liste d'articles de blog à partir d'un API est très utile. v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。


单个根元素


当构建一个 <blog-post> 组件时,你的模板最终会包含的东西远不止一个标题:

<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    ></blog-post>
  </div>
</div>

最最起码,你会包含这篇博文的正文:

Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button>
        Enlarge text
      </button>
      <div v-html="post.content"></div>
    </div>
  `
})

然而如果你在模板中尝试这样写,Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。你可以将模板的内容包裹在一个父元素内,来修复这个问题,例如:

<button>
  Enlarge text
</button>

看起来当组件变得越来越复杂的时候,我们的博文不只需要标题和内容,还需要发布日期、评论等等。为每个相关的信息定义一个 prop 会变得很麻烦:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

所以是时候重构一下这个 <blog-post> 组件了,让它变成接受一个单独的 post prop:

<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>
<button v-on:click="$emit('enlarge-text', 0.1)">
  Enlarge text
</button>

上述的这个和一些接下来的示例使用了 JavaScript 的模板字符串来让多行的模板更易读。它们在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。

现在,不论何时为 post 对象添加一个新的属性,它都会自动地在 <blog-post> 内可用。


监听子组件事件


在我们开发 <blog-post> 组件时,它的一些功能可能要求我们和父级组件进行沟通。例如我们可能会引入一个辅助功能来放大博文的字号,同时让页面的其它部分保持默认的字号。

在其父组件中,我们可以通过添加一个 postFontSize

Pour l'instant, c'est à peu près tout ce que vous devez savoir sur prop. Si vous avez fini de lire cette page et maîtrisez son contenu, nous vous recommanderons de revenir prop Terminez la lecture.


Élément racine unique


Lors de la création d'un composant <blog-post>, votre modèle finira par contenir bien plus qu'un simple titre :
🎜
<blog-post
  ...
  v-on:enlarge-text="postFontSize += $event"
></blog-post>
🎜Au minimum, vous Contient le texte de cet article de blog : 🎜
<blog-post
  ...
  v-on:enlarge-text="onEnlargeText"
></blog-post>
🎜 Cependant, si vous essayez d'écrire ceci dans un modèle, Vue affichera une erreur et expliquera quechaque composant doit avoir un seul élément racine (chaque composant ne doit avoir qu'un seul élément racine). élément racine) . Vous pouvez résoudre ce problème en enveloppant le contenu du modèle dans un élément parent, par exemple : 🎜
methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}
🎜 Il semble qu'à mesure que les composants deviennent de plus en plus complexes, nos articles de blog ont non seulement besoin du titre et du contenu, mais aussi de la publication. date, commentaires et plus encore. Définir un accessoire pour chaque article pertinent peut s'avérer fastidieux : 🎜
<input v-model="searchText">
🎜 Il est donc temps de refactoriser le composant <blog-post> pour accepter un seul accessoire code>post : 🎜
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>
<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>
🎜Ceci et certains des exemples suivants utilisent JavaScript Chaîne de modèle pour rendre les modèles multilignes plus lisibles. Ils ne sont pas pris en charge sous IE, donc si vous devez prendre en charge IE sans compiler (via des outils comme Babel ou TypeScript), veuillez utiliser caractère d'échappement de ligne de rupture à la place. 🎜
🎜Désormais, chaque fois que vous ajoutez une nouvelle propriété à l'objet post, elle sera automatiquement disponible dans <blog-post>. 🎜🎜
🎜

Écouter les événements des composants enfants


🎜Lorsque nous développons le composant <blog-post>, certaines de ses fonctions peuvent nécessiter que nous communiquions avec le composant parent. Par exemple, nous pourrions introduire une fonction auxiliaire pour augmenter la taille de police d'un article de blog tout en conservant le reste de la page à la taille de police par défaut.
🎜🎜Dans son composant parent, nous pouvons prendre en charge cette fonctionnalité en ajoutant un attribut de données postFontSize : 🎜
Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})
🎜Il peut être utilisé dans les modèles pour contrôler la taille de la police de tous les articles de blog : 🎜
<custom-input v-model="searchText"></custom-input>
🎜Maintenant, nous ajoutons un bouton avant le texte de chaque article de blog pour agrandir la taille de la police : 🎜
<alert-box>
  Something bad happened.
</alert-box>

Le problème est que ce bouton ne fait rien :

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

Lorsque l'on clique sur ce bouton, nous devons dire au composant parent d'agrandir le texte de tous les articles de blog. Heureusement, les instances Vue fournissent un système d'événements personnalisé pour résoudre ce problème. Le composant parent peut écouter tous les événements de l'instance du composant enfant via v-on, tout comme la gestion des événements DOM natifs : v-on 监听子组件实例的任意事件:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

同时子组件可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件:

<table>
  <blog-post-row></blog-post-row>
</table>

有了这个 v-on:enlarge-text="postFontSize += 0.1" 监听器,父级组件就会接收该事件并更新 postFontSize 的值。

3.gif


使用事件抛出一个值

有的时候用一个事件来抛出一个特定的值是非常有用的。例如我们可能想让 <blog-post> 组件决定它的文本要放大多少。这时可以使用 $emit 的第二个参数来提供这个值:

<table>
  <tr is="blog-post-row"></tr>
</table>

然后当在父级组件监听这个事件的时候,我们可以通过 $event 访问到被抛出的这个值:

rrreee

或者,如果这个事件处理函数是一个方法:

rrreee

那么这个值将会作为第一个参数传入这个方法:

rrreee


在组件上使用 v-model

自定义事件也可以用于创建支持 v-model 的自定义输入组件。记住:

rrreee

等价于:

rrreee

当用在组件上时,v-model 则会这样:

rrreee

为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value 特性绑定到一个名叫 value 的 prop 上

  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

写成代码之后是这样的:

rrreee

现在 v-modelrrreee

En même temps, le composant enfant peut appeler le $emit

et transmettez le nom de l'événement pour déclencher un événement : rrreeeAvec cet écouteur v-on:enlarge-text="postFontSize += 0.1", le composant parent recevra l'événement et mettra à jour la valeur de postFontSize.

3.gif

Utilisez un événement pour lancer une valeur


Parfois, il est très utile d'utiliser un événement pour lancer une valeur spécifique . Par exemple, nous pourrions souhaiter que le composant <blog-post> détermine de combien son texte doit être agrandi. A ce moment, vous pouvez utiliser le deuxième paramètre de $emit pour fournir cette valeur :

rrreee

Ensuite, lors de l'écoute de cet événement dans le composant parent, nous pouvons passer $event Accédez à la valeur qui a été renvoyée :

rrreee

Ou, si le gestionnaire d'événements est une méthode : 🎜rrreee🎜 alors la valeur sera transmise à la méthode comme premier paramètre : 🎜rrreee🎜🎜🎜

🎜Utilisez v-model sur les composants🎜🎜🎜🎜Les événements personnalisés peuvent également être utilisés pour créer un support Composant d'entrée personnalisé pour v-modèle. N'oubliez pas : 🎜rrreee🎜 équivaut à : 🎜rrreee🎜 Lorsqu'il est utilisé sur un composant, le v-model ressemblera à ceci : 🎜rrreee🎜Pour le faire fonctionner correctement, le à l'intérieur de ce composant < ;input> doit : 🎜

  • 🎜Lier son attribut value à un objet nommé Sur l'accessoire de valeur🎜
  • 🎜Lorsque son événement input est déclenché, la nouvelle valeur sera renvoyée via l'événement input personnalisé 🎜
🎜Après avoir écrit le code, cela ressemble à ceci : 🎜rrreee🎜Maintenant, v-model devrait pouvoir fonctionner parfaitement sur ce composant : 🎜rrreee🎜Jusqu'ici Jusqu'à présent, c'est sur tout ce que vous devez savoir sur les événements personnalisés des composants. Si vous avez fini de lire cette page et maîtrisez son contenu, nous vous recommandons de revenir lire 🎜Événements personnalisés🎜. 🎜🎜🎜🎜🎜🎜🎜Distribuer du contenu via des slots 🎜🎜🎜🎜🎜Comme les éléments HTML, nous devons souvent transmettre du contenu à un composant, comme ceci : 🎜rrreee🎜 pourrait afficher quelque chose comme ceci : 🎜

3.jpg

Heureusement, l'élément <slot> personnalisé de Vue rend cela très simple : <slot> 元素让这变得非常简单:

rrreee

如你所见,我们只要在需要的地方加入插槽就行了——就这么简单!

到目前为止,关于插槽你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把插槽读完。


动态组件


有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:

4.gif

上述内容可以通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现:

rrreee

在上述示例中,currentTabComponent 可以包括

  • 已注册组件的名字,或

  • 一个组件的选项对象

你可以在这里查阅并体验完整的代码,或在这个版本了解绑定组件选项对象,而不是已注册组件名的示例。

到目前为止,关于动态组件你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把动态和异步组件读完。


解析 DOM 模板时的注意事项


有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。

这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

rrreee

这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is 特性给了我们一个变通的办法:

rrreee

需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在