Intro to Vue.js: Components, Props, and Slots


なるべくもとの文章に忠実に翻訳するようにしましたが、原文だけでは自分が理解できない箇所に関しては、大幅に文言を追加しています。

元の記事

Intro to Vue.js: Components, Props, and Slots

Components and Passing Data

コンポーネントとコンポーネントにデータを渡す方法

If you’re familiar with React or Angular2, the idea of components and passing state won’t be new to you. In case you’re not, let’s go through some of the main concepts.

React や Angular2 に馴染みがある人であれば、コンポーネントという概念をご存知でしょうし、状態をコンポーネントに渡すというのも、目新しいものではないとおもいます。しかし、今回はあまり馴染みがない人のために、コンポーネントの主要な機能について体験していこうと思います。

Websites large and small are usually composed of different pieces, and abstracting them into smaller pieces makes them easy to structure, reason about, reuse, and makes our code more legible. Instead of digging through all of the markup in long, multi-faceted page, we could comprise it of components like this:

ウェブサイトは大きなものであれ小さなものであれ、様々な断片によって構成されていますが、これらの断片を抽象化することによって、構造化することも、理解することも、使いまわすことも、簡単になりますし、そしてなによりもコードの可読性を上げてくれます。何ページにもわたる非常に長いコードを書くのではなく、次のようにコンポーネントで構成することができます。

This is a simplified example, but you can see how useful this type of composition can be as you start to build out the structure of your site. If you were to dive into this code as a maintainer, it wouldn’t take much to understand how the application is structured or where to look for each piece.

これは単純化された例であって実際に機能するものではありませんが、このように構成することができればどんなに便利なことか、御理解いただけるはずです。もちろん一からウェブサイトの構成を作り上げるときにも便利ですし、また他人が作ったコードをメンテナンスする際にも、どのような構造になっているのか理解するための時間を減らすことになりますし、どこに何があるのか探すのも簡単です。

Vue lets us create components in a few different ways. Let’s work from simple to complex, keeping in mind that the complex example is truest to form for what an average Vue application would look like.

Vue.js でコンポーネントを作る方法はいくつかあります。簡単なものから初めて複雑なものにも一緒にチャレンジしていきましょう。?次の一文わからず

This works, but isn’t terribly useful as it can only be used once and we’re not yet passing the information to different components. One way to pass data from a parent to a child is called props.

これでも機能はしますが、そこまで便利というわけではありません。なぜなら使いまわすことができませんし、情報を異なるコンポーネントに渡してもいません。情報を親から子へ渡す方法として「Props」という機能があります。

This is as simple an example as I could make, so that it’s super clear. Remember that the :text in the HTML is a shortcut for Vue binding. We covered this last time in the section on directives. Binding can be used for all kinds of things but in this instance, it keeps us from having to place the state in a mustache template, like this {{ message }}.

次の例はできるだけ簡単になるように努力しましたので、非常にわかりやすいはずです。「:text」は v-bind の省略形です。(詳しくは前回を見てください) Vue.js におけるバインディングは様々な用途に用いられますが、今回は口ひげ構文({{ message}})と書くことなく状態 = state を配置するために使用しています。

訳者解説:次のcodepenを参照してください。v-bind:text=””と:text=””は同じ効果を発揮します。そしてその効果は、コンポーネントの props に””内の値を渡すことです。ただし、v-bind 等のディレクティブを付けた場合、””内は JS として理解されますので、”message”は単なる文字列ではなく、Vue.message、つまりVueインスタンス内のデータの message プロパティの値を参照します。 また、単に文字列を渡すだけであれば、v-bind を用いずに、text=””とし、 props に渡すこともできます。同様に text=”message” のように、v-bind は用いずに、しかしVue インスタンスを参照した場合には、アップデートはされず、DOM が生成された時に一度だけ参照することになります。

See the Pen used vue.jscomponent by nakanishi (@nakanishi) on CodePen.

In the code below, Vue.component is the component, and new Vue is called the instance. You can have more than one instance in an application. Typically, we’ll have one instance and several components, as the instance is the main app.

下記のコードでは、「Vue.component」はコンポーネントを作成しており、「new Vue」はインスタンスを作成しています。アプリケーション中でいくつものVuie インスタンスを作ることができますが、通常は 1つだけのインスタンスに、複数のコンポーネントを関連付けて使用します。

Now we can reuse this component as many times as we like through our application:

上記のようにすれば、コンポーネントを好きなだけ何回でも使いまわすことができます。

We can also add validation to our props, which is similar to PropTypes in React. This is nice because it’s self-documenting, and will return an error if it’s not what we expected, but only in development mode:

