- 投稿日:2020-01-15T23:36:11+09:00
【Vuetify】<v-spacer>を使っていて画面の横揺れが発生したら<v-container>の不足を疑おう
はじめに
Vuetifyを使って開発をしていて、
「なぜか画面の横スクロールが発生するな」
と思って調べたときの記録です。画像でいうと右端の余白です。
こいつのせいでガタガタするので、除去します。環境
OS: macOS Catalina 10.15.1 Vue: 2.6.10 vuetify: 2.1.0結論:
<v-spacer>
には<v-container>
が必須そのまんまです。
何かの変更の際に、誤って
<v-container>
から<div>
に変更してしまったようで、それから横揺れが発生していました。
<div>
で囲われている<template> <div> <v-row> <div></div> <v-spacer></v-spacer> <div></div> </v-row> </div> </template>
<v-container>
で囲われている<template> <v-container> <v-row> <div></div> <v-spacer></v-spacer> <div></div> </v-row> </v-container> </template>
<v-spacer>
は<v-contaniner>
の横幅を基準にして余白計算を行うので、<div>
だと今回のようにはみ出す現象が発生するんですねーおわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2020-01-15T23:16:19+09:00
ポートフォリオ作ったらNuxtJSを理解できたのでやったことを書いてく
今年で30になったプログラマですが、ポートフォリオサイトを作りました。
はじめに
Vue、NuxtJSともに未経験だったので一通りチュートリアルをやったのですが、正直全然理解出来てない感があって「これじゃあ戦力にならんなぁ」と思い、ある程度のサイト作りぐらいは出来るようにするためポートフォリオサイトを作成しました。実際にやってみて、約1ヶ月で困らない程度には理解できたかなと思っています。この程度のポートフォリオサイトであればベライチのhtmlで済む話なのですが、昨今のフロントエンドは状態管理、SSR×SPAなどいろいろやらなければいけないことが多いので基本的な技術は全て使うよう開発をしました。
構成
フロントがNuxtJS、Vue、ホスティングがNetlify、バックエンドが.NET Core、C#をコンテナ化し、ホスティングはHerokuとなっています。
バックエンドは今回の勉強対象外なので、経験のある(なにも調べなくても書ける).NET Coreを使いました。公開しないのであればJSON Serverでも良いかもしれません。期間
昨年の12/22から開始したので約1ヶ月間、1日1時間程度コツコツと進めました。僕は毎日集中出来るタイプでもなくたまにサボりたくなるので、そんな気分のときはスタイルいじったり、調べ物する日にしたりと気分転換してました。決め事
最近のフロントエンドはやることが多いです。やることは多いけれども、3大フレームワークで抑えておくべきスキルセットは共通してきたと思うので以下にあげる技術は絶対に取り入れると決めました。
- HttpClient(axios)
- SSR(universal)
- 状態管理(vuex)
- eslint + prettier
- jest
- storybook
jestはカバレッジを100%、storybookはknob addonを使った動的なコンポーネントの変更を出来るようにしています。あとこれは僕の完全な感覚なのですが、vueはtypescriptの導入が一歩遅く導入しているところは多くはないと思うのであえてesで書くようにしました。
雰囲気を掴むためにチュートリアルを一周する
NuxtJSとはなんぞや?というところからVuexまで一通りやります。1日30分、1週間やれば一通り出来ると思います。僕はリポジトリを作ってチュートリアルのやりながらコミットを積んで行きました。
勉強用リポジトリカバレッジを100%にする
はじめにカバレッジを100%にしましょう。テストははじめにやらないと一生出来ないので
create-nuxt-app
の直後にやりましょう。jest.config.jscoverageThreshold: { global: { branches: 100, functions: 100, lines: 100, statements: 100 } },これでもう逃げられません。さらに初期設定では
page
とcomponent
しかテスト対象でないのでstoreやその他もろもろ追加しましょう。jest.config.jscollectCoverageFrom: [ '<rootDir>/src/**/*.{js,vue}', '!<rootDir>/src/layouts/**/*', '!<rootDir>/src/plugins/**/*' ],Storybookを準備する
これもはじめにやらないとやる気がなくなるので早いうちに準備しておきましょう。正直、初学でテストとStorybookは鬼畜です。非常に辛いのですが、簡単な設計にするようになり、全体的に可読性が自然とよくなるのでここは我慢です。テスト出来るってことはある程度構造化されているってことなんですねー。
Storybookは自動セットアップがあるので私は
npx -p @storybook/cli sb init --type vue
しました。後々addons
やpreview-head
など必要になるので手動セットアップの方がよいかと思いました(僕はここで時間を潰しました)。componentを動的に変更するためにknobを入れましょう
npm i -save @storybook/addon-knobs
.storybook/addons.jsimport '@storybook/addon-knobs/register';僕が思うstorybookの位置付け的にデザイナーさんとのやりとりツールというイメージだったのでアドオンはknobsしか入れませんでした。
これはどちらでもよいかと思いますが
npm run build-storybook
するとstorybook-static/
がソース管理対象になるので.gitignoreに追加してもよいかもしれません。http-serverを入れる
今回はuniversalモードで作るのでNetlifyに
nuxt generate
でホスティングしました。ローカルで確認するためにhttp-serverを入れます。
npm i http-server --save
package.json"scripts": { ... "webserver": "http-server ./dist -o -p 8888" },これでdist直下のファイルを確認することが出来ました。
拡張を入れる
開発するための必需品です。私はVSCodeを使っているのでVSCodeのプラグインを記載します。
- Vue.js devtools
Storeの中身など見れるので便利です。必須。
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
- VSCode拡張
ここを参考にさせてもらいました。
https://qiita.com/dayoshix/items/c61a75a971331418c348Netlifyの準備をする
- appのパスを変える
npx create-nuxt-app <project-name>
をすると<project-name>
直下にnuxt appが生成されるのでNetlifyでデプロイエラーになりました。nuxt.config
やpackage.json
はrootに持っていき、ソースは別のディレクトリで管理するよう変更しました。nuxt.config.jsexport default { ... srcDir: 'src/' ... }
nuxt generate
ではindex.htmlしか生成されないため、リロードすると「Page Not Found」になってしまいます。こちらの対策方法は以下の通りです。nuxt.config.jsexport default { ... generate: { fallback: true } ... }エラーハンドリングを作る
- エラーページ
存在しないページには404ページへ、エラーは500ページへなどエラーハンドリングも実装します。nuxtはエラーページという機能を持っているので以下のように実装します。詳しくはこちらに載っています。
layouts/error.vue<template> <div class="container"> <h1 v-if="error.statusCode === 404">ページが見つかりません</h1> <h1 v-else>エラーが発生しました</h1> <nuxt-link to="/">ホーム</nuxt-link> </div> </template> <script> export default { props: { error: { type: Object, default: null } } } </script>
error.statusCode
でh1を切り替えています。リッチナブルにしたい場合はステータスコード毎にcomponentを作ってもいいかもしれませんね。
- axiosのエラーハンドリング
axiosでエラーが発生した場合はinterceptorsのonErrorヘルパでキャッチしました。これでエラーページへ遷移します。
plugins/axios.jsexport default function({ $axios, error }) { // ref. https://axios.nuxtjs.org/helpers $axios.onError((err) => { const code = parseInt(err.response && err.response.status) error({ statusCode: code }) }) }ライブラリを使う
やっとちゃんとしたコーディングに入れるようになってきました。
- VeeValidate
フォームのバリデーションがすごく簡単に書けるライブラリです。問い合わせフォームに使いました。
plugins/vee-validate.jsimport { extend } from 'vee-validate' import { required, email } from 'vee-validate/dist/rules' // No message specified. extend('email', email) // Override the default message. extend('required', { ...required, message: 'This field is required' })Babelで変換しないといけないので以下を追加します。
nuxt.config.jsbuild: { transpile: ['vee-validate/dist/rules'], extend(config, ctx) {} ... }
- axios-mock-adapter
axiosのモックを作成するためのライブラリです。テストするためには必須でした。
- marked
Nuxtを勉強しているはずが、cssにかける時間が多かったのでマークダウンを投入しました。ポートフォリオの詳細ページで利用しています。
- analytics-module
公式が出しているanalyticsのライブラリです。ものすごく楽です。
妥協する
カバレッジは100%にすると書きましたが、100%にすれば良いんです(ダメ)。v-modelでBranchが100%に出来なかった、、だれか教えてください。。
jest.config.jsmodule.exports = { ... coveragePathIgnorePatterns: ['xxx.vue'] }モックはJsonをそのまま返しちゃえ
var result = JsonSerializer.Deserialize<dynamic>(jsonString); return Ok(result);storybookに全てのコンポーネントを反映しなくてよいよね
一通りvueを味わってみて
簡単と言われていましたが、普通に難しかったです。SPAしか開発したことがなかったのでCSRとSSRがわかってなかったことを痛感しました。でもNuxtJS面白いのでおすすめです。
一応Github公開しています
- 投稿日:2020-01-15T22:05:51+09:00
focusイベントがキャンセルできない問題。
Vueを触っている時、ブラウザを切り替えたタイミングでフォーカスを外したいコンポーネントにあうことはありませんか?
そんな時まず思い浮かぶのは、eventをpreventDefaultすることだと思います。
事実、私もそう思い実行しました。
しかし、うまくいきませんでした。
なぜ?調べてみました。focusイベントについて
MDNを見てみる。
focus イベントは、要素がフォーカスを受け取ったときに発生します。このイベントと focusin との違いは、 focusin がバブリングを行うのに対し focus は行わないことです。
focus の反対は blur です。
バブリング なし キャンセル可能 いいえ キャンセル不可のため、preventDefalutは効かないらしい・・・。
というこで、フォーカスを発生させないためには逆にフォーカスを外してやればいい。
まぁそういえばそうかといった感じですが・・・正解コード(javascript)
el-tooltipはコンポーネント内のフォーカスを外したい要素のクラス名称です。
App.jsmounted: () => { window.onfocus = () => { const onFocusElm = window.document.activeElement; if (onFocusElm.classList.contains("el-tooltip")) { onFocusElm.blur(); } }; }正解コード(typescript)
App.tsmounted() => { window.onfocus = () => { const onFocusElm = window.document.activeElement; if (onFocusElm.classList.contains("el-tooltip")) { (onFocusElm as HTMLElement).blur(); } }; }
- 投稿日:2020-01-15T17:55:16+09:00
VueとTypeScript emitの書き方!
emitとは
まず
emit
とはどんな時に使うのか。
子コンポーネントが親コンポーネントにデータとか渡したい。
そんな時にemitを使います。親コンポーネントと子コンポーネント、どっちがどっち?
わかる人は飛ばしてください。
親コンポーネントと子コンポーネントがどっちかがわからなくなっている人!
(↑私も最初そうでした。。)コンポーネントを呼んでいる方が親 (import)。
呼ばれている方が子供。emitをTypeScriptでかく!
大事なところは
子供から渡すところと親が受け取るところ!
this.emit()
の部分と@sample=""
のところを良くみてね。⬇︎子コンポーネント
<template> <v-btn text v-on:click="buttonClick"></v-btn> </template> <script> import { Component, Vue, Prop } from "vue-property-decorator"; @Component() export default class StackedForm extends Vue { private buttonClick(): void { // ⬇︎親の@マークの名前 this.$emit("sample", "aaa"); // ⬆︎引数指定できる! } } </script>⬇︎親コンポーネント
// ⬇︎子から受け取る <stack-form :text-fields="textFields" @sample="emitChan"></stack-form> <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import StackForm from "@/components/organisms/forms/StackedForm.vue"; @Component({ components: { StackForm } }) export default class Login extends Vue { // ⬇︎子から引数でデータ受け取って実行とかできる! private emitChan(value: any): void { console.log(value); } } </script>簡単にいうと!
emitの第1引数に@の名前がきて第2引数からはemitChanに引数で渡されるよ!
ちなみに第3引数以降も指定して渡しても大丈夫!はてなブログもやってます
はてなブログのVue系⬇︎ぜひご覧ください!
https://taitoajiki.hatenablog.com/archive/category/Vue
- 投稿日:2020-01-15T17:14:11+09:00
VueとTypeScript 算出プロパティとメソッドの違い
算出プロパティとは
算出プロパティとは1回実行されたら、
キャッシュに保存して変更がない限り同じ結果を返す。それに対してメソッドは、算出プロパティとは違って
毎回処理を実行する。算出プロパティをTypeScriptで書いてみる。
基本的には名前の前にgetを記述するだけ。
getアクセサを使う感じだね!public get isSample(): boolean { return True; }ちなみにsetアクセサもできるよ!
public set foo(): string { return '私はサンプルです。'; }
Vue
ではcomputedの部分になるね!ちなみにアクセサとは
他のクラスとか外からアクセスするためのメソッド!
使う使わないの賛否両論は私にはわかりません。。はてなブログもやってます
はてなブログのVue系⬇︎ぜひご覧ください!
https://taitoajiki.hatenablog.com/archive/category/Vue
- 投稿日:2020-01-15T17:03:03+09:00
【Nuxt.js】pagination導入編:まずは大枠を理解しよう!
前置き
ページネーションをやっていきます。
いくつかに分けて書いていきます。
今回は導入編として
どんな形でつくるかの大枠を説明?いつも通り
超簡潔に説明できれば良いので
まずはこれだけ作りましょう??
…
は?これだけ?…
そうです?笑表示はこれだけですが、
導入としてこちらをやります。
・ページ遷移のmethods
・現在いるページにclassを付与Let's try?
Step1: dataを用意
基本はページ番号 = 配列番号を
増やしたり減らしたり、
該当番号にクラスをつけるだけですね。なのでまずはdataを用意?
初期値はそれぞれ0です。
・now = 現在のページ
・last = 残りのページ一応ページコンテンツとして
数字の繰り返しだけいれておきましょう。index.vue<template> <div class="page"> <ul> <li v-for="n in 5"> {{ n }} </li> </ul> </div> </template> <script> export default { data() { return { newsSection: { nav: { now: 0, last: 0, }, }, } }, } </script>Step2: methodsを用意
【動作】
矢印を押すとそれぞれmethodsで
Step1で用意したdataを動かしていきます。
5ページ(0~4番号)あるので、
はみ出す分はそれぞれ-1と5
はみ出したら0か4に戻します。
・now
・last
現在のページ数を反映させるものは
実践編で解説します。【methods】
・←を押すとclickPrevNewsPage
・→を押すとclickNextNewsPage【式】
三項演算を使用
式1 ? 式2 : 式3
式1がtrueなら式2、falseなら式3index.vue<template> <div class="nav"> <div class="prev" @click="clickPrevNewsPage()" > <svg /> // 省略 </div> <ul> <li> ページ番号 </li> </ul> <div class="next" @click="clickNextNewsPage()" > <svg /> // 省略 </div> </div> </template> <script> export default { data() { return { newsSection: { nav: { now: 0, last: 0, }, }, } }, methods: { clickPrevNewsPage() { this.newsSection.nav.now = this.newsSection.nav.now - 1 < 0 ? this.newsSection.nav.now : this.newsSection.nav.now - 1 }, clickNextNewsPage() { this.newsSection.nav.now = this.newsSection.nav.now + 1 > this.newsSection.nav.last - 1 ? this.newsSection.nav.now : this.newsSection.nav.now + 1 }, }, } </script>【解説】
◾️clickPrevNewsPage
※nowが0の時は1ページ目。
式1
・パターン1
現在1ページ(配列番号0)なら
0 = 0 - 1 < 0
trueなので式2を実行
・パターン2
現在4ページ(配列番号3)なら
3 = 3 - 1 < 0
falseなので式3を実行式2 true
now = 0なので0番目(1ページ目)にする
= - 1番目(0ページ目)にはいけない
式3 false
- 1して1ページ戻る順番的に分かりにくいかもしれませんが要は
nowが0以上の時は - 1していって、
- 1になったら0に戻すと言うことですね。◾️clickNextNewsPage
※nowが4の時は最終ページ。
式1
・パターン1
現在5ページ(配列番号4)なら
残り2ページ(配列番号4, 5)
4 = 4 + 1 > 2 - 1
2 - 1を左辺にもっていくので
・ - 1にチェンジ
・符号も逆向きにチェンジ
4 - 1 =< 5
trueなので式2を実行
・パターン2
現在4ページ(配列番号3)なら
残り3ページ(配列番号3, 4, 5)
3 = 3 + 1 > 3 - 1
3 + 2 =< 4
falseなので式3を実行式2 true
now = 4なので4番目(5ページ目)にする
= 5番目(6ページ目)にはいけない
式3 false
+ 1して1ページ進む複雑になってきましたが
1度理解すればテンプレとして使えますね?classを付与
1ページ目にいる時は
そこだけボーダーをつけます。
ついでにクリックしたら
該当ページに飛ぶように@clickも?index.vueページ表示部分を変更します。 // 変更前 <ul> <li> ページ番号 </li> </ul> // 変更後 <ul> <li :class="{ selected: newsSection.nav.now === 0}" @click="newsSection.nav.now = 0" > 1 </li> </ul>クラス部分追加。
index.vue<style lang="scss" scoped> .nav { ul { li { &.selected { padding: 3px 5px; border: 1px solid #4B4B4B; } } } } } </style>【解説】
:class="{ class名: 式 }"
クラスバインディングで
nowが0と完全一致した場合のみ
selectedクラスを付与します。
つまり0番目の1ページにいる時は
1ページだけにボーダーつけてくれます。
https://jp.vuejs.org/v2/guide/class-and-style.html
==と===の違いはこの記事が分かりやすいです!
https://www.sejuku.net/blog/23942@click="newsSection.nav.now = 0"
now = 現在いるページ
0にするので0番目(1ページ目)にするここまでのコード
毎度のことですが、
cssはほとんど省いております。
今回は
・ページ遷移のmethods
・現在いるページにclassを付与
でした?
続きは実践編などで公開します。index.vue<template> <div class="nav"> <div class="prev" @click="clickPrevNewsPage()" > <svg /> // 省略 </div> <ul> <li :class="{ selected: newsSection.nav.now === 0}" @click="newsSection.nav.now = 0" > 1 </li> </ul> <div class="next" @click="clickNextNewsPage()" > <svg /> // 省略 </div> </div> </template> <script> export default { data() { return { newsSection: { nav: { now: 0, last: 0, }, }, } }, methods: { clickPrevNewsPage() { this.newsSection.nav.now = this.newsSection.nav.now - 1 < 0 ? this.newsSection.nav.now : this.newsSection.nav.now - 1 }, clickNextNewsPage() { this.newsSection.nav.now = this.newsSection.nav.now + 1 > this.newsSection.nav.last - 1 ? this.newsSection.nav.now : this.newsSection.nav.now + 1 }, }, } </script> <style lang="scss" scoped> .nav { ul { li { &.selected { padding: 3px 5px; border: 1px solid #4B4B4B; } } } } } </style>このアカウントでは
Nuxt.js、Vue.jsを誰でも分かるよう、
超簡単に解説しています??
これからも発信していくので、
ぜひフォローしてください♪
https://twitter.com/aLizlab
- 投稿日:2020-01-15T15:45:18+09:00
出来る限り何も考えずにVue.jsを扱う
概要
Vue.jsもすでにバージョンを重ねて、開発環境等々とり得る選択肢が増え、自分のような初学者(元々組込み系ソフトウェア設計者として何年か働いているので、初学者というには微妙ですが)は混乱してしまったため、備忘録。
初学者ほど、意外にツールや設計の重要性についてはあまり意識していないようなので、そういった観点で本記事は作成しています。
前提知識は以下の通り。
- Node.js
- Bootstrap
- なんか適当にWebの知識がある
- ターミナルでコマンド
Vue.jsとは
Webフレームワークの一つ。基本的には中規模~大規模向けと言われています。この規模ベースの表現は分かりにくいですが、個人的には以下のような機能の有無が関係しているものと思います。
- 小規模:DOM操作ができる
- 中規模:グローバル変数を格納できる機能がある
- 大規模:クライアント側(Webブラウザ側)にてルーティング機能がある
開発環境
あらかじめ「node.js」が導入されている前提で記述します。ここでは、ほとんど「npm」を用いたパッケージ管理ソフトとして使用しています。パッケージ管理ソフトとは、コマンド一発でパッケージ(ライブラリ)をインストールできるものと考えておいてもらえればいいと思います。
余談ですが、私は「node.js」はバージョン管理ソフト「nodist」から入れています。「node.js」はバージョンアップが早く、またweb系は何だかんだ様々な環境を想定するだろうなと思い、導入しています。
今回は「vue/cli」を採用します。「nuxt.js」を採用している人もいますが、現行の「vue/cli」であれば簡単なアプリケーションを開発分には事足りそうだったので。コマンド一発で「router」と「vuex」を使えますし。
言語も基本は「Javascript」にします。オブジェクト指向的に書きたいと思い、typescriptの採用も検討しましたが、しっかりコンポーネント毎に作ることとしていれば良さそう。静的型付けしたい感もありますが、そこまで大規模化しなければ神経質になる必要もないかなと思い、「typescript」の採用は見送りました。単純にそのあたりの勉強がメンドイということもありますが。
また、「bootstrap」も利用します。デザインセンスが壊滅的にないので、付け焼き刃ですが使用します。
Bootstrapとは?
wikipedia的には「Webアプリケーションフレームワーク」。「見た目」を「それっぽく」「レスポンシブ」にするフレームワークです。ここでいう「レスポンシブ」とは画面の大きさに合わせて見た目を変えられるという意味です。
エディタ(VScode)
Javascriptを使用する上でよく使用されているのは「VScode」だと思います。特別宗教上の理由がない限りはVScodeを使用すると良いと思います。
- Visual studio Code:https://azure.microsoft.com/ja-jp/products/visual-studio-code/
ブラウザ(Google Chrome)
これは私の場合ですが、私は「Google Chrome」を使用しています。一応ChromeのプラグインとしてVue.js用のデバッグツールがあります。ここでは、このツールの使い方を説明しませんが、一応入れとくといいのかもしれません。
- Vue.js devtools:https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=ja
「vue/cli」の導入/プロジェクトの作成
コンソールにて、以下を入力。そうすると、npm環境におけるグローバル領域において「vue/cli」がインストールされます。
npm install -g @vue/cli導入出来たら、何も考えずに次のコマンド。
vue create myProject ※「myProject」は任意の名前このコマンドは「vue/cli」におけるプロジェクト作成のコマンドです。このコマンドを打つと以下のようになります。
Please puck preset:(Use arrow keys) default(babel,eslint) >Manually select featuresdefeultでもいいですが、自分はManuallyでプロジェクトを作成します。その後、自分は最低限以下の用のような項目にチェックを入れてプロジェクトを作成します。あとは適当にEnterを押します。
?Please pick a preset: Manually select features ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i > to invert selection) >(*) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support (*) Router (*) Vuex ( ) CSS Pre-processors (*) Linter / Formatter ( ) Unit Testing ( ) E2E TestingBootstrap-vueのインストール
Bootstrap-vueは、そのBootstrapをVue.jsをベースにして使用するためのライブラリです。インストールの仕方は簡単で、以下のコマンドを押下すると良いです。
cd myProject npm install bootstrap-vue「cd myProject」でプロジェクトフォルダに移動後、インストールします。
Vus.jsによるプロジェクトのファイル構成
プロジェクトを作成すると、自動に様々なファイルが作成されます。私はレガシーな組込み系ばかり関わっていたので、作成されるファイル数に面食らいました。主なファイルの構成は以下の通りです。「:」以下は自分の何となくのイメージ。
myProject ├─node_modules:npmコマンドで入れたライブラリの格納する場所。ほとんどいじらない ├─public │ └─index.html:ほとんど何もいじらないhtmlファイル。いじってもCDNのリンクを張るくらい └─src ├─assets:静的ファイルリソース ├─components:単一コンポーネントファイルを格納する場所 ├─router │ └─index.js:ルーティング設定ファイル ├─store │ └─index.js:グローバル変数を格納するところ ├─views:ページレイアウトのためのコンポーネント ├─App.vue:最上位コンポーネント └─main.js:ライブラリとかの設定するファイルユーザは基本的に「src」以下のファイルしか編集しません。Vue.jsでは、基本的に「コンポーネント」ごとにまとめやすいようにしているように思えます。このあたりは最近のwebフレームワークでは共通した特徴だと思いますが、他のフレームワークよりは意識するファイルが少ない印象を受けます。似たフレームワークであるところの「Anguler」の方が、プラグイン的なフレームワークが整っているように思いますが、初学者としてはBootstrap+Vue.jsの方が理解が早いし、業務上の応用が利きそうな雰囲気があります。
コンポーネントの作成
コンポーネントとは、ソフトウェアを構成する部品である。ただし、ここでいうコンポーネントとは「コンポーネント指向におけるコンポーネント」ということに注意すること。具体的には以下の4つ。
- 構造
- スタイル
- 状態(≒データ)
- 機能
思想としては「オブジェクト指向」もあるが、こちらが指向するのは「データ」と「機能」だけど、「コンポーネント指向」ではそれをもう少し大きくしたイメージである。
単一コンポーネントファイル
上記のコンポーネントを作成するため、vue.jsでは拡張子「.vue」にて単一コンポーネントファイルを作成する。
単一コンポーネントファイルは以下のような要素で構成される。個人的な見方だが、コンポーネント指向としては以下のような対応付け。
- template:構造
- script:状態、機能
- style:スタイル
sample.vue<template> <div class="com"> </div> </template> <script> default export { name:'com', data(){ return { dt1: "", dt2: "" } }, components:{} } </script> <style> </style>ページ作成
新しいページを作成するときは「view」ディレクトリを編集します。基本的な構成は単一コンポーネントファイルと同様である。
違いは、基本的にここでは、単一コンポーネントファイルの組み合わせで記述し、データの保持は基本的にはこっちで行う(ここで基本的にと言っているのは、ページ間で共有する場合は「vuex」にてデータの保持を行うが、ここでは詳しく記述しない)。
ルーティング(router)
基本的に「router」ディレクトリのindex.jsしか編集しません。
念頭に置くのは以下のふたつです。
- ルーティングの設定(これは「router」のindex.js)
- 設定したパスに移動するための設定
「routes」変数の中を以下のような感じに定義します。
「views」フォルダの中に定義したコンポーネントファイルを呼び出すように設定します。/router/index.jsimport Vue from 'vue' import VueRouter from 'vue-router' // viewsフォルダにあるコンポーネントファイルを呼び出し import Auth from '../views/Auth.vue' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/',// パスの設定 name: 'auth',// 名前 component: Auth// 呼び出したコンポーネントファイルを設定 }, { path: '/home',// パスの設定 name: 'home',// 名前 component: Home// 呼び出したコンポーネントファイルを設定 } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default routerグローバル変数の管理(vuex)
ここでは「vuex」について説明します。正直個人的にこれが一番面倒だった。可能であれば、ページ間でのデータの共有のない設計をすべきだと考えます。
「vuex」はページ間にてデータを共有するための機能です。変数/ゲッター関数/セッター関数の定義
「store」フォルダにあるindex.jsに定義します。以下に例を示します。
- state:変数の定義
- getters:ゲッター関数(値の取得)の定義
- mutations:セッター関数(値の変更)の定義
/store/index.jsimport Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { // 変数の定義 user: "", password: "" }, getters:{ // ゲッター関数の定義 user(state){return state.user}, password(state){return state.password} }, mutations: { // セッター関数の定義 setUser(state, payload){state.user = payload}, setPassword(state, payload){state.password = payload} }, actions: { }, modules: { } })関数の呼び出し
this.$store.commit("setUser", this.user);手順
最後に簡単に手順を示します。
- プロジェクトを作成
- 「views」に作成したいページ分コンポーネントファイルを用意
- 「router」のindex.jsに「views」に用意したページ分パスを設定
- 「store」のindex.jsにページ間をまたいで使用する変数を設定
- あとはガツガツコンポーネントファイルを編集する
最後に
ここでは、Vue.jsについて簡単にではありますが、説明しました。詳細な説明は何かしらの参考書を見ることをお勧めします。以下は私が買った参考書です。
- 基礎から学ぶ Vue.js:https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6-Vue-js-mio/dp/4863542453/ref=sr_1_3
参考
- フロントエンドのコンポーネント設計に立ち向かう/qiita:https://qiita.com/seya/items/8814e905693f00cdade2
- Vue.js:https://jp.vuejs.org/index.html
- BootstrapVue:https://bootstrap-vue.js.org/
- 投稿日:2020-01-15T10:36:39+09:00
VueコンポーネントをjQueryで動的にマウントする
VueコンポーネントをjQueryで動的にマウント
Kintoneのプラグイン開発でVueコンポーネントを動的にマウントしなければいけない場面があり、情報がなかなか集まらず苦戦したのでメモする。
0. webpackを使用している場合はaliasを追加
最後この情報を得ることができず解決まで時間がかかったので、この設定が一番重要かもしれない。
webpack.config.jsmodule.exports = { resolve: { alias: { "vue$": "vue/dist/vue.esm.js" } } }1. Vue.extendを使う
まず純粋に
Vue.component
でコンポーネントを作るのではなく、Vue.extend
を使いVueのサブクラスを作成する。(参照:API - Vue-extend)const Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } })2. jQueryでDOMを作成
次に作成したサブクラスのマウント先になるDOMをjQueryで作成し、任意の場所に追加する。
const app = $("<div id='app'></div>"); $("body").append(app);3. Vueサブクラスをマウントする
const component = new Profile(); component.$mount("#app"); // 2で作成したDOMを指定
- 投稿日:2020-01-15T09:48:36+09:00
Nuxt.jsで作成したAPIで、Markdownの書式を自動的に生成
要約
- MarkdownのリンクとかLaTeXの\hrefを作成するときにwebページのタイトルを調べるのが面倒
- タイトルを取得するプログラムをクライアント環境で実行するとCORS違反
- サーバ環境でタイトルを取得するためにNuxt.js環境にAPIを作成
はじめに
MarkdownとかLaTeXで文章を書いていて、webページへのリンクを作成するために、こんなことをしていました。もっと便利な方法があるのかもしれませんけど。
- リンクを作成したいページをブラウザで開く。
- ctrl-Uでソースコードを表示する。
- ctrl-Fでtitleタグを検索して、
<title>
タグを見つける。<title>
と</title>
の間をマウスで選択してから、ctrl-Cでコピーする。- Markdownだったら、[と]の間にコピーしたタイトルを貼り付けて、その後ろの(と)の間にwebページのURLを貼り付ける。
これでは、プログラマの三大美徳の「怠慢」を違反しますので、ツールを作りました。node.jsの開発サーバー環境で動作するツールです。http://localhost:3333にアクセスします。他のnode.jsの開発サーバー環境とポートを競合しないように、デフォルトのポート番号から変更しています。
GitHub - kubotama/must: MarkUp Support Tool
テキストエリアにURLを入力して、ボタンをクリックするとリンクの形式に変換します。上のGitHubへのリンクも、このツールで作成しました。他にもLaTexの\hrefの形式に変換したり、MarkdownやLaTeXの特殊文字のエスケープ処理をします。
APIの作成
クライアント環境で実行するJavaScriptのプログラムからwebページにアクセスしてタイトルを取得しようとするとCORSに違反します。そのため、クライアント環境から直接アクセスするのではなく、指定したwebページにアクセスしてタイトルを取得するAPIを作成します。
nuxt.config.jsの設定
Nuxt.jsではnuxt.config.jsにserverMiddlewareを設定することで、APIを作成できます。
nuxt.config.js}, serverMiddleware: [{ path: '/api', handler: '~/api/index.js' }] }http://localhost:3333/apiにアクセスすると、Nuxt.jsのプロジェクトディレクトリの直下のapiディレクトリに作成したindex.jsがが呼びされます。
APIとして呼び出されるスクリプト
タイトルを取得したいwebページがhttp://www.google.co.jpだとすると、クライアント環境からは、http://localhost:3333/api?url=http://www.google.co.jpにアクセスします。
/api/index.jsimport url from 'url' import { JSDOM } from 'jsdom' import HttpStatus from 'http-status-codes' import axios from 'axios' export default (req, res, next) => { // 1. クライアント環境からURLで引き渡されたwebページのアドレスを取得 const targetUrl = new url.URL(req.url, 'http://localhost').searchParams.get( 'url' ) // 2. 取得したアドレスのwebページのコンテンツをaxios.getで取得 axios.get(targetUrl).then((response) => { const body = response.data // 3. 取得したwebページのコンテンツからjsdomモジュールでタイトルを取得 const dom = new JSDOM(body) const title = dom.window.document.getElementsByTagName('title')[0].textContent // 4. 取得したタイトルをJSON形式のレスポンスとしてクライアント環境に返信 res.writeHead(HttpStatus.OK, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ title })) }) }このプログラムでは以下の処理をします。
- クライアント環境からURLで引き渡されたwebページのアドレスを取得
- 取得したアドレスのwebページのコンテンツをgetで取得
- 取得したwebページのコンテンツからjsdomモジュールでタイトルを取得
- 取得したタイトルをJSON形式のレスポンスとしてクライアント環境に返信
jsdomモジュールを利用するための設定
APIの作成とは直接関係ありませんが、Nuxt.js環境でjsdomモジュールをimportしてyarn devを実行するとエラーが発生するため、nuxt.config.jsに以下の設定が必要です。
nuxt.config.jsbuild: { /* ** You can extend webpack config here */ extend(config, { isDev, isClient }) { if (isClient) { config.node = { fs: 'empty', child_process: 'empty', tls: 'empty', net: 'empty' } } } },クライアント環境
テキストエリアとボタン
webページのアドレスを入力するテキストエリアと、ボタンが表示されています。ボタンをクリックすると、変換結果でテキストエリアを更新します。
MustUi.vue<template> <div> <div> <el-input id="mustArea" v-model="mustArea" :rows="10" type="textarea" placeholder="変換する文字列を入力してください" > </el-input> </div> <div> <el-button id="btnLinkMd" @click="clickLinkMd">リンク</el-button> </div> </div> </template>スクリプト
ボタンをクリックされると、テキストエリアに入力されているアドレスを引数にして、APIを呼び出して、webページのタイトルを取得します。取得したタイトルを使って作成したリンクをテキストエリアに設定します。
MustUi.vue<script> export default { name: 'MustUi', data() { return { mustArea: '' } }, methods: { getTitle(url, callback) { this.$axios .get(`${location.protocol}//${location.host}/api?url=${url}`) .then((response) => { this.mustArea = callback(url, response.data.title) }) }, clickLinkMd() { this.getTitle(this.mustArea, (url, title) => { return `[${title}](${url})` }) } } } </script>終わりに
文章を書くときに定形作業が必要な場合、VS Codeとかの拡張機能もいいですけど、変換前後がよくわかるので、こういうツールも便利じゃないかと思いました。
記事のいいね!などもらえたら、とても励みになりますので、よろしくお願いします。
- 投稿日:2020-01-15T09:25:54+09:00
【Nuxt.js/Vue.js】 手軽に長い文字列を省略して末尾を三点リーダー(…)にする方法
plugins/filter.jsVue.filter('truncate', function(value, length, omission) { var length = length ? parseInt(length, 10) : 20; var ommision = omission ? omission.toString() : '...' if(value.length <= length) { return value; } else { return value.substring(0, length) + ommision; } })nuxt.config.jsplugins: [ '~/plugins/filter' // 追記する ],text.vue<div> {{ text | truncate(50) }} </div> // 引数に表示したい文字数を入れる
- 投稿日:2020-01-15T02:50:26+09:00
Vue.js のアーキテクチャについて
はじめに
Vue.js で簡単なプロダクトを作成する前に特徴を把握すべく、以下で Vue.js のアーキテクチャを確認しました。
Vue.js ガイド: はじめに
※私が確認した範囲での理解となりますので、誤りや不足がある場合は、ご指摘いただけますと幸いです。
Vue.js のアーキテクチャ
以下を記載します。
- ソフトウェア・アーキテクチャ上の分類
- Model・View・ViewModel それぞれの機能
ソフトウェア・アーキテクチャ上の分類
Vue.js は、ソフトウェア・アーキテクチャのパターンとしては、MVVM (Model - View - ViewModel) に分類され、全体像としては以下の図の通りとなります。
Model・View・ViewModel それぞれの機能
Model:Javascript オブジェクトやデータオブジェクト(データ自体)です。
View:実際に表示される DOM 部分です。
ViewModel:Vue.js の中核となる部分で、Vue インスタンスとして定義することによって View を管理し、Model からデータの状態を取得する・通知を受けることで、変更内容が自動的に、迅速に同期されます。
DOM の Listener やディレクティブがこれに該当します。※ディレクティブ:「v-text」や「v-bind」、「v-if」といった形で DOM 中に記載します。
Model
Javascript オブジェクトやデータオブジェクトのことであり、Vue インスタンス内で使用されると View との間で同期の対象となります。
明記されていませんが、Model も ViewModel によって管理されると言えます。注意点としては、同期の対象(Vue インスタンスによる監視の対象)になると setter によってのみ、オブジェクトの内容が変更されたことがディレクティブによって使用されて View に反映されるということです。
例えば、Javascript で通常行うような「a = 1;」では変数 a に 1 は反映されず、$set の構文を使用することで反映されます。
(詳細は後述の「オブジェクトの内容変更に関する動作について」をご参照ください。)View
実際は DOM なので、<html> や <div>、<p> といったものが該当します。
ただ、この DOM において、ViewModel で定義したディレクティブや値を使用しますので、View は ViewModel に管理されていると言えます。
ViewModel
以下のような形で定義し、View と Model を管理下に置き、双方の内容を同期します。
var vm = new Vue ({ ... ※定義や実行内容 })オブジェクトの内容変更に関する動作について
オブジェクトの内容変更に関する動作の概要は以下の図の通りです。
※明記されてはいませんが、Watcher は ViewModel に含まれると考えられます。
a.b の値が変更されると、setter が ViewModel の Water に変更されたことと変更後の値を通知し、ViewModel 内でさらに Watcher からディレクティブに通知されます。
そして、最終的に View で取り扱われる値が変更されます。
まとめ
- Model, View, ViewModel の 3 階層で構成される。
- ViewModel が View と Model を同期する(管理する、とも言える)。
- 同期は自動的に、迅速に実行される。
- 投稿日:2020-01-15T00:20:50+09:00
Nuxt.jsで`window is not defined` または `document is not defined`になったときの対処法
公式ドキュメントにある通りにやれば問題ないんですが、どうやればいいのかいまいち具体的にピンとこなかったので書き残しておきます。
(particles.jsを使うのにめっちゃ手間取った。)if (process.client) { require('external_library') }window または document が undefined - NuxtJS
VueのSFCにおいて、具体的にはこんな感じ。
importしたライブラリでエラーとなった場合
import 'external_library'
として読み込むのではなく、こうする。<template> <div>hoge</div> </template> <script> // requireで読み込む if (process.browser) { require('external_library') } export default { // hoge } </script>メソッドなどでwindowを呼び出そうとした場合
mountedで使う場合は意識しなくても使えるはず。
<template> <div>hoge</div> </template> <script> // こうとか if (process.browser) { // ここに window とか document を使った処理 } export default { // 作成時に使いたかったらこうとか created() { if (process.browser){ // ここに window とか document を使った処理 } }, // DOM生成し終わってから使いたかったらこう mounted() { // ここに window とか document を使った処理 } } </script>
- 投稿日:2020-01-15T00:20:08+09:00
Vuetifyを使ってデザインをつけてみた
今回の記事は前回の記事の続編で、Vuetifyを使って、デザインを整えていきたいと思います。
前回の記事ではGraphQLでDynamoDBに保存されたデータを実際に画面上に出力する方法を記載しているので、興味のある方は是非見てみてください
GraphQL+DynamoDBを使って画面にデータを出力して見た環境
- Pug を使用
- VuetifyのコンポーネントをUIで使用
- コーディング規約は Nuxt.js に則って StandardJS
- Visual Studio Code
現状
部屋番号はsortされているが、デザインがない状態
データ構造
v-iconを使ってみよう
v-icon
コンポーネントを使って好きなアイコンを使ってみます。アイコンの一覧は公式のMaterial Iconsにページにまとめられてあります。部屋のタイプに合わせてアイコンを以下のように表示させていこうと思います。
※アイコンの種類には特に拘っていないのでご了承ください。
Type Icon single person double wc triple people family home 実際にv-iconを表示させてみる
switch文を使い、部屋のデータに合わせて、v-icon名を返す
getRoomTypeIcon
メソッドを用意しましたmethodsgetRoomTypeIcon (room) { if (room.guestInfo) { switch (room.type) { case 'single': return 'person' case 'double': return 'wc' case 'triple': return 'people' case 'family': return 'home' } } return 'no_meeting_room' }Vue上で
getRoomTypeIcon
メソッドを呼び出してあげましょうhome.vue<template lang="pug"> v-container v-layout( row wrap ) v-flex( xs12 sm6 md4 lg4 v-for="room in rooms" :key="room.name" ) v-card.ma-3( flat ) v-card-title | {{ room.number }} v-spacer span.mr-1( ) {{ room.status }} v-card-text( style="text-align:center; font-size:24px;" ) v-icon( large ) {{ getRoomTypeIcon(room) }} v-spacer span( v-if="room.guestInfo" ) {{ room.guestInfo.name }} </template>以下のようにデザインが変わります。いい感じですね。
2つの日付の差を求めてみる
宿泊者のデータには宿泊開始日(
startedAt
)とチェックアウトをする日(leavesAt
)のデータを持っています。
何日間宿泊するか、各部屋ごとに表示させていきたいと思います。あらかじめjavascriptが用意してくれている組み込み関数new Dateオブジェクトを使って日数の差を計算していきます。
methodsgetRoomDate (room) { if (room.guestInfo) { const day1 = new Date(room.guestInfo.startedAt) const day2 = new Date(room.guestInfo.leavesAt) const termDay = (day2 - day1) / 8640000 //24 * 60 * 60 * 1000 return termDay } else { return 0 } }
termDay
でday2
からday1
の差を求めた後、 86400000で割っているのは、
DateオブジェクトからDateオブジェクトを引くとミリ単位で値が返ってくるからです。
そのため、値を1日分のミリ秒(24時間x60分x60秒x1,000ミリ秒=86,400,000ミリ秒)である事で日数を求めます。
※参考:DateオブジェクトMDNVue上で
getRoomDate
メソッドを呼び出してあげましょうhome.vue<template lang="pug"> v-container v-layout( row wrap ) v-flex( xs12 sm6 md4 lg4 v-for="room in rooms" :key="room.name" ) v-card.ma-3( flat ) v-card-title | {{ room.number }} v-spacer span.mr-1( ) {{ room.status }} v-card-text( style="text-align:center; font-size:24px;" ) v-icon( large ) {{ getRoomTypeIcon(room) }} v-spacer span( v-if="room.guestInfo" ) {{ room.guestInfo.name }} v-card-actions div.mr-2 v-icon.mr-1( small ) event | {{ getRoomDate(room) }} {{ "days" }} </template>vuetifyのアイコンを中心に紹介していきました。最近ライブラリが新調され他にもデザインを豊かにするコンポーネントがたくさん用意されています。是非試してみてください!