- 投稿日:2019-08-11T23:35:33+09:00
create-nuxt-appで静的サイトを作るためにセットアップしたこと
やりたいこと
Nuxt.jsを使って静的サイトを作りたい。(今流行りのStatic Siteをgenerateしたい。)
つい最近まで素のHTMLとCSS(Scss)を書いていることが多かったけど、いろんな局面でNuxtの1つや2つくらい使えると便利だと思い、頑張ってもろもろを勉強しています。これまでの自分のやり方で、今後Nuxtでも実現したいこと
- リセット系cssを読み込む
- 共通scssを読み込む(フォントなどの指定)
- 共通scss変数を読み込む(色やブレイクポイントの数値など)
※
assets
配下にcss
とscss
フォルダを作成して、その中にこれらのファイルを入れます。
リセット系CSSを読み込む
nuxt.config.js
内にcss
という指定があると思うので、そこに読み込むファイルを追加する。nuxt.config.js/* ** Global CSS */ css: ['~/assets/css/ress.css']※ちなみにリセットにはress.cssを使っています。
参考:2019年版!おすすめのリセットCSSまとめ - ress.css
共通SCSSを読み込む(フォントなどの指定)
別途パッケージをインストールすれば、先程と同様に
nuxt.config.js
内のcss
に書くことで認識してくれます(CSSとは名ばかりSCSSも読んでくれる)パッケージのインストール
npm i -D node-sass sass-loader
nuxt.config.js
のcss
に追記nuxt.config.js/* ** Global CSS */ css: ['~/assets/css/ress.css', '~/assets/scss/base.css']
共通scss変数を読み込む
参考:Nuxt.jsでグローバルSass変数を使いたい - Qiita
あれ、
base.scss
と一緒に読み込まない(nuxt.config.js
のcss
内に書かない)のは何でなんでしたっけ…?(前にどっかで読んだけど正確に覚えてない…多分重複して読み込まれるスタイルが発生するから…?)とりあえず別途ミドルウェアを使用します。
いろんな種類があるぽいけどここでは@nuxtjs/style-resources
を使用します。インストール
npm i -D @nuxtjs/style-resources
nuxt.config.js
のmodule
に追記nuxt.config.js/* ** Nuxt.js modules */ modules: [ //他のモジュール設定 '@nuxtjs/style-resources' //追記 ]
nuxt.config.js
にstyleResources
を追記nuxt.config.js//buildとかの他の設定と同じ階層に追記する styleResources: { //読み込みたいファイルを指定 scss: ['@/assets/scss/variables.scss'] },こまってること[WIP]
- LinterやFormatterが何かとつらみ
「きれいなコードを書こう!」と意気込んで、LinterやFormatterを有効にすると、結構きびしくエラーを吐かれるので「もうちょっと素早く作業を進める予定だったのに…」みたいなことになりがちです。vscodeの
formatOnSave
の設定と、lint --fix
の自動実行みたいなところをうまくミックスしたいのですが、まだできていません…おわりに
「
create-nuxt-app
のようなボイラープレートを使っていれば自分で設定することなんてない!(どや)」なんて甘く見ていたんですが、意外と追加で設定する必要のある項目が多かったので、自分のメモ書きとして初めてQiitaの記事を書いてみました。今後も無理をせずに自分にとって有益な情報を発信していきたいとおもっとりゃす。
- 投稿日:2019-08-11T21:29:51+09:00
Vuetifyが2.0にアップデートして、ようやくgridsome.jsへの導入方法が分かったので共有してみたり
参考
https://github.com/gridsome/gridsome.org/pull/225/commits/96442da8cd154ae19d1eb6cabb9ee20cfad978fd
まもなく、公式にアップされそう
とりあえず、半分は自分用のメモという事でめちゃくちゃ迷走したが、結局何の事は無い
appOptionsに追加するだけだった
main.jsimport DefaultLayout from '~/layouts/Default.type1.vue' import Vuetify from 'vuetify' import 'vuetify/dist/vuetify.min.css' import colors from 'vuetify/es5/util/colors' export default function (Vue, { router, head, isClient, appOptions }) { // use Vue.use(Vuetify) appOptions.vuetify = new Vuetify({ customVariables: ['~/assets/css/variables.scss'], theme: { dark: false, themes: { dark: { primary: colors.grey.darken4, accent: colors.shades.black, secondary: colors.amber.darken3, info: colors.teal.lighten1, warning: colors.amber.base, error: colors.deepOrange.accent4, success: colors.green.accent3 } } } }) (省略)...... }gridsome.server.jsも少しいじる
その前にwebpack-node-externalsモジュールをインストール
npm i webpack-node-externalsgridsome.server.jsconst nodeExternals = require('webpack-node-externals') // Server API makes it possible to hook into various parts of Gridsome // on server-side and add custom data to the GraphQL data layer. // Learn more: https://gridsome.org/docs/server-api // Changes here require a server restart. // To restart press CTRL + C in terminal and run `gridsome develop` module.exports = function (api) { api.chainWebpack((config, { isServer }) => { if (isServer) { config.externals([ nodeExternals({ whitelist: [/^vuetify/] }) ]) } }) (省略)...... }こんだけでOKのようです!
sass-loaderとかnode-sassはいる
- 投稿日:2019-08-11T21:26:56+09:00
【Vue.js】Vue CLIでVue.jsを動かす〜プロジェクト作成まで
Vue.jsとは
- JavaScriptのフレームワーク。
- どのような規模・段階のアプリケーションにも対応することができる(プログレッシブフレームワーク)
- ページの一部で部分的に使用することもできるし、大規模なSPA(シングルページアプリケーション)を構築することもできる。
プログレッシブフレームワークとは、Vue.jsの生みの親であるEvan You氏の提唱する概念です。
参考記事
Vue.jsについて学習してみた <基礎編>
Vue.js概要?Vue CLIとは
- 迅速なVue.js開発を支援するためのコマンドラインインターフェイス。
- Vue.jsを利用した開発には、様々なパッケージやツールについての知識・複雑な設定が必要になる。それを自動化し、開発環境の構築を補助してくれる。
- 個別に公開されているパッケージ(ビルドツール、コンパイラなど) の集合体でもある。Vue CLIを利用することにより、そこから必要な機能だけを使うことができる。
参考記事
インストール
あらかじめディレクトリを作成し、初期化しておきます。(今回初期化の必要はないかもしれませんが、念の為。)
$ mkdir vue_cli_test $ cd vue_cli_test $ npm initnpmによるインストール
グローバルインストールで行っています。
$ npm install -g @vue/cli加えて、
@vue/cli-init
もインストールします。$ npm install -g @vue/cli-init@vue/cli-initのインストールがされていない場合、プロジェクト作成のコマンド
npm init
実行時に以下のエラーが発生します。Command vue init requires a global addon to be installed. Please run npm install -g @vue/cli-init and try again.グローバルインストールの場合、パッケージは
npm list -g
で確認できるディレクトリ配下のnode_modulesディレクトリ
にインストールされます。$ npm list -g /Users/yuki/.nodebrew/node/v12.7.0/libパッケージ名を指定して確認することもできます。
$ npm list -g @vue/cli /Users/yuki/.nodebrew/node/v12.7.0/lib └── @vue/cli@3.10.0環境変数の設定
このままだとvueコマンドが使えないため、
.bash_profile
にパスを設定します。
まず、コマンドがどこに存在するのか確認します。$ npm bin -g /Users/yuki/.nodebrew/node/v12.7.0/bin.bash_profileに追記します。
$ vi ~/.bash_profileexport PATH="$HOME/.nodebrew/node/v12.7.0/bin:$PATH"反映させます。
$ source ~/.bash_profileVue.jsのバージョン確認
vueコマンドが通るようになったので、Vue.jsのバージョンを表示してみます。
確認ができれば、Vue CLIのインストールは成功です。$ vue -V 3.10.0プロジェクト作成
npm init テンプレート名 プロジェクト名
でプロジェクトを作成します。今回、テンプレートはwebpackを指定します。
テンプレートの種類については、こちらの記事が参考になります。
Vue-cli(webpack)解剖 ーディレクトリ構成ー$ vue init webpack my-appいくつか質問をされます。
今は余分な機能を入れたくなかったので、以下の質問はNoにしました。// SPA構築に必要なVue.js公式ルータ ? Install vue-router? No // 構文チェックツール ? Use ESLint to lint your code? No // テストツール ? Set up unit tests No // テストツール ? Setup e2e tests with Nightwatch? Noプロジェクト名である
my-appディレクトリ
が作成され、必要な機能がインストールされます。
最後の方にあるメッセージの通り、my-appディレクトリに移動します。To get started: cd my-app npm run dev$ cd my-app次に
npm run dev
でサーバを起動します。
dev
というエイリアスコマンドは、プロジェクト名のディレクトリ/package.json
のscript
で設定されています。デフォルトでは以下の設定になっています。
npm start
やnpm run start
でもサーバを起動することができるようです。myapp/package.json"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "build": "node build/build.js" },$ npm run dev表示されるメッセージの通り
http://localhost:8080
にアクセスして、Vue.jsのデモ画面が確認できれば成功です。Your application is running here: http://localhost:8080
- 投稿日:2019-08-11T18:10:10+09:00
【読切】非同期処理の沼~Vue.js Django編~
あらすじ
2019年8月10日。私はVue.jsとDjango REST Frameworkを使い、ある業務システムの画面開発を担当していた。
フレームワークなんて代物はまだ触り始めて2,3か月。とは言えどこを触れば何が変わるか多少は理解してきている自負もあった。
そんな良くも悪くも弾みのついていた私を戒めるかのように、単体テスト中、そいつは突然現れた。出会い
『更新中の…ステータスが…… エラーコード:409』
「(動作確認では何度も正常に作動していた機能開発...そんなはずは...)」
NG判定とエラー内容をテスト記録として記述する。"""
HTTP 409 Conflict はリクエストが現在のサーバーの状態と競合したことを示すステータスコード。競合は PUT メソッドを使用したリクエストのレスポンスで最も発生しやすい。例えば、サーバーにすでに存在しているファイルよりも古いバージョンのファイルをアップロードした際に409の応答が返され、バージョン管理システムの競合が発生する可能性がある。
"""
409 Conflict困った困った。なけなしの知識と経験であれやこれやと思考した。
そして画面の再描画後、再度そいつは現れた。『更新中の…ステータスが…… エラーコード:409』
非同期処理の沼
「ここか。」
答えに辿り着くのに長くはかからなかった。heavyHoge.ts// post(url, data, loading) // url : 呼び出し先のURL // data : REST側に送りたいデータ // loading : 処理終了までローディング画面を表示 // 重ための処理 private async heavyProcess(): Promise<void> { const payload = '画面から送る何かのデータ群'; this.$http.post(url='hoge-url1', data={'payload': payload}, loading=false); this.$http.post(url='hoge-url2', data={'payload': payload}, loading=false); // 画面の更新処理 this.onShown(); }ボタンを押下して、この処理が走っている間に他の作業も進められるようにしたい。数十秒~数分なんて流石に待っていられない。
そのような声は当然にあり、この処理は上記の通り非同期処理で実装していた。
ここでの2つのhttp.post(以下、処理①と処理②)は類似処理を行っているが、具体的には下記の処理を行っている。1.画面内のデータを取得しREST側へPOST
2.取得したデータの条件が合えば、外部APIへ有料の関連データを取得
3.種々のCRUD処理を行う更にエラーの発生パターンについて調べたところ、大きく下記の2つであった。
1.処理①が上記1~3を終えて、戻り値を返して処理②を行おうとした直後
2.処理①のみor処理②のみが走るように画面側で予め条件設定し、1度目の処理を終えた直後に同じ処理のトリガーを引いた直後つまり、処理量の多さにCRUD処理が実際には完了しきいっていない状態の時に再び処理①②を行うことでエラーが起きていた。
それを示すように awaitを付記するとloading画面は処理終了まで続くがエラーは消える。(当然っちゃ当然だが)heavyHoge.ts// post(url, data, loading) // url : // data : 何かのデータ群 // loading : 処理終了までローディング画面を表示 // 重ための処理 private async heavyProcess(): Promise<void> { const payload = '画面から送る何かのデータ群'; await this.$http.post(url='hoge-url1', data={'payload': payload}, loading=false); await this.$http.post(url='hoge-url2', data={'payload': payload}, loading=false); // 画面の更新処理 this.onShown(); }とにかく、今回のエラーの発生要因やその背景なんかを整理すると
・外部APIが有料ということもあり、連続or多数の本チャンデータ取得パターンの確認を怠った。
・1度のボタン押下で行う非同期処理を2つに分けてしまった。
(※片方は他画面で使用しているViewに飛んでいた為、このような実装になった)
・REST側で各処理を行うViewを呼び出す為の、共通クラスを用意しなかった。
(※やってることは2個目と同じ)しかし、上記の課題を全てクリアして当初の設計通り実装しようとしても、まだエラーの発生パターン2が解決しない。
そう、ユーザーが短時間に連続で同じ処理を行おうとした時だ。完全に非同期処理の沼に嵌まってしまったのである。2019年8月11日。27歳の誕生日を迎えた今日、未だ解決策は見つかっていない。
To Be Continued
- 投稿日:2019-08-11T17:04:08+09:00
Vuetify のアップデート(v1.5.x => v2.x) with TypeScript and Webpack
本記事の立ち位置
ここの拡張版。
Vuetifyとは
Vue.js
用のUIフレームワークです。相違点
vue-cli
を利用しないpackage
とwebpack
を更新してアップデート- サンプルプログラムを抜粋して記載
How to
1.
package
のアップデート
npm update
を利用してもvuetify
などの一部パッケージが最新版にならない場合があります。これは既存のアプリを破壊(動かなく)してしまう可能性があると判断されているからです。- それを抑えてなお破壊的な更新を行いたい場合はncuを利用しましょう!
$ npm update # まずは普通にアップデート $ ncu # アップデートできるパッケージの確認 $ ncu -u # 破壊的アップデート2.
webpack
の更新
vuetify
をコンパイルするためにvuetify-loader
をインストールします。webpack-cli
# 以下はやってない人 $ npm i -g webpack webpack-cli $ exec $SHELL -l # コマンドプロンプトの再起動(linux, mac) $ npm i -D vuetify-loaderwebpack.config.jsconst VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin') let baseWebpack = { /* 略 */ plugins: [ new VuetifyLoaderPlugin() ], /* 略 */ }; module.exports = baseWebpack3. Vuetify の読み込み
src/plugins
は作成する必要はありませんが、ライブラリは別ファイルで管理しておくと可読性が良くなります。
tsconfig.json
への設定も忘れずにtsconfig.json{ *** "types": [ "vuetify" ], *** }src/plugins/vuetify.tsimport Vue from 'vue' import 'vuetify/dist/vuetify.min.css' import Vuetify from 'vuetify/lib' Vue.use(Vuetify); /* // directiveも利用する場合 import { Touch } from 'vuetify/lib/directives' Vue.use(Vuetify, { directives: { Touch } }); */ export default new Vuetify({ icons: { iconfont: 'fa' } });src/main.tsimport Vuetify from './plugins/vuetify'; import App from './components/App' new Vue({ vuetify: Vuetify, template: '<App/>', components: { App } }).$mount('#app')以上!
- 投稿日:2019-08-11T15:08:23+09:00
Vue.js超基本の使い方まとめ
最近やっているプロジェクトでjQueryを使ってたのですが、jQueryの面倒さに発狂したくなってしまい、なんとかならないものかと思っていたところ、Vue.jsに出会いましたので学習をはじめました。
参考
今回udemyを一通り受講し、振り返りをしているため非常に参考とさせていただきました。非常に分かりやすい構成になっているため、初めて学習を始める人はまず受講してみることをオススメ致します。
Vue.jsの基本構文
データバインディング
Mustache構文
Vue.jsではMustache構文を使うことで、簡単にVueインスタンスデータとHTMLを結びつけることができます。
このデータの結びつけのことをデータバインディング
といいます。下記の例を表示すると
{{ message }}
にはHello Vue.js!が表示されます。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Vue Sample</title> </head> <body> <div id="example"> {{ message }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> </body> </html>main.jsvar app = new Vue({ el: "#example", data: { message: "Hello Vue.js!" } });v-bind
簡単なテキストのデータバインディングであればMustache構文を使えましたが、DOM要素の属性をデータバインディングする場合にはMustache構文はエラーとなり使えません。
※以降Vueに関わる部分のみHMTLを記載
index.html<div id="example-error"> <a href="{{ url }}">Google</a> </div>main.jsvar app = new Vue({ el: "#example-error", data: { url: "https://google.com" } });そこで使用するのが
v-bindディレクティブ
です。
v-bindディレクティブ
を使えばDOM要素の属性を動的に切り替えることができます。例1:href属性のデータバインディング
下記例だとhref属性のurlにGoogleのURLがデータバインディングされます。index.html<div id="example1"> <a v-bind:href="url">Google</a> </div>main.jsvar app = new Vue({ el: "#example1", data: { url: "https://google.com" } });例2:value属性のデータバインディング
下記例ではinputタグのvalue属性がデフォルトでおはよう!
と表示されます。index.html<div id="example2"> <input v-bind:value="message"> <p>{{ message }}</p> </div>main.jsvar app = new Vue({ el: "#example2", data: { message: "おはよう!" } });しかし初期値の
おはよう!
は正しく表示されていましたが、文字を書き換えた時にpタグが書き換わっていなかったことに気づくと思います。これはv-bindによるデータバインディングは
・jsで更新したdataはテンプレートに更新されるが、 ・テンプレート側で更新してもdataが更新されないという性質があるためです。そこで登場するのが、
v-on
メソッドです。v-on
v-on
メソッドはイベント処理をするためのディレクティブです。
input要素のテキストエリアに入力する度にinputイベントが発生するので、v-bindに追加でv-onを追加してdataを更新する処理を追加します。index.html<div id="example"> <input v-bind:value="message" v-on:input="message = $event.target.value" > <p>{{ message }}</p> </div>main.jsvar app = new Vue({ el: "#example", data: { message: "おはよう!" } });
- 初期値で
message
におはよう!が入力されています- そのためMustasche構文でかいているpタグにも
おはよう!
が表示されます- 入力欄に
おはよう!
の!を削除するとinputイベントが発生し、messageを書き換えます- するとMustasche構文でかいているpタグにも
おはよう
に更新されますこのようにjsからテンプレートのデータを更新するだけでなくく、テンプレートからもデータを更新することを 双方向データバインディングといいます。下記の2行は全く同じ表現で、どちらもmessageに対して双方向データバインディングすることができます。
v-model
v-bindとv-onを組み合わせた双方向データバインディングの例を示しましたが、これは
v-model
というディレクティブで表現することができます。// どちらも同じ <input v-model="message"> <input v-bind:value="message" v-on:input="message = $event.target.value">v-bindとv-onの省略記法
v-bindとv-onは頻繁に使用するため省略記法があります。
ディレクティブ 完全な構文 省略記法 :v-bind <a v-bind:href="url"> ... </a>
<a :href="url"> ... </a>
:v-on <a v-on:click="func"> ... </a>
<a @click="func"> ... </a>
繰り返し処理
v-for
v-for
を使えば繰り返し文を使うことができます。
またv-for="(user, index) in users"
とすることで配列のキーを取り出すことも可能です。リストの例<ul id="example"> <li v-for="(user, index) in users" > {{ member }} </li> </ul>var app = new Vue({ el: "#example", data: { users: ["佐藤", "鈴木", "山田"] } });表示・非表示の切り替え
v-if, v-if-else, v-else
v-if
ディレクティブではブロックを条件に応じて描画することができます。<div id="app"> <div v-if="toggle"> <p>Hello</p> <div v-else> <p>World</p> </div> </div>var app = new Vue({ el: '#app', data: { toggle: true // 切り替える } })v-show
v-show
ディレクティブは属性値がtrueとなる場合に要素を表示します。<div id="app"> <div v-show="toggle"> <p>Hello</p> </div> </div>var app = new Vue({ el: '#app', data: { toggle: true // 切り替える } })v-ifとv-showの比較と使い分け
比較
v-ifとv-showは表示・非表示の方法に明らかな違いがあります。
v-if v-show 切り替え方法 ブロックごと作成・削除 ブロックを残したまま表示・非表示を切り替え 初期描画コスト 低い 高い 描画切り替えコスト 高い 低い 一方でv-showは非表示にする時にブロックに対してCSSの
display:none
を使って切り替えます。
使い分け
v-if
・ 初期描画コストが低く、描画切り替えのコストが高い
・ そのため切り替えの頻度が低いなら、初期描画コストの低いv-if
を使用する
v-show
・ 初期描画コストが高く、描画切り替えのコストが低い
・ そのため切り替えの頻度が多いなら、描画切り替えコストの低いv-if
を使用する算出プロパティ
computed
算出プロパティは関数で算出したデータを返すことができるプロパティのことです。要はgetterやsetterの役割だと思ってもらえれば良いかと思います。
<div id="app"> {{ reverseMessage }} </div>var app = new Vue({ el: '#app', data: { message: "Hello world!" }, computed: { // data:messageの文字列を反転させる reverseMessage: function() { return this.message.split('').reverse().join('') } } })算出プロパティとメソッドの比較
先ほどの例だと、メソッドを使っても同じことが実現できます。
<div id="app"> {{ reverseMessage }} {{ reverseMessageMethod() }} </div>var app = new Vue({ el: '#app', data: { message: "Hello world!" }, computed: { reverseMessage: function() { return this.message.split('').reverse().join('') } }, methods: { reverseMessageMethod: function() { return this.message.split('').reverse().join('') } } })そこで算出プロパティとメソッドの違いをまとめると3点の違いがあります。
算出プロパティ メソッド 記法 ()不要 ()必要 使い方 getter, setter getter キャッシュ される されない 算出プロパティのキャッシュ
算出プロパティとメソッドの使い分けとしてキャッシュの有無があります。
Mathのランダム関数を使ったときの例を用いて説明します。<div id="example"> メソッド <ol> <li> {{ getDate() }}</li> <li> {{ getDate() }}</li> <li> {{ getDate() }}</li> </ol> 算出プロパティ <ol> <li>{{ date }}</li> <li>{{ date }}</li> <li>{{ date }}</li> </ol> </div>var app = new Vue({ el: "#example", computed: { date: function () { console.log("computed"); return Math.random(); } }, methods: { getDate: function () { console.log("methods"); return Math.random(); } } });出力結果メソッド 0.523744236259859 0.29754866940841374 0.3024997149055251 算出プロパティ 0.19494595412700444 0.19494595412700444 0.1949459541270044このように同じMath.random()を使っても、算出プロパティは全て同じ値に対してメソッドは全てバラバラの値になりました。
また算出プロパティは描画は3度していますが、コンソールログには1度しかログが出ていません。算出プロパティとメソッドの使い分け
算出プロパティ
・ getter以外にもsetterを定義したい場合
・ キャッシュを効かせて、処理を高速化させたい場合メソッド
・ あえて処理を高速化させたくない場合さいごに
今回初めてVue.jsを使ってみましたが、jQueryのように直接DOM操作をするわけでなく、少ない量でかつわかりやすくDOMの操作をすることができ非常に期待が高まりました。
使い分けに関する情報など、初学者に少しでも役に立てば幸いです。
- 投稿日:2019-08-11T13:47:50+09:00
スロットコンテンツの書き方メモ
3連休だしVueを1から初めて遊んでみようと思った。
その中でスロットコンテンツを使ってコンポーネントの差し替えを行おうとした。
参考書には正しい例しか載っていないので、実験がてら試したことの考察Vueの定義
vue01.vuevar headerTemplate = ` <div id="test"> <slot name="header">No Title</slot> </div> ` Vue.component('page-header', { template: contentTemplate })失敗例
index01.html<page-header> <span><div slot="header">hoge</div></span> </page-header><div id="test">No Title</div>成功例
index02.html<page-header> <span slot="header"><div>hoge</div></span> </page-header><div id="test"><span><div>hoge</div></span></div>考察
挙動から読み取れる限り下記のようなロジックがありそう。
① コンポーネントタグはVueで定義したコンポーネントを呼び出すだけで、その間にdiv等のタグを含めてもロストしてしまい、テンプレートがそのまま表示される。
② スロットコンテンツを使えば、コンポーネントタグ内の情報をVue側に渡せるので、それはロストせずテンプレートへの上書きを行う。失敗例を見る限り、コンポーネントタグの最初のタグがslot属性を持っていなければ、その下のネストのタグがslotを持っていても②のロジックで判断されず、システム側がコンポーネントタグ配下のタグは①のロジックで扱っていいものだと認識して、slotをロストさせてしまっているのではないだろうか。
# わざわざ失敗例のようなタグ構成にする必要は何もないとは思いますが…Vue有識者の方、もしよろしければアドバイスをいただけると助かります。
- 投稿日:2019-08-11T13:21:48+09:00
Vue.jsでサクサク!SVG内に線を引いてみる
はじめに
前回の記事続きです。
SVGとVue.jsを組み合わせて今回は、Pathを使って線を引いてみるという
処理を実装して見たいと思います。前回の記事と組み合わせることで、
マインドマップのようなWebアプリも作れるかもしれないです。
(その場合はpath要素と図形要素を保持して、線を設定する形になると思います。)早速コードを見ていきましょう!
Path要素を入れる
まず、線を表現するのに必要なPath要素を、
SVGタグ内に記載いたします。
v-model内にリストで表現し、線でつないだ分だけ可変的に生成できるようにします。線を引いている時(確定前要素)と、線を引いた後(確定後要素)で、
Pathを分けております。
今回オプション要素で、線の太さや、色を全体に可変的に適用する設定もおまけで入れてます。SVGDemo.vue<template> <div class="container"> <div class="setting-box"> <!-- 色を変えるとこ --> <p>色</p> <input type="color" v-model="strokeFill" /> <!-- 線の太さとか --> <p>線の太さ</p> <input type="range" v-model="strokeWidth" /> </div> <!-- SVG定義 --> <svg :width="width" :height="height" :viewBox="viewport" @wheel="zoompan" @mousedown="lineNode($event, idx)"> <!-- 線を引いた後 --> <path :d="path" fill="none" :stroke="strokeFill" :opacity="strokeOpacity" :stroke-width="strokeWidth" stroke-linecap="round" v-for="(path, idx) in pathList" :key="idx" v-if="pathList.length > 0"/> <!-- 線を引いている最中 --> <path :d="pathTemp" fill="none" :stroke="strokeFill" :opacity="strokeOpacity" :stroke-width="strokeWidth" stroke-linecap="round" v-if="isLineNode"/> </svg> </div> </template>イベントハンドラを実装
今回は図形からドラッグしていくと
線が生成され、図形同士で繋げた場合に
生成されるようにしていきます。SVGDemo.vue<script> export default { name: 'SVGDemo', data () { return { width: 500, height: 500, ratio: 1, dx: 0, dy: 0, viewport: '0 0 500 500', isLineNode: false, beforeMouseX: null, beforeMouseY: null, selectIdx: 0, pathList: [], pathTemp: '', strokeFill: '#525', strokeOpacity: 1, strokeWidth: '5', moveLineX: 0, moveLineY: 0, startLineX: 0, startLineY: 0, } }, // マウス操作関連 mounted () { console.log('MOUNT LISTENER ON') document.addEventListener('mouseup', this.mouseUp) document.addEventListener('mousemove', this.mouseMakeLine) }, beforeDestroy () { console.log('MOUNT LISTENER OFF') document.removeEventListener('mouseup', this.mouseUp) document.removeEventListener('mousemove', this.mouseMakeLine) }, methods: { zoompan (e) { var [x, y, w, h] = this.viewport.split(' ').map(v => parseFloat(v)) if (e.ctrlKey) { // 拡大(Y軸が上がる場合) 縮小(Y軸が下がる場合) if (e.deltaY > 0) { w = w * 1.01 h = h * 1.01 } else { w = w * 0.99 h = h * 0.99 } this.makeViewBox(x, y, w, h) this.ratio = w / this.width e.preventDefault() } else { // 移動 if ((this.dx + e.deltaX > -this.width && this.dy + e.deltaY > -this.width) && (this.dx + e.deltaX < this.width * 2 && this.dy + e.deltaY < this.width * 2)) { this.makeViewBox(x + e.deltaX, y + e.deltaY, w, h) this.dx += e.deltaX this.dy += e.deltaY } } }, // viewboxを作成 makeViewBox (x, y, w, h) { this.viewport = [x, y, w, h].join(' ') }, // マウスのクリックが終わった段階で初期化 mouseUp (e) { this.isLineNode = false this.beforeMouseX = null this.beforeMouseY = null this.pathList.push(this.pathTemp) this.pathTemp = null e.preventDefault() }, // move中は前回まで動かした差分を取りながら座標を変化させていく mouseMove (e) { if (!this.isMove) return var mouseX = e.offsetX * this.ratio + this.dx var mouseY = e.offsetY * this.ratio + this.dy var dx = 0 var dy = 0 if (this.beforeMouseX && this.beforeMouseY) { dx = mouseX - this.beforeMouseX dy = mouseY - this.beforeMouseY } this.beforeMouseX = mouseX this.beforeMouseY = mouseY var tempX = dx + Number(this.rects[this.selectIdx].x) var tempY = dy + Number(this.rects[this.selectIdx].y) if (tempX > 0) this.rects[this.selectIdx].x = tempX if (tempY > 0) this.rects[this.selectIdx].y = tempY e.preventDefault() }, lineNode (e, i) { this.startLineX = e.offsetX * this.ratio + this.dx this.startLineY = e.offsetY * this.ratio + this.dy this.moveLineX = e.offsetX * this.ratio + this.dx this.moveLineY = e.offsetY * this.ratio + this.dy this.isLineNode = true e.preventDefault() }, mouseMakeLine (e) { if (!this.isLineNode) return var mouseX = e.offsetX * this.ratio + this.dx var mouseY = e.offsetY * this.ratio + this.dy var dx = 0 var dy = 0 if (this.beforeMouseX && this.beforeMouseY) { dx = mouseX - this.beforeMouseX dy = mouseY - this.beforeMouseY } this.beforeMouseX = mouseX this.beforeMouseY = mouseY var tempX = this.moveLineX var tempY = this.moveLineY tempX += dx tempY += dy if (tempX > 0) this.moveLineX = tempX if (tempY > 0) this.moveLineY = tempY this.pathTemp = 'M' + this.startLineX + ',' + this.startLineY + ' L' + this.moveLineX + ',' + this.moveLineY } } } </script>完成
ここまでロジックを記載していくと
このような形になるかと思います。
ベジェ曲線など組み込んでカーブさせても
面白そうですね!最後に
いかがでしたでしょうか?
Vue.jsとSVGを使うと色々なことができるので、
また色々記事を書いていけたら、と思います。現在弊社では、HRモンスターと呼ばれる
採用の新しいスタイルを提供するサービスをローンチいたしました。ローンチ後のさらなる機能追加、改善などのPDCAサイクルを回すべく、
エンジニアを募集しております。
https://www.wantedly.com/projects/341182Kubernetes、Vue.js(Javascript)、Django(Python)といったモダンな技術を使って、
開発しておりますので、もしご興味がある方はぜひ、ご応募お待ちしております。
- 投稿日:2019-08-11T10:46:29+09:00
Bulma+Vue.js モバイル対応用 Navbarの動作
下記の記事を参考に実装したが、
バーガーメニューが開いてNavメニューをクリックしてもバーガーメニューが閉じてくれない。「BulmaのハンバーガーメニューをVue.jsで実装する」
https://qiita.com/seki19/items/649b317c10e84fb36e06ポイントは、下記にメニューなどをクリックした場合を追加
<div id="navbarBasicExample" class="navbar-menu" v-bind:class="{'is-active': isOpen}">
@click="isOpen = !isOpen"を追加する。
<div id="navbarBasicExample" class="navbar-menu" @click="isOpen = !isOpen" v-bind:class="{'is-active': isOpen}">
となる。
- 投稿日:2019-08-11T05:14:34+09:00
【Vue.js】コンポーネントファイルの命名とタグの扱いをざっくりとまとめてみる
こんにちは、d4teです。
Vueのお作法に関する備忘録です。内容はコンポーネントファイルの命名・SFCでのタグの扱いなどです。
ここで書くことはすべてVue公式のスタイルガイド -Vue.jsに書いてあるので、興味のある方はそちらも読むことをおすすめします。SFCのファイルの命名
単一ファイルコンポーネントのファイル名はパスカルケース か ケバブケースにすべき
Bad
components/ |-mycomponent.vue |-myComponent.vuegood
components/ |-MyComponent.vue |-my-component.vue密結合のコンポーネントファイルの命名
親子間の関係が密結合な場合は、親コンポーネントの名前をプリフィックスとして含むべき
bad
components/ |-TodoList.vue |-TodoItem.vue |-TodoButton.vuegood
components/ |-TodoList.vue |-TodoListItem.vue |-TOdoListItemButton.vue最初僕はディレクトリでわけることで、理解しやすくしようと思っていましたが、スタイルガイドによればアンチパターンのようです。
components/ |-TodoList.vue |-TodoList/ |-TodoItem.vue |-TodoItem/ |-Button.vueたしかにこうしてみるとわかりにくいです。
- 同じような名前のファイルがたくさんできてしまい、コードエディタ上で素早くファイルを切り替えるのが難しくなります。
- ネストしたサブディレクトリがたくさんできてしまい、エディタのサイドバーでコンポーネントを参照するのに時間がかかるようになります。
自己終了形式のコンポーネント
単一ファイルコンポーネント・文字列テンプレート・JSXでは自己終了型で書くべきです
しかしDOMテンプレート(HTML)では開始タグと終了タグをセットで書きます。単一ファイルコンポーネント・文字列テンプレート・JSX
bad
<MyComponent></MyComponent>good
<MyComponent />また、単一ファイルコンポーネント内では常にパスカルケースで書くべきです
DOMテンプレート(HTML)
bad
<MyComponent />good
<my-component><my-component />
DOMテンプレート内では開始タグ・終了タグを書くことに加えてケバブケースで書く必要があります。単一ファイルコンポーネントのトップレベルの属性の順序
単一ファイルコンポーネントでは
template
,<script>
,<style>
タグを一貫した順序で書くべきです。
また、<style>
は最後に書きます。bad
<style></style> <template></template> <script></script><!-- A.vue --> <script></script> <template></template> <style></style> <!-- B.vue --> <template></template> <script></script> <style></style>good
タグが一貫した順序で、
<style>
が最後に書かれている。<!-- A.vue --> <template></template> <script></script> <style></style> <!-- B.vue --> <template></template> <script></script> <style></style>