validation (訳注:データの型やデータの有無をチェックする仕組み) を加えることもできます。これは React のPropTypes とよく似た機能です。これによってどんな値が入るべきなのか、コード自体が自分自身を説明してくれますし、制限外の値が入ってきた場合には、開発モードであればエラーを返します。

In the example below, I’m loading Vue in development mode, and purposefully passing an invalid type into our prop validation. You can see the error in the console. (It also helpful lets you know you can use Vue’s devtools and where to find them).

下記の例では、開発モードで意図的に誤った値を validation を与えた prop に代入してみました。すると、エラーがコンソールに表示されます。

Objects should be returned as a factory function and you can even pass as a custom validator function, which is really nice because you can check values against business, input, or other logic. There’s a nice write-up of how you’d use each type in the guide here.

(訳に自信なし、というか英文ではなくて挙動をもとに書いてます、ここ) コンポーネントの props に値を渡すためには、Vue インスタンスの data の中身は、factory function によって作られたオブジェクトが return されるようにしましょう。またこうすれば validation をも与えることができます。 (つまりdata(){return {message:”hello”, count:0} }というような形で書かれる必要がある。このdata関数は、実行されると新しいオブジェクトを作成する。このあたらしくオブジェクトを作成する性質=ファクトリーだと思われる。こうしなくてはいけない理由は次にカウントアップのコードをみるとわかる。毎回別のオブジェクトがつくられないと、データーの中身を共有することになるので、同一のコンポーネントから作られたものは、全てデーターがおなじになってしまう。それを避けるためには、コンポーネントが作成された際に、data関数が実行されて、新しいオブジェクトが返されるようにすればいい、ということだろうか…)

You don’t need to necessarily pass the data in props to the child, either, you have the option of using state or a static value as you see fit:

コンポーネントに props を介してデータを、必ず与えなくてはいけないわけではありませんし、渡す場合にも Vue インスタンスの 状態=state を渡す選択肢と、固定値 = static value を渡す選択肢の2つがあります。これは自分の必要とする機能に応じておこなえばよいでしょう。

The difference is whether or not you’re passing a property and binding it:

Vue インスタンスのプロパティを渡してバインディングするか、しないかの違いについて説明します。

Not using the state
<child count=”1″></child>

上記の例は Vue インスタンスのプロパティを渡していませんし、またバインディングもしていません。つまり単に定数の 1 を渡しているだけです。

vs

Using the state
<child :count=”count”></child>

上記の例は「:」によってバインディングをしているし、Vue インスタンスの値、つまり状態 = state である「count」を props を通して渡しています。

Up until now, we’ve been creating content in our child component with a string, and of course if you’re using babel so that you can process ES6 in all browsers (which I highly suggest), you could use a template literal to avoid potentially hard-to-read string concatenation:

バベルを使って ES6 を使えば、面倒な文字連結が簡単になるよ。

This is a little more useful, but there’s still a limit to how much content we probably want to put in that string, even with the help of template literals. Eventually in this comment form we’d want to have photos and the names of the authors, and you can already probably guess how crowded it would get with all that information. We also won’t have any useful syntax highlighting within that string.

上記のコードのように、コンポーネントにテンプレートを与えると便利なことは確かなのですが、文字列を延々書いていくことになるので、限界があります。最終的にはこのコメントフォームにコメントした人の画像と、名前を入れたいと思っているのですが、そうするとかなりソースがごちゃごちゃしそうですよね。しかし、残念なことに、コンポーネントにテンプレートを与えて、その中に文字列を書いていく方法では、これ以上うまく書く構文はありません。

With all those things in mind, let’s create a template. We’ll wrap some regular HTML in special script tags and use an id to reference it to create a component. You can see that this is a lot more legible when we have a lot of text and elements:

こういった問題を念頭において、テンプレートをつくってみましょう。普通の  HTML を特別なスクリプト・タグで包み込み、これをidで参照することでコンポーネントを作ります。こうすることで可読性がさらに高まり、テンプレートの中に沢山の要素を配置して文字列が長くなっても理解できます。

See the Pen Photo App post with Vue.js by Sarah Drasner (@sdras) on CodePen.

ここまでの訳者の個人的なまとめ

props と template をコンポーネントに与える書き方。

See the Pen used vue.js matome1 by nakanishi (@nakanishi) on CodePen.

HTML 内に script としてテンプレートを規定して、コンポーネントから ID でそれを参照する記法。

See the Pen used vue.js matome2 by nakanishi (@nakanishi) on CodePen.

