- 投稿日:2019-05-30T17:42:54+09:00
Nuxt.jsでカレンダーを自作してみた
概要
チームで運用するスケジュール表を自作しようと思い、NuxtとAPIで作成できないか試行錯誤してみた内容を
記録しておく為の記事になります。CSSの説明は省きますが、完成イメージはこんな感じの物を紹介していきます。
※今回API通信の説明は行いません。また、Nuxt.jsの環境構築などはできている物と仮定します。
pages
index.vue<template> <div> <Calendar /> </div> </template> <script> import Calendar from '~/components/schedule/Calendar.vue' export default { components: { Calendar } } </script>今回は機能の一部となる
components/schedule/Calendar.vueを読み込んでそれを表示させているだけです。store
スケジュールを表示するための『日付情報』などを保持する「schedule.js」ファイルと
選択されているチームの情報を表示するための『メンバー情報』などを保持する「member.js」ファイルを作成しました。schedule.jsの説明
schedule.jsexport const state = () => ({ year: new Date().getFullYear(), month: new Date().getMonth()+1, today: new Date().getDay() }) export const mutations = { changeCalendar(state, add){ if(state.month + add > 12){ state.year += add state.month = 1 }else if(state.month + add < 1){ state.year += add state.month = 12 }else{ state.month += add } } }states
まず、「schedule.js」ファイルで保持するstatesは3つで、
『year:年』『month:月』『today:日付』です。そのままですね
初期値は当日の「年・月・日」を取得するようにしてあります。mutations
mutaionは、store内のstateを変更するための関数です。
changeCalendarでは引数で渡されている値と現在の『state.month』を足した値によって処理を分けています。
後程HTML部分で説明しますが、ここの引数は「-1」か「1」が渡されるようになっています。
『state.month』と『引数add』が「12」より多かった場合、「1」より小さかった場合は
『state.year』の方に『add』を足すことで、年数を変更するようにし、『state.month』を1月か12月にしています。
それ以外の場合は、『state.month』にそのまま『add』を足しています。member.jsの説明
member.jsexport const state = () => ({ // チーム名とチームに所属するメンバーのkey teams:[ { key:0, name:"チーム①", member:[0,1], flag:false }, { key:1, name:"チーム②", member:[2,3], flag:false }, { key:2, name:"チーム③", member:[4,5,6], flag:false } ], // メンバーのkeyと名前 members:[ { key: 0, name: 'テスト マン1'}, { key: 1, name: 'テスト マン2'}, { key: 2, name: 'テスト マン3'}, { key: 3, name: "テスト マン4"}, { key: 4, name: 'テスト マン5'}, { key: 5, name: 'テスト マン6'}, { key: 6, name: 'テスト マン7'} ], // 選択されているチームのメンバー selectMember:[] }) export const mutations = { // チーム選択時呼ばれるmutation selectTeam(state, select){ state.teams.map((val) => { val.flag = false }) // フラグを立てる state.teams[select].flag = true // 選ばれているチームのメンバーのIDを格納 state.selectMember = state.teams[select].member } }states
「member.js」で保持するstatesも3つです。それぞれの概要はコメントアウトを参照してください。
「state.teams」は『name:チーム名』『member:チームに所属しているメンバーのキー』
『flag:スケジュール表に表示しているか』という構造になっています。「state.members」は『key:ユーザーキー』『name:ユーザーネーム』という構造です。
「state.selectMember」は選択されているチームメンバーのキーを格納する用のstateになります。
mutations
『selectTeam』では選択されたチームの
まず、map関数で「state.teams」の『flag』を全てfalseにします。
その後、選択されたチームの『flag』をtrueにしています。後程説明しますが、
この『flag』はclass名の出し分けで使われるものになります。次に、『state.selectMember』に選ばれているチームのメンバーキーを格納します。こちらも後程説明しますが、
このキーの数だけループを回し、htmlを出力する処理を行います。components
下記例で記載はしてありますが、CSSの説明については省かせていただきます。
schedule/Calendar.vue<template> <div class="mainschedule"> <div class="heading"> <div class="schedule-ttl">schedule</div> </div> <div class="teams"> <div class="teambox" v-for="team in teams" :key="team.key" :class="{active: team.flag}" @click="showMember(team)"> {{ team.name }} </div> </div> <div class="schedule-month"> <span class="arrow" @click="changeMonth(-1)"><<</span> {{ schedule.year }}年{{ schedule.month }}月 <span class="arrow" @click="changeMonth(1)">>></span> </div> <div class="calendar-box"> <table> <tr> <th class="name-field"></th> <th v-for="day in lastday" :key="day">{{ day }}</th> </tr> <tr> <td class="name-field"></td> <td v-for="day in lastday" :key="day">{{ week[(firstweek + day) % 7] }}</td> </tr> <tr v-for="num in selectMember" :key="num"> <td class="name-field">{{ members[num].name }}</td> <td v-for="day in lastday" :key="day"></td> </tr> </table> </div> </div> </template> <script> export default { computed: { teams () { return this.$store.state.member.teams }, members () { return this.$store.state.member.members}, selectMember () { return this.$store.state.member.selectMember}, schedule () { return this.$store.state.schedule }, // 月初曜日の取得 firstweek () { return new Date(this.$store.state.schedule.year, this.$store.state.schedule.month-1, 1).getDay()-1 }, // 月末の日数の取得 lastday () { return new Date(this.$store.state.schedule.year, this.$store.state.schedule.month, 0).getDate() } }, methods: { showMember(team) { this.$store.commit('member/selectTeam',team.key) this.selectMember = team.member }, changeMonth(add) { this.$store.commit('schedule/changeCalendar', add) } }, data() { return{ week: ["日", "月", "火", "水", "木", "金", "土"] } } } </script> <style lang="scss"> .mainschedule { padding: 10px; .heading{ padding: 25px; .schedule-ttl { font-size: 32px; text-align: center; border-bottom: solid 2px #b2bec3; } } .teams { display: flex; justify-content: center; .teambox { display: flex; justify-content: center; border: solid 1px #dfe6e9; padding: 10px; margin: 10px; width: 300px; &.active { background: #dfe6e9; color: #2d3436; } } } .schedule-month { font-size: 24px; padding: 10px; text-align: center; .arrow { font-size: 20px; cursor: pointer; } } .calendar-box{ display: flex; justify-content: center; align-items: center; } table { th,td { width: 35px; height: 35px; text-align: center; border: solid 1px; } .name-field { width: 150px; } } } </style>script部分
script部分<script> export default { computed: { teams () { return this.$store.state.member.teams }, members () { return this.$store.state.member.members}, selectMember () { return this.$store.state.member.selectMember}, schedule () { return this.$store.state.schedule }, // 月初曜日の取得 firstweek () { return new Date(this.$store.state.schedule.year, this.$store.state.schedule.month-1, 1).getDay()-1 }, // 月末の日数の取得 lastday () { return new Date(this.$store.state.schedule.year, this.$store.state.schedule.month, 0).getDate() } }, methods: { showMember(team) { this.$store.commit('member/selectTeam',team.key) this.selectMember = team.member }, changeMonth(add) { this.$store.commit('schedule/changeCalendar', add) } }, data() { return{ week: ["日", "月", "火", "水", "木", "金", "土"] } } } </script>computed
ページロード時に実行される関数です。それぞれの関数で呼び出しているのは下記の様なものになります。
関数名 内容 teams Storeのmember.js内の『state:teams』を
呼び出しています。members Storeのmember.js内の『state:members』を
呼び出しています。selectMember Storeのmember.js内の『state:selectMember』を
呼び出しています。schedule Storeのschedule内の『state』を
丸ごと呼び出しています。firstweek Storeのschedule内の『state:year』と
『state:month』を元に表示する月の
最終日の曜日を取得しています。lastday Storeのschedule内の『state:year』と
『state:month』を元に表示する月の
月末日を取得しています。computedでStore内のstateを呼び出すことによって、html文内で毎回長々と呼び出し分を書く必要がなくなります。
firstweekとlastdayの取得の仕方の詳細は省かせていただきます。methods
イベントによって実行される関数を定期しています。
関数名 内容 showMember Storeのmember.js内の『mutations:selectTeam』
を呼び出しています。
引数には選択したチームのidが渡されています。changeMonth Storeのchedule.js内の『mutations:changeCalendar』
を呼び出しています。
引数には『<<』を選択したら「-1」
『>>』を選択したら「1」を渡しています。methods内ではStore内の情報を直接変更できなくなっています。
そのため、それぞれのmutationsにcommitを行い、stateを書き換える必要があります。data
ページ内で使用できる変数を作成しています。曜日の配列はわざわざstateで管理する程でもないと
思ったのでこちらでweekという変数として設定しました。html部分
<div class="teams"> <div class="teambox" v-for="team in teams" :key="team.key" :class="{active: team.flag}" @click="showMember(team)"> {{ team.name }} </div> </div> <div class="schedule-month"> <span class="arrow" @click="changeMonth(-1)"><<</span> {{ schedule.year }}年{{ schedule.month }}月 <span class="arrow" @click="changeMonth(1)">>></span> </div> <div class="calendar-box"> <table> <tr> <th class="name-field"></th> <th v-for="day in lastday" :key="day">{{ day }}</th> </tr> <tr> <td class="name-field"></td> <td v-for="day in lastday" :key="day">{{ week[(firstweek + day) % 7] }}</td> </tr> <tr v-for="num in selectMember" :key="num"> <td class="name-field">{{ members[num].name }}</td> <td v-for="day in lastday" :key="day"></td> </tr> </table> </div>チーム選択出力部分
まず、チーム選択部分出力の説明です。
<div class="teams"> <div class="teambox" v-for="team in teams" :key="team.key" :class="{active: team.flag}" @click="showMember(team)"> {{ team.name }} </div> </div>
v-for="team in teams" :key="team.key"
v-forで『teams( computed内にある関数で取ってきたstate )』がなくなるまでループを回しています。
phpのforeach文で言うと、下記の様な事をしていると考えていただければ良いかと思います。foreach(teams as team){ echo "hoge"; }v-forを使用する際にはkeyの設定が必要になります。今回は『teams.key』をkeyに設定しています。
:class="{active: team.flag}"
:classでclassを付けるか付けないかの条件分岐をする事ができます。今回の場合、『team.flag』が
trueだった場合には、「active」というクラスが付与されるようになっています。
このクラスでどのチームが選択されているか分かりやすいようCSS調整しています。
@click="showMember(team)"
この要素がクリックされた時に実行される『methods』の関数を指定しています。
この場合はshowMemberの関数が呼び出され、引数で選択された『team』の情報が渡されます。
{{ team.name }}
それぞれのチームの名前を表示しています。月変更部分
<div class="schedule-month"> <span class="arrow" @click="changeMonth(-1)"><<</span> {{ schedule.year }}年{{ schedule.month }}月 <span class="arrow" @click="changeMonth(1)">>></span> </div>
<span class="arrow" @click="changeMonth(-1)"><<</span>
『 << 』部分がクリックされた時に『methods』内のchangeMonth関数が呼ばれるように指定しています。
ここの場合は先月に戻りたいので、引数には「-1」を渡しています。カレンダー部分
<table> //日付 <tr> <th class="name-field"></th> <th v-for="day in lastday" :key="day">{{ day }}</th> </tr> //曜日 <tr> <td class="name-field"></td> <td v-for="day in lastday" :key="day">{{ week[(firstweek + day) % 7] }}</td> </tr> //メンバー <tr v-for="num in selectMember" :key="num"> <td class="name-field">{{ members[num].name }}</td> <td v-for="day in lastday" :key="day"></td> </tr> </table>日付・曜日
日付と曜日部分の
v-forは全く同じで、computedで取得したlastday( 表示月の最終日 )
の回数分だけループを回しています。
<th class="name-field"></th>
選択されたチームの名前が曜日の下の行から入るので、空文字で設置してあります。
{{ week[(firstweek + day) % 7] }}
曜日の上記箇所については、script内のdata()で設定した『week』変数を使用しています。
firstweekはcomputedで取得した月初日の曜日の引数です。こちらと現在の日付を足して7で割ることで
現在の曜日に対応する配列番号を指定する事ができます。メンバー
<tr v-for="num in selectMember" :key="num">
ここでは<tr>をselectMemberの数だけループを回しています。
<td class="name-field">{{ members[num].name }}</td>
上記でselectMemberに対応するメンバーの名前を取得し表示しています。
<td v-for="day in lastday" :key="day"></td>
ここのループは日付・曜日部分のループと全く同じで、中身は空で渡しています。以上で完成です
!
おわりに
かなり長々となってしまいましたが、最後まで読んでくださった皆様ありがとうございました!
中々複雑な構造になってしまいましたが、もっとスマートなStoreの呼び出し方や、
v-forの回数を減らせる記述方法などがあれば教えていただきたいです。
- 投稿日:2019-05-30T16:51:15+09:00
Vue Routerのreload実装する
Vue Routerにはreload機能がたいため、自作スクリプト
methods: { reload() { this.$router.go({path: this.$router.currentRoute.path, force: true}); }, something() { // reloadを呼び出すことで画面リロード this.reload(); } }
- 投稿日:2019-05-30T14:52:22+09:00
[Vue.js]filterをコンポーネントのscript内で使えるようにするメモ
やりたいこと
filterをコンポーネントのscript内で使えるようにしたい
ダメな例
<script> export default { name: 'ThePieGraph', methods: { fillData() { this.datacollection = { datasets: [ { data: [ 変数 | filter ], // ↑★ここにfiletrを置きたいが、こういう書き方だとダメ }, ], } }, }, }やったこと
いい例
src直下にpluginsディレクトリを切って、その中にfilter.jsを置く
prototypeを用いると、コンポーネントのscript内でも、関数としてfilterを呼び出すことができる
/** * 配列をオブジェクトから名前抽出 * @param {String} key * @param {{}} obj * @returns {String} */ const objToName = (key, obj) => { if (obj[key] !== undefined) { return obj[key] } return key } export default { install(vue) { // 数値を小数点に変換する const decimalPointShaping = value => { // console.log('decimalPointShaping',value) if (!Number(value)) { return value } return Math.round(value) } vue.filter('decimalPointShaping', decimalPointShaping) // 全体から平均的なパーセントを割り出す const percentageCalculation = (value, sum) => { console.log('percentageCalculation', value, sum) return (value / sum) * 100 } vue.filter('percentageCalculation', percentageCalculation) // APIの結果から人柄の結果を変換する const personalityConversion = value => objToName(value, big4) vue.filter('personalityConversion', personalityConversion) // APIの結果からbig5の結果を変換する const big5Conversion = value => objToName(value, big5) vue.filter('big5Conversion', big5Conversion) // 全体から平均的なパーセントを割り出し、かつ数値を整数に整形する const percentAndDecimal = (value, sum) => decimalPointShaping(percentageCalculation(value,sum)) /********************** 対象の記述↓ **********************/ // フィルターをコンポーネントスクリプト内で使えるようにする vue.prototype.$customFilter = { decimalPointShaping, personalityConversion, big5Conversion, percentageCalculation, percentAndDecimal, } /********************** 対象の記述↑ **********************/ }, }使い方
<script> export default { name: 'ThePieGraph', methods: { fillData() { this.datacollection = { datasets: [ { data: [ this.$customFilter.percentAndDecimal(this.big4_average,this.big4_sum), this.$customFilter.percentAndDecimal(this.big4_reserved,this.big4_sum), this.$customFilter.percentAndDecimal(this.big4_role_models,this.big4_sum), this.$customFilter.percentAndDecimal(this.big4_self_centered,this.big4_sum), ], //★↑ここ }, ], } }, }, }
- 投稿日:2019-05-30T14:49:03+09:00
はじめての Nuxt.js
Nuxt はずっと前から気にはなってて、でも「今回は SSR しないからいいや」とか「今回は静的サイトジェネレーターで作るからいいや」とか思って避けてきたけど、なんか最近 Nuxt 使うって話を 3 回くらい聞いたので、ちょっと使ってみて構成とか眺めてみようと思う
先に使ってみた感想を述べておくと
- Nuxt の公式ドキュメントがちゃんと作られているので、これだけ見れば大体解決した
- 後述してますが TypeScript とか Vuex とかとても簡単に導入できるので感動した
- webpack 周りが隠蔽されてる
- 前に VueCLI v2 使ってみた時はけっこうビルド周りがむき出しになってた気がする(VueCLI v3 ではこの辺解決されてるっぽい)
- Vue で SSR する時に Nuxt を使うという印象を持っていたが、SSR モードか SPA モードか選べたので考えが違った
- フロントエンドチームが多いチームであれば、Angular などのガッチリ系フレームワークも検討しようと思っていたが、Vue が好きなら Nuxt でもいいかもしれない
- TypeScript のサポートも昔よりはかなり良くなってて、Vue v3 がリリース(今年リリースされる?)されたら Vue 自体が TypeScript で作り直されるので、さらに Vue + Nuxt + TypeScript 環境が良くなっていくのではないかと思う
今回作業したリポジトリは以下
https://github.com/kurosame/nuxt-boilerplateインストール
create-nuxt-appを使うと良さそう
npx でいけるとかすばらしいnpx create-nuxt-app nuxt-boilerplateプロジェクト名は GitHub のリポジトリ名にした
? Project name nuxt-boilerplate ? Project description My groundbreaking Nuxt.js project ? Use a custom server framework express ? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Prettier, Axios ? Use a custom UI framework vuetify ? Use a custom test framework jest ? Choose rendering mode Universal ? Author name kurosame ? Choose a package manager yarnとりあえず全部入れといたが、以下に該当する場合はインストールから除外してもいいと思う
(でも環境を CLI ツールで管理する時点で環境周りをメンテナンスすることは基本的に無いと思うので、全部入れてしまっても問題無いと思う)
- SSR しない
? Use a custom server framework None ? Choose rendering mode SPA
- モバイル対応しない
? Choose features to install Progressive Web App (PWA) Supportを選択しない
- HTTP リクエストしない(ほぼ無いと思うが)
? Choose features to install Axiosを選択しない
- デザインは全部自前
? Use a custom UI framework Noneサーバーの Node フレームワークはあまり詳しくないが、特に今使っているのが無ければ Express か Koa あたりを選んでおけば良いと思う
Vuetify は 1 年くらい使ってるが、かなり完成度高い UI フレームワークと思っているので、入れた
Linter や Prettier や Jest などのテストフレームワークは必須で入れておいた方が良い実行
package.json を開くと、いくつかスクリプトが設定してあるが、とりあえず以下を実行
yarn dev2 回目以降は
node_modules/.cacheの下にbabel-loaderのキャッシュを作ってる影響からか、少し速くなっている
でもホットリローディングをサポートしているので、1 回起動すれば遅さは気にならない
nodemonを使って./serverディレクトリ内のファイルを監視してサーバの再起動を行っている画面が表示されたら HTML ソースを見てみると、
server-rendered="true"及び HTML の各要素がそのままレンダリングされていると思う
ちゃんと SSR されていることが分かる
ちなみに SPA モードであれば、JS が実行されてから画面がレンダリングされるので、読み込む JS ファイルのみが指定してあり、HTML はスタイルなどを除けばほとんど空であるまた、実行すると
.nuxtというディレクトリができており、サーバを立ち上げた時に読み込ませるコンテンツが格納されている
./server/index.jsを見てみると、Nuxt のインスタンスを作成して、Promise を返すnuxt.renderを Express に Middleware としてセットしているしばらく開発で使うのは、dev スクリプトだけで良さそう
以下の他のスクリプトはある程度 Nuxt でアプリケーション作った後にちゃんと使ってみようと思う
- build
- minify して
.nuxt/dist/に格納- start
- production モードでサーバを立ち上げる
- generate
- Vue を静的コンテンツとしてビルドして
dist/に格納してくれる- Netlify や Firebase などでそのままホスティングできる(はず)
- VuePress 使ったことがあれば、それ
使ってみる
ちょっとだけ使ってみてやったことや思ったことを書いてみようと思う
TypeScript を使う
yarn add -D @nuxt/typescript ts-node
tsconfig.jsonがあればnuxtコマンドで勝手にデフォルト値を書いてくれるらしいtouch tsconfig.json npx nuxt先程インストールした
ts-nodeは Node で TS をトランスパイルせずに実行できるツールでこれが無いとnpx nuxtが動かないこれで導入は完了
後は、拡張子を
.jsから.tsに変えて中身を書き換えていく
以下はserver/index.jsを TS に修正している例server/index.tsimport NuxtConfiguration from '@nuxt/config' ... // Import and Set Nuxt.js options const config: NuxtConfiguration = require('../nuxt.config.js') config.dev = !(process.env.NODE_ENV === 'production') ...TS 導入前は上記の
config.devが any 型だったが、導入後はちゃんと型定義の boolean 型を参照しているpackage.json も忘れずに修正
package.json"scripts": { - "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server", + "dev": "cross-env NODE_ENV=development nodemon server/index.ts --watch server", - "start": "cross-env NODE_ENV=production node server/index.js", + "start": "cross-env NODE_ENV=production ts-node server/index.ts", }Vue コンポーネントの TS 化についてはドキュメントでは
vue-property-decoratorの使用を薦めているyarn add vue-property-decorator
layouts/default.vueを例に修正すると<script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component class Default extends Vue { clipped: boolean = false drawer: boolean = false fixed: boolean = false items: { icon: string; title: string; to: string }[] = [ { icon: 'apps', title: 'Welcome', to: '/' }, { icon: 'bubble_chart', title: 'Inspire', to: '/inspire' } ] miniVariant: boolean = false right: boolean = true rightDrawer: boolean = false title: string = 'Vuetify.js' } export default Default </script>ちなみに
vue-property-decoratorを使わなくても書ける<script lang="ts"> import Vue from 'vue' export default Vue.extend({ data(): { clipped: boolean drawer: boolean fixed: boolean ... } { return { clipped: false, drawer: false, fixed: false, ... } } }) </script>Vuex を使う
Nuxt をインストールした時にルート直下に store ディレクトリができており、既に Vuex 入ってる感があるが、中身は空である
Nuxt は store ディレクトリが存在すれば Vuex をインポートしてくれて、store をルートインスタンスに流してくれるらしいなので
- Vuex を手動で入れる必要がない
- どのコンポーネントからでも
this.$storeで参照できるまた、モジュールモードとクラシックモードの 2 つのモードがあるが、クラシックモードは廃止予定らしい
肥大化してくるとモジュールごとに State が参照できた方が良いので、モジュールモードで良さそうNuxt 側でモジュールモードとクラシックモードのどちらで Vuex インスタンスが作られるかの判定は
store/index.(js|ts)が存在して、Store インスタンスをエクスポートしていれば、クラシックモード
それ以外はモジュールモードモジュールモードで
store/index.(js|ts)という名前で作った場合、ルートモジュールという扱いで Store インスタンスの直下に State が存在する
store/モジュール名.(js|ts)という名前で作った場合、名前付きモジュールとして Store インスタンスの modules プロパティ配下に State が存在するそれぞれの State を参照する場合
- ルートモジュールは
this.$store.state.counterで参照できる- ルート以外は
this.$store.state.todos.listで参照できる
- 上記は
store/todos.(js|ts)というファイルを作った場合ちなみに私は上記のルートモジュールと呼ばれてる Store インスタンスの直下に State を置くのは 1 度もやったことない
以下のファイルを store ディレクトリ配下に追加
store/sample.tsinterface IState { counter: number } export const state: IState = { counter: 123 }簡単すぎる例だが、ファイルを追加するだけで Store に counter が保持できる
そして以下のように参照する
適当なVue<script> export default { mounted() { console.log(this.$store.state.sample.counter) // 123 } } </script>また、Vuex のヘルパー関数も使える
適当なVue<script> import { mapState } from 'vuex' export default { computed: { ...mapState({ sample: 'sample' }) }, mounted() { console.log(this.sample.counter) // 123 } } </script>Linter とコードフォーマッターを使う
TS を使っているが、TSLint は非推奨となるらしいので、ESLint の TS パーサーを使う
.eslintrc.jsを見る限り ES(JS)だけの Linter で良ければ、Nuxt をインストールした時点で出来てるので、後は rules や extends でルールを追加するだけで良さそう
package.jsonを見るとeslint-plugin-prettierとeslint-config-prettierが依存パッケージとしてインストールされており、.eslintrc.jsの extends に prettier が設定してあるので、ESLint と Prettier の競合は気にしなくて良さそうTS 用にLinterを使う場合の修正箇所は以下
yarn add -D @typescript-eslint/eslint-plugin.eslintrc.jsparserOptions: { - parser: 'babel-eslint' + parser: '@typescript-eslint/parser' }, - plugins: ['prettier'], + plugins: ['@typescript-eslint', 'prettier'],package.json"scripts": { - "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", + "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .", }また、ESLint のルールだと以下のような import は誤検知して、「no-unused-vars」を出してしまう
import NuxtConfiguration from '@nuxt/config'この場合、以下のように ESLint のルールは握りつぶして、
@typescript-eslintの方のルールを有効化する必要があるらしい
こちらの記事に書いてありますが、いくつかこのような誤検知するルールがあるらしいので、その都度同様の対応が必要.eslintrc.jsrules: { 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'error' }Airbnb の ESLint ルールを入れてみる
yarn add -D eslint-config-airbnb-base.eslintrc.jsextends: [ 'airbnb-base', ... ]いい感じに動いているが、Vue とか Vuex は Nuxt に直接依存しており、こちらで
package.jsonに書く必要が無いため、以下のような Lint エラーが出てる.../nuxt-boilerplate/pages/index.vue 63:1 error 'vuex' should be listed in the project's dependencies. Run 'npm i -S vuex' to add it import/no-extraneous-dependencies .../nuxt-boilerplate/plugins/vuetify.js 1:1 error 'vue' should be listed in the project's dependencies. Run 'npm i -S vue' to add it import/no-extraneous-dependencies上記は以下のように対応可能
.eslintrc.jssettings: { 'import/core-modules': ['vue', 'vuex'] },他にも Nuxt と ESLint で競合するルールはありそう
vue-router を使う
Nuxt ではエントリーポイントとなる JS などで routes を書いて
new VueRouterをやる必要はない
pages ディレクトリ配下にルーターから直接呼ばれるコンポーネントを配置するだけで良い以下の 2 つを配置
pages/sample/index.vue<template> <span>サンプルインデックス!</span> </template>pages/sample/test.vue<template> <span>サンプルテスト!</span> </template>それぞれ以下のようにしてルーティングさせる
適当なVue<nuxt-link to="/sample">Go sample index</nuxt-link> <nuxt-link to="/sample/test">Go sample test</nuxt-link>ファイル名が
index.vueであれば、ディレクトリ名が path になり、それ以外のファイル名であれば、ディレクトリ名/ファイル名が path になる
先程の Vuex とルールが似ているパラメータ付きの動的ルーティングもファイル名もしくはディレクトリ名の先頭にアンダースコアを付けることで実現できるらしい
nuxt-link というコンポーネントは router-link を extends したコンポーネントとなっており、nuxt-link を使わずに今まで通り
<router-link>を使って実装することも可能
ただし、nuxt-link を使うと画面表示領域にリンクがあれば、そのリンク先のコンテンツを先読みしてくれるので、遷移が速くなる試しに、以下のコードを適当な Vue の下の方に配置して、ファーストビューから見えないようにして、スクロールしてリンクを表示させると
index.vueだけプリフェッチされることが分かる
Chrome DevTool の Network タブで確認すると分かりやすい適当なVue<nuxt-link to="/sample">Go sample index</nuxt-link> <router-link to="/sample/test">Go sample test</router-link>その他
選択した UI フレームワークでサンプルコードが作られている
私は Vuetify を選んだのだが、割としっかりサンプルコードが Vuetify で作られてて、全部の UI フレームワーク分作られてると思うとちゃんとしてるなって思った
テストのサンプルがほぼ無い
Logo コンポーネントの 1 ケースしかテストのサンプルコードが無い
E2E のサンプルコードは無いでも最近は Vue のテスト周りのエコシステムが充実してきて、ドキュメントも豊富なので、問題ないと思う
Nuxt のアップデートは簡単にできるのだろうか
Angular みたいなマイグレーション機能はたぶん無いと思うが、Nuxt とか CLI 系のツールを使っているとアップデートが楽にできるとかなりありがたい
簡単にネットで調べた限りは Nuxt を最新にして出たエラーを潰してるっぽいさいごに
主にツールの導入になってしまい、まだコードはほとんど書いてないのですが、これから ToDo アプリ程度のものは作ってみて、Nuxt を学んでみようかと思います
続きは別記事でまた書こうかなと思いますフロントエンドは全部 1 から作る派なのですが、たまに自動で環境作ってくれる系ツールを使うとそのお手本のような構成に勉強になることがあります
また、今後フロントエンドの環境を作る上で活かせることもあると思うので、試しに使ってみるのもありかなと思いました
- 投稿日:2019-05-30T14:35:09+09:00
Vuetify2.0のBeta版を使ってみる
Vuetify2.0を試してみたところVuetify1.5系のコードを持って行ったら全然動かなかったのでVuetify2.0を使う上で書き換えなければいけない部分を現時点で調査ができてる範囲でレポート
公式ドキュメント
まずこれが全然参考にならないのが大きな関門。サイトドメインはnext.vuetifyjs.comとなっているけれどもどうやらその内容の大半は1.x系の内容のコピーからアップデートされていない様子
githubのリリースノート
どうやらこれが正規の以降ガイドな模様
const opts = { ... } -Vue.use(Vuetify, opts) +Vue.use(Vuetify) -new Vue(...).$mount('#app') +new Vue({ + vuetify: new Vuetify(opts) +}).$mount('#app')こんな風にオプション情報の指定方法を変えろとの事
またテーマについても
const opts = { - dark: true, theme: { - primary: '...', + dark: true, + themes: { + light: { + primary: '...', + ... + }, + dark: { + primary: '...', + ... + } + } } }の様にprimaryなどの定義はthemes > light,darkの中に定義しろとの事
ただしこれでも上手くいかない
ブラウザコンソールでエラーを確認したところv-appが定義されてないみたいなエラーgithubのリリースノートにたどり着く以前に試した
components: { VApp, },をoptsに追記してみるがこれもダメ
というか実はこの書式はVue.use(Vuetify...に以下の様に配置した場合themeカラーは使えなかったものの
ボタン表示まではできていたので再度componentsだけこっちに移動Vue.use(Vuetify, { components: { VApp, }, }全体としては以下の様に書けば動作した
main.jsimport Vue from 'vue' import App from './App.vue' import colors from 'vuetify/es5/util/colors' import 'vuetify/dist/vuetify.min.css' import Vuetify, {VApp,VBtn} from 'vuetify/lib' const VuetifyComponents = { components: { VApp, VBtn, }, } Vue.use(Vuetify, VuetifyComponents) const opts = { theme: { dark: false, themes: { light: { }, dark: { } } } } new Vue({ vuetify: new Vuetify(opts), render: h => h(App), }).$mount('#app')App.vue<template> <v-app> <div class="text-xs-center"> <v-btn color="primary">Primary</v-btn> <v-btn color="secondary">Secondary</v-btn> <v-btn color="accent">Accent</v-btn> </div> </v-app> </template>opts.theme.dark===falseでthemesを以下の様に変えてみた場合
themes: { light: { primary: colors.green.base, secondary: colors.green.darken4, accent: colors.purple.base, }, ...最初の触りとしてこれまでできれば移行は視野に入れられるのかなという感じ
- 投稿日:2019-05-30T12:32:10+09:00
Vuetifyのv-iconに自作SVGアイコンを(楽に)使いたい!!
TL;DR
SVGアイコンをWebfontにしてstyle.cssをインポートすると
<v-icon>{名前}</v-icon>で使えるようになるよ!Vuetifyのリファレンスがかなりアレ
Vuetifyのv-iconってFontAwesomeとかのアイコンを利用できるじゃないですか。
でも自作のSVGアイコンも使いたいですよね。一応リファレンスにやり方が載っています。
簡単に訳すと、
SVGファイルを単一コンポーネントにしてVue.useの段階でセットすれば$vuetify.icons.{名前}で利用できるよ!
ということです。
追加したいアイコンが100個ぐらいあった場合、1個1個単一コンポーネントにしてVue.useで読み込ませるのは非常に不便ですしコードが長くなるしで割と最悪です。そこで色々調べていたところ,このような投稿がありました。
Vuetify Custom Icons Documentation is Horrendous : vuetifyjs
一番最後の投稿に
Well, for anyone from the future who stumbles across this, I found a way to do it without using Vuetify at all. So that site that I found, fontello.com, gives a css file that references the icons by class name, as I alluded to above. This class name is set on the site when you download the zip. So if you just import the css file as I did above, and then you can just reference the icon by name (with the prefix you used) in a v-icon and it should work. If anyone sees this and is having trouble getting it to work, shoot me a message. I'm usually pretty good about answering within a couple days.というコメントがあります。
fontello.comというサイトでcssファイルを使ってimportすれば読み込めるぞ!という内容です。
色々あって今回はfontello.comではなく同じ様なことをしてくれるicomoon.ioで試してみます。
サイトにアクセス
Icon Font & SVG Icon Sets ❍ IcoMoon
アクセスしたら右上のIcoMoon Appをクリックしましょう
でこんな感じになったら自作SVGアイコンをドラッグアンドドロップ
アップロードしたアイコンが表示されたら、Webfontを生成したいアイコンをクリック(複数可能です)
クリックしたら右下のGenerate Fontをクリックして先に進みましょう
この画面では アイコンの名前とコードを決められます。
コードは一意に定まる様にし、名前はわかりやすい名前をつけます。ここでの名前は実際に使用する際に使われます設定が終わったら右下のDownloadボタンをクリックするとicomoon.zipファイルのダウンロードが始まります
Vueのプロジェクトに追加
解凍したicomoonフォルダをsrc/assetsに配置しましょう。
だいたいこんな感じになると思います。早速使ってみましょう
今回はVueのプロジェクトにvuetifyを入れたあと、HelloWorld.vueを編集し動作の確認を行います。
style.cssをインポートしての中に
icon-{名前}を入れて、実行してみましょう。僕は先程 svgという名前とemoiという名前をアイコンに付けているので使用する際は上記の様になります。
ここで実行してみましょう
うまくいってますね。 これでアイコンを表示させることが出来ました。
追加の設定
インポートしたstyle.cssを見ると、colorの設定があります。
v-icon側でcolorを設定したい場合、style.cssにあるcolorの設定を削除する必要があります。
実際の運用
親コンポーネントでimportするのはアレなのでv-iconをラッピングしたoreore-v-iconみたいな単一コンポーネントを作ってそこでインポートするのが運用としては正しいと思います。
- 投稿日:2019-05-30T12:21:09+09:00
element ui enterキーで フォーム送信
以下でフォームを送信せず、メソッドを動かせる。
onSubmit メソッドを動かす。
参考
http://imejin.biz/magazine/el-form-submit-on-enter/<el-form :rules="rules" ref="form" :model="form" @submit.native.prevent="onSubmit('form')"> <el-form-item label="占いたい人の名前" prop="name"> <el-input v-model="form.name"></el-input> <div class="t-c view-count"><span v-if="fortune.higawari">毎日変わるよ!</span></div> </el-form-item> <el-form-item style="text-align: center;"> <el-button type="success" native-type="submit">占う</el-button> </el-form-item> </el-form>
- 投稿日:2019-05-30T12:19:29+09:00
[GASで実装する] 同一ページ内で任意の場所までスクロールするやつ
GASは1プロジェクト1ページ
ページ内遷移も簡単ではない
移動したい先に<タグ id='xxx'>をおいて、そこに<a href="#xxx"></a>みたいなことはできない情報量が増えてくると、何らかのナビゲーションは必要
ということで調べてみた
複数ページを作成して、
doget.gsにパラメータを設定して分岐させる。という方法をわかりやすく説明されているのがこちら
GoogleAppsScript(GAS)でページ遷移を擬似的に実装する方法
同様の実装は複数のブログなどでも紹介されているので、割とポピュラーな実装方法なのかもjQueryならなんかできるっぽい?
わからなかった(どうもjQueryのコードは見ても理解できない)
jQueryがダメならVueで何とかできる?
Watchは使えるか?
watchwatch: { '$route': function (val) { if (val.hash.match(/^#/)) { document.getElementById(val.hash.replace(/^#/, '')).scrollIntoView() } } }今回のケースでは使えなかった
smoothscrollってどうよ
続いて参考にさせていただいたサイト
意外と簡単!Vue.jsでスムーススクロールとトップへ戻るボタンを実装する方法CDNから
CDNのリンクをhtmlに追加<!-- vue.js --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- vue scroll 画面スクロール用 --> <script src="https://cdn.jsdelivr.net/npm/vue-smooth-scroll@1.0.13/dist/vue-smooth-scroll.min.js"></script>smoothscroll.html<a href="#section1" v-smooth-scroll>go to Section1</a> . . . <div id="section1">セクション1</div>vuedirective.jsVue.directive('scroll', { inserted: function (el, binding) { let f = function (evt) { if (binding.value(evt, el)) { window.removeEventListener('scroll', f) } }; window.addEventListener('scroll', f) } });上記をVueインスタンスの前に記述
以上でとりあえず動く
「トップへ戻る」的なボタンの実装がうまくいかないのが気になるので引き続き調べる
参考
- 投稿日:2019-05-30T07:14:51+09:00
昔、作ったWebサービスをTypeScriptとVue.jsで作り直した話
はじめに
Vue.jsとTypeScriptでWebサービスを作ってみたいけどアイデア浮かばなかったので昔作ったアプリを作り直しました。
作ったもの
Qnow
Qiitaで新規投稿された記事のみを取得して表示するサービスです。
タグ名でフィルタリングして表示します。
表示した記事のタグ名をクリックすることでクリックしたタグ名で再検索することができます。
ただ、今の仕様だと日付が変わってすぐだと記事が取得できないので後々変更しようと思います。使用した技術
- Vue.js
- TypeScript
- Github
- ソースコードの管理
- CircleCi
- masterにプッシュされたらビルドとデプロイを実行
- Firebase
- 静的ファイルの配信
Vue.js
Vue Cli3を使用しました。
Vue Cli3ではTypeScriptが正式採用されているのでTypeScriptを使用しました。
でも、TypeScriptを全然書いたことなくてany型を使っているところがちょくちょくあるので修正をしたいと考えています。
Vueの単一コンポーネントはスタイルにスコープをあてることができるのでとても便利ですね。
CSSはSASSのSCSS記法で書いています。これも標準でサポートしてあるので楽でした。Github
ソースコードを管理するのに使用しました。
英語が全然わからないのでGoogle翻訳で翻訳しながらコミットメッセージを書いたので変なコミットメッセージが多いかもです。CircleCi
Githubにpushされたらビルドして自動的にデプロイするようにしました。
circleci/config.ymlversion: 2 jobs: build: docker: - image: circleci/node:10.15.3 working_directory: ~/repo steps: - checkout - restore_cache: # 復元するキャッシュのkey keys: - v1-dependencies-{{ checksum "package.json" }} - v1-dependencies- # 依存関係インストール - run: name: Install Project command: npm install # ビルド - run: name: Build command: npm run build # ビルドの確認 - run: name: Check dist command: ls -la dist # キャッシュの保存 - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} - run: name: Install Firebase-tools command: npm install --save-dev firebase-tools - run: name: Deploy to Firebase hosting command: ./node_modules/.bin/firebase deploy --project "$FIREBASE_PROJECT_ID" --token "$FIREBASE_DEPLOY_TOKEN"設定手順
- CircleCiにログイン
- Githubアカウントでログインしていると思うのでプロジェクトは紐付けられる
- Githubのリポジトリに.circleci/config.ymlを追加する
- GithubにPushするごとにビルドが実行される
- firebaseでプロジェクトを作成する
- firebaseで
firebase-toolsをインストール- firebaseにログインする
$ firebase login画面にしたがってログインする
8. firebaseをCI上で使用するために以下のコマンドを打つ$ firebase login --reauth $ firebase login:ciこれでtokenを取得できる
9. .circleci/config.ymlにfirebaseでデプロイするコマンドを追加する
10. CircleCi上の設定でfirebaseのtokenとfirebaseのプロジェクトIDを追加するおわりに
このサービスを開発するにあたって自分の技術のなさを改めて知ることができました。
これからも日々勉強をしようと思います。
よろしければフィードバック頂けると嬉しいです。
ソースコード: https://github.com/gba-3/qnow
- 投稿日:2019-05-30T05:14:01+09:00
ReactやVueなどのJavascriptフレームワークからRailsの多対多モデルに対して、関連付けも含めて一気に作ってみた
こんにちは!@hairgaiです。
突然ですが、とあるTeamを作成するときにMemberを一緒に所属させたい!と思うことって一日に一回くらいありませんか?
今回は、それを実装するときにJavascriptからRailsへのパラメータで若干悩んだので、それを共有しようかなと思います。前提
よくある多対多です。
- Teamモデル
has_many :team_membershas_many :members, through: :team_members- TeamMemberモデル
belongs_to :teambelongs_to :member- Memberモデル
has_many :team_members: :destroyhas_many :teams, through: :team_membersReact 16.8.6、Rails 5.2.2
実装
Memberが数人いて何かTeamを作るときに、一度作成して後から所属させるのは面倒なので、当然ながら「Teamを作るときにMemberを所属させつつ作れたらいいのに」と思うと思います。
なので、フロントでTeamを作成するときに、既に登録済みのMemberを一覧で表示して、それを選択させることでパラメータを同時に送らせることにします。こんな感じですね。
また、Railsでは、モデルを作成するときに、その多対多関係にあるモデルを一緒に関連付けることが可能です。
なので、上記のように「Teamを作るときにMemberを所属させつつ作れたらいいのに」と思ったら、pry(main)> Member.create!(email: 'a@example.com') => #<Member:0x00007fb1498f04d8 id: 1, email: "a@example.com", # ... pry(main)> team = Team.create!(name: 'hoge', member_ids: [1]) => #<Team:0x00007fb142722860 id: 1, name: "hoge", # ... pry(main)> team.members => [#<Member:0x00007fb149951af8 id: 1, email: "a@example.com", # ... ]のように、
[モデル名]_idsというパラメータに配列を渡すと関連付けることが出来ます。
なので、フロント側でこのパラメータが渡ってくるようにURLパラメータを生成すれば良いということですね。パラメータ送りたい
ググると色々な記事が出てくる通り、JavascriptからURLにパラメータ用のクエリを乗っけるときは、qsを使いました。
qsは、Javascriptの連想配列(Hash)からURLパラメータを生成してくれるライブラリで、例えばimport qs from 'qs'; // or var qs = require('qs'); const params = { team: { name: 'hoge' }}; qs.stringify(params) // => 'team%5Bname%5D=hoge'という感じで使います。
なので(今回はパラメータの話なので解説しませんが)、フロント側でいい感じにパラメータを連想配列として生成します。// 実際はフレームワーク上でユーザの入力に合わせて作りますが、下記のような連想配列が生成されます。 const params = { team: { name: 'hoge', member_ids: [2, 3] } } qs.stringify(params) // => 'team%5Bname%5D=hoge&team%5Bmember_ids%5D%5B0%5D=2&team%5Bmember_ids%5D%5B1%5D=3'これをライブラリ等を用いてRailsに送ってみると…(axiosを使いました)
15: def update 16: binding.pry => 17: @team.update!(team_params) # ... [1] pry(#<Api::V1::TeamsController>)> params => <ActionController::Parameters {"team"=>{"id"=>"1", "name"=>"hoge", "member_ids"=>{"0"=>"1", "1"=>"2", "2"=>"3"}}, "format"=>"json", "controller"=>"api/v1/teams", "action"=>"update", "id"=>"1"} permitted: false>あれ?なんか変な形で送られてますね。
クエリの生成方法のオプションがあった
qsが生成したURLパラメータをデコードしてみると…team[name]=hoge&team[member_ids][0]=2&team[member_ids][1]=3ということで、
team[member_ids][0]=2&team[member_ids][1]=3のように指定されています。これをRailsでは"member_ids"=>{"0"=>"2", "1"=>"3"}と解釈してしまうようですね。(確かにHashっぽいです)
では、Railsではどう記述すればArrayと解釈されるのか、ということで、逆にArrayからクエリを生成してみましょう。pry(main)> { name: 'hoge', member_ids: [2, 3] }.to_query('team') => "team%5Bmember_ids%5D%5B%5D=2&team%5Bmember_ids%5D%5B%5D=3&team%5Bname%5D=hoge" # => team[member_ids][]=2&team[member_ids][]=3&team[name]=hoge順番は違いますが、若干指定の仕方が違いますね。Arrayのクエリでは、
[]のように数字を入れずに指定すれば良いようです。
現在はその数字の処理はqs側にまかせています。qsのGitHubをよく読んでみると…You may use the arrayFormat option to specify the format of the output array:
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) // 'a[0]=b&a[1]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) // 'a[]=b&a[]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) // 'a=b&a=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' }) // 'a=b,c'とのことです。
なので、フロント側でURLパラメータを生成する際に、const params = { team: { name: 'hoge', member_ids: [2, 3] } } qs.stringify(params, { arrayFormat: 'brackets' }) // => 'team%5Bname%5D=hoge&team%5Bmember_ids%5D%5B%5D=2&team%5Bmember_ids%5D%5B%5D=3'とオプションを追加してみると…
[1] pry(#<Api::V1::TeamsController>)> params => <ActionController::Parameters {"team"=>{"id"=>"1", "name"=>"hoge", "member_ids"=>["2", "3"]}, "format"=>"json", "controller"=>"api/v1/teams", "action"=>"update", "id"=>"1"} permitted: false>出来ました!レコードも正常に作られます。
結論
Railsは、Railsが提供するWayに沿えば非常に強力なフレームワークですが、その分色々な決まりがあるので、こういった細かいところもキチンと調べていきたいですね。
この記事が誰かの参考になれば幸いです。
参考
ありがとうございました!
- 投稿日:2019-05-30T00:57:41+09:00
2019年に知っておくべきJava ScriptのフレームワークTOP5
こんにちは、ベイマックスのでかい人形を買おうかどうか迷って半年になるテノヒラです
最近チームでwikiを書く文化が芽生えつつあるんですよ。
わたしもビギナーに向けてトレンドとかをお伝えするものがかければと思って記事を漁っていたら、フレームワークについてこじんまりかついい感じにまとめてある素晴らしい記事を発見したので翻訳してご紹介します!
翻訳する記事はこちら?
Top Javascript frameworks you should know in 2019
世界のトレンドと日本のトレンドはちょっと違ったりするので要チェックですところどころ意訳をしていますが、もし致命的な翻訳ミスがありましたら編集リクエストしてください
はじめに
JavaScriptは世界で一番人気のあるプログラミング言語です。
インターネットでほぼ全てのwebサイトのフォームに使われています。
JavaScriptまたはJavaScriptのフレームワーク、ライブラリ関係なくどこでもJavaScriptを見つけることができます。JavaScriptはフロントエンドだけでなくサーバーサイドの言語として使われています。
いまではJavaScriptでwebサイトを完全に構築させることができるのです下記が2019年で最も人気なフレームワーク5選です。
1) Angular
2) React
3) NodeJS
4) Vue.js
5) Backbone.js
Angular
Angularは最も人気のあるJavaScriptのフレームワークで、Typescriptのオープンソースです。
最初は2016年9月14日にGoogleがリリースしました。
AngularはMVC (Model-View-Conrtoller)パターンをベースにしています。
これは完璧なフレームワークで、企業のwebアプリケーションを構築するすべてを提供しています。
なのでAngularはwebアプリケーションを構築する上で最も人気のあるJavaScriptなのです。
大企業では大抵採用されています。React
Reactはユーザーインターフェイスを創るためのオープンソースのライブラリです。
2013年にFacebookによって開発されました。
Reactはコンポーネントベースで拡張性があり、webインターフェースを構築するのに速いフレームワークです。
Angularと異なっている部分は、Reactはwebインターフェースを構築する機能を提供しています。最近はReactがwebインターフェースを構築する最も人気があるJavaScriptライブラリのひとつとなりました。
ただ、Reactは最も手のかかるJavaScriptのライブラリです。
需要高いのでやれるようになれば思わず笑みが溢れるよう給与がもらえますNode JS
Nodejs、Node.jsもしくはNodeはオープンソースで、Google ChromeのJavaScript V8 Engineに基づいたサーバーサイドのJavaScriptのフレームワークです。
主に速い構築、拡張性、サーバーサイドアプリケーションをベースとした信頼性の高いネットワークを主に設計しています。
Indeed.comには現在、8,905つの求人があります。
Node開発者の平均的な給与はだいたい 10万5千ドル(約1,148万円)です。Vue.js
Vueもしくはvue.jsはユーザーインターフェースを構築するための革新的なフレームワークです。
ライブラリのコアは、表示のレイヤーだけに焦点を合わせている、簡単に習得できる、他のライブラリもしくは既存のプロジェクトとの統合といった部分です。
違う視点からみると、Vueはモダンなツールとサポートしているライブラリとを一緒に使った時に洗礼されたシングルページアプリケーションにすることができます。
webページの表示に焦点を当てたVueもフロントエンド界隈では人気のあるフレームワークとなります。Indeed.comには現在、1,316つの求人があります。
平均的な給与はおよそ10万ドル(約1,093万円)もしくはそれ以上です。Backbone.js
BackboneもしくはBackbone.jsはRESTful JSONインターフェースのフレームワークで、model–view–presenter (MVP)アプリケーションデザインパターンをベースとしているものです。
Backboneは2010年にJeremy Ashkenasによってつくられ、7年後には最も人気のあるフレームワークのひとつとなりました。
Indeed.comには現在、5,135つの求人があります。
backboneフロントエンドとbackboneフルスタックエンジニアの平均的な給与はそれぞれおよそ10万2千ドル(約1,115万円)と10万1千ドル(約1,104万円)です。React Vs Angular
ReactとAngularはweb開発者によって最も討論されることです。
これらを学習したプログラマーと開発者にとってどちらを学ぶべきなのかは悩むところです。
webサイトを開発する人にとってはどちらを使うべきかを悩みます。
どちらを採用するかを決定するのはいつも課題となります。Angularは完全なweb applicationの構築する機能を提供しており、Reactはインターフェースだけを構築するのに使われているライブラリです。
アーキテクチャーの概念によると、Angularはモデルと表示、制御の機能を処理しますが、Reactは表示のみを処理します。
AngularはTypescriptをベースとしていますが、ReactはJavascript上で動きます。
ReactはFacebookによって開発されましたが、AngularはGoogleによって開発されています。
AngularのIonicはモバイルアプリケーションの構築に使われますが、
Reactはモバイルアプリケーション開発のためにReact Nativeを提供します。
- 投稿日:2019-05-30T00:07:01+09:00
Vue.jsで昔懐かしのframeタグを実現する方法
前置き
HTML5以前の時代には、frameタグなるものが存在していました。
1つのWEBページの中に複数のWEBページを埋め込んで上下や左右に並べて表示させるタグです。
2000年代前半あたりにはframeタグを活用した個人サイトなどが数多く存在したような気がします。
(index.htmlの中にframesetを書いてフレームの左側にmenu.html、右側にtop.htmlとかやってたり)HTML5が登場してからはframeタグはframesetタグと共に非推奨となり、今ではもうこのタグを使っているサイトはほぼ無いと思います。
ですがもしかするとframeタグが今でも好きな方がいらっしゃるのではと思い、代替案としてVue.jsを用いてframeタグらしきものを再現してみました。
(実際に需要があるかどうかは特に調査していませんので気にしないでください。)デモ
vue-frame
(frameの境目をドラッグしてコンテンツの幅を変えられます)動作イメージ
ざっくり解説
構成
左側
LeftFrame.vue<template> <div class="left-frame" v-bind:style="{width:width + 'px'}"> <div class="left-frame-content">ヒダリー</div> <div class="frame-border" @mousedown="$emit('startResize')"></div> </div> </template> <script> export default { props: { width: Number } }; </script> <style scoped> .left-frame { background-color: rgb(240, 240, 255); display: flex; } .left-frame-content { flex-grow: 1; padding-left: 10px; } .frame-border { width: 3px; background-color: rgb(208, 208, 208); border-left: solid 0.5px rgb(170, 170, 170); border-right: solid 0.5px black; } .frame-border:hover { cursor: col-resize; } </style>
- コンテンツの横幅は可変にするため、変数で保持
- フレームの境界線にはドラッグイベントを持たせるため、borderっぽいdiv要素(frame-border)を配置
右側
jRightFrame.vue<template> <div class="right-frame">ミギー</div> </template> <style scoped> .right-frame { background-color: white; flex-grow: 1; padding-left: 10px; } </style>
- 画面右側に表示させたいコンテンツを配置するだけ
本体
Frame.vue<template> <div class="frame" v-bind:class="{dragged: isDragged}" @mousemove="resizeFrame" @mouseup="endResizeFrame"> <left-frame v-bind:width="leftWidth" @startResize="startResize"></left-frame> <right-frame></right-frame> </div> </template> <script> import LeftFrame from "./LeftFrame.vue"; import RightFrame from "./RightFrame.vue"; const LEFT_FRAME_MIN_WIDTH = 45; const FRAME_ADJUSTED_SETTING = 2; export default { components: { LeftFrame, RightFrame }, data() { return { isDragged: false, leftWidth: 200 }; }, methods: { startResize() { this.isDragged = true; }, resizeFrame(event) { if (event.buttons === 0) { this.endResizeFrame(); return; } if (this.isDragged) { if (event.clientX + FRAME_ADJUSTED_SETTING < LEFT_FRAME_MIN_WIDTH) { this.leftWidth = LEFT_FRAME_MIN_WIDTH; return; } this.leftWidth = event.clientX + FRAME_ADJUSTED_SETTING; } }, endResizeFrame() { this.isDragged = false; } } }; </script> <style scoped> .frame { display: flex; flex-direction: row; height: 100vh; } .dragged * { cursor: col-resize; } </style>
- ドラッグ開始(startResize())はLeftFrameから発火させる
- mousemoveイベントでマウスの位置を監視する
- event.buttonsの値でマウスの押下状態を判断(===0ならマウス押下無し)
- マウスの位置(event.clientX)でleftFrameの幅を変更させる(フレームのstyleに合わせて適当に微調整)
ソースコード全体
参考資料





















