- 投稿日:2020-03-28T23:48:26+09:00
【Vue.js】XserverにVue CLIで作った超簡易ページをデプロイしてみた。
はじめに
Vue CLIを使ったポートフォリオの制作は初めてだったので、とりあえず制作に取り掛かる前にデプロイができるのかどうか試してみました。
(せっかく作ったのにデプロイできないなんてなったら悲しすぎるので。。。笑)
結論、割と簡単にできました◎
制作アプリに関して
猫本を参考に、Vue CLIでVue Routerを用いた簡易ページをとりあえず作りました(本当に簡易的です。)ナビバーにHomeとお問い合わせボタンがあり、そこでビューを切り替えるだけです。
(もはやjQueryでやれ。。。)Vue CLIで制作したアプリのリリースには、npm run buildでまずビルドする必要があるみたいです。
distディレクトリが作成され、その中にビルドされたファイルがあります。
ところがアクセスしたところ表示されない。。。
どうやら
config/index.js
を以下のように書き換える必要があるとのこと。
// assetsPublicPath: '/', assetsPublicPath: '',
あとはdistフォルダをじぶんの好きな名前(URLに反映される)に変えてFTPでアップロード。僕の場合はXserver(WordPressに利用しているドメイン)を使っているのでpublic_htmlにhomeというフォルダ名でアップロードしました。
(ドメイン名)/home(アップロードしたフォルダ名)でアクセス。
無事表示されました◎
参考サイト
vue-cliでwebアプリケーションを作って、GitHubPagesで無料で爆速でリリースした話
- 投稿日:2020-03-28T23:30:12+09:00
Vue.js私的備忘録まとめ
Vue.jsの私的なまとめです。
テンプレート
私的テンプレ
import XxxComponent from './components/XxxComponent' import xxxMixins from './mixins/xxxMixins' new Vue({ el: '#app', components: { 'xxx-component': XxxComponent }, props: { item: { type: Object, required: true, default: () => { return { data: [] } } } }, data () { return { title: 'タイトル', item: [] } }, created () { // }, mounted () { // }, watch: { item (value, oldValue) { // }, item: { handler (value, oldValue) { // }, deep: true } }, computed: { xxx (param) { } }, methods: { /** * 説明 * @param {string} param 内容 * @return {string} return 内容 */ xxx (param) { } }, mixins: [xxxMixins] })el
elはマウントする要素
el: '#app'
el: '#app'
としたらHTMLのid="app"
の中にVue.jsで使いたいHTMLを書く<div id="app"> // 中略 </div>data
dataはvue.jsで使うデータを定義する
data () { return { title: 'タイトル', list: { data: [] } } }マスタッシュ構文
- HTMLの中に
{{ }}
でdataやcomputedを表示させる$data
を付けるとdataやcomputedと区別が付きやすくなるdata () { return { text: 'タイトル' } }<h1>{{ $data.text }}</h1>v-for
v-forで配列からHTMLにループ処理ができる
data() { return { list: [ 'りんご', 'ばなな', 'すいか' ] }; }<ul> <li v-for="item in $data.list">{{ item }}</li> </ul>v-model
<input type="text" v-model="$data.text">v-text
<p v-text="$data.text"></p>v-if
v-if="!error"
v-if="text !== 'OK'"
などの書き方もできる<div v-if="error"> <p>エラー</p> </div>v-show
<div v-show="error"> <p>エラー</p> </div>v-ifとv-showの違い
- v-showはCSSのdisplay要素が変わる
- v-ifはHTML要素が変わる
- 頻繁に変わる場合はv-showを使う
created
- createdはelとDOM作成前
created () { // 処理 }mounted
- mountedはDOM作成後
mounted () { // 処理 }createdとmountedの違い
- createdはelとDOM作成前
- mountedはDOM作成後
- 詳しくはライフサイクルダイアグラムを参照
watch
- watchは変更があったら処理される
- 第1引数が変更後の値
- 第2引数が変更前の値
watch: { item (value, oldValue) { // 処理 } }
- 配列はhandlerを使う
- deepネストされた値もみる
watch: { item: { handler (value, oldValue) { // 処理 }, deep: true } }computed
- computedはキャッシュされる
computed: { xxx (param) { // 処理 } }methods
- methodsにはJSDocを入れる
methods: { /** * 説明 * @param {string} param 内容 * @return {string} return 内容 */ xxx (param) { // 処理 } }mixins
- 他のファイルからインポートできる
- 共通部分などに使う
import xxxMixins from './mixins/xxxMixins' new Vue({ mixins: [xxxMixins] })components
<xxx-component>import XxxComponent from './components/XxxComponent' new Vue({ components: { 'xxx-component': XxxComponent }, })props
- componentsに受け渡す値を定義する
- type:型
- required:必須かどうか
- default:初期値
props: { item: { type: Object, required: true, default: () => { return { data: [] } } } }template
import template from './templates/XxxTemplate.html' export default { template: ` <div> <p>test</p> </div> ` }
- templateは別ファイルにすることができる
- HTMLを分けたほうがHTMLの可読性が良くなる
import template from './templates/XxxTemplate.html' export default { template: template }$emit
- 親コンポーネントにmethodsを動かす
action (param) { this.$emit('change-emit', param) }<xxx-component @change-emit="action" ></xxx-component>axios
- Ajaxで使う
getData () { const action = '/api/' const params = { params: 'xxx' } axios.get(action, params) .then(response => { // 成功時 }).catch(error => { console.error(error) // エラー時 }) }transition
- transitionはアニメーションと使うことができる
<transition name="fade"> <div v-show="error"> <p>エラー</p> </div> </transition>
- CSSが必要
.fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; }
- 投稿日:2020-03-28T23:30:12+09:00
Vue.js私的まとめ
Vue.jsの私的なまとめです。
テンプレート
私的テンプレ
import XxxComponent from './components/XxxComponent' import xxxMixins from './mixins/xxxMixins' new Vue({ el: '#app', components: { 'xxx-component': XxxComponent }, props: { item: { type: Object, required: true, default: () => { return { data: [] } } } }, data () { return { title: 'タイトル', item: [] } }, created () { // }, mounted () { // }, watch: { item (value, oldValue) { // }, item: { handler (value, oldValue) { // }, deep: true } }, computed: { xxx (param) { } }, methods: { /** * 説明 * @param {string} param 内容 * @return {string} return 内容 */ xxx (param) { } }, mixins: [xxxMixins] })el
elはマウントする要素
el: '#app'
el: '#app'
としたらHTMLのid="app"
の中にVue.jsで使いたいHTMLを書く<div id="app"> // 中略 </div>data
dataはvue.jsで使うデータを定義する
data () { return { title: 'タイトル', list: { data: [] } } }マスタッシュ構文
- HTMLの中に
{{ }}
でdataやcomputedを表示させる$data
を付けるとdataやcomputedと区別が付きやすくなるdata () { return { text: 'タイトル' } }<h1>{{ $data.text }}</h1>v-for
v-forで配列からHTMLにループ処理ができる
data() { return { list: [ 'りんご', 'ばなな', 'すいか' ] }; }<ul> <li v-for="item in $data.list">{{ item }}</li> </ul>v-model
<input type="text" v-model="$data.text">v-text
<p v-text="$data.text"></p>v-if
v-if="!error"
v-if="text !== 'OK'"
などの書き方もできる<div v-if="error"> <p>エラー</p> </div>v-show
<div v-show="error"> <p>エラー</p> </div>v-ifとv-showの違い
- v-showはCSSのdisplay要素が変わる
- v-ifはHTML要素が変わる
- 頻繁に変わる場合はv-showを使う
created
- createdはelとDOM作成前
created () { // 処理 }mounted
- mountedはDOM作成後
mounted () { // 処理 }createdとmountedの違い
- createdはelとDOM作成前
- mountedはDOM作成後
- 詳しくはライフサイクルダイアグラムを参照
watch
- watchは変更があったら処理される
- 第1引数が変更後の値
- 第2引数が変更前の値
watch: { item (value, oldValue) { // 処理 } }
- 配列はhandlerを使う
- deepネストされた値もみる
watch: { item: { handler (value, oldValue) { // 処理 }, deep: true } }computed
- computedはキャッシュされる
computed: { xxx (param) { // 処理 } }methods
- methodsにはJSDocを入れる
methods: { /** * 説明 * @param {string} param 内容 * @return {string} return 内容 */ xxx (param) { // 処理 } }mixins
- 他のファイルからインポートできる
- 共通部分などに使う
import xxxMixins from './mixins/xxxMixins' new Vue({ mixins: [xxxMixins] })components
<xxx-component>import XxxComponent from './components/XxxComponent' new Vue({ components: { 'xxx-component': XxxComponent }, })props
- componentsに受け渡す値を定義する
- type:型
- required:必須かどうか
- default:初期値
props: { item: { type: Object, required: true, default: () => { return { data: [] } } } }template
import template from './templates/XxxTemplate.html' export default { template: ` <div> <p>test</p> </div> ` }
- templateは別ファイルにすることができる
- HTMLを分けたほうがHTMLの可読性が良くなる
import template from './templates/XxxTemplate.html' export default { template: template }$emit
- 親コンポーネントにmethodsを動かす
action (param) { this.$emit('change-emit', param) }<xxx-component @change-emit="action" ></xxx-component>axios
- Ajaxで使う
getData () { const action = '/api/' const params = { params: 'xxx' } axios.get(action, params) .then(response => { // 成功時 }).catch(error => { console.error(error) // エラー時 }) }transition
- transitionはアニメーションと使うことができる
<transition name="fade"> <div v-show="error"> <p>エラー</p> </div> </transition>
- CSSが必要
.fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; }
- 投稿日:2020-03-28T18:19:55+09:00
【Vue.js】猫本を参考に基本的なところをまとめてみた。
はじめに
2か月程前、Vue.jsに関して以下の本を用いて学習を始めました。
2週間ほど勉強して、少しだけアレンジして作成したToDoリストが以下になります。(Bootstrapでデザインして、状態ボタンを1つ追加しただけだけど)
https://chobimusic.com/vue_nekotodo_arrange/
その後、Laravelと組み合わせたポートフォリオ作成を目指して、Laravelの勉強に専念。
やっとこさ最近LaravelとVueの連携について理解できてきたので、とりあえずVue.jsで新しいポートフォリオのフロントサイドを作り始めようと思ったのですが。。。
手が一切動かない。全然覚えてない。悲しい。。。泣
2週間程度勉強したのですが、1か月経ち、気づけば頭からすっぽり抜けてしまっていました。。。
(記憶力なんてこんなもん)ってことで改めて復習がてらVue.jsの基本を記事にまとめてみました。(今後の備忘録も兼ねて)
※WordPressで体裁を整えたものをコピペしたところ、テーブルの形が崩れてしまいました。。。もし当記事が気になりましたら、以下よりご覧いただけますと幸いです。。。
https://chobimusic.com/vuejs_nekobook_summary/
Vue.jsの表示
まずはVue.jsでHello worldしてみる。
index.html
<div id="app"> <p>{{ message }}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="main.js"></script>
appの内側にテンプレートを書き込んでいく。
インターネットに接続できる環境であれば、Vue.jsのインストールにCDNが利用可能。
マスタッシュ記法を用いて{{}}内にプロパティ名を記述するとその値が描画される。
main.js
var app = new Vue({ el: '#app', data: { message: 'Hello world' } })
コンストラクタ(クラスのインスタンス生成時に実行されるメソッド)関数Vueを使ってルートとなるVueインスタンスを作成。data内のデータをテンプレート側に送っている。変数化するとコンソールからもアクセスできるが必須ではない。
実際の描画
<p>hello</p>
基本機能(ディレクティブ/組み込みコンポーネント)
v-bind
クラス、スタイル、属性などタグ内のバインドに使用。※Mustache{{}}はテキストコンテンツ特有の記法のため、タグ内で使用することができない。
<input type="text" v-bind:value="message">
省略パターン
<input type="text" :value="message">
v-for
リストデータを用いて要素を繰り返し描画する際に、繰り返したいタグに対して使用。
v-on
「クリックしたとき」「要素が変わったとき」などDOMイベントのハンドリング(イベント)を受け取り、処理を行う際に使用。※猫本では、イベントに紐づける処理の内容を「イベントハンドラ」、イベントハンドラをイベントと紐づけることを「ハンドル」と呼んでます。
<button v-on:click="handleClick">クリック</button> <!--handleClickはコンポーネントのmethodsオプションに定義-->
省略形「@」
<button @click="handleClick">クリック</button>
v-model
データとフォームの入力項目をバインド(同期)する際に使用(双方向データバインディング)。下記でフォームの文字を編集すると、同期して画面のメッセージも更新される。
<div id="app"> <input v-model="message"> <p>{{ message }}</p> </div>
var app = new Vue({ el: '#app', data: { message: 'hello' } })
v-if
プロパティがTrueの時だけ条件分岐でテンプレートを描画したい際に使用。条件を満たさなかった際はコメント化。似たディレクティブにv-showがあり、こちらはstyle="display: none;"のスタイルが付与されて描画。<templete>タグを使うと複数の要素をグループ化できる。
<templete v-if="ok"> <h1>タイトル</h1> <p>コンテンツ</p> </templete>
また、v-else-if、v-elseを組み合わせることで複数の条件を指定できる。
<transition>タグ
組み込みコンポーネント。CSSトランジションやアニメーションを容易に適用できる。
基本的なオプションの構成
Vueクラスのインスタンスの生成テンプレート
//main.js vue = Vue.new({ el: '#app', data: { message: "Hello Vue.js" }, computed: { computedMessage: function(){ return this.message + "!" }, }, created: { // アクションフック }, methods: { myMethod: function(){ }, }, })
el: mountする要素
アプリケーションを紐づけるセレクタ。
data:
アプリケーションで使用するデータの登録。
computed:
算出プロパティ。関数によって算出されたデータ。
メソッド内ではthis(Vueインスタンスを指す)を付ける。
テンプレートの可読性を保つためにここに記述。
created:
ライフサイクルハック。特定のタイミングで自動的に呼び出す。呼び出したいタイミングによってメソッドは変える。
処理を割り込ませる仕組みのことを「フック」と呼ぶ
methods:
アプリケーションで使用するメソッド。
コードを管理しやすくするために処理を分けたり、イベントハンドラなど細かな実装を担当する。
コンポーネント
機能をもつUI部品ごとにテンプレートとJavaScriptを1つのセットにして、ほかのUI部品とは切り離した開発、管理をできるようにする仕組み。設計図。再利用が容易になる。※UI(User Interface)とは、ユーザーがPCとやり取りをする際の入力や表示方法などの仕組み。
index.html
<h1>Vue.js</h1> <div id="app"> <hello /> </div> <script src="https://unpkg.com/vue"></script> <script> Vue.component('hello', { template: '<p>Hello!</p>' }) var app = new Vue({ el: '#app', }); </script>
コンポーネントの定義
Vue.component(名前,{設定情報});
コンポーネントの出力
<コンポーネント名 />
<h1>Vue.js</h1> <div id="app"> <mycomponent /> </div> <script src="https://unpkg.com/vue"></script> <script> var component = { template:'<p>localcomponent</p>' } new Vue({ el: '#app', components: { 'mycomponent':component } }); </script>
ローカルコンポーネントの登録
Vueオブジェクトの「components」プロパティに登録すると、そのコンポーネントのスコープ内だけで使用するように制限できる。components:{'コンポーネントタグ':コンポーネント名}
※Vue.componentsはグローバルコンポーネント
親子間のコンポーネントデータフローは、「props(親から子)」と「カスタムイベント$emit(子から親)」を使用する。
拡張フレームワークには「Nuxt.js」や「VuePress」がある。
「Vuex」や「Vue Router」といった拡張ライブラリを導入することで効率よく目的に応じたスケールアップも可能。
また、UIコンポーネントサイトとしては「Element」や「Onsen UI」がある。
用語
ディレクティブ
テンプレートとロジックを関連付ける機能。テンプレート内で「v-if」など独自の属性で記述する。オプションで引数や修飾子を扱うことも可能。
マウント(mount)
配置する要素とアプリケーションを紐づけること。
データバインディング
データと描画を同期させる仕組みのこと(JavaScriptのデータとそれを使用する場所を紐づけ、データに変化があれば自動的にDOMを更新する)。リアクティブシステムによって実現している機能の1つ。
DOM(Document Object Model)
JavaScriptでhtmlの要素を操作するための仕組みのこと。(ファイルの特定の部分に目印を付けて「この部分」に「こういう事をしたい」という処理を可能にするための取り決め。)
オプション
Vueインスタンスの中で使用するデータやメソッドを定義する場所。
今回まとめで割愛したこと
テンプレート制御ディレクティブ(v-preやv-htmlなど)各ディレクティブの修飾子
スクロールイベントの取得(スムーススクロール) P114
算出プロパティ詳細(ゲッターとセッター、キャッシュ機能、watchオプション)
コンポーネントの親子間のデータフロー P154
トランジション P194
Vue CLI P216
Vuex P252
Vue Router P282
予備知識
jQueryとの併用
Vue.jsを使用すると、jQuery(DOM操作系ライブラリ)を併用する機会は減る。マウントした要素内のDOMを直接操作しても仮想DOMは更新されず、データが変わらないため。DOMを直接参照したい際は、$elや$refsなどカスタムディレクティブを使用する。
所感
復習してみて、今回割愛した各ディレクティブの修飾子がかなり重要な役割を果たす上に、数が多いので把握するのが大変だなと感じた。まずはポートフォリオにトランジションを用いたテキストアニメーションを実装しながら学んでいこうかな。Vue RouterでSPAも憧れるけどVue CLIの導入が必須っぽいので、まずはVue CLI導入せずにできるところからポートフォリオ制作に活かしつつ、勉強していこうと思います。
参考サイト
猫本公式サポートページ今後活用したいチュートリアルなど
Vue.js/Vuexを使ってTrello風アプリを作成しよう!Vue CLI活用
Vue.js & FirebaseでTwitterライクなSNSアプリを作ってみよう!
Vue CLI活用 (Vue Routerアリ、Vuexは使用しない)
Nuxt.js & Contentfulでハイスペックなポートフォリオサイトを超簡単に公開しよう!【JAMstack】
- 投稿日:2020-03-28T18:17:56+09:00
Vue.jsでTypeScriptを使う(with vue-property-decorator)
Vue.jsのscript内に型定義有りのコードを書きたい
通常のJSで書いていくのは辛いので、vue-property-decoratorを利用してTypeScriptで開発していきます。
対象のプロジェクトにはあらかじめTypeScriptがインストールされているものとします。1インストール
$ npm install --save vue-property-decorator基本
Component
<script lang="ts"> import { Vue, Component } from 'vue-property-decorator' @Component export default class MyComponent extends Vue { } </script>これはJavaScriptで以下のように書いたものと同じです。
<script> export default { name: 'MyComponent' } </script>Vue.jsとTypeScriptでコンポーネントのロジック部分を書いていく際は
vue-property-decorator
というパッケージを利用します。
name
は明示的に宣言することもできますが、クラス名として宣言することで省略可能です。Data
コンポーネント内のdataは、クラスのメンバ変数として宣言します。
比較のために、JavaScriptで書くケースも載せておきます。// TypeScript @Component export default class MyComponent extends Vue { private msg = 'サンプルメッセージ' private users: Array<string> = [ '山田', '田中' ] } // JavaScript export default { data() { return { msg = 'サンプルメッセージ', users: [ '山田', '田中' ] } } }Computed
TypeScriptではcomputedはプレフィックス
get
をつけて記述します。
戻り値として指定した型とreturn
で返す値の型は一致させる必要があります。// TypeScript export default class MyComponent extends Vue { get profile(): string { return `Hi, My name is ${this.name} and I am ${this.age} years old!` } } // JavaScript export default { computed: { profile() { return `Hi, My name is ${this.name} and I am ${this.age} years old!` } } }Props
vue-property-decorator
から{ Prop }
をインポートし、コンポーネント内に記述します。
JavaScriptのPropsの受け渡しと同様に、TypeやRequired等の属性を指定できます。// TypeScript import { Component, Vue, Prop } from 'vue-property-decorator' @Component export default class MyComponent extends Vue { @Prop() message: string @Prop({ default: '山田' }) name: string @Prop({ required: true }) age: number @Prop({ type: String, required: false, default: 'Nagoya' }) address: string } // JavaScript export default { props: { message, name: { default: '山田' }, age: { required: true }, address: { type: String, required: false, default: 'Nagoya' } } }Methods
// TypeScript import { Component, Vue } from 'vue-property-decorator' @Component @Component export default class MyComponent extends Vue { public incrementAge(): void { this.age++ } public sum(num1: number, num2: number): number { return num1 + num2 } } // JavaScript export default { methods: { incrementAge() { this.age++ }, sum(num1, num2) { return num1 + num2 } } }Watch
@Watch
を宣言して以下のように書きます。// TypeScript import { Vue, Component, Watch } from 'vue-property-decorator' @Component export default class MyComponent extends Vue { private message: string private age: number @Watch('message') changedMessage() { this.message = 'The message has been changed.' } @Watch('age') changedAge(newVal: number, oldVal: number) { console.log(`new: ${newVal}, old: ${oldVal}`) } } // JavaScript watch: { message() { this.message = 'The message has been changed.' }, age(newVal, oldVal) { console.log(`new: ${newVal}, old: ${oldVal}`) } }おわりに
今回はVue.jsとTypeScriptでdecoratorを使用したComponent, Data, Props, Methods, Watchの書き方を説明しました。
他にも方法があるようなので、また勉強して記事を書こうと思います。
最後まで読んでいただき、ありがとうございました!
- 投稿日:2020-03-28T16:03:44+09:00
Vue.jsでDRF製APIにデータを送信する
以下の記事で作成したAPIに対して、今度はPOSTでデータを送信、データベースに登録したい。
JsfiddleのVue.jsからDRFで作成したapiにアクセスしたい
環境
book/models.pyfrom django.db import models import uuid from django.utils import timezone # Create your models here. class Book(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(verbose_name='タイトル', max_length=50) price = models.IntegerField(verbose_name='価格') created_at = models.DateTimeField(default=timezone.now) def __str__(self): return self.titleまた、Vue.jsはjsfiddle上に記載しています。
登録したいデータ
Bookテーブルに新規のデータとして、titleフィールドとpriceフィールドを登録したいです。
idとcreated_atは自動で追加されるのでスルーでOK。Vue.jsを書いていく
html<div id="app"> <div> <h1>GET</h1> <p>タイトル : {{ results[1].title }}</p><hr> </div> <div> <h1>POST</h1> <h2>タイトル : {{ title }}</h2> <h2>¥{{ price }}</h2> <input v-model='title'> <input v-model='price'> <button v-on:click='postBook'>送信</button> </div> </div> <script src="https://unpkg.com/axios/dist/axios.min.js"></script>javascriptnew Vue({ el: '#app', data: { results: [], title: '', price: '', }, methods:{ postBook: function(){ console.log('Hello') axios.post('http://127.0.0.1:8000/apiv1/book/', { title : this.title , price : this.price }) } }, mounted() { axios.get('http://127.0.0.1:8000/apiv1/book/') .then(response => {this.results = response.data}) }, })
input
タグでv-modelディレクティブを使い、title
とprice
設定。
Vueインスタンスに同じ名前のdataオブジェクトを定義し、中身は空にしておきます。methodsに
postBook
を登録。buttonタグ
にv-onディレクティブを使い、登録します。
postBook
メソッドでは、axios.post('一覧画面のエンドポイント', { フィールド名:データ, フィールド名:データ})
とし、フィールドとそれに対するデータをPOSTしている。送信する
送信を押すと...
追加されてます。
数字にカンマをつける
値段を表示する際にカンマをつけたい場合は、Vueインスタンス内にfilter作り、適用させたいテンプレートのマスタッシュにパイプ=
|
でfilterを適用させます。vueインスタンスfilters: { comma(value) { if (!value) return '' value = value.toString() return value.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') } }html<h2>¥{{ price | comma }}</h2>
- 投稿日:2020-03-28T14:57:49+09:00
Vue routerについて調べたこと
vue routerについて
- ルーター対応アプリで、ユーザーナビゲーションを有効にするためのコンポーネント
- ターゲットの場所は、
to
プロップで指定する<router-link to="/foo">Go to Foo</router-link>使用するコンポーネント
Vue Routerでは、二つのコンポーネントを使う
1.router-link
・・・リンクを作成するためのもの
2.router-view
・・・それぞれのリンク先に基づいた内容を表示するためのもの
3.router-view
が、/
だったら、routervieの中身がHome.vueになり、/Users
であれば、Users.vueになる
4. URLの内容によって、router-view
の中身が変わる、動的コンポーネントのようなもの a使用するに当たって設定すべき事項
- Vue.use()とすることでプラグイン(router)を使用できるようにする (Vue.jsが公式で提供してるプラグインを使うためのもの)
router.jsimport Router from "vue-router"; Vue.use(Router)
- 表示させたいページを作る(HomeとUsersの2ページ作ってみます)
Home.vue<template> <div> <h3>Home</h3> </div> </template>Users.vue<template> <div> <h3>Users</h3> </div> </template>
- 表示させたいページへのルートをかく
- このURLの場合は、このコンポーネントを表示する、というもの
- URLとコンポーネントをマッピングする作業
- routesは配列になっててその中はオブジェクトでかける
router.jsnew Router({ routes:[{path: '/', component: 'Home'}, {path: '/users.vue', component: 'Users'}] })
- 最初のページをHomeにするが、作成したHome.vueをインポートさせる必要がある
router.jsimport Home from './Home.vue';
- new Routerをexport させる
router.jsexport default new Router({ routes:[{path: '/', component: 'Home'}, {path: '/users.vue', component: 'Users'}] })
- routerの中身をvue.jsに適用させる
- main.jsの中で、ルーターの中身をVue.jsに適用させる
- routerの中身は、router.jsの中で、
export default
してる部分- new Vue のなかでrouterを登録する
- (
router.js
で書いた)Vue.use
とかくことで、使用できるようになるmain.js//⬇︎ここ import router from './router.js'; new Vue ({ //⬇︎ここ router: router, render:h =>(App) }).$mount('#app');ここまで書いても、そのままURLにHomeやUsersと書いても、App.vueが表示されてしまう
ので、どうするかというと、、App.vue<template> <div> <!-- 動的コンポーネント的な動きをする --> <router-view></router-view> </div> </template>その上で、urlにUsersなどと入力すると、Usersページを表示することができる
つまり、URLの内容によって、<router-view>
の表示するものが変わってくる!ということですちなみに。。
- そのままでは、URLに#がついて、それ以降にUserと入力すると、 の部分にUsersのコンポーネントが表示されるが、この#を表示したくない場合は、 router.jsの部分に、
mode:'history'
と書くことで、#が消えた状態でURLを表示することができる- しかし、毎回index.htmlを返す、という処理を書いておかないとerrorがでる
router.jsVue.use(Router); export default new Router({ //⬇︎こんな感じ mode:'history', routes:[{path: '/', component: 'Home'}, {path: '/users.vue', component: 'Users'}] })
- 投稿日:2020-03-28T14:28:04+09:00
JsfiddleのVue.jsからDRFで作成したapiにアクセスしたい
経緯
Vue.jsのプロジェクトをWeb上で手軽に作ることができるjsfiddleで、Django Rest Framework(以下DRF)で作成したapiにアクセスしたいと思い、試しました。
本記事に書くこと
- DRFにおけるシンプルなAPIの作成と、シンプルなVue.jsプロジェクトの作成。
- Vue.jsからDRFのapiにアクセスする方法と、その際の留意点。
手順
- DRFでapiを作成する
- Vue.jsからDRFで作成したapiにアクセスする
1. DRFでapiを作成する
環境を作るmkfir simple_drf cd simple_drf python3 -m vena venv source venv/bin/activate pip install django pip install djangorestframework django-admin startproject config. -- モデル用のbook API作成用のapiv1 -- python manage.py startapp book python manage.py startapp apiv1
book/models.pyfrom django.db import models import uuid from django.utils import timezone # Create your models here. class Book(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(verbose_name='タイトル', max_length=50) price = models.IntegerField(verbose_name='価格') created_at = models.DateTimeField(default=timezone.now) def __str__(self): return self.titlebook/admin,pyfrom django.contrib import admin # Register your models here. from .models import Book class BookModelAdmin(admin.ModelAdmin): list_display = ('title', 'price', 'id', 'created_at') ordering = ('-created_at',) readonly_fields = ('id', 'created_at') admin.site.register(Book, BookModelAdmin)apiv1/serializers.pyfrom rest_framework import serializers from book.models import Book class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ['id', 'title', 'price']apiv1/views.pyfrom rest_framework import viewsets from rest_framework.permissions import IsAuthenticatedOrReadOnly from book.models import Book from .serializers import BookSerializer class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializerapiv1/urls.pyfrom django.urls import path, include from rest_framework import routers from apiv1 import views router = routers.DefaultRouter() router.register('book', views.BookViewSet) app_name = 'apiv1' urlpatterns = [ path('', include(router.urls)) ]config/urls,pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('apiv1/', include('apiv1.urls')), ]2. Vue.jsからDRFで作成したapiにアクセスする
jsfiddleにアクセスし、各フィールド(html, css, javascript)に以下の通り記載。
HTML<div id="app"> {{ results }} <hr> {{ results.id }} <hr> {{ results.title }} <hr> {{ results.price }} <hr> </div> <script src="https://unpkg.com/axios/dist/axios.min.js"></script>axiosをcdnで使えるようにしておく
CSSは適当にbody { background: #20262E; padding: 20px; font-family: Helvetica; } #app { background: #fff; border-radius: 4px; padding: 20px; transition: all 0.2s; }Vuenew Vue({ el: '#app', data: { results: [] }, mounted() { axios.get('http://127.0.0.1:8000/apiv1/book/d5da82ff-8f63-4718-9ddb-e989832170d1/') .then(response => {this.results = response.data}) } }, )ライフサイクルフックのmountedでapiを使います。
axiosのgetメソッドに、DRFで作った記事のエンドポイントを指定。レスポンスのデータをresultsリストに格納します。
HTMLのMustache構文で{{ results }}などとしているため、getしてきた結果が表示されるはずですが...表示されへん
エラー
コンソールfiddle.jshell.net/takuto/4vjsdpag/64/show/:1 Access to XMLHttpRequest at 'http://127.0.0.1:8000/apiv1/book/d5da82ff-8f63-4718-9ddb-e989832170d1/' from origin 'https://fiddle.jshell.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Failed to load resource: net::ERR_FAILED
表示されません。コンソールを見ると上記の通りエラーが。
origin 'https://fiddle.jshell.net' has been blocked by CORS policy
https://fiddle.jshell.net
がCORSポリシーによってブロックされたと言ってます。原因は?
同一オリジンポリシーです。
詳しく:https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policyセキュリティの観点から、異なるオリジン同士でリソースをやり取りすることを防ぐものです。
オリジンは、スキーム、ポート、ホスト
の3つの組み合わせのこと。
これらが一致していない場合は、同一オリジンとは言えません。オリジンとは:https://www.atmarkit.co.jp/ait/articles/1311/26/news007.html
異なりオリジンへのアクセスを許可するためには、CORSという機能を使います。HTTPヘッダに設定を加える必要があります。
今回エラーが起きているのは、DRFで作成したAPIのオリジンと、Jsfiddleのオリジンが異なるためです。
- DRF : http://127.0.0.1:8000
- Jsfiddle : https://fiddle.jshell.net
解決するために...
DRFで
django-cors-headers
を使えば、CORSを設定することができます。DRFに
django-cors-headers
を入れるhttps://github.com/adamchainz/django-cors-headers
インストール
$ pip install django-cors-headersDRFのsettings.pyに追記
settings.pyINSTALLED_APPS = [ ... 'corsheaders', ... MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', ... ]CORS_ORIGIN_ALLOW_ALL か CORS_ORIGIN_WHITELISTを設定する
settings.pyCORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = [ "https://fiddle.jshell.net", ]CORS_ORIGIN_ALLOW_ALLは、他のオリジンをすべて許可する。
CORS_ORIGIN_WHITELISTは、許可するオリジン指定する。これでもう一度試してみる
やったー。
睡眠が大事
という本の情報を取得できました。
- 投稿日:2020-03-28T13:12:43+09:00
Vue.jsでTO DOアプリを作る
はじめに
Vue.jsの基本的な使い方まとめの続きです。
チュートリアルの鉄板であるTO DOアプリを作成していきます。
テンプレートを用意する
雛形となるHTMLとJSを作成します。
todo
ディレクトリを作成して、その中にindex.html
とindex.js
を作成します。todo/index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>TO DOアプリ</title> </head> <body> <div id="app"> <!-- ここにVueインスタンスを展開する --> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script> <script src="./index.js"></script> </body> </html>続いてVueインスタンスを作成する。
todo/index.jsvar app = new Vue({ el: '#app' })入力フォームとメソッドの追加
form
要素の中に入力フォームとボタンを追加する。<div id="app"> <h2>TODO List</h2> <form> <input type="text"> <button> 追加 </button> </form> </div>続いて
button
要素にv-on:click
を設定する。buttonがクリックされたら
addItem
メソッドを呼び出す。<button v-on:click="addItem">Vueインスタンスに
addItem
メソッドを記述する。一旦、正常に動いているか確認するため、アラートを表示させてみる。
var app = new Vue({ el: '#app', // メソッド定義 methods: { addItem: function(event) { alert() } } })次に
button
をクリックした時にsubmit
イベントが発動して、ページがリロードされるのを防ぐ処理をform
に記述する。
prevent
はデフォルトの動作を停止させる。<form v-on:submit.prevent>TO DOリストにタスクを登録
input要素に
v-model
を記述して双方向データバインディング出来るようにする。これで、inputに入力した値がdataオプションで定義するプロパティと同期する。
<!-- v-modelを定義 --> <input type="text" v-model="newItem">dataオプションに
newItem
と、入力した値(todoのタスク)を保存する配列todo
を定義する。var app = new Vue({ el: '#app', data: { newItem: '', // 入力フォームと双方向データバインディング todos: [] // 入力したtodoを保存する配列 }, methods: { addItem: function(event) { alert() } } })さらに、入力フォームで入力した値を配列に保存できるように
addItem
メソッドを書き換える。プロパティには
this.プロパティ名
でアクセスできる。この
this
はVueインスタンスを指す。var app = new Vue({ el: '#app', data: { newItem: '', todos: [] }, methods: { addItem: function(event) { var todo = { item: this.newItem // todoオブジェクトに入力した値を保存 } this.todos.push(todo) // todoを配列に追加 } } })TO DOリストの改善
button
をクリックしてタスクを追加した後に、inputに文字列が残ってしまうため、文字列をクリアにする記述をする。さらに、空の文字列を保存するのを防ぐための処理を記述する。
methods: { addItem: function(event) { // 追加 if (this.newItem === '') return // 空の文字列の場合はここで処理を終える var todo = { item: this.newItem } this.todos.push(todo) // 追加 this.newItem = '' // 入力フォームの文字列をクリア }TO DOリストの表示
HTML側でリスト表示させる記述をする。
v-for
ディレクィブを使って繰り返し処理をする。<div id="app"> <h2>TODO List</h2> <form v-on:submit.prevent> <input type="text" v-model="newItem"> <button v-on:click="addItem"> 追加 </button> </form> <!-- 追記 --> <ul> <li v-for="todo in todos"> {{ todo.item }} </li> </ul> <!-- 追記 --> </div>タスクの完了と未完了の実装
タスクの完了と未完了を管理するチェックボックスを設定する。
addItme
メソッドの変数todo
を編集する。チェック済みかどうか判定する
isDone
を追加し、初期値をfalse
に設定する。var todo = { item: this.newItem, isDone: false // 追加 }次に、HTMlにチェックボックスを追加し、
v-model
ディレクティブにtodo.isDone
を双方向データバインディングさせる。これで、チェックされると
isDone
がtrue
になる。<ul> <li v-for="todo in todos"> <input type="checkbox" v-model="todo.isDone"> <span>{{ todo.item }}</span> </li> </ul>タスクの削除を実装
登録したタスクを個別に削除するため、配列の
index
を取得する。
v-for="(値, index) in 配列"
で配列のindex
が取れる。続いて削除ボタンを追加し、タスクを削除するメソッド
deleteItem
を定義する。
deleteItem
メソッドの引数には配列のindex
を持たせる。<ul> <li v-for="(todo, index) in todos"> <!-- indexの取得 --> <input type="checkbox" v-model="todo.isDone"> <span>{{ todo.item }}</span> <!-- 追加 --> <button v-on:click="deleteItem(index)">Delete</button> </li> </ul>続いて
deleteItem
メソッドを定義する。配列から要素を削除するJavaScriptの
splice
メソッドを使用して、登録されたタスクを削除する。methods: { addItem: function(event) { // 〜省略〜 }, //追加 deleteItem(index) { this.todos.splice(index, 1) // sprice(削除を始めるindex, 削除する長さ) } }まとめ
DOMの取得やデータの更新を意識することなく書けるので、コードの見通しがスッキリする。
そして、データバインディング楽しい
- 投稿日:2020-03-28T12:07:31+09:00
スプレットシートのデータをVue.js(SPA)で表示する方法【vue-tables-2とaxiosを利用】
スプレットシートのデータをVue.js(vue-table-2)でいい感じに表示するまでの流れの解説です。
GoogleスプレッドシートをAPIサーバー化
本記事の作成にあたって参考にしたサイトのご紹介です。
(参考1)
Qiita[@ksyunnnn] | Googleスプレッドシートのデータを表示させてリッチな静的サイトを作る
スプレットシートのAPIサーバ化/Vue.jsで取得データの描画について参照(特にHTML/JS/CSSはベースにさせて頂きました。)(参考2)
Vue.js公式ページ | axios を利用した API の使用
axiosを用いたAPIからのデータ受け取りについて参照(参考3)
SIerだけど技術やりたいブログ | Vuejs vue-tables-2 でテーブル表示
vue-table-2のお作法について参照【今回活用するデータ】
過去の記事でも取り扱っている会議の参加者リストを例として活用します。スプレットシートは公開しているので、必要に応じてコピペしてご活用ください。今回扱うデータセットはこちらです。
GoogleスプレッドシートをAPIサーバー化
まずはスプレットシートを用意していただいて、参考記事(Googleスプレッドシートのデータを表示させてリッチな静的サイトを作る)にならってAPIサーバー化を行ってください。
念のための補足ですが、スプレッドシートのIDは、黄色下線部の部分に該当するのでお手元のスプレットシートをご確認ください。
Vue.js(SPA)でデータを描画(HTML/JS/CSS)
Vue.jsでの表示にあってのコードは以下の通りとなります。
index.html<meta http-equiv="content-type" charset="utf-8"> <link rel="stylesheet" href="main.css"> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <div id="app"> <h3 class="vue-title">厳かな会合参加者リスト</h3> <v-client-table :columns="columns" :data="data" :options="options"> <a slot="画像" slot-scope="props" :href="props.row.画像" target="'_blank'"> <img :src="props.row.画像" width="40" height="40" /> </a> </v-client-table> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.8/vue.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script> <script src="https://rawgit.com/matfish2/vue-tables-2/master/dist/vue-tables-2.min.js"></script> <script src="main.js"></script>main.jsVue.use(VueTables.ClientTable); new Vue({ el: '#app', data() { return { loading: true, errored: false, columns: [ '画像', '名前', '出身地', '主な実績' ], data: [], options: { sortable: [ 'name' ], texts: { filterPlaceholder: 'search' }, columnsDropdown: true, perPage: 25 } } }, filters: { currencydecimal(value) { return value.toFixed(2) } }, mounted() { // axiousを用いてスプレットシート(APIサーバ化)から値を取得 axios .get('https://script.google.com/macros/s/AKfycbz792Y12WYDaiW2U5CiUdgCj8Uu9xWIDfq6cYfyXqq5m3MFRxU/exec') .then(response => { this.data = response.data }) .catch(error => { console.log(error) this.errored = true }) .finally(() => this.loading = false) } })main.css* { box-sizing: border-box; font-size: 15px; } #app { max-width: auto; margin: 0 auto; padding: 10px; } td { white-space: nowrap; } table { width: 100%; border-collapse: collapse; } thead th { border-bottom: 2px solid #0099e4; /*#d31c4a */ color: #0099e4; } th, th { padding: 0 8px; line-height: 40px; } thead th.id { width: 50px; } thead th.state { width: 100px; } thead th.button { width: 60px; } tbody td.button, tbody td.state { text-align: center; } tbody tr td, tbody tr th { border-bottom: 1px solid #ccc; transition: all 0.4s; } tbody tr.done td, tbody tr.done th { background: #f8f8f8; color: #bbb; } tbody tr:hover td, tbody tr:hover th { background: #f4fbff; } button { border: none; border-radius: 20px; line-height: 24px; padding: 0 8px; background: #0099e4; color: #fff; cursor: pointer; } /* columns choice button */ button.btn.btn-secondary.dropdown-toggle { margin-right: 10px; }アプリイメージ(実行結果)
最終的に、このようなアプリとして動作します。
vue-tables-2
のオプションを変更すれば、フィルタなどのオプションを簡単に付与することができます。冒頭にご紹介した記事(SIerだけど技術やりたいブログ | Vuejs vue-tables-2 でテーブル表示)で、大変分かりやすくデモをまとめてくださっているのでぜひ気になる方はぜひ確認してみてください。
スプレットシートのデータを
Vue.js(SPA)
で表示する方法についてでした。この組み合わせで且つ、axious
やvue-tables-2
を併用している参考サイトがなかったので、冒頭ご紹介したブログを参考にさせて頂きながら組み合わせでアプリを組んでいます。簡単にWebアプリケーションが作れるのは大変魅力的です。ちなみに社内利用に閉じた利用ですが同じ構成で作ったデータビューワアプリを
Firebase
でデプロイしています。認証機能もさくっと作れるので大変ありがたいです(何より無料枠でまかなえるありがたさ)。
- 投稿日:2020-03-28T04:54:49+09:00
新型コロナウイルス対策サイト(石川県版)を開発した学生の話
自己紹介
まだまだひよっこ中のひよっこなので暖かい目で見ていただけると幸いです。(どMなので厳しいのでもOK!)
最近は、何かと大変ですね。僕も春休みに入りインターンも一時休業となりました。
給料がなくて悲しいのです、、、
もし、お仕事あれば連絡ください!!開発の経緯
避難も兼ねて地元に石川県に帰ったわけですが、あらびっくりやることがございません。
なのでずっとゴロゴロ。。。
そんな日々を2週間続けてたある日、なにやら面白い活動が、、、東京都 新型コロナウイルス対策サイトの話ですねー。
俺も作りたい!!
そう思いまして、作ることになりました。
クソな経緯でごめんなさいまずなにから?
どうやって作ろうか、、、考えました。
ソースコードを持って来れば行けそうですね!!https://github.com/tokyo-metropolitan-gov/covid19
実は、貢献する方法も乗せて来れているんですねー。
https://qiita.com/FPC_COMMUNITY/items/b9cc072813dc2231b2b2
じゃあ簡単じゃん!!!
実は、vueとかnuxtとかさっぱりなんですねー。
でも、とりあえず東京都の部分を全部石川県に置き換えますねー。
ogp?favicon?も初めて知ったのですが、adobeを使ってうまく石川県ようにしますねー。割と簡単に置き換えられた
なんだ!簡単じゃーん!!余裕余裕。。。。
肝心のデータがないじゃん!!
これほど準備も計画もなにも立てていないことを悔やんだことはありません。
どれどれ、石川県のコロナウイルスのデータはどこかな〜??
ちゃんとあるじゃーん!!
ポチッと、、
https://www.pref.ishikawa.lg.jp/kansen/coronakennai.html
はーい!おっけー。なんらかのcsvとかexcelデータはないんだ。(悲)
それでも一応オープンデータ?!
大学で学んだことを生かして、、、しっかりスクレイピング
割とpythonならできると思っていたので、bs4とpandasでデータを整形しますねー。
まあこんな感じで書けましたわい。(冗長なコードがあったら教えて欲しいと甘えたい)https://github.com/Retsuki/covid19-ishikawa/blob/development/tool/scraping.py
いよいよデプロイ?
さてとどうすればあげられるのか??
私はherokuしか使ったことのないものだったので、、、
んー、悩みました。そして見つけた一筋の光 〜netlifyとの出会い〜
https://tech.moyashidaisuke.com/entry/covid19-netlify
こんな簡単にできるの?!
すげー時代だなと感心しました。よっしゃー!公開できたぜ!
https://ishikawa-covid19.netlify.com/
まあまあええやん!
しかし!!!ここでもう一つの問題が、、、
これってデータの更新は手動??いや、ちょっと待てちょっと待てお兄さん。。。
実は、インターン先でcronを使った経験があったので
すぐに察しましたよ!さくらさんにサーバーを借りて、、、
一瞬でしたね。
さくらさんにメールをしたら迅速に対応してくださり
多分、サーバーの費用はかからないはず!
クラウドサーバーに仕掛けます。
シェルに実行コマンドを書いてhttps://github.com/Retsuki/covid19-ishikawa/blob/development/run-regularly.sh
cronで定期実行
完成しましたね!!
かかった日数
4日?
1日目:サイトを石川版に改良
2日目:データをスクレイピング
3日目:悲しいことがあって、、休んだ。
4日目:データ自動更新
5日目:石川県のコロナウイルスサイトの情報開示が変わったため、再度スクレイピングコードを修正取り組んでみて
非常に楽しかったよ!!
ほぼほぼパクリだけど、東京都を作った人たちは本当に偉大ですよ。
全部その人たちのおかげでできましたから。
今年は「達成」が目標の僕だったので
実は、大変嬉しい日々でした。完成物
https://ishikawa-covid19.netlify.com/
日本へ
頑張ろうとは言いません。
困っている人がいたら助ける。それで少しは平和になるんじゃないかな。
世界に平和を祈って、、、
we can do it!!!お世話になったサイトたち
[東京都新型コロナウイルス対策サイト]
https://stopcovid19.metro.tokyo.lg.jp/
[東京都新型コロナウイルス感染症対策サイトをforkしてnetlifyでdev環境を立ち上げる手順]
https://tech.moyashidaisuke.com/entry/covid19-netlify
[新型コロナウイルス感染症の県内の患者発生状況]
https://www.pref.ishikawa.lg.jp/kansen/coronakennai.html
[さくらクラウド]
https://cloud.sakura.ad.jp/
[我が学部]
https://www.iniad.org/