コンポーネントにそれぞれ個別の値を持たせるためには、コンポーネントの定義にfunction(){return{オブジェクト}}を与える。そうすれば、コンポーネントが作成された時に、そのインスタンスにこの値が与えられる。factory 関数。

See the Pen used vue.js matome3 by nakanishi (@nakanishi) on CodePen.

Slots

This is a lot better. But what happens when we have two components with slight variations, either content or style deviations? We could pass all the different content and styles down into the component with props, and switch everything out each time, or we could fork the components themselves and create different versions of them. But it would be really nice if we could reuse the components, and populate them with the same data or functionality. This is where slots come in really handy.

上記のサンプルはかなりよくなりました。でも、微妙に異なる2つのコンポーネントを使用したい場合、どうすればいいでしょうか?コンテンツやスタイルが少し異なる場合です。今のところ props を使って、それぞれ微妙に異なるコンテンツやスタイルを渡すことで実現してきました。しかしもっと簡単にそれを実現する方法があります。それが slot です。

Let’s say we have a main app instance using the same <app-child> component twice. Inside each child we want some of the same content, and some different content. For the content we want to stay consistent, we would use a standard p tag, and for the content we want to switch out, we’ll put an empty <slot></slot> tag.

同じコンポーネントを二回使いながら、しかしそれぞれに異なるコンテンツと、それからもちろん共通するコンテンツを含むようにしていきましょう。次のコード例では、変化しない共通部分を通常の p タグで記述し、入れ替えたい部分を slot タグで記述しました。

Then, in the app instance, we can pass content inside the <app-child> component tags and it will automatically fill up the slots:

そうすることで、<app-child>コンポーネントの中に記述したコンテンツを(訳注:例えば<h3>this is slot..</h3>や<small>I can put more ..</small>など)自動的にスロットの中に挿入してくれるのです。

You can have default content within slots as well. If, in the slot itself, rather than writing <slot></slot>, you can populate it with:

slot に対してデフォルトのコンテンツを設定することもできます。次のように記述してください。

<slot>I am some default text</slot>

That default text will be used until you fill the slot with other material, which is so useful! High fives all around.

デフォルトテキストは、slotに何もないときにだけ使用されます。便利ですね!やった!

You can also have named slots. If you were to have two slots in a component, you could differentiate between them by adding a name attribute <slot name=”headerinfo”></slot> and we could access that particular slot by writing <h1 slot=”headerinfo”>I will populate the headerinfo slot!</h1>. This is extremely useful. If you have multiple slots that are named and one that isn’t, Vue will put the named content into the named slots, and whatever is left will be used to fill the remaining unnamed slots.

名前を slot につけることもできます。コンポーネント内に2つのスロットがあるとすれば、名前をつけることで識別することができます。<slot name=”headerinfo”></slot>と名前を付けたのならば、<h1 slot=”headerinfo”>とすることで、そのスロットにアクセスできます。これはとても便利です。名前が付けられたスロットとそうではないものがある場合、名前を指定したコンテンツは対応する名前付きのスロットに配置され、なにも指定しなかったコンテンツは、名前がついていないスロットに配置されます。

Here’s an example of what I mean:

Personally, if I am using more than one slot at a time, I will name all of so that it’s super clear what is going where for other maintainers, but it’s nice that Vue provides such a flexible API.

個人的には複数のスロットをコンポーネントに使う場合には全てのスロットに名前を付けます。そうしたほうが、他の誰かがメンテナンスをする時にわかりやすいからです。でも、Vue.js が柔軟な API を用意してくれているのもとてもいいと思います。

Slots example

Alternatively, we can have particular styles assigned for different components, and keep all of the content inside the same, therefore quickly and easily changing out the appearance of something. In the wine label maker below, one of the buttons will toggle the component and color based on what the user selects, and the background of the bottle and label and text will all switch, while keeping the content within stable.

ほかのアプリケーションをつくってみましょう。今度は、コンテンツは全てキープしたまま、異なるコンポーネントに切り替えてみます。つまり見た目などだけを簡単に切り替えれるようなアプリケーションです。次のコードは、ワインのラベルを作るアプリケーションで、ボタンを押すとコンポーネントが切り替わります。ユーザーが選んだ色にバックグランドが変わって、ラベルやテキストのスタイルもユーザーがボタンで選んだ内容によって切り替わります。しかし、その中のコンテンツは一定のままにしたいとしましょう。

Now, we’re putting all of the SVG image data in the main app, but it’s actually placed inside the <slot> in each component. This allows us to switch out pieces of content, or style things differently based on the usage, which is a really nice feature. You can see that we’ve allowed the user to decide which component they’ll be using by creating a button that changes the “selected” value of the component.

