はじめに
この記事は、Makuake Product Team Advent Calendar 2018 16日目の記事です。
はじめまして。今年11月に株式会社マクアケにジョインした@ryosuke-morishigeです。
まだ、ジョインから2ヶ月未満ですが、初アドベントカレンダーや新オフィス移転(こちらは後日紹介記事があると思いますのでお楽しみに!!)などイベント目白押しで良い意味で目が回っております。
Makuakeでは、現在UIライブラリとして主にVue.js
を採用しております。以前はReact
しか扱ったことのなかった私は開発参加にあたってVue.js
を学ぶ必要がありました。
というわけで、今回はReactエンジニアに送る10分ちょっとで(大体)わかるVueコンポーネントというお題目で、半ば自分の備忘録も兼ねて書かせていただきます。
想定読者
- Reactはそこそこ書ける・理解している人
- 最近Vue流行り始めたし気になっている人
- 初歩的なところは飛ばしてさっと概要だけ掴みたい人
書かないこと
- 環境構築周り:とりあえず触りたいならvue-cli入れればいいと思います
- Flux(Vuex)周り:話が長くなるので
- 単体テスト周り:現場次第ですがReactエンジニア的にはJest + vue-test-utilsでいいと思います
本題
VueとReactを比較しつつ、簡単に要点のみ紹介していきます。Reactの記法については様々あると思いますが一例ということでご容赦ください。
単一ファイルコンポーネント
<template>
<div class="greeting">
{{greeting}}
</div>
</template>
<script>
export default {
name: 'Greeting',
props: {
greeting: {
type: String,
required: true,
}
}
};
</script>
<style scoped>
.greeting {
color: blue;
}
</style>
Vueには単一ファイルコンポーネント以外の簡単な方法も用意されていますが、Reactを書いたことがあるならばこちらの方が馴染みにやすいと思います。単一ファイルコンポーネントとは.vue
で示されるファイルの中にtemplate
とscript
、style(css, scssなど)
をひとまとめに定義したものです。ReactだとStyleは置いておくとして、.jsx
なファイルの中に書いてきたものをイメージするとよいです。
template
HTML準拠なテンプレートです。いくつかVue独自のパラメータや要素が存在し、アクションの割当や表示の条件分岐を行っていきます。
文字展開
<template>
<div>{{ msg }}</div>
</template>
二重中括弧で指定します。括弧内には<script>
に定義されているdata
, props
, computed
などの値が指定できます。特に指定しない限りは値が変更されれば自動的に再レンダリングされます。(これは文字展開だけに限らず、バインディングされた値が変更されると再レンダリングが走ります。)
if
- Vue
<template>
<div v-if="isShow">Hello</div>
</template>
Reactだと
{isShow && <div>Hello</div>}
const hello = isShow ? <div>Hello</div> : null;
render() {
return hello;
}
if-else
<template>
<div v-if="isBlue">Blue</div>
<div v-else>Red</div>
</template>
Reactだと
{isBlue ? <div>Blue</div> : <div>Red</div>}
const color = isBlue ? <div>Blue</div> : <div>Red</div>;
render() {
return color;
}
for-in
<template>
<template v-for="item in items">
<div key="item.id">{item.name}</div>
</template>
</template>
※ <template>
はブラウザ上には反映されない要素です。
Reactだと
{items.map((item) => (<div key={item.id}>{item.name}</div>))}
コンポーネント呼び出し
<template>
<div>
<awesome-component />
</div>
</template>
<script>
import AwesomeComponent from '@/AwesomeComponent';
default export {
name: 'Root',
components: {
AwesomeComponent
}
}
</script>
コンポーネントファイルをimportして、コンポーネント名を独自タグとして呼び出すところはReactと同じですね。Vueの場合は<script>
内でcomponents
に指定してあげる必要があります。
<template>
内で呼び出す場合は、若干面食らいますがコンポーネント名をケバブケース(kebab-case
)にして記述するのがスタンダードです。(Reactのようにキャメルケースで呼び出すことも可能です。)
アクション指定
<template>
<button v-on:click="handleClick">Click me</button>
<!-- 略記法 -->
<button @click="handleClick">Click me</button>
</template>
<script>
default export {
name: 'Root',
methods: {
handleClick(e) {
e.preventDefault();
console.log('click');
}
}
}
</scirpt>
Reactだと
handleClick = (e) => {
e.preventDefault();
console.log
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
slot
子コンポーネントをwrapするようなコンポーネントに使用します。
<template>
<div class="something-wrapper">
<slot />
</div>
</template>
Reactだと
render() {
return <div>{children}</div>;
}
script
メソッドや呼び出すコンポーネントなどを記述する箇所です。
name
コンポーネントの名称を指定します。独自タグ等として扱われます。基本的にUpperCamelCase。
<script>
default export {
name: 'ComponentName'
}
</script>
Reactだと
class ComponentName extends React.Component {
/* 略 */
}
// or
const ComponentName = (props) => {
/* 略 */
}
props
Reactの同名のものと同じ役割を持ちます。基本的にlowerCamelCase。
<template>
<div v-if="isShow">{{message}}</div>
</template>
<script>
export default {
name: 'ComponentName',
props: {
message: {
type: String,
required: true
},
isShow: {
type: Boolean,
default() {
return true;
}
}
}
}
</script>
typeやvalidationなどは必ずしも指定する必要はありません。(指定した方がコンソールで警告してくれるので安全ですが)
<script>
export default {
name: 'ComponentName',
props: ['message', 'isShow']
}
</script>
data
Reactのstate
に近い役割を持ちます。state
とは違い、setState()
を用いなくても変更時に再レンダリングが走ります。
<template>
<div @click="incriment">カウント:{{count}}</div>
</template>
<script>
export default {
name: 'ComponentName',
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count = this.count + 1;
}
}
}
</script>
computed
React(の標準)では類似のない機能です。算出関数を書いてあげると、依存する値が変更されたときに自動的に検出して再計算してくれます。
<template>
<div @click="incriment">カウント:{{count}}</div>
<div>2倍:{{doubleCount}}</div>
</template>
<script>
export default {
name: 'ComponentName',
data() {
return {
count: 0
}
},
computed: {
doubleCount() {
return this.count * 2;
}
}
methods: {
increment() {
this.count = this.count + 1;
}
}
}
</script>
methods
computed
、filter
にあたらない関数を実装するための箇所です。ちなみにアロー関数の使用は非推奨されています。
style
Vueは標準でコンポーネント内でCSSなどを用いたスタイル定義を行えます。通常のCSSと変わりなく記述できます。
<template>
<div class="blue">Hello</div>
</template>
<style>
.blue {
color: blue;
}
</style>
scoped
styleの適用範囲を自コンポーネントのみに抑えます。他コンポーネントのclass名を気にせず書けるようになるので非常に便利です。
<template>
<div class="blue">Hello</div>
</template>
// 他のコンポーネントには適用されない!!
<style scoped>
.blue {
color: blue;
}
</style>
最後に
ReactとVue.jsは同じUIライブラリですが、コンポーネントの書き方やAPIを並べると思想の違いの輪郭が浮かび上がってくるような気がします。
粗はあると思いますが、自分と同じVueの初学者の方に役立てていただければ幸いです!
また、株式会社マクアケは各種エンジニアを募集しております。興味を持っていただけた方がおられましたら、ぜひ「各種エンジニア募集! クラウドファンディングMakuake」からご連絡ください。
残すところ後2週間ですが、「Makuake Product Team Advent Calendar 2018」は毎日更新で続きます!明日もお楽しみに!