- 投稿日:2019-09-15T18:24:32+09:00
【Vue.js】ミックスインでコンポーネントオプションを追加する
ミックスインとは
- オプションを他のコンポーネントに混ぜ込む(マージする)機能
- 複数のコンポーネントで共通の処理を実行したい場合に使える
- ミックスインオブジェクトとコンポーネントの定義がコンフリクトした場合は、コンポーネントのデータが優先される
- フック(createdなど)は、ミックスインに定義されているものから先に呼び出される
- 1つのコンポーネントから複数のミックスインオブジェクトを呼び出すことも可能
- 複数のミックスインオブジェクト同士で定義がコンフリクトしている場合は、後で呼び出されているオブジェクトの定義が優先される
- 上記の場合、createフックは先に呼び出されているミックスインオブジェクトから実行される
補足
extendとdataオプション
extendを使用する場合、extendしたいオブジェクトのdataオプションの記述方法が少し異なります。
この記述方法だとエラーが発生します。
vue_mixin_test.html<script> data: { foo: 'abc', message: 'hello' }, </script>[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.メッセージの通り、dataオプションをfunctionで返せば問題ありません。
以下の記述方法はextendを使用しない場合も使えるので、こちらの方が無難かもしれません。vue_mixin_test.html<script> data: function () { return { foo: 'abc', message: 'hello' } }, </script>公式サイトにも説明があります。
ライフサイクルフック
createdフックなど、インスタンス生成時に初期化として実行される関数のことです。
ミックスインオブジェクトに定義されているフック関数は、コンポーネントのフック関数より先に呼び出されます。
ミックスインオブジェクトはこの時点で既にマージされているので、定義がコンポーネントとコンフリクトしている場合は、コンポーネントのデータが呼び出されます。フック関数は複数あり、それぞれ実行されるタイミングが異なります。
ソースコード
公式サイトからお借りしました。
vue_mixin_test.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_mixin_test</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <script> var mixin = { data: function () { return { hoge: 'hoge', foo: 'abc', // 重複するプロパティ message: 'hello' } }, methods: { fooMethod: function () { console.log('foo') }, // 重複するメソッド conflicting: function () { console.log('from mixin') } }, // this.$dataはmixinがマージされた後の状態で表示される created: function () { console.log('mixin', this.$data) this.fooMethod() this.conflicting() } } var mixin2 = { data: function () { return { foo: 'abc2', message: 'hello2' } }, methods: { fooMethod: function () { console.log('foo2') }, conflicting: function () { console.log('from mixin2') } }, created: function () { console.log('mixin2', this.$data) this.fooMethod() this.conflicting() } } // 複数のmixinを呼び出すことも可能 var Component = Vue.extend({ mixins: [mixin, mixin2] }) new Component() var vm = new Vue({ // mixinをマージする mixins: [mixin], // mixinと定義が重複している場合、コンポーネントのデータが優先される data: function () { return { bar: 'def', // 重複するプロパティ message: 'goodbye' } }, methods: { barMethod: function () { console.log('bar') }, // 重複するメソッド conflicting: function () { console.log('from self') } }, created: function () { console.log('self', this.$data) this.barMethod() this.conflicting() } }) vm.fooMethod() vm.barMethod() vm.conflicting() </script> </body>
- 投稿日:2019-09-15T16:44:02+09:00
Vue.js で Moment.js を使ってお手軽に日付フォーマットする
Vue.js のフィルタでフォーマット
Moment.js を使ったフィルタを用意すると以下のようにお手軽に日付のフォーマットが可能です。
<template> <h1>{{ new Date() | moment('LTS') }}</h1> </template>JavaScript の日付処理は罠が多い
Date#getYear
で1900年からの経過年数が返るDate#getMonth
で 0 ~ 11 が返る。JavaScript の日付処理ライブラリである Moment.js を使えばそういった罠を回避し、お手軽に JavaScript の日付を扱うことができます。
現在、Github のスターは 40000 を超え JavaScript の日付処理ライブラリでは最もメジャーなものの一つです。
フィルタの作り方
サンプルプロジェクトを作成します。
既存のプロジェクトに組み込む場合、ここは必要ありません。# Vue CLI のインストール ※すでにインストール済みなら必要なし $ npm install -g @vue/cli $ npm install -g @vue/cli # yarn global add @vue/cli $ cd 任意のディレクトリ $ vue create moment-filter $ cd moment-filterMoment.js をインストールします。
# Moment.jsをインストールする $ npm install moment --save # yarn add momentMoment.js を使ったフィルタを作成します。
import moment from "moment"; /* 中略 */ filters: { /** * @param {Date} value - Date オブジェクト * @param {string} format - 変換したいフォーマット */ moment(value, format) { return moment(value).format(format); } }上記で完成です。簡単。
テストの表示
テストデータを用意して試してみましょう。
data() { const formats = [ "MMMM Do YYYY, h:mm:ss a", "dddd", "MMM Do YY", "YYYY [escaped] YYYY", "LTS" ]; return { tests: new Array(formats.length) .fill(new Date()) .map((date, i) => ({ date, format: formats[i] })) }; }今回はとりあえず以下のように並べて表示してみます。
<template> <div id="app"> <h2 v-for="({date, format}, index) in tests" :key="index">{{date | moment(format)}}</h2> </div> </template>ローカルサーバーを立ち上げます。
$ npm run serve # yarn serveフォーマットされた日付が表示できました。
今回作成したコンポーネントの全体は以下のとおりです。
./src/App.vue<template> <div id="app"> <h2 v-for="({date, format}, index) in tests" :key="index">{{date | moment(format)}}</h2> </div> </template> <script> import moment from "moment"; export default { name: "app", data() { const formats = [ "MMMM Do YYYY, h:mm:ss a", "dddd", "MMM Do YY", "YYYY [escaped] YYYY", "LTS" ]; return { tests: new Array(formats.length) .fill(new Date()) .map((date, i) => ({ date, format: formats[i] })) }; }, filters: { moment(value, format) { return moment(value).format(format); } } }; </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; } </style>
- 投稿日:2019-09-15T16:32:23+09:00
plunkerでvue その15
概要
plunkerでvueやってみた。
ライフサイクル、調べてみた。成果物
https://embed.plnkr.co/Ojw2bGF1l52QnVXjh3oF/
以上。
- 投稿日:2019-09-15T14:30:53+09:00
【Vue.js】computedとmethodとwatchの使い分け
以下3つのオプションの区別が曖昧だったので、メモしておきます。
ソースコードは公式サイトよりお借りしました。computedオプション
- 算出プロパティ
- リアクティブな依存関係にもとづきキャッシュされる
- リアクティブな依存関係が更新されたときにだけ再評価されるので、逆に言えばリアクティブな依存がない場合は二度と更新されない
- ゲッター(getter関数)とセッター(setter関数)の両方が利用できる(デフォルトはゲッターのみ)
ゲッターとセッター
computedでセッターを使用した場合、dataオプションで設定されたプロパティを更新することもできます。
ゲッター
特定のプロパティ値を取得するためのメソッド
セッター
特定のプロパティ値を設定するためのメソッド
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こりますが、nowプロパティはリアクティブな依存関係にないため、最初に読み込まれた以降は変化しません。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, computed: { // fullName/now: 算出プロパティ名 fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>methodsオプション
- メソッド
- 再描画が起きると常に関数を実行する
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こるので、それに合わせてnowプロパティも変化します。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName() }}</p> <p>now: {{ now() }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, methods: { fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>watchオプション
- 監視プロパティ
- 既にセットされているプロパティを監視する
監視するプロパティの名前
と、そのプロパティに変化(トリガー)があった場合に実行する関数(ハンドラ)
を対にして指定する- 関数は、更新後・更新前のプロパティの値を引数に取ることができる
- 処理は実行するが、データは返さない
- computedでは行えないコストの高い処理を実行できる
以下、公式サイトより。
この場合では、watch オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。
ソースコード
fullNameプロパティの表示はwatchオプションで描画することもできますが、逆に冗長なコードになってしまいます。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '', // プロパティはあらかじめセットしておく fullName: '', now: '', }, watch: { // firstName/lastName/now: 監視対象のプロパティ名 // newValue: 更新後のプロパティの値 // oldValue: 更新前のプロパティの値 firstName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = newValue + ' ' + this.lastName }, lastName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = this.firstName + ' ' + newValue }, // 監視しているが、値に変化がないので変更されることはない now: function () { return Date.now() } } }) </script> </body>参考サイト
- 投稿日:2019-09-15T14:30:53+09:00
【Vue.js】computedとmethodsとwatchの使い分け
以下3つのオプションの区別が曖昧だったので、メモしておきます。
ソースコードは公式サイトよりお借りしました。computedオプション
- 算出プロパティ
- リアクティブな依存関係にもとづきキャッシュされる
- リアクティブな依存関係が更新されたときにだけ再評価されるので、逆に言えばリアクティブな依存がない場合は二度と更新されない
- ゲッター(getter関数)とセッター(setter関数)の両方が利用できる(デフォルトはゲッターのみ)
ゲッターとセッター
computedでセッターを使用した場合、dataオプションで設定されたプロパティを更新することもできます。
ゲッター
特定のプロパティ値を取得するためのメソッド
セッター
特定のプロパティ値を設定するためのメソッド
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こりますが、nowプロパティはリアクティブな依存関係にないため、最初に読み込まれた以降は変化しません。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, computed: { // fullName/now: 算出プロパティ名 fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>methodsオプション
- メソッド
- 再描画が起きると常に関数を実行する
ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こるので、それに合わせてnowプロパティも変化します。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName() }}</p> <p>now: {{ now() }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, methods: { fullName: function () { return this.firstName + ' ' + this.lastName }, now: function () { return Date.now() } } }) </script> </body>watchオプション
- 監視プロパティ
- 既にセットされているプロパティを監視する
監視するプロパティの名前
と、そのプロパティに変化(トリガー)があった場合に実行する関数(ハンドラ)
を対にして指定する- 関数は、更新後・更新前のプロパティの値を引数に取ることができる
- 処理は実行するが、データは返さない
- computedでは行えないコストの高い処理を実行できる
以下、公式サイトより。
この場合では、watch オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。
ソースコード
fullNameプロパティの表示はwatchオプションで描画することもできますが、逆に冗長なコードになってしまいます。
vue_test01.html<!DOCTYPE> <head> <meta charset="UTF-8"> <title>Vue.js_test01</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input v-model="firstName" placeholder="firstName"> <input v-model="lastName" placeholder="lastName"> <p>firstName: {{ firstName }}</p> <p>lastName: {{ lastName }}</p> <p>fullName: {{ fullName }}</p> <p>now: {{ now }}</p> </div> <script> var vm = new Vue({ el: '#app', data: { firstName: '', lastName: '', // プロパティはあらかじめセットしておく fullName: '', now: '', }, watch: { // firstName/lastName/now: 監視対象のプロパティ名 // newValue: 更新後のプロパティの値 // oldValue: 更新前のプロパティの値 firstName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = newValue + ' ' + this.lastName }, lastName: function (newValue, oldValue) { console.log(newValue, oldValue); this.fullName = this.firstName + ' ' + newValue }, // 監視しているが、値に変化がないので変更されることはない now: function () { return Date.now() } } }) </script> </body>参考サイト
- 投稿日:2019-09-15T13:59:38+09:00
vue/dist/vue.esm.js って何~【とりあえず動くからいいや】からの卒業~
- イチロー 功労セレモニーでの英語スピーチ - YouTube をBGMにモチベーション爆上がり中のsukezaneです。
本記事執筆の経緯
- こちらの素晴らしいチュートリアル記事の中で筆者さんがわからないって言っていたモノに対して調べようと思ったことがきっかけ
- Vue.jsとRailsでTODOアプリのチュートリアルみたいなものを作ってみた
- ※本記事のタイトルは決して上記の筆者さんを否定するものではないですし、むしろ自分で調べなかったら絶対気にすることなかったので感謝です!!
何についての記事か
vueを読み込む際に使っているコレ
import Vue from 'vue/dist/vue.esm.js'
対象読者
- vueの入門者(わたし)
どうやって使っているか
app/javascript/packs/todo.jsimport Vue from 'vue/dist/vue.esm.js' import Header from './components/header.vue' var app = new Vue({ el: '#app', components: { 'navbar': Header, } });こんな感じで意味もわからずなんとなく使っている
でも時々
import Vue from 'vue'
って書いてるやん?何が違うか
import Vue from 'vue/dist/vue.esm.js'
: 完全ビルド(vue.esm.js)import Vue from 'vue'
: ランタイム限定ビルド(vue.runtime.esm.js)って呼ぶらしい。
何が違うかPart2
公式より抜粋
ランタイム + コンパイラとランタイム限定の違い
もしクライアントでテンプレートをコンパイルする必要がある (例えば、 template オプションに文字列を渡す、もしくは DOM 内の HTML をテンプレートとして利用し要素にマウントする) 場合は、コンパイラすなわち完全ビルドが必要です。
hoge.js// これはコンパイラが必要です new Vue({ template: '<div>{{ hi }}</div>' }) // これはコンパイラは必要ありません new Vue({ render (h) { return h('div', this.hi) } })どうすればいいのか1
html側にテンプレートの参照を書かずに単一ファイルコンポーネント側にまとめ、html側ではマウントするDOMの情報だけを書くようにする
例)クリックした時に何かするやつ(修正前)
click.html<div id=#app> <button v-on:click="hoge">Click me!</button> </div>click.jsimport Vue from 'vue/dist/vue.esm.js' const app = new Vue({ el: "#app", methods: { myClick() { alert("click") } } })
- 例)クリックした時に何かするやつ(修正後)
click.html<div id=#app></div>click.jsimport App from '../app.vue' document.addEventListener('DOMContentLoaded', () => { const app = new App app.$mount('#app') })click.vue<template> <button v-on:click="hoge">Click me!</button> </template> <script> import Vue from 'vue' export default Vue.extend({ methods: { myClick() { alert("click") } } }) </script>疎結合になってメンテもしやすそう!!
どうすればいいのか2
- 一応エイリアスを作成すれば完全ビルドの書き方のままでも動くんだけど
ランタイム限定ビルドは完全ビルドに比べおよそ 30% 軽量
と公式にあるため基本は1がいいと思うが
30%くらい別にいいわ
っていうときはエイリアスを作成すればimport Vue from 'vue'
としてimportできるようになります。(実態は完全ビルドなんですけどね
)エイリアスを作成する(パフォーマンス30%減)
webpack
の場合webpack.config.jsmodule.exports = { // ... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' webpack 1 用 } } }2年目も後半になったので
とりあえず動くからいいや
は卒業しようと思います。なんかあったらコメントくださいませ
以上。
- 投稿日:2019-09-15T12:41:19+09:00
TypeScript導入(Vue.js)
Vue.jsで開発したアプリケーションにTypeScriptを導入したので、その備忘録を。
TypeScriptをinstall
globalでinstallする記事がありましたが、ここはプロジェクトごとにinstallさせました。
$ yarn add -D typescripttsconfig.jsonを作成と設定
$ tsc --init設定項目
- コンパイラが出力するJavaScriptのバージョンはes5を対象とする
- ブラウザ向けのプログラムなので
DOM
を指定- source mapをtrueにする
- 厳格モードをtrue(必須)
- 対象ファイルをsrcディレクトリのtsファイルにする
- node_moduleの中のtsファイルは対象外
- angularのテストファイルは対象外
tsconfig.json"compilerOptions": { "target": "es5", "module": "es2015", "lib": ["DOM", "ES2015"], "sourceMap": true, "strict": true, "moduleResolution": "node", }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ]webpackでtsファイルをバンドル対象にする
webpackでバンドルするときにtsファイルも対象にしたいので指定
- ts-loaderをinstall
- webpackでtsファイルをバンドル対象にする
- ファイルをimportするときに拡張子を指定しなくてもいいようにextensionsを指定
$ yarn add -D ts-loaderwebpack.config.jsmodule: { rules: [ { test: /\.ts$/, use: 'ts-loader', }, ] } resolve: { extensions: ['.ts'], },npmのタスクランナーでwatchさせる
package.json"scripts": { "start": "concurrently \"webpack-dev-server --config webpack.dev.js\" \"yarn run tsc -- -w\"", "tsc": "tsc -p ./" }設定時のエラー
コンパイルしたときに下記のエラーが出た場合
moduleResolutionの設定
File change detected. Starting incremental compilation... [1] [1] node_modules/@types/babel__core/index.d.ts(13,20): error TS2307: Cannot find module '@babel/types'. [1] node_modules/@types/babel__core/index.d.ts(14,31): error TS2307: Cannot find module '@babel/parser'. [1] node_modules/@types/babel__generator/index.d.ts(9,20): error TS2307: Cannot find module '@babel/types'. [1] node_modules/@types/babel__template/index.d.ts(9,31): error TS2307: Cannot find module '@babel/parser'. [1] node_modules/@types/babel__template/index.d.ts(10,54): error TS2307: Cannot find module '@babel/types'. [1] node_modules/@types/babel__traverse/index.d.ts(10,20): error TS2307: Cannot find module '@babel/types'.
tsconfig.json
のmoduleResolution
をnode
にすると解決tsconfig.json"compilerOptions": { "moduleResolution": "node",moduleの設定
Uncaught ReferenceError: exports is not defined Object.defineProperty(exports, "__esModule", { value: true });
tsconfig.json
のmodule
をes2015
にすると解決tsconfig.json"compilerOptions": { "module": "es2015",参考サイト
- 投稿日:2019-09-15T09:40:37+09:00
plunkerでvue その14
概要
plunkerでvueやってみた。
xhrでFORMをPOSTしてみた。成果物
https://embed.plnkr.co/9YE7qy8by9gaa86dVr2r/
以上。
- 投稿日:2019-09-15T09:21:04+09:00
plunkerでvue その13
概要
plunkerでvueやってみた。
axiosでFORMをPOSTしてみた。成果物
https://embed.plnkr.co/zfmrxh5coVlJz1DH8UFU/
以上。
- 投稿日:2019-09-15T01:20:20+09:00
便利ページ:Javascriptでカラーピッカー
便利ページ:Javascriptでちょっとした便利な機能を作ってみた のシリーズものです。
今回は、色の選択とRGB値の表示です。
HTMLにカラーピッカーがあるので、それを使います。また、プリセットカラーで選択できるようにします。こんな感じのページです。
毎度の通り、デモページとGitHubです。
GitHub
https://github.com/poruruba/utilitiesデモページ
https://poruruba.github.io/utilities/(2019/9/15 追記)
一番近い色の名前を検索できるようにしました。色差を計算するのに以下を使わせていただきました。
https://gka.github.io/chroma.js/
一口に色と言っても奥が深いですね。カラーピッカーを表示する
HTMLは以下の通りです。
index.html<div class="form-inline"> <input type="color" v-on:change="color_change" v-model="color_value"> <label>RGB:</label> <input type="text" size="7" class="form-control" v-model="color_value"> </div> <br> <table class="table table-borderless"> <td v-bind:bgcolor="color_value"> </td> <td><label>R:</label><input type="range" min="0" max="255" v-model.number="color_r" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_r" v-on:change="color_range"></td> <td><label>G:</label><input type="range" min="0" max="255" v-model.number="color_g" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_g" v-on:change="color_range"></td> <td><label>B:</label><input type="range" min="0" max="255" v-model.number="color_b" v-on:input="color_range"><input type="number" class="form-control" v-model.number="color_b" v-on:change="color_range"></td> </table>カラーピッカーは、type="color"のinputです。
あとは、color_valueに格納されたRGB値をテーブルのセルの背景色として表示しています。
type="range"のinputのゲージでもRGBの各値を変更できるようにしました。
ここらへんは、Vueのデータバインディング機能が大活躍です。基本16色などのテーブルがありますが、プリセットとしてcolor.jsに配列として格納しておいたものを、テーブル表示しているだけです。
単純ですね。。。
以上