SVG 画像のデータは全てメインの HTML ファイルに書いてあります。これはスロットを介して渡されています。こうすることで見た目や一部のコンテンツを切り替えることができます。素晴らしい機能です。このアプリケーションはユーザーにどのコンポーネントを使うのか、ボタンで選ばせています。このボタンはコンポーネント選択のために参照している”selected”の値変更します。

Right now we have everything in one slot but we could also use multiple slots, and differentiate them through naming if we’d like:

いまはスロットは一つだけですが、もちろん名前をつけて複数のスロットを使うこともできます。

We can switch between different components with the same referenced slots easily, but what happens when we want to be able to switch back and forth, but hold on to the individual state of each component? Currently, when we switch between black and white the templates switch out and the content stays the same. But maybe we have a situation where we want the black label to be completely different than the white label. There’s a special component you can wrap it in called <keep-alive></keep-alive> that will retain the state as you switch.

違うコンポーネントに切り替える場合、スロットの中身を同じものにするのは簡単です。しかし「それぞれ」のコンポーネントの状態を残したまま、コンポーネントを切り替えるにはどうしたらいいでしょうか。今のコードだと、コンポーネントを黒から白に切り替えた場合、テンプレートが切り替わりますが、コンテンツはそのままです。しかし黒のラベルのコンテンツと白のラベルのコンテンツをそれぞれ完全に分けたいこともあるはずです。そんな場合には<keep-alive><keep-alive>でラップすることで、コンポーネントを切り替えても、コンテンツをそれぞれキープしてくれます。

Check out this deviation of the example above- create a black label, and then a different white label, and switch between them. You will see that the state of each is preserved, and are different from one another:

下記のコードと上記のコードの挙動の違いを確認しましょう。黒いラベルを作成した後に、白いラベルにいって違う内容を作ります。そして両者を切り替えてください。それぞれの状態が保存されているのが確認できるはずです。それぞれの状態は異なっていますね。

See the Pen used simpler Vue Wine Label Maker by nakanishi (@nakanishi) on CodePen.

I love this feature of the API.

This is all nice, but for simplicity’s sake, we’ve been sticking everything in one or two files. It would be much better organized while we build out our site if we could separate the components out into different files, and import them as we need them, and truly that’s how real development in Vue is typically done, so let’s run through that next. Tune in for the next part when we talk about Vue-cli, build processes, and Vuex for state management!

私はこの API を愛しています。これでもとても素晴らしいのですが、問題を簡単にするために1つか2つのファイルの中に全てを詰め込んでしまいました。しかし、そうするよりもさらに整頓できる方法があります。コンポーネントを別のファイルに分けて、それを必要な時にインポートするという方法です。これが実際の開発現場で Vue を使う方法です。次回はこれをやってみましょう。次回は Vue-cli、ビルド、Vuex による状態管理を扱います。

ここまでの訳者の個人的なまとめ

<component :is=”状態を参照”>で、動的にコンポーネントを変えることが出来る。参照している状態が変われば、コンポーネントが変わる。

コンポーネントの中でスロットを使ってコンテンツを挿入する場合、状態とバインドした値を使っていれば、コンポーネントが変わっても、バインドされているので残される。

See the Pen used simpler dynamic component by nakanishi (@nakanishi) on CodePen.

さらに変化するコンポーネントをキープアライブで囲っておくと、コンポーネントごとに値を保ってくれる?ので、白から黒にコンポーネントを変えても、白のコンポーネントの中身は保存されており、黒から白へ戻した時に、フォントカラーや文字の内容が戻る。

See the Pen used simpler dynamic component keep-alive by nakanishi (@nakanishi) on CodePen.

名前について

キャメルケース(superMan)とケバブケース(super-man)を使い分ける必要性について。HTMLは大文字小文字を区別しないので、HTMLの中で、属性を記述するときにはケバブケースで書くこと。

  • コンポーネント名はキャメルケースで書き(childFood)、HTMLの中でカスタム要素のように書く場合にはケバブケースで書けば(child-food)、何故か対応するようにしてくれる。
  • propsについても同様で、コンポーネントの中ではhelloWorldと書いて、HTMLの中では:hellow-world=””と書けば対応する。なお、{{}}の仲やv-bind=””の中はJSになるので、キャメルケースで書いて良い。むしろキャメルケースじゃないと正確に認識しない。

See the Pen used vue.js camel by nakanishi (@nakanishi) on CodePen.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です