- 投稿日:2020-01-23T23:26:37+09:00
Vue.js のディレクティブの記述を膨らませないために
概要
本記事では、Vue.jsのディレクティブって便利だけどなんかかさばって可読性が・・という方に
改善する方法をv-bindを例にして紹介していきます。元のコード
aタグに対して2つのv-bindが設定されています。
2つくらいなら・・と思うかもしれませんがこれが膨張していくと恐ろしいことになります。index.html<div id="app"> <a :href="url" :id="number">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { url: 'https://sample.com', number: 23 } })改善
ひとつのオブジェクトにまとめることができます。
画面上の表示はなにも変わりません。index.html<div id="app"> <a v-bind="{href: url, id: number}">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { url: 'https://sample.com', number: 23, } })さらに改善
dataの中にオブジェクトとして持たせておいて、html上ではbindObjectとバインドさせているだけになりました。こちらも画面上の表示はなにも変わりません。
こちらの方がhtmlに記載する量が少ないのでVue.jsを書く上での理想の形です。そして管理もしやすくなりますね。index.html<div id="app"> <a v-bind="bindObject">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { bindObject: { url: 'https://sample.com', number: 23, } } })最後に
可読性を下げず、チームでも個人でも管理しやすい形でコーディングできるように自分ももっと気をつけていこうと思います。
- 投稿日:2020-01-23T23:21:45+09:00
Vue.jsのSPAをFlaskのバックエンドで支えて、Herokuで動かす!
前提
開発環境
- mac OS X
- Python 3.7.1
- pip 19.3.1
- Pipenv, version 2018.11.26 (インストール:
python -m pip install pipenv
)- Flask
- Node.js v10.16.0
- Vue.js (@vue/cli v4.1.2)
- Herokuのアカウント、あるよ。
ディレクトリ構成の目標
[approute] |-- [server] -- サーバーサイド(Flask app) |-- [client] -- クライアントサイド(Vue app) `-- サーバー起動にまつわる設定ファイルサーバーサイドの準備
仮想空間の作成
$ pipenv install flask→プロジェクト用の仮想空間&[Pipfile][Pipfile.lock]ファイルが作成される
[approute] |-- Pipfile `-- Pipfile.lock (pc)Users/xxxxxx/virtualenvs/xxxxxxxFlask appのシンプルな内容のコードを取り急ぎ置いてみる
HOME URLにやってきたら「Hello world」って返すだけ。
approute/server/main.pyfrom flask import Flask app = Flask(__name__) @app.route('/') def hello(): hello = "Hello world" return hello if __name__ == "__main__": app.run(debug=True)仮想空間で実行するscriptを作成
packageは仮想空間にあるので、そちらを実行するようなスクリプトを作成
approute/Pipfile[[source]] name = "pypi" url = "https://pypi.org/simple" verify_ssl = true [dev-packages] [packages] flask = "*" [requires] python_version = "3.7" [scripts] //←追記 start = "python server/main.py" //←追記
$ pipenv run start
で実行できるようになるHerokuにdeploy
gunicornをインストール
HerokuのPythonサポートはgunicornというWEBサーバーを通して行なっている様です。
ということで
$ pipenv install gunicorn
approute/Pipfile[[source]] name = "pypi" url = "https://pypi.org/simple" verify_ssl = true [dev-packages] [packages] flask = "*" gunicorn = "*" //←ここが追記された [requires] python_version = "3.7" [scripts] start = "python server/main.py"そして、実行スクリプトを記載。
server/main.py
のappを実行するには、↓のように書くapproute/Procfileweb: gunicorn server.main:app --log-file -一応、ローカルでも
$ gunicorn server.main:app
で実行確認してみるHerokuでapp作成
Heroku( https://jp.heroku.com/ )でアカウントを作成して、appも作成。
そしたらdeploy手順が掲載されているので、順次実行していく。$ heroku login // Herokuにログイン $ cd approute //作業ルートに移動 $ git init //git初期化 $ heroku git:remote -a hogehoge //Herokuにリモートレポジトリ $ git add . $ git commit -am "make it better" //gitにコミット $ git push heroku master //Herokuにpush $ heroku open //デプロイしたアプリを開くHerokuにデプロイしたappから「hello world」言われました。
世界が開けたようです。よかったよかった。クライアントアプリを作成
Vue UIでプロジェクトを作成
Vue UI、使いやすいので活用しようと思います。
↑今回はアプリルートのgit使っているのでoffります。
これ以降のプリセットとかはとりあえず好きにすればいいと思う。
SPAなので、Routerは必須。そしたら、しばらくダウンロードとかで待つと。。。
できあがるので、↓の画面から実行スクリプトも実行できますよと。SPA
クライアントサイドでSPA作る
Vue UIの「プロジェクト設置>Vue CLI>アセットディレクトリ」からでも、
主導で設定ファイルを作ってでもいいので、staticファイルの書き出し先をstaticに設定。approute/client/vue.config.jsmodule.exports = { assetsDir: 'static' }で、Vue UIでも、termimnalでもいいから、
$ npm run build
を実行→appがコンパイルされる[approute] |-- [client] |--[dist] //←生成される |--[static] //←jsとか書き出される `--index.html //←rootのHTMLができる |--[public] |--[src] |--vue.config.js `--その他設定ファイルサーバーサイドでクライアントアプリをテンプレートとして設定
結論として、こんな感じに書き換えます。
approute/server/main.pyimport os from flask import Flask, render_template app = Flask(__name__, static_folder='../client/dist/static', template_folder='../client/dist') @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def index(path): return render_template('index.html') if __name__ == "__main__": app.run()ちっと分解。
from flask import Flask, render_template (略) return render_template('index.html')↑Flaskのrendar_templateを追加しまして、Vueのapp側で書き出したルートファイルである[index.html]をセットします。
app = Flask(__name__, static_folder='../client/dist/static', template_folder='../client/dist')↑Vueのapp側で書き出したstaticファイルの場所をこのファイルから見た相対パスで書きます。
@app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def index(path):↑どんなURLパスでやってきても、こちらで受け止めます、な感じ。
pipenv run start
→ お、世界に色が宿りました。改めて、Herokuにpush
$ git add . $ git commit -am "color from vue" $ git push heroku master $ heroku openしてみたら、、
500 Internal Server Errorjinja2.exceptions.TemplateNotFound: index.htmlとのことで、ローカルでは見つかっていたVueによって生成されたindex.htmlが見つからない...ってことですかね?
世界が闇に包まれました。Herokuにdeploy・改
複数のbuildpackに対応する
すでに、pipfileによって、自動的にpythonサーバーが起動する。
そこに、node.jsサーバーもたちあげたい。https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app
$ heroku buildpacks:add --index 1 heroku/nodejs
$ heroku buildpacks === xxx Buildpack URLs 1. heroku/nodejs 2. heroku/pythonで、このまま起動しても
remote: ! The 'heroku/nodejs' buildpack is set on this application, but was remote: ! unable to detect a Node.js codebase. remote: ! remote: ! A Node.js app on Heroku requires a 'package.json' at the root of remote: ! the directory structure.と言われるわけで、package.jsonをルートに持ってこいと言われます。
Vue.jsのアプリのディレクトリ構造を変更する
いったん書き出していた[dist]ディレクトリを削除して、[client]ディレクトリの中身をまるっとルートに移動させる
[approute] ===Flask=== |--[server] -- main.py |--Pipfile |--Procfile |--その他設定ファイル ===Vue.js=== |--[public] |--[src] |--package.js |--babel.config.js |--vue.config.js `--その他設定ファイルそれにともなって、書き換え
approute/server/main.pyapp = Flask(__name__, static_folder='../dist/static', template_folder='../dist')Herokuにpushしてみたら、動いた!世界に光が戻りました。
ということで、このベースの世界から発展開発をしていきたいと思います。結論
とりあえず、やったことを書いてみましたが。。
あったんだよね、先人の知恵
https://github.com/gtalarico/flask-vuejs-template
https://github.com/oleg-agapov/flask-vue-spaなんか、ディレクトリ構成がね〜。
vue.config.jsとか設定して、1段掘れないかしら。参考
- 投稿日:2020-01-23T23:08:26+09:00
Vue.jsでイベントオブジェクトを取得する
概要
本記事では、Vueのディレクティブである v-on を使ってイベントオブジェクトを取得する方法を紹介します。
イベントオブジェクトとは
イベントオブジェクトは、イベントハンドラーおよびイベントリスナーにおいて実行される関数の引数として受け取ることのできるオブジェクトです。 そのイベントオブジェクトから、発生したイベントに関わる様々な情報(プロパティ)を知ることができ、またそのイベントを制御するメソッドを活用することができます。
詳しくは下記記事を参考にしてください。
https://phpjavascriptroom.com/?t=js&p=event_object実際のコード
sampleTextというidを持たせたp要素の上にマウスを乗せた時、そのマウスの位置を取得し
その下のresultというidを持たせたp要素に表示させるという流れ。index.html<div id="app"> <p id="sampleText" v-on:mousemove="mousePosition">ここにマウスを載せると下のX、Yの値が変わるよ</p> <p id="result">X:{{x}}, Y:{{y}}</p> </div>index.jsnew Vue({ el: '#app', data: { x: 0, y: 0 }, methods: { mousePosition: function(event) { this.x = event.clientX; this.y = event.clientY; // eventの中を見てみると、全てのイベントオブジェクトが入っている console.log(event); } } })補足
今回はclientX(Y)を取得したが、eventの中をconsole.logで見ると全ての情報が入っている。
補足2
引数に渡しているeventは好きなものに変えてもちゃんと動く。
- 投稿日:2020-01-23T20:30:08+09:00
初心者によるプログラミング学習ログ 218日目
100日チャレンジの218日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
218日目は
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) January 22, 2020
218日目
・Vue.jsでアプリ作成講座#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-01-23T19:22:26+09:00
【Vue.js】条件に応じて表示するリストを切り替えるサンプルコード
はじめに
先日、こちらの記事を書きました。
ここで登場したコードに追記して、条件に応じて表示するリストを切り替える方法について記載します。
環境
OS: macOS Catalina 10.15.1 Vue: 2.6.10 vue-router: 2.6.10 vuetify: 2.1.0結論
Sample.vue<template> <v-list> <v-list-item v-for="item in selectedItems" :key="item.id" @click="triggerClick(item.action)" > <router-link :to="{ name: item.link }"> <v-list-item-title> {{ item.name }} </v-list-item-title> </router-link> </v-list-item> </v-list> </template> <script> export default { data() { return { selectedPattern: 1, //ここで条件を切り替える items: [ {id: 1, name: '1へのリンク', link: 'Component1', action: '', pattern: 1}, {id: 2, name: '2へのリンク', link: 'Component2', action: '', pattern: 2}, {id: 3, name: 'action1を実行', link: '', action: 'action1', pattern: 1}, {id: 4, name: 'action2を実行', link: '', action: 'action2', pattern: 2}, ] } }, computed: { /* * filterを使って事前にリストを精査しておく * patternが1か2かで表示する内容を変更 */ selectedItems(){ return this.items.filter(item => item.pattern === this.selectedPattern) } }, methods: { triggerClick(action) { if (action === 'action1') { anyAction1() } else if (action === 'action2') { anyAction2() } } } } </script>この例では、
selected
を手動で1
としていますが、
応用としてVuexで作成したストアからstate
を呼び出し、それに応じてリストを切り替える、といったようなことも可能になります。※ログインしないとログアウトを表示しない、など。
補足:
v-if
を使わない理由: 効率悪化今回のパターンでは、つい
v-if
とv-for
を同時に使えば切り替え出来るのでは?となりがちですが、公式ドキュメントでは非推奨となっています。v-if と v-for を同時に利用することは 推奨されません。 詳細については スタイルガイド を参照ください。
スタイルガイドによると、
v-if
とv-for
を同時に使用した場合再レンダリングするたびにリスト全体を繰り返し処理する必要があります。
computed
プロパティ内でfilter
を使っていた場合フィルタリングされたリストは配列に関連する変更があった場合に のみ 再評価されるので、フィルタリングがはるかに効率的になります。
描画効率が悪くなってしまうんですね
おわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2020-01-23T19:12:47+09:00
Vue 3.0 系 Composition API への移行理由についてまとめてみた
はじめに ( Summary )
vue 3.0 の来たる日が近づいてきた(勝手にそう思っているだけ。)ので、公式サイトをほぼそのまま英訳した内容を踏まえて、お勉強をしようと思います、
コンポジションAPIの紹介
コンポーネントロジックの柔軟な合成を可能にする、function ベースのAPIの追加vue2.0 → 3.0 への動機 ( Motivation )
なぜバージョンを上げ、コーディングの方法が大きく変わったのか、このセクションで詳しく説明がなされています。
vue 2.0 系の終焉
ロジックの再利用とコードの編成
Vueが非常に簡単に手に入り、小規模から中規模のアプリケーションを簡単に構築できることを皆が気に入っています。しかし今日、Vueの採用が拡大するにつれて、多くのユーザーがVueを使用して大規模プロジェクトを構築しています。これは、複数の開発者のチームによって長期間にわたって繰り返され、維持されています。長年にわたり、これらのプロジェクトのいくつかは、Vueの現在のAPIに伴うプログラミングモデルの限界に直面しました。
何が問題なのか?
問題は2つのカテゴリに要約できます。
1つ目
機能が時間とともに成長するにつれて、複雑なコンポーネントのコードを推論するのが難しくなります。これは特に、開発者が自分で書いていないコードを読んでいるときに起こります。根本的な原因は、Vueの既存のAPIがオプションによるコード編成を強制することですが、場合によっては、論理的な懸念によりコードを編成する方が理にかなっています。
コンポーネント間の推論が困難に
コンポーネント階層が増えたことにより、データの受け渡し(prop
,emit
)が増えました。これは中途参入者のコードのリーディングのコストを高めています。柔軟性の欠如
data
などのオプションがあることにより、強制力が働き、共通的なコーディングの恩恵が得られています。
しかし、その恩恵の制約が諸刃の剣となり、コーディングの柔軟性を失う原因となっています。
(詳しくはRFC
の詳細設計で話されています。)2つ目
複数のコンポーネント間でロジックを抽出および再利用するための、クリーンでコストのかからないメカニズムの欠如。
大規模な設計に対する柔軟でクリーンな設計の崩壊
Options API
では、メソッドはmethods
に、データはdata
に処理・宣言を行います。
これは、外部への処理の抽出を困難とする理由の1つとなっています。
this
の多様や再代入が、クリーンアーキテクチャの導入を難しくしています。Composition API はどうなのか?
このRFCで提案されているAPIは、コンポーネントコードを整理する際にユーザーに柔軟性を提供します。オプションによってコードを常に整理するように強制される代わりに、コードは、それぞれが特定の機能を処理する関数として整理できるようになりました。また、APIを使用すると、コンポーネント間、またはコンポーネント外でもロジックを抽出して再利用できます。
ロジックの切り出し
オプションによるコードの強制は残るものの、オプションが中心であるOptions Api
から、機能単位をベースとするComposition Api
になることで、コードが読みやすくなります。
関数の役割が明確になることでロジック抽出が容易になり、クリーンな設計が可能となります。これらの目標がどのように達成されるかは、詳細設計セクションで示します。
より良い型推論 ( Better Type Inference )
大規模なプロジェクトに取り組んでいる開発者からのもう1つの一般的な機能要求は、TypeScriptサポートの向上です。 Vueの現在のAPIは、TypeScriptとの統合に関していくつかの課題を提起しました。
・・・
Vueの既存のAPIは型推論を念頭に置いて設計されたものではなく、TypeScriptでうまく動作させようとすると、非常に複雑になります。 TypeScriptでVueを使用するほとんどのユーザーは、vue-class-componentを使用しています。
...
これは、構築する上でかなりリスクの高い基盤となります。クラスベースの頓挫
クラスベースの設計が序盤は提唱されたものの、基盤のリスクから撤廃されてしまいました。クラスベースの設計を可能とするためにデコーレータの依存が大きくなってしまったのが大きな理由です。比較すると、このRFCで提案されているAPIは、ほとんどの場合プレーンタイプの変数と関数を使用します。提案されたAPIを使用して記述されたコードは、手動のタイプヒントをほとんど必要とせずに、完全なタイプ推論を楽しむことができます。また、提案されたAPIで記述されたコードはTypeScriptとプレーンJavaScriptでほとんど同じに見えるため、TypeScriptを使用していないユーザーでもIDEのサポートを改善するために入力の恩恵を受ける可能性があることを意味します
Composition API
現在提唱されているComposition Api
は型推論のサポートを強めています。
このAPIでは、JavaScript と TypeScript でのコードの差異はほとんどありません。つまり型推論のサポートと同様のサポートを JavaScript においても IDE から受けられる可能性を秘めています。まとめ
- 散らばっていたロジックや変数をまとめて記述することが可能になるため自由度が高くなる
- 既存の Options Api を Composition Api に移行させる価値は大いにあり
- 自由度が高まることで可読性が上がり、実装スピードが向上しそう
- Vue 2.0 でもライブラリを入れることで Composition Api を利用できる
- まだ正式リリースではないため、今後のアップデートを含めてポジティブな期待が高まる
- 投稿日:2020-01-23T17:03:16+09:00
【Nuxt.js】TypeScript基礎編:Vue.extendでシンプルなコードを書こう
前置き
今回はNuxt.jsでTypeScript??
大まかに3つに分けて書いています✍️
・TSのメリット
・TSの書き方3つ
・Vue.extendコード例TSのメリット
すごく簡単に言うと、
安全な開発がしやすくなります??♀️
ここが参考になります!
https://qiita.com/SoraKumo/items/43fba2ad2d10336a665TSの書き方
TSを入れた場合の書き方は3パターン
・Vue.extend
・vue-class-component
・vue-property-decoratorシンプルで書きやすいのがVue.extendです?
Vue.extendのメリット
簡単⭕️
1番とっつきやすい書き方?何故なら!
通常と書き方がほとんど変わらないから!
Vue、Nuxtらしさを保ったままTSが使えます。TSなしの場合と変わるのは3点のみ!!!
・script langをtsに変更
・Vueモジュールをimport/extend
・コンポーネント、ページ自体にクラス名を付与他は通常と変わらない
これがVue.extendの良さ?他の書き方は?
@Componentとかになるあれ。
書き方が結構変わりますよね〜!
・vue-class-component
・vue-property-decoratornuxtの場合はこちら
・nuxt-class-component
https://github.com/nuxt-community/nuxt-class-component
・nuxt-property-decorator
https://github.com/nuxt-community/nuxt-property-decorator詳しい書き方はこちらが参考になります!
https://qiita.com/potato4d/items/c9c0c8e674f20c85948aVue.extend
【基礎構文】
通常と比較しても
シンプルで非常に分かりやすいですね!?公式はこちら
https://typescript.nuxtjs.org/ja/
https://jp.vuejs.org/v2/guide/typescript.html#基本的な使い方index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ // コンポーネント・ページ自体にクラスを付与 name: 'Component', components: { }, props: { }, data() { return { } }, computed: { }, mounted () { }, methods: { }, created () { console.log('CLICK!!!')// eslint-disable-line }, }) </script>【通常】
index.vue<script> export default { components: { }, props: { }, data() { return { } }, computed: { }, mounted () { }, methods: { }, created () { console.log('CLICK!!!')// eslint-disable-line }, } </script>data
Stringの場合は
" "(double quote)ではなく
' ' (single quote)のみ⭕️index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', data () { return { userName: '', } }, }) </script>props
TSなしの場合と変わりません。
・type
・required
・validator
全てそのまま書けます✍️index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', props: { status: { type: String, required: false, validator (value) { return [ 'default', ].includes(value) }, }, }, }) </script>methods
こちらも同様
$emitなども通常通り記載可能です。index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', methods: { alert () { this.$emit('componentAlert') }, }, }) </script>console.log()
methodsやライフサイクルで
console.log()を使用する場合ESlintにひっかかります。
その場合は// eslint-disable-lineを追記すれば⭕️
https://eslint.org/docs/rules/no-consoleindex.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', created () { console.log('created!!!')// eslint-disable-line }, }) </script>
- 投稿日:2020-01-23T15:19:22+09:00
Nuxt.jsのbeforeDestroyed()でイベントリスナーを削除できなかった時の対処法。
発端
偉い人「このページは横画面でしか表示したくない」
蝦「はい」環境
OS: windows10 64bit
node.js v10.16.3
Nuxt.js v2.10.2イベントリスナーを追加
蝦「画面の向きが変わるたびにVueファイル内のdata()の値を変えてやればおk」
とりあえずMDNのサンプルをそのままつかう。
Window: orientationchange イベント - Web API | MDNebi.js<script> //中略 beforeMount() { console.log("beforeMount"); window.addEventListener("orientationchange", this.checkRotate, false); } </script>しかし
イベントリスナーが消えない。
「後はbeforeDestroy()でイベントリスナーを削除するように書けばおk」
kani.js<script> //中略 beforeDestroy() { console.log("beforeDestroy"); window.removeEventListener("orientationchange", this.checkRotate, false); }, </script>消えない。
追加はされるが削除はされないので、該当ページに遷移するたびにイベントリスナーが増える。やばい原因: beforeDestroy()もDestroy()も実行されてない
プリントデバッグしてみると、ページ離脱時にbeforeDestroy()もDestroy()も実行されてない。
再起動してもキャッシュを消しても実行されないという不思議。解決策: ナビゲーションガードを使う。
どうにもならなくなったらbeforeRouteLeave(to, from, next)で削除すればおk。
ナビゲーションガード | Vue Router
Vue-Routerのナビゲーションガードを使ってみる - Qiitauni.js<script> //中略 beforeRouteLeave(to, from, next) { if (to.name) { next(); window.removeEventListener("orientationchange", this.checkRotate, false); return; } return; }, </script>別のプロジェクトでは再現しなかったので、多分もう使うことはほぼないだろうけど備忘録代わりに書いておく。
原因がわかる人がいたらだれか教えてください。
- 投稿日:2020-01-23T13:50:03+09:00
今一度、NuxtJS v2.11.0でTypeScript v3.7.5を適用する方法を探してみた。
環境
- Vue.js v2.6.11
- NuxtJS v2.11.0
- TypeScript v3.7.5
ご親切なお方がいらっしゃいましたら、間違っている箇所に関しては変更依頼を出していただけると幸いです。自身でも気づいた場合には速やかに修正を行います。
NuxtJSでTypeScriptを設定する
https://typescript.nuxtjs.org/guide/setup.html#installation
ドキュメントに従い、packageのインストールと、ビルド時の設定を行う。
npm i -D @nuxt/typescript-build touch tsconfig.json vue-shim.d.tsnuxt.config.jsexport default { buildModules: ['@nuxt/typescript-build'] }tsconfig.json{ "compilerOptions": { "target": "es2018", "module": "esnext", "moduleResolution": "node", "lib": [ "esnext", "esnext.asynciterable", "dom" ], "esModuleInterop": true, "allowJs": true, "sourceMap": true, "strict": true, "noEmit": true, "baseUrl": ".", "paths": { "~/*": [ "./*" ], "@/*": [ "./*" ] }, "types": [ "@types/node", "@nuxt/types" ] }, "exclude": [ "node_modules" ] }vue-shim.d.tsdeclare module "*.vue" { import Vue from 'vue' export default Vue }従来のAPIの場合
https://typescript.nuxtjs.org/ja/cookbook/components/#template Options API
最も既存のコードから変更が少ない方法をまず試してみた。
Vue.extend
を使うだけのやり方である。export interface Animal { name: string link: string } export default Vue.extend({ name: 'AnimalComponent', props: { title: { type: String, required: true }, content: { type: String, required: true }, animals: { type: Object, required: false } as PropOptions<Array<Animal>> } })
npm run dev
によるエラーは発生しないが、ブラウザ上で実行時に下記のプロパティエラーが発生するビルド時に型解決したものが実行時にエラーになるというのがなぜかよくわからない。
何かいい方法が見つかればよかったのだが、ドキュメントにこれ以上の情報がないので、一旦別の方法を探すことにした。vue.runtime.esm.js?2b0e:619 [Vue warn]: Invalid prop: type check failed for prop "animals". Expected Object, got ArrayクラススタイルのVueコンポーネントの場合
https://typescript.nuxtjs.org/cookbook/components/#template Class API
割と主流の書き方だと思われる。また、TypeScriptとして扱うためのデコレータを使うので、理解しやすい。ただし、その反面次のコンポジションAPIとの思想の差がある。
npm i -S vue-property-decorator
import { Vue, Component, Prop } from 'vue-property-decorator' export interface Animal { name: string link: string } export default class AnimalComponent extends Vue { @prop() title: string @prop() content: string @prop() animals: Array<Animal> }ビルド時にエラーが出ず、実行時にエラーもブラウザ上で出なかったが、実行すると今度はterminal側のconsoleに以下のエラーが出た。
10:22 Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.これは、まさに今後変更の可能性があるという話で、実験的にデコレータ機能を使いたいならそういう設定をしろという警告である。
今回は、使いたいので下記の設定を末尾に加えようと思う。tsconfig.json{ "experimentalDecorators": true }次は、以下のエラーが発生している。これは、プロパティの初期化時のチェックを厳格にしているがゆえに発生するエラー。
Property 'title' has no initializer and is not definitely assigned in the constructor. Property 'content' has no initializer and is not definitely assigned in the constructor. Property 'animals' has no initializer and is not definitely assigned in the constructor.今回は、初期化時のチェックの必要がないので、以下の対応を行う
import { Vue, Component, Prop } from 'vue-property-decorator' export interface Animal { name: string link: string } export default class AnimalComponent extends Vue { @prop() title!: string @prop() content!: string @prop() animals!: Array<Animal> }コンポジションAPIの場合
https://www.vuemastery.com/courses/vue-3-essentials/why-the-composition-api/
とりあえずなにこれ?と思い、なぜコンポジションAPIか?という動画だけ見れたのでスキップ気味にサクッと見た。既存Vue.jsの課題点
Vue.jsのコンポーネントを作成して、機能追加や変更を加え続けると、そのコンポーネントが肥大化してくるので、内容を把握しにくくなってくること
課題への解決策
- なので、そのコンポーネントの中を整頓して意味を付けたブロックごとに分割してMixinする
- Mixinパターンの解決策の欠点は名前空間の強いルール付が必要なことや、中身を読まないといわゆるプロパティ情報が不透明であること
- slotを利用する
- 個人的にもMixinに比べコンポーネント単位で分割されていたりして、Vue.jsっぽいなぁと思い好んでいるが、slotで分割する分だけコンポーネントが増加するのでパフォーマンスが悪いと言われる
https://vue-composition-api-rfc.netlify.com/#summary
RFCも読んでみると、既存の課題解決方法の欠点を補うためにコンポジションAPIを使ったやり方に変えたいみたいだ。ということで、3系を念頭にこの戦略を取るのは一つの手ではある。
ただし、今回は2系で主流のやり方をまとめたかったのでここは割愛した。最後に
下記のように、Vue.jsが3系になる事を考えながら書くというのも一つの手だと思われる
https://stackoverflow.com/questions/58367558/best-practices-for-easy-migration-from-vuejs2-to-vuejs3
- 投稿日:2020-01-23T13:42:20+09:00
Vuetify2.xでサウンド再生した
概要
外部でなく、自前(ローカル)にあるファイルをぺろっとVuetifyのプロジェクトで音再生したかった。
そのあたりのメモ。こまったこと
ぐぐったかんじwebpack.config.jsとかでwavファイルとかmp3ファイルをローディングできるように設定する例があった。
しかしvuetifyの標準プロジェクトだと、設定ファイルとしてplugins/vuetify.jsとvue.config.jsがあるが、webpack用なるファイルはない。Vue + Nuxtでボードゲームを6時間で作ってみたという記事ではnuxtの設定ファイルで拡張子ルールを追記してる...
sassとかのローディングもおなじような設定したことが昔はあったなと、ググると次の記事をみつけた。
→ Vuetify v1.3で追加されたvuetify-loaderが便利だったこれはimportすれば自動で解決してくれるってこと?
解決編
コンポーネントでimportすれば勝手に解決してくれた。
再生部分については、こちらの記事を参考にした。main.jsVue.prototype.$playSound = (path, volume = 1) => { var audio = new Audio(path); audio.volume = volume audio.play(); }Hoge.vue<template lang="pug"> v-btn(@click="playSound") ほげー (中略) <script> import sndHoge from '../assets/sounds/hoge.wav' (中略) methods: { playSound() { this.$playSound(sndHoge, 10) }, (以下略)まとめ
うごいてるからヨシ! (buildしたときにデカくなるとか何か影響はでそうだが、未確認)
- 投稿日:2020-01-23T07:37:38+09:00
サーバ管理なしでWebサービス公開 -Firebase(Authentication, Hosting, Firestore) + GAEで『LogCrow(ログ情報共有サービス)』開発-
FirebaseとGoogle App Engineを中心に活用し、Webサービスをローンチしたのでそのアーキテクチャ & サービス概要を紹介します。
背景・動機
- 私は日頃業務で、システムの保守・運用の効率化・改善について検討しており、いろんな運用者の知見が共有できている状態が作れると運用者が幸せになれるのではないかと考えています。
- そんな中で、特に保守・運用時にキーポイントとなるログ情報についてフォーカスし、こういうログが出たときには「原因」として何が考えられ、どういった「対処」が必要となるのかといった情報が幅広く共有されると良いのではないかと思っています。
- Google検索はもちろん優秀で、発生したログ文面をキーワードに検索すればある程度情報にたどり着くことはできますが、公開できない情報やログに特化した検索といったことはできないのでそれなりに検索結果を更に精査していく必要が出てきます。
チャレンジ
- そこで、以下のような機能要件で手軽に利用できるサービス提供ができないかと思うようになりました。
- ログのメッセージ内容を軸に、そのログの原因や対処方法を記録して管理できる
- その記録はPublicに公開するものと非公開なものを管理できる
- 登録されたログに対して検索できる(その際、非公開のものも効率よく検索できる)
- 検索結果の中から特に自身に必要なログに対してお気に入り登録することができる
- API経由で検索をできるようにし、監視ツール等で検知した後にその関連情報を自動連携できる
- 保守・運用は組織で実施されることを想定し、非公開情報については個人・グループで権限分けできる
- その他の要件としては、個人で開発することもあり、極力スモールスタートにしたいという要件もあります。
- 必要としてくれる人がいるかどうかもわからないので極力費用をかかずにスタートを切りたい。
- 開発工数も最小限に
- 環境費用も最小限に
LogCrow(ログ情報共有サービス)の2020/1時点のできること
ドキュメントはこちらに公開しています。
初回のリリース時点では今の所以下の機能が実装できています。
- ログの登録/編集/削除機能
- ログの検索機能
- ログの対象ソフトウェア、バージョンによる絞り込み検索機能
- お気に入り追加機能
- 公開ログ/非公開ログの管理機能
- パスワード認証によるユーザ登録機能
LogCrowのアーキテクチャ
上記の要件を満たすため、以下のようなアーキテクチャを採用しています。
(あまり細かく比較検討できているわけではないのでベストな選択かどうかはわからないので参考までに。)
項目 採用製品・SW コメント backend APIのサービス基盤 Google App Engineスタンダード環境 GoogleのPaaS基盤。全部function化も可能かと思うけど、APIがある程度種類あるので基本的に呼び出して実行されるものはPaaS基盤で動かすことにした。 backend APIの開発言語 Golang Pythonでも良かったけど、型の定義とか厳密にできて実装しやすく管理しやすそうなので。Golang好きなので。 frontendの開発言語 Vue.js + Vuex 仕事ではReact使っているのでその対比のためにVue.jsも使ってみたかった。学習コストも低そうなので。 frontendのデザインフレームワーク Vue Bootstrap 仕事ではMaterial UI使っているので、少し古いかもしれないがBootstrapベースも試してみたかった。 frontendのホスティング先 Firebase Hosting 認証系をFirebaseにしたので、ホスティングもそのままFirebaseで。単純なホスティングだけじゃなくモニタリング系も充実しているので。 バッチ的な処理実行 Cloud Functions for firebase 基本的にはFirebase GAEにあわせて利用。初回ユーザ登録時にバックエンドで初期登録処理とか走らせる用途で利用。 全文検索エンジン Algolia Cloud Firestoreの検索は簡易なことしかできなさそうで、GAEの公式ブログでもおすすめされていたので。 認証基盤 Firebase Authentication 非常に手軽に実装できて無償で使い始められたので。 データ管理基盤 Firestore GAEからもFirebaseからも呼び出しがかんたんなので。 ドキュメント管理、Issue管理 GitHub + GitHub Pages Markdownでドキュメント内容が管理できて、Gitのリポジトリで差分管理できるので。 パフォーマンス監視 Firebase Performance monitoring HostingしているVue.jsの中にSDKを組み込むことで応答速度等簡易に監視ができる。通知とかはなさそうだが、Firebase上のダッシュボードで適度に確認できるので。 各サービス無償枠などあるので、初期費用は特にかからずスタート可能になっています。
図で示すと以下のような感じになっています。
処理の流れ
基本的にはロジック系は全てバックエンドのGAE側のGo実装のAPIが担っています。
フロントエンドはFirebase Authenticationとのやり取りによる認証処理と、バックエンドのAPIとのやり取りを通してあくまでViewの機能のみを持ちます。例えば、認証~ログ検索の流れは以下となります。
Algoliaの活用によるログの検索機能
Algoliaは非常に優れた全文検索のSaaSです。APIを通してデータ登録することで簡単にGoogle検索のようなデータ検索機能が実現できます。
どのような順序でフィルタをかけるかとか、スペル誤りを考慮した曖昧検索など色々と使い勝手が良いです。FirebaseとGAEは基本的にGoogle内で完結しているのですが、Algoliaは別サービスの利用ということになるので、こちらは別管理となる点は少し手間がかかります。このサービスでは以下のようにログの登録の際、Firestoreへのデータ登録と同時にAlgoliaへもデータをPOSTし、Firestoreへの登録ログデータのID情報と紐付けてAlgoliaでインデックス管理するようなアーキテクチャを採用しています。今後の計画
まだほんの一部機能のみが実現できたという感じになっています。今後は以下のような追加機能開発も予定しています。
とはいえ、実際に使ってみて、こんな機能があった方が良いなとか、要望をいただければそれを優先的に作りたいと思っています。
ぜひ、要望はメールorGitHubのIssueに追加お願いします。
- グループ管理の機能
- 各ログへのコメント投稿機能
- Google認証への対応等、他のOAuth認証対応
- API機能提供
- 監視ツール等他ツールからの呼び出しによる効果的利用促進
- ログデータの登録簡略化機能
- 生ログデータに対するログカテゴライズ機能提供 (こちらについては一部CLIでログクラスタリングできるツールをOSSで開発中。log-cluseter)
などなど
まとめ
このサービスは皆さんからのログがたくさん蓄積されていることでより効果が発揮されるものになっていますが、冒頭でも書いた通り、非公開ログを個人管理するユースケースにも対応しているのでひとまず個人メモとして記録するだけでも活用してみてください。
このサービスを作るに際し、得られた技術的な情報の詳細については別途まとめる予定です。ゼロからWebサービス開発するチュートリアル的な資料とする予定なのでお楽しみに。
- 投稿日:2020-01-23T00:52:54+09:00
html2canvasを使ってVue.js のサイトを画像で切り取る
html2canvasを使いたい
今回はVue.jsで作ったhtmlをhtml2canvasで画像にしたいと思います
参考資料
【2019年度版】Windowsでhtmlを画像化する方法(html2canvasの使い方)
【資料2】html2canvasとVue.jsでつくるコラ画像ジェネレータ
【エラー用資料】Vue.jsの初歩的なミス環境
Windows 10
Visual Studio Code: 1.40.1 (system setup)
Chrome: 76.0.3809.146
Node.js: 12.4.0
V8: 7.6.303.31-electron.0
OS: Windows_NT x64 10.0.18362index.htmlを作成
基本コード
<!DOCTYPE html> <html lang="ja" dir="ltr"> <head> <meta charset="utf-8"> <title>"html2canvas" </title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <!--Vue.jsの適用headに入れると全部に適応される--> <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-alpha.12/dist/html2canvas.min.js"></script> <!--html2canvasの適用headに入れると全部に適応される--> <style type="text/css"> /* コラ画像用のCSS */ #preview { overflow: auto; width: 100%; } /* #preview_inner { background: url(https://3.bp.blogspot.com/-cPqdLavQBXA/UZNyKhdm8RI/AAAAAAAASiM/NQy6g-muUK0/s800/syougatsu2_omijikuji2.png) no-repeat left bottom; background-size: 200px auto; position: relative; padding-bottom: 250px; width: 600px; }*/ #baloon { position: relative; border: 4px solid #ff8888; background: #fff; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 1.2rem; color: #444; padding: 10px; min-width: 200px; white-space: pre-wrap; } #baloon:before, #baloon:after { content: ''; display: block; width: 0; height: 0; border-style: solid; border-width: 50px 15px 0 15px; border-color: #fff transparent transparent transparent; position: absolute; left: 20px; bottom: -50px; } #baloon:before { left: 16px; bottom: -60px; border-width: 58px 19px 0 19px; border-color: #ff8888 transparent transparent transparent; } </style> </head> <body> <div id="preview"> <!--<div id="preview_inner">--> <div id="baloon"> <p>{{ message }}</p> </div> <!--</div>--> <p> <img width="200" src="https://3.bp.blogspot.com/-cPqdLavQBXA/UZNyKhdm8RI/AAAAAAAASiM/NQy6g-muUK0/s800/syougatsu2_omijikuji2.png"> </p> <textarea class="form-control" v-model="message"></textarea> </div> <div> <button class="btn btn-primary btn-block" v-on:click="generate">画像を生成</button> </div> <script> var app = new Vue({ el: '#preview', data: { message: '' }, methods: { } }) </script> <script> var generate = html2canvas(document.querySelector("#preview")).then(canvas => { document.body.appendChild(canvas) }); </script> </body> </html>HTML
にアクセス。一部は画像に変換できたものの、
ターミナルとブラウザで検証をして見つかるエラーは消せたのですが
画像を含めて生成はできませんでした。
文字とかは画像化できるのですが、、、あと、画像を生成を押す前からずっと出ています。
せっかく作ったボタンの意味!!
ここの原因はたぶんここ<script> var generate = html2canvas(document.querySelector("#preview")).then(canvas => { document.body.appendChild(canvas) }); </script>見るからにうまく組めていない怪しいコード。
ボタンを押した関数のgenerateで動いて欲しい、かつhtml2canvasを使いたい。
色々試して書いたのですが思いついたものはどれも駄目でした(´;ω;`)あとはおみくじ画像がうまく画像化されない件。
こちらもstyleに埋め込んでいるから駄目なのかとimgタグでdivの中に持ってきたりしましたがどちらも駄目でした。【エラー】Uncaught ReferenceError: Vue is not defined
これはそもそもHTMLとVue.jsの仕組みがきちんと分かっていない為に起きたエラーでした。
備忘録的に分かった事を書いておこうと思います。
下記コードの黄色線を引いてる部分を/bodyの直前にもってきていたのが失敗で、
読み込めていませんでした。完成品
html2canvasとVue.jsは使えたものの目指していた形にはなりませんでした。
アドバイスありましたら随時募集中です。もしくは万が一少しでも参考になりましたら幸いです。
ありがとうございました。