- 投稿日:2020-03-29T23:56:04+09:00
Vue.js + AWS の組み合わせ
基本的なこと
Vue.jsの開発はVue-CLIを使うのが基本
まず前提として、Vue.jsは今となってはVue-CLI無しでの開発は"かなりキツイ"と捉えてください。
実際それほどでもないと言えばそうなんですが、Vue-CLIがかなり便利になってきたので、その恩恵を受けられない構成での開発を基本フロントは嫌がります。Laravel + Vue という悪習
そういうことなので、Laravelに入っていたVueを雛形にしてそのまま使ってしまっている場合は将来切り離すスケジュールを立てるべきです。
この構成は、通常LaravelMixでコンパイルするのでVue-CLIが使えません。移行には具体的に以下の作業が発生します。
- リポジトリの切り分け
- vue-routerを用いたSPAの対応
- APIのCORS対応
元々Laravelが担当していた機能は /home → /about のように『ルートが切り替わったら別のHTMLを表示する』という事をしていただけなので、ページ切替がフロントで管理できるようになった今となってはバックエンドのwebルートは完全に不要になりました。
恩恵として、フロント側でページ遷移した場合はリロードが発生しません。
リロードしてしまうとJSの変数に格納したデータが全て消えてなくなってしまうのですが、SPAは基本的にリロードが無いので、ページ遷移の度に同じデータをダウンロードしなくてもページ間でデータを共有できるようになり、サイトの高速化に繋がります。デプロイ環境
デプロイ環境に関してはサーバーもNode.js環境も必要ありません。S3だけで事足ります。
ブラウザがコンパイル済みのindex.htmlとそこでインポートされているJSやCSSファイルを読み出せればOKです。
完全に静的なファイルを置いておくだけで大丈夫なんです。
コンパイルにNode.js環境を必要としますが、後で出てくるAmplifyのビルド環境にNode.jsが入っていますので心配しなくて大丈夫です。やっちゃいけないこと
- Laravel+Vue.jsのようなバックエンドのリポジトリにVue.jsが付属する構成は難儀するので、LaravelはAPI専用にして後方に配置し、LaravelとVue.jsは別々のリポジトリで管理しましょう。
- とりあえずCodeStarでやろうとするのはNG。正解は全然別の所にあるAmplifyの方です(UI統一してくれ..)。似たようなものですが、より実践的な設定が簡単にできるのでAmplifyを使いましょう。
使用するAWSのサービス
- Amplify
Amplify
ほぼ対象のリポジトリを選択するだけで簡単に『リポジトリの更新を検知→自動テスト→自動デプロイ』のパイプラインが出来上がります。
たぶんSPA前提なのでS3のサーバーレス環境にデプロイされます。
AmplifyにはFrontセクションとBackendセクションがあり、上記がFrontセクションに該当します。
Backendセクションでは、Cognito、AppSync、Lambda、S3などを用いて自在にバックエンド開発ができます。
『Amplify = AWSフレームワーク』に近いものなので、基本的な部分はAmplifyで作成してしまい、追加サービスとして他のAPIを付け足していくという感じになっていくと思います。Amplifyについて詳しくはまた次回に書こうと思います。
- 投稿日:2020-03-29T23:01:52+09:00
Nuxt TypeScript + Vuex によるカウンターのサンプルコード
概要
- Nuxt TypeScript + Vuex を使用して「+」「-」ボタンで数値が増減するカウンターを作成する
今回の環境
- Node.js 13.12.0
- Nuxt.js 2.12.1
- TypeScript 3.8.3
- Nuxt TypeScript (Nuxt.js 向け TypeScript サポート)
Nuxt TypeScript + Vuex によるカウンターのサンプルコード
ソースコード一覧
├── nuxt.config.ts ├── package.json ├── pages │ └── counter.vue ├── store │ └── counter.ts ├── tsconfig.json └── vue-shim.d.tsnuxt.config.ts
nuxt.config.js の TypeScript 版。
import { Configuration } from '@nuxt/types' const config: Configuration = { buildModules: ['@nuxt/typescript-build'] } export default configpackage.json
必要なライブラリの記述等。
{ "name": "my-app", "scripts": { "dev": "nuxt-ts", "build": "nuxt-ts build", "generate": "nuxt-ts generate", "start": "nuxt-ts start" }, "dependencies": { "@nuxt/typescript-runtime": "0.4.1", "nuxt": "2.12.1", "nuxt-property-decorator": "2.5.1" }, "devDependencies": { "@nuxt/typescript-build": "0.6.1" } }pages/counter.vue
カウンター表示用のページコンポーネント。
<template> <div> <p>{{ count }}</p> <p> <button @click="increment">+</button> <button @click="decrement">-</button> </p> </div> </template> <script lang="ts"> import { Component, Vue } from 'nuxt-property-decorator' import { CounterState } from '~/store/counter' @Component export default class CounterComponent extends Vue { get count () : number { console.log('Call the computed count') return (this.$store.state.counter as CounterState).count } // 「+」ボタンクリック時に呼ばれる increment () : void { console.log('Call the methods increment') this.$store.commit('counter/increment') } // 「-」ボタンクリック時に呼ばれる decrement () : void { console.log('Call the methods decrement') this.$store.commit('counter/decrement') } } </script>store/counter.ts
カウンターの値を管理するストア。
import { MutationTree } from 'vuex' export const state = () => ({ count: 0 }) export type CounterState = ReturnType<typeof state> export const mutations: MutationTree<CounterState> = { increment: (state) => { console.log('Call the mutations increment') state.count++ }, decrement: (state) => { console.log('Call the mutations decrement') state.count-- } }tsconfig.json
ほぼ Nuxt TypeScript の公式資料通りだが、Nuxt Property Decorator (nuxt-property-decorator) を使うため experimentalDecorators の設定を追加している。
{ "compilerOptions": { "experimentalDecorators": true, "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.ts
Nuxt TypeScript の公式資料通り。
Vue ファイルの型を提供するための型宣言。declare module "*.vue" { import Vue from 'vue' export default Vue }Node.js サーバを起動
package.json に記述したライブラリをインストール。
$ npm installNode.js サーバを起動。
$ npm run dev > my-app@ dev /Users/foobar/my-app > nuxt-ts ╭─────────────────────────────────────────────╮ │ │ │ Nuxt.js v2.12.1 │ │ Running in development mode (universal) │ │ │ │ Listening on: http://localhost:3000/ │ │ │ ╰─────────────────────────────────────────────╯ ℹ Preparing project for development ℹ Initial build may take a while ✔ Builder initialized ✔ Nuxt files generated ℹ Starting type checking service... ✔ Client Compiled successfully in 8.15s ✔ Server Compiled successfully in 6.50s ℹ Type checking in progress... ℹ Waiting for file changes ℹ Memory usage: 230 MB (RSS: 311 MB) ℹ Listening on: http://localhost:3000/Web ブラウザで http://localhost:3000/counter にアクセスすると「+」「-」ボタンと数値が増減するカウンターが表示される。
参考資料
環境構築
- セットアップ | Nuxt TypeScript
- Runtime(オプション) | Nuxt TypeScript
- Nuxt.js v2.9にTypeScriptとExpress.jsを対応してみた - Qiita
- 3分でつくる2019年版 Nuxt.js TypeScript 開発環境設定 - Qiita
Nuxt Property Decorator (nuxt-property-decorator)
Handy ES / TypeScript decorators for class-style Vue components in Nuxt (based on Vue class component) and Property decorators for Vue (bases on Vue Property Decorator) and Vuex (based on Vuex Class)
This library fully depends on vue-class-component.
To enable experimental support for decorators, you must enable the experimentalDecorators compiler option either on the command line or in your tsconfig.json
機能実装
- 投稿日:2020-03-29T20:29:12+09:00
Vue.jsでアコーディオン
n番煎じですが試行錯誤した結果を残しておきます。
いろんなやり方があると思いますが、個人的に一番しっくり来たやり方です。結論
<template> <div class="a-accordion"> <button @click="opened = !opened"> Click </button> <transition @before-enter="beforeEnter" @enter="enter" @before-leave="beforeLeave" @leave="leave" > <div v-if="opened" class="a-accordion-inner"> Accordion </div> </transition> </div> </template> <script> export default { data() { return { opened: false } }, methods: { beforeEnter(el) { el.style.height = '0' }, enter(el) { el.style.height = el.scrollHeight + 'px' }, beforeLeave(el) { el.style.height = el.scrollHeight + 'px' }, leave(el) { el.style.height = '0' } } } </script> <style scoped lang="scss"> .a-accordion { .a-accordion-inner { overflow: hidden; // 閉じるときに他要素に被らないように必須。 transition: height 0.2s ease-in-out; // 高さの変更に対して連続的に変化させる。 } } </style>ポイントは
- 開閉する要素に
overflow
,transition
を指定する。transion
のフックでheight
を変更する。の2つです!
参考
https://jp.vuejs.org/v2/guide/transitions.html
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions
http://lab.astamuse.co.jp/entry/2018/10/15/154737
https://qiita.com/mimoe/items/7ad8d9ea9fc4299da6bc
- 投稿日:2020-03-29T20:08:17+09:00
nuxt.jsインストール手順備忘録
nuxt.jsインストール備忘録
- バージョン 2.12.1
- CDでインストールするディレクトリに移動
- 以下のコマンドでインストール
- project-name はプロジェクトの名前を入れる
npxを使う場合
npx create-nuxt-app <project-name>yarnを使う場合
yarn create nuxt-app <project-name>? Project name
? Project name xxx
- プロジェクト名を入力
? Project description
- 説明を入力
? Project description xxxxxxAuthor name
- 作者の名前を入力
? Author name xxxChoose programming language
- TypeScriptを使うか選択
? Choose programming language (Use arrow keys) ❯ JavaScript TypeScriptChoose the package manage
- YarnとNpmのどちらかを使うか選択
? Choose the package manager (Use arrow keys) ❯ Yarn NpmChoose UI framework
- UIフレームワーク使うか選択
? Choose UI framework (Use arrow keys) ❯ None Ant Design Vue Bootstrap Vue Buefy Bulma Element Framevuerk iView Tachyons Tailwind CSS Vuesax Vuetify.jsChoose custom server framework
- サーバーサイドのフレームワークを選択
? Choose custom server framework (Use arrow keys) ❯ None (Recommended) AdonisJs Express Fastify Feathers hapi Koa MicroChoose the runtime for TypeScript
- Runtimeを使うかの選択
- Runtimeについてはドキュメント参照
? Choose the runtime for TypeScript (Use arrow keys) ❯ Default @nuxt/typescript-runtimeChoose Nuxt.js module
- Axios と PWA を使うか選択
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to inv ert selection) ❯◯ Axios ◯ Progressive Web App (PWA) Support ◯ DotEnvChoose linting tools
- 静的解析の選択
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to inver t selection) ❯◯ ESLint ◯ Prettier ◯ Lint staged files ◯ StyleLintChoose test framework
- ユニットテストの選択
? Choose test framework (Use arrow keys) ❯ None Jest AVAChoose rendering mod
- SSRかSPAの選択
? Choose rendering mode (Use arrow keys) ❯ Universal (SSR) Single Page AppChoose development tool
jsconfig.json
とSemantic Pull Request
を入れるか選択jsconfig.json
はVS Code
向けのツールSemantic Pull Request
はGitHub向けのツール? Choose development tools (Press <space> to select, <a> to toggle all, <i> to i nvert selection) ❯◯ jsconfig.json (Recommended for VS Code) ◯ Semantic Pull Requestsインストール完了
起動する
yarnの場合
yarn run devnpmの場合
npm run dev
- http://localhost:3000/ にアクセス
ビルド
dist
以下にファイルが出力されるyarn run buildgenerate
dist
以下にHTMLの静的ファイルが出力させるyarn run generateその他
Sasaを使う
- Sassをインストール
npm install --save-dev node-sass sass-loader
- vueファイルで
lang="scss
またはlang="sass
にするとSassが使える<style lang="scss" scoped>Pugやcoffeeも可能
- ドキュメントはこちら
Pug
npm install --save-dev pug@2.0.3 pug-plain-loader<template lang="pug"> h1.red Hello {{ name }}! </template>CoffeeScript
npm install --save-dev coffeescript coffee-loader<script lang="coffee"> export default data: -> { name: 'World' } </script>コンポーネント以外の独自で作るCSSを読み込む
nuxt.config.js
のcssの部分にファイル名を入れるcss: [ '@/assets/css/main.scss' ],ルーティング
this.$route
でURLが取れる- リンクを貼るときは
nuxt-link :to=""
を使う- パラメーターで動的に使うには
_id.vue
のようにファイル名にアンダースコアを付ける<nuxt-link :to="'/xxx">リンク</nuxt-link>
- generateするときに吐き出したいファイルを指定する時は
nuxt.config.js
に設定する- 例
/controller/action
の時generate: { routes: [ '/controller/action' ] }
- 投稿日:2020-03-29T17:50:47+09:00
vueコンポーネント内でsassを使用するときにディープセレクタを指定する方法
vue コンポーネント内の style を scss にして、その中でディープセレクタを使用したかったのですが、
>>>
を使用すると動かなかったので、調べた内容についてまとめました。<style lang="scss" scoped> // ここで >>> を使いたい! >>> .hoge { // } </style>ディープセレクタについて
scoped スタイルのセレクタを "deep" にしたい、つまり子コンポーネントに及ぼしたい場合は、>>> コンビネータを使用することができます
::v-deep
を使用するvue-loaderのディープセレクタに下記の記載がありました。
SASS のようないくつかのプリプロセッサは、>>> を適切に解析できないかもしれません。そのようなケースでは /deep/ コンビネータを代わりに使用することができます。それは、>>> のエイリアスで、全く同じような動作します。
ただ、下記の issue によると、実際には
/deep/
を使用すると chrome で warning が出るそうで、::v-deep
を使用するのが良いようです。実際に試したところ、warning ではなくて error が出ました。
https://github.com/vuejs/vue-loader/issues/913<style lang="scss" scoped> // >>> をそのまま ::v-deep に変更するだけでOK ::v-deep .hoge { // } </style>
::v-deep
の具体例
::v-deep
を使う具体例をみたい場合には、こちらのPRで/deep/
から::v-deep
へ変更を行なっているため、参考になると思います。
https://github.com/aeternity/aepp-base/pull/1347/filesドキュメントの修正状況
ドキュメントの修正に関しては、変更の提案の issue はあがっていたので、そのうち修正されると思います。
Remove /deep/ from documentation for future releases for Deep Selectors
- 投稿日:2020-03-29T17:20:21+09:00
Vue.jsでTo-Do Listを作ってみた。
Vue.jsの学習を本日から始めました!
めちゃくちゃ面白かったので、記事にします。やったこと
「今日のTo-Do List」を作成しました。
普段はUdemyや本を参考に学習することが多いのですが、今回は久しぶりにドットインストールを使ってみました。
ドットインストールさん、分かりやすかったです。ありがとうございます。回し者ではありません。ドットインストールを参考に作ったので、見た目はほぼドットインストールのままですが、ToDoをする予定時刻を自分で入れてみました。
これからもっと手のこんだアプリケーションをVue.jsで作っていきたいです。学んだこと
導入が楽
scriptタグをhtmlファイルに記載するだけで使えました。
開発環境の構築は大変な時が多々ありますが、Vue.jsは非常に楽でした。双方向データバインディング
テキストボックスに入力した途端に出力される文字が変わる、といったことがこの双方向データバインディングに例になると思います。
これを最初学んだときは驚きました。しかも、それが数行のコードですぐ簡単に実装できてしまうのです。学びやすい
学習コストが少し低いように感じました。まだ最初だからだと思いますが、最初にしてはとっつきやすかったです。
これからVue Routerを使ってSPAを作ろうと思っておりますが、おそらくその過程で苦戦することでしょう。。。これから
初めてJavaScriptのフレームワークを本格的に学習してみましたが、非常に興味深かったです。
上でも書いたようにこれからはVue.jsでシングルページアプリケーションを作ってみます。
その際にはまた記事にします!参考
- 投稿日:2020-03-29T16:04:16+09:00
Vue Composition API の computed についてまとめてみた
はじめに
Vue Composition API の reactive と ref についてまとめてみた の続きになります。
ref
,reactive
は値の検知を監視してリアクティブにレンダリングコンテキストを変更する便利なオブジェクトでした。今回は
メソッドであり、リアクティブなオブジェクトしての機能も保持している
computed ( 算出プロパティ )
についてまとめていきます。
computed
【前提知識】 なぜ使うのか ?
テンプレート内に式を書けるのはとても便利ですが、非常に簡単な操作しかできません。テンプレート内に多くのロジックを詰め込むと、コードが肥大化し、メンテナンスが難しくなります ( Vue.js 公式より )
公式に書かれていますが、vue はテンプレート内部に簡単な比較や計算などを直接記述することができます。
しかし、複雑な処理をテンプレート内部で記述できるため、同様の処理を至る所に書いてしまいがちです。
ここで、仕様が変更されたとすると複数箇所を修正しなければいけなくなり、保守性を低下させてしまいます。そこで利用するのが computed ( 算出プロパティ ) です。
【基本】 呼び出し方
computed
メソッドを利用して生成します。
これまでの computed オブジェクト内への記述とは異なります。書き方は 2 通りあります。
computed-型定義(1)// computed に渡す引数 interface Option<T> { get: () => T, set: (value: T) => void } // getter として使うとき export declare function computed<T>(getter: Option<T>['get']): Readonly<Ref<Readonly<T>>>; // getter, setter として使うとき export declare function computed<T>(options: Option<T>): Ref<Readonly<T>>;getter のみを利用するときは引数なしの コールバック を用いることで、処理を記述することができます。
computed-getter(2-1)const cost = ref<number>(100); const tax = 0.5; // computed メソッドで生成する const calcPrice = computed(() => cost.value * tax); // 値を取得するとき const price = calcPrice.value;getter, setter どちらの機能も利用したいときは、
get
,set
を持つオブジェクトを渡す必要があります。
getter で利用している リアクティブな値が変わった場合は再度処理が行われ、新たな値を返します。computed-getter-setter(2-2)<template> <div> <div>{{calcPrice}}</div> </div> </template> <script lang='ts'> import { ref, computed } from "@vue/composition-api"; export default { setup() { const cost = ref<number>(100); const tax = 0.5; // computed メソッドで生成する const calcPrice = computed({ get: () => cost.value * tax, set: (value: number) => (cost.value = value) }); // 値を取得するとき const price = calcPrice.value; // 値をセットするとき calcPrice.value += 100; // 200 console.log(cost.value); return { calcPrice }; } }; </script>【基本】 readonly
TypeScript だとクラス構文で利用される
readonly
があるので、馴染み深いものかと思います。
リアクティブなオブジェクトに対しての値変更の不可を設定できます。readonly(1)const readOnlyValue = readonly(100);getter のみの computed は readonly で生成されるため 直接の値変更は不可 になります。
処理内で利用しているリアクティブ変数を書き換えることで間接的に値を変更できます。【提案】 Computed or Method ?
こちらは vue 2.0 系統での記事を多く見かけるので細かい説明は省こうと思います。
2 つの使い分けは キャッシュさせたいかそうでないか で判断すると良いです。どちらもロジックを綺麗にまとめることができるため、使わないという選択は無いと思います。
computed
- computed はキャッシュされる ( ブラウザのメモリの節約効果 )
- キャッシュされるので 2 度目以降の画面表示は早い
- リアクティブな値を用いる場合 はこちらを利用
method
- UI イベントによる処理 は、キャッシュのデータによる誤送信が考えられるため method を利用する
終わりに
今回は、ref, reactive と同様にリアクティブな値を取得できる computed についてまとめました。
繰り返しになりますが、今後も随時 vue Composition API について記事を書いていこうと思います。
是非、引き続き追っていただけると嬉しいです。
- 投稿日:2020-03-29T15:30:08+09:00
よそ様のgithubリポジトリを初めて使ってみたら、最高の開発者体験ができた話(vue + githubActions + github-pages)
対象読者
vuecliやnuxtなど、静的サイトを生成するツールで静的サイトをローカルでビルドし、githubにプッシュすることでgithub pagesでホスティングしている人すべて
やること
github actionsを利用して、ローカルでのビルド時間を0秒にする
背景
コロナの影響で時間ができたため、サークルを企業に紹介する静的サイトを作成しようと思い、github pagesをテスト環境にしてメンバーからのフィードバックを得るということをしていました。使用していたのはvuecliだったので、開発中は
npm run serveこのコマンドでホットリローディングしながら常時localhost:8080にアクセスしてコーディングしていました。
サークルメンバーも同様にコロナの影響で暇しているので、フィードバックはとても早く「これってこんな感じがいい?」と実際にコードを書きながら確認することが多くありましたが、ここに若干めんどうなところがありました。
github-pagesに公開する際は、npm run buildしてからリモートに上げることでgithub-pagesの内容を更新していました。ところがこのコマンドを叩くには一旦
Ctrl + C
でタスクキルする必要があり、ビルド後再度npm run serve
を打たなくてはいけませんでした。これは非常に面倒です。解決策
調べたところ、github actionsという、github上での様々なイベントをトリガーにlinux,Windows,Macのインスタンス(コンテナの方があっているのでしょうか)を無料で利用でき、そのうえで自由に仕事をさせることができることを知りました。これを設定しておけば、以下のように改善することができます。
before.shnpm run serve # hot reloading Ctrl + C npm run build # 一分ほどTwitterを見に行く # ビルド結果をgithub-pagesにプッシュ => 公開 npm run serveafter.shnpm run serve # hot reloading # ソースコードをgithubにプッシュ => github上でビルド => 公開gitの操作はGithub Desktopを利用しているため、最初にホットリローディングを開始すれば、開発中途切れさせる必要はありませんでした。ビルドとサーブの開始でTwitterを見に行く必要はもうありません。
github actionsに先駆者がいた。
本題です。
もともとはこの部分を「よし!僕がいっちょ作って公開してQiitaにアウトプットしたら大ヒットや!」と考えいろいろ調べていたところ、同じ考えをした(しかも後続にやさしい)先駆者がいました。
https://github.com/peaceiris/actions-gh-pages
Qiitaで日本語記事も書いてくださっています(神)
https://qiita.com/peaceiris/items/d401f2e5724fdcb0759dgithub actionsには、再利用可能にパッケージ化されたワークフローが公開されており、この作者peaceiris様も公開してくださっていました。
このアクションはビルドされた静的コンテンツを入れたディレクトリのパスを渡すだけで、あとはgithub pagesに公開してくれるものです。え?ビルド結果を特定のディレクトリに入れて公開するだけなら簡単そう
と僕と同じように思う方も多いと思いますが、github actionsで生成されたものはActionsの終了と同時にすべて破棄されてしまいます。また、リモートにコミットして反映させる手法も確かに取れますが、この作業が非常にだるく、このコミットによってループにならないようにしたり、そもそも権限を付与してあげないといけなかったりと、とにかくめんどう!その点、このワークフローはビルド結果のディレクトリと指定されたトークンやシークレットを渡すだけで公開してくれるため、神がかっているのです。
それぞれの方法については作者様のgithubリポジトリに紹介されているので、ご参照ください。私も今回は
https://github.com/marketplace/actions/github-pages-action#%EF%B8%8F-vue-and-nuxt
こちらをコピペして、npm run generate
をnpm run build
に書き換えた程度でした。(package.jsonに定義したビルドコマンドに合わせただけ)
細かいことも、ここで下手に説明するより上記URLから参照したほうがよろしいかと思いますので省きます。詰まったところ
github pagesの設定をもともとmaster ブランチのdocsディレクトリに設定していたため、ビルド結果が反映されず、あれ?となりました。設定ページでgh-pagesブランチに設定しなおしたところちゃんと反映されましたので、
導入前からgithub-pagesを利用していたが、なぜかビルド結果が反映されない
というかたは参考になるかもしれません。最後に
nodejsやaxiosといった大手のOSSを利用することはあれど、個人開発のリポジトリを利用するというのは初めてで、最初は「動くのかなあ」とか「動かなかったらデバッグめんどくさそうだ...」と消極的でした。しかし利用し始めてみるとドキュメントも充実していて使いやすく、また、原因不明な不具合なども発生しませんでした。いいプロジェクトに出会えたから、という理由も大きいとは思いますが、githubでよそ様の作ったものを取り入れてみようかなとも思ったというお話でした。
- 投稿日:2020-03-29T15:12:52+09:00
Vuexについて勉強したこと
まずはVuexをダウンロード
npm install vuex
main.js内のVueインスタンスに含まれている全てのコンポーネントでVuexにアクセスできるようにするまでの流れ
- store.jsを作る
- store.jsでVuexでVuexをimportする
- Vue.use(Vuex)でVuexというプラグインを使えるようにする
- new Vue.Store({ })のなかに、Vue.js全体で使える、グローバル変数のようなものを用意する
- main.jsで読み込むために、
export default
とする- main.jsでstoreを読み込む
- main.jsのnew Vueインスタンス内で、
store:store
とする(es6なので、store,
でも可)store.jsimport Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vue.Store({ state: count:2 })main.jsimport store from "./store"; new Vue ({ store:store }).$mount("#app")例えばHome.vueというコンポーネントから$storeにアクセスしたい時
- Home.vueのどこに書いてもいいけど、store.jsの中身が変わったときに、実際にそれを計算してDOMを更新する必要があるのかどうかとかを賢くやってくれるのがcomputedプロパティなので、computedに書く
Home.vue<template> <div> <p>{{ count }}</p> </div> </template> <script> export default { computed:{ //⬇︎名前はなんでもいいけどcount()にしとく counte(){ //$storeは、store.jsのVue.sotreの部分 //stateはstore.jsのそのあとのstate:の部分 return this.$store.state.count } } } </script>例えば、また他のページで、カウントしたい時
Header.vue<template> <div> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> <script> export default { methods: { increment(){ this.$store.state.count++; }, decrement(){ this.$store.state.count--; } }こんな感じでHome.vueと同じように使えます
gettersについて
gettersとは
- Vuex用の算出プロパティ
算出プロパティはcomputedプロパティですよね。
computedプロパティとは
- どのデータを使って、別の計算をするか
それがVuexにもあるという感じ
gettersの使い所
- 式が複雑化してきたときに、その式をいちいち書くことなく、Vuexにまとめることができる どうするかというと、gettersを使う
gettersの使い方
- 下のように書くと、Vuex側で、カウントを2倍するという処理ができる
store.jsexport default new Vuex.Store({ state:{ count: 2 }, getters: { //stateを引数にとる //state.cuontとすることで、上のcountが取れる doubleCounter: state => state.count * 2 } });例えば、Home.vueにて
先ほどはこのように書いていましたが、
Home.vue<script> export default { computed:{ //⬇︎名前はなんでもいいけどcount()にしとく counte(){ //$storeは、store.jsのVue.sotreの部分 //stateはstore.jsのそのあとのstate:の部分 return this.$store.state.count } } } </script>gettersを使用すると、
Home.vue<script> improt { mapGetters } from "vuex" export default { computed:{ //ここから counte(){ return this.$store.getters.doubleCount; } } //ここまで } </script>と、書くことで、countを算出することができる
mapGettersヘルパーについて
computedのなかにgettersを書いてると思うのですが、もっとコンパクトに書きたい時は、
vuexから、mapGetters
というものをインポートする。さっきこのように書いてあった、computedの部分を、、
Home.vue<script> improt { mapGetters } from "vuex" export default { computed:{ //ここから //doubleとtripleに計算を増やしてみました doubleCounte(){ return this.$store.getters.doubleCount; }, tripleCount(){ return this.$store.getters.tripleCount; } } //ここまで } </script>一旦がさっと削除して、代わりに
mapGetters
、その中に配列で一つ目にdoubleCount
、tripleCount
とすると、Home.vue<script> improt { mapGetters } from "vuex" export default { computed:mapGetters(["doubleCount","tripleCount"]) } </script>という感じで一行で終わるコンパクトさになります
mapGettersの部分を、下記のようにオブジェクトで書いたりもできる
Home.vue<script> improt { mapGetters } from "vuex" export default { computed:mapGetters({ //キーはなんでもよいが、バリューに`doubleCount`をかく myComponentDoubleCount:"doubleCount" }) } </script>computedの部分に他にも書きたい、、という場合
- ES6のスプレッド演算子
...
と書くと、このオブジェクトの中にうまく組み込んでくれる...
と書くことで、他に書いたcomputedも動くHome.vue<script> improt { mapGetters } from "vuex" export default { computed:{ //computedプロパティにオブジェクトではかけないので ...mapGetters([myComponentDoubleCount:"doubleCount"]) }, } </script>
- 投稿日:2020-03-29T14:21:39+09:00
Vuexを初めから丁寧に(1)~Vuexを理解するために必須の前提知識~
はじめに
この記事を読むと
- Vuexを理解するために必要な知識を習得できます
- Vuexを学ぶためのマイルストーンが明確となります
想定読者
- Vue.js や Nuxt.js の初級〜中級者
- Vuex を何となく雰囲気で使っている
前提知識
JavaScript 及び Vue についての基本知識があることは前提とします。
(Vue の基本知識がない方はこちらが入門書として最も最適です。)
『Vue.js 超入門』(掌田津耶乃/秀和システム)またJavaScriptにおいては特に、オブジェクトの使い方にも慣れておくとスムーズでしょう。
(こちらの第9章が最も良い説明だと思います。)
『初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発』(Ethan Brown, 武舎広幸,武舎るみ/オライリージャパン) )Vuex の理解が難しい原因
なぜ Vuex が難しいと感じるのでしょうか?
私の場合は専門用語の意味が省略されていることに起因していました。
さらに問題なのは、 「Vuexを理解するためのキーとなる用語」が、全く違う意味で使われているにも関わらず見た目は一般的な日本語と一緒なのでなんとなくわかった気になり、「何が分からないのか分からない」状況に陥ることです。
例えば Vuex における「状態」は
「アプリケーションが保持するデータ」
のことを指します。
なので、「Vuex は状態管理ライブラリである」「Vuex は状態を管理するために単方向データフローを採用している」といった説明や図解※を見ても、肝心の「状態」が分からないので、文章の意味が消化できないまま頭を素通りしていくだけでした。しかし逆に言うと、用語の意味さえ押さえておけば Vuex はスラスラ理解できます。
Vuex を理解するためのツボ
さて、前置きが長くなりましたが本題です。
たった 4 つだけです。
用語を正確に理解する
- 「状態」
- 「データフロー」
「データフローの設計」と「状態管理」の意義を理解する
- 信頼できる唯一の情報源(Single Source of Truth)
- 単方向フロー(one-way data flow)
- 情報と取得のカプセル化(Encapsulation of sorce and receiving)
Vuex の構成要素の役割と使い方を理解する
- State
- Getters
- Mutations
- Actions
※「ストアのモジュール分割」は一旦省略します
Vuex に入る前に
いきなり Vuex に入るより、まず状態管理やデータフローの基本知識を押さえておくと、スムーズに理解が進みます。
「状態」とは
状態とは
「アプリケーションが保持するデータ」
のことです。
ユーザーの操作やイベントの発生などによってその値が更新されていきます。例えば、EC サイトのショッピングカートです。カートは何も入っていない空の状態から始まり、ユーザーが商品をカートに入れる操作を行うことでカートは空の状態に戻り、購入処理が完了します。
規模が大きいアプリケーションは保持する状態の数、それぞれの組み合わせの数も多くなり、そのままでは扱いきれなくなります。
繰り返しになりますが、Vuex において「状態」は普段の日本語とは異なる特別な意味がある言葉なので注意してください。
データフローとは
「データフロー」とは
「状態を含む、アプリケーションが持つデータの流れ」
のことを指します。
具体的には、どこにデータを保持し、データを読み込む時や更新するときはどこからどのように行うのかという点を表すことが多いです。データフローの設計において、以下の三つのプラクティスが重要です。
信頼できる唯一の情報源
「信頼できる唯一の情報源」(single source)とは、「管理する対象のデータを一箇所に集約することで管理を容易にすることを目的とする設計のパターン」です。
- どのコンポーネントも同一のデータを参照するため、データや表示の不整合が発生しづらい
- 複数のデータを組み合わせた処理を比較できる容易に実装できる
- データの変更のログ出力、現在のデータの確認などの開発に便利なツールを作りやすい
「状態の取得・更新」のカプセル化
「状態の取得・更新」のカプセル化を行うことで、状態管理のコストを下げることができます。
例えばカウンターアプリの例では更新処理を store 内に記述することでカプセル化しており、コンポーネント側からは具体的にどのような実装がされているかは隠されています。
- 状態の取得・更新のロジックを様々な場所から利用できる
- 詳細な実装をビューから隠すことで、データ構造や取得、更新処理の変更の影響範囲を小さくする
- デバッグ時に確認する場所が限られるため、デバッグが容易になる
単方向データフロー
単方向データフローにすることで、状態の取得、更新のコードが簡潔になります。
データが単方向でないと、データの取得と更新の両方を同時にできてしまい、より複雑な処理になり理解が難しくなってしまいます。
- データを取得しつつ更新するといったようなことができなくなり、実装やデバッグが単純になる
- データを取得、更新するために何をするかの選択肢が絞られて、理解が容易なコードをかきやすい
まとめ
ここまでデータフローの三つのプラクティスを見てきましたが、実はVuex は先ほど紹介したデータフローのプラクティスを全て満たします。
まず、Vuex はアプリケーションの状態やそれに付随するロジックが一つの場所(ストア)にまとまるように設計されているため、「信頼できる唯一の情報源」を満たします。
また、Vuex において状態の更新はミューテーションでのみ行うことができ、取得に関してもゲッターという機能で詳細な実装は隠蔽できるため「状態の取得と更新」のカプセル化も満たします。
さらに、状態の取得と更新の窓口が異なるため(冒頭の図解をもう一度参照ください)、強制的に実装が単方向データフローになります。
おわりに
いかがだったでしょうか。VueやNuxtで開発を行う方が、Vuexを理解するための助けになれば幸いです。
「状態管理」「データフロー」についてはバッチリですか?
次の記事ではいよいよ Vuex による状態管理について見ていきます。参考文献
『Vue.js入門 基礎から実践アプリケーション開発まで』(川口和也, 喜多啓介, 野田陽平, 手島拓也, 片山真也/技術評論社)
Vue.jsについての書籍は増えてきていますが、問題なのはその殆どがVuexについての説明を省略していることです。Vue.jsやNuxt.jsを用いた実際の開発においてVuexによる状態管理は必須ですが、学習の障壁になるとして避けてしまっているのでしょう。私が読んだ中で唯一、Vuexについて丁寧に説明していたのが本書です。Vuex以外の内容も素晴らしいの一言。本書はVue.js・Nuxt.jsの開発に関わるエンジニアや組織にとって必携です。保存用・実用用・観賞用に3冊購入しましょう。あるいは、あなたが経営者の場合はぜひエンジニアに対して一人一冊ずつ買い与えてください。
ただし、全くVueについて未経験という方への第一歩としては内容が本格的すぎるかもしれません。その場合は『Vue.js 超入門』がおすすめです。『Vue.js 超入門』(掌田津耶乃/秀和システム)
とにかく分かりやすく、まず概要を把握するために最適の一冊です。「なんとなくで良いので概要を把握する」⇨「より詳細で厳密な理解する」という流れで学ぶとスムーズです。『初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発』(Ethan Brown, 武舎広幸,武舎るみ/オライリージャパン) )
JavaScriptの根本的な理解ができる、革命的な良書です。分厚いので手強そうに見えますが、実際はとても親切で分かりやすい作りです。本書も一人一冊は欲しいところです。
- 投稿日:2020-03-29T14:01:57+09:00
Vuetifyスクラッチインストール時にiconを使いたい
概要
vue-cliなどを使わず、スクラッチインスールした場合、iconがない。
app.ts
Vuetify アイコンを参考に設定する。
以下具体例。
app.tsimport Vue from "vue"; import Vuetify from "vuetify"; import '@mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader import "vuetify/dist/vuetify.min.css"; import App from "./components/Main.vue"; import router from './router' Vue.use(Vuetify); new Vuetify({ icons: { iconfont: 'mdi', } }); new Vue({ router: router, render: h => h(App), vuetify: new Vuetify() }).$mount('#app')
- 投稿日:2020-03-29T14:01:21+09:00
サーバーレスWebアプリにメールフォームを追加実装する 〜 フロントエンド編 〜
サーバーレスWebアプリにメールフォームを追加実装する 〜 フロントエンド編 〜
はじめに
AWSを活用したサーバーレスWebアプリの制作で作ったWebアプリにメールフォームを追加実装します。
フロントエンド・バックエンドの2部構成にしています。
バックエンド編はこちら。
サーバーレスWebアプリにメールフォームを追加実装する 〜 バックエンド編 〜フロントエンド
Vue.jsのWebアプリにメールフォーム用のページを追加します。
VeeValidateの利用
メールフォームの各入力値チェックのために、VeeValidateというものを利用しました。
VeeValidateとは、Vue.js用のバリデーションコンポーネントライブラリです。利用するために、まずはVeeValidateをプロジェクトにインストールする必要があります。
npm i vee-validate使い方などの詳細は以下のページを参照してください。
https://logaretm.github.io/vee-validate/overview.html#getting-startedコンポーネントの実装
VeeValidateを利用してバリデーションを効かせたメールフォーム画面のコンポーネントを実装します。
デザインコンポーネントは例によってVuetifyを利用しています。
必要な入力項目をすべて適切に入力しないとSUBMITボタンが有効にならないようにバリデーションを効かせ、ただ、SUBMITボタンを押下しても入力内容をアラートするだけにしておきます。(バックエンドの呼び出しは後で実装します。)src/components/Mail.vue<template> <v-container> <p>Mail Form</p> <ValidationObserver ref="observer" v-slot="{ validate, reset, invalid }"> <ValidationProvider v-slot="{ errors }" name="name" rules="required|max:25"> <v-text-field label="Name" v-model="name" :counter="25" :error-messages="errors" required ></v-text-field> </ValidationProvider> <ValidationProvider v-slot="{ errors }" name="email" rules="required|email"> <v-text-field label="E-mail" v-model="email" :error-messages="errors" required ></v-text-field> </ValidationProvider> <ValidationProvider v-slot="{ errors }" name="contens" rules="required|max:300"> <v-textarea label="Message" v-model="message" :error-messages="errors" :counter="300" rows="8" required ></v-textarea> </ValidationProvider> <v-btn @click="onSubmit" :disabled="invalid">submit</v-btn> <v-btn @click="onValidate">validate</v-btn> <v-btn @click="onClear">clear</v-btn> </ValidationObserver> </v-container> </template> <script> import { required, email, max } from "vee-validate/dist/rules" import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from "vee-validate" setInteractionMode("eager"); extend("required", { ...required, message: "{_field_} can not be empty", }); extend("max", { ...max, message: "{_field_} may not be greater than {length} characters", }); extend("email", { ...email, message: "Email must be valid", }); export default { name: "Mail", components: { ValidationProvider, ValidationObserver, }, data: () => ({ name: "", email: "", message: "", }), methods:{ async onSubmit(){ alert(this.name + " / " + this.email + " / " + this.message); }, onValidate(){ this.$refs.observer.validate(); }, onClear(){ this.name = ""; this.email = ""; this.message = ""; this.$refs.observer.reset(); } } } </script>実行結果
バックエンド
バックエンド編へ記載しています。
終わったら戻ってきてください。フロントエンドからAppSyncの呼び出し
バックエンドが済んだら、最後にWebアプリからの呼び出しです。
バックエンド編でAppSyncに追加したIF(processSendMail)をWebアプリからリクエストします。src/graphql/mutations.jsexport const processSendMail = ` mutation processSendMail($input: ProcessSendMailInput!) { processSendMail(input: $input) { statusCode body } } `;src/components/Mail.vue: <script> import { required, email, max } from "vee-validate/dist/rules" import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from "vee-validate" import { Auth, API, graphqlOperation } from 'aws-amplify'; import { processSendMail } from "../graphql/mutations"; : methods:{ async onSubmit() { let apiResult = await API.graphql(graphqlOperation(processSendMail, {input : {name: this.name, email: this.email, message: this.message}}) ).catch(error => { console.error(error); }); },実行して、フォームに入力し、SUBMITボタン押下によりメールが届くことを確認してください。
あとがき
バックエンド編へまとめて書きます。
- 投稿日:2020-03-29T12:59:41+09:00
Vue.js×TypeScriptでのテキストコピー(iOS対応)
Vue.js×TypeScriptで「ワンタップでテキストをコピーする」ボタンを作ったらハマりポイントがたくさんありました。
生jsやjQueryでの解決策はたくさん見つかりましたが、Vue.js×TSは見つからなかったのでメモです。やりたいこと
フォームに文字入力した時に、飾り文字を追加した文章を出力して、ワンタップでコピーできるようにする。
完成したコード
<template lang="pug"> .CopyText button(@click.prevent="copyTexts") span.copy-message クリップボードにコピー .formatted-text span.recipe span#copy-text {{formattedTitle}}<br> </template> <script lang="ts"> import { Recipe } from "../../components/molecules/RecipeTitle.vue"; import Vue, { PropType } from "vue"; export default Vue.extend({ props: { recipe: { type: Object as PropType<Recipe>, default: {} } }, computed: { //inputで入力した内容ではなく、ここでフォーマットしたテキストがコピー対象 formattedTitle(): string { return this.recipe.title ? `【${this.recipe.title}】` : ""; } }, methods: { //iOSの判定 isIOS() { const agent = window.navigator.userAgent; return agent.indexOf("iPhone") != -1 || agent.indexOf("iPad") != -1; }, //コピー copyTexts(): void { if (this.isIOS()) { //iOSの場合 const doc: HTMLInputElement = document.getElementById( "copy-text" ) as HTMLInputElement; const selected = window.getSelection(); const range = document.createRange(); range.selectNodeContents(doc); selected!.removeAllRanges(); selected!.addRange(range); document.execCommand("copy"); } else { //それ以外 const formattedText = `${this.formattedTitle}`; navigator.clipboard.writeText(formattedText); } } } }); </script>参考:Javascriptによるコピー機能(クロスブラウザ対応)
ハマった部分の解説
iOSでのコピー
jsでコピーをしようと思ったらnavigator.clipboardを使用するのが一般的かと思います。
ユーザーエージェントなどの情報を扱うNavigatorインターフェイスにclipboardプロパティを追加して、writeText()メソッドを呼び出すことで、テキストがコピーできます。
navigator.clipboard.writeText(text);しかし、このnavigator.clipboardはiOSの10以降、textareaなど一部のタグからしかコピーできないなど仕様が変わっています。
今回はinputに入力した文字ではなく、フォーマットをかけたテキストをコピーするため、まさにこの条件に引っかかり、iOSのsafariとchromeで動作しませんでした。
参考: Copy to clipboard using Javascript in iOS
そのため、iOSとそれ以外でコピーの処理を変える必要があります。
iOSかどうかの判定
isIOS() { const agent = window.navigator.userAgent; return agent.indexOf("iPhone") != -1 || agent.indexOf("iPad") != -1 || agent.indexOf("iPod") != -1; }navigator.userAgentを使います。
今回はブラウザではなくiOSかどうかだけ判定するので、上記のようにしてみました。
iOS用のコピー
iOSのコピーは、コピーしたい文章を選択→コピーの実行という流れで行います。
const doc = document.getElementById("copy-text"); const selected = window.getSelection(); const range = document.createRange(); range.selectNodeContents(doc); selected.removeAllRanges(); selected.addRange(range); document.execCommand("copy");ユーザーはワンタップするだけですが、内部の動作はマウスなどで文章選択→コピーをするのと同じです。
2行目のwindow.getSelectionはselectionオブジェクトを取得するものです。
selectionオブジェクトは、ユーザーが選択した範囲のDOMに関する情報を持つことができます。
3行目のcreateRangeはdocument中のテキストやノードに関する情報を持つrangeオブジェクトを作成します。
rangeオブジェクトを作成しただけでは何も情報を持っていないため、4行目のrange.selectNodeContents(doc)で、最初に取得した要素を渡します。
5行目は2行目に取得したselectionオブジェクトが現在持っているrangeに関する情報をあらかじめ削除する処理です。文章がすでに選択されてselectionオブジェクトに情報が設定されている場合、この後の処理が無視されるので先に削除してしまいます。
これにより、6行目でselectionオブジェクトに作成したrangeオブジェクトを追加することができます。
最後のdocument.execCommand()はhtmlのdocumentオブジェクトを操作するコマンドを実行します。copyは選択範囲をクリップボードにコピーするコマンドです。
これでiOSでもテキストコピーができるようになりました!
参考: memo: テキスト全選択の JavaScript コードが動かなくなったので修正した
TSで"Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Node'"
上記のコードはType Scriptを使うと以下の部分でエラーを吐きます。
//Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Node' const doc = document.getElementById("copy-text");document.getElementById() はHTMLElement型もしくはnullを返しますが、nullを返す可能性があるとTSがエラーを出すようです。
そのため、返り値がHTMLElement型であることを明示的に示します。
const doc: HTMLInputElement = document.getElementById("copy-text") as HTMLInputElement;TSでObject is possibly 'null'エラー
rangeの削除、追加部分でも型エラーが出ます。
selected.removeAllRanges(); //Object is possibly 'null' selected.addRange(range); //Object is possibly 'null'これはselectedの部分がnullの可能性があることで出るエラーです。
そこで、!をつけて、selectedがnullでもundefinedでもないことを推論させます。ただ、この方法はESlintで"Forbidden non-null assertion"の警告が出ます。
- 投稿日:2020-03-29T12:04:52+09:00
宮崎県COVID-19対策サイトを見つけ、出身者として何かしたいと思ったから、した。
出身の宮崎県用の対策サイトが出来、同時に感染者数が3人に増えていたことを知った。何かできる事は無いかと思ったので、Qiitaに記録しつつ、色々やってみた。
Introduction
References
Who I am
- 宮崎県出身
- PRしたことない人
- Vue, TypeScript何も分らん
- 現在、住所不定無職。再就職活動中
Environment
- Ubuntu 18.04 ( vm with Vagrant
- Node.js: v12.16. (>= 10.19.0
- yarn: 1.22.4
Environment Setup
リポジトリのREADME.md中の開発者向け情報->環境構築の手順に従う
- Gitの設定(今回は新しく環境を用意したので。
- git config --global user.name "usename"
- git config --global user.email "email"
- Vimの設定
- oriverk/dotfiles/.vimrcからコピペ
- 環境のデフォルトエディタを変更し、vimを選択
sudo update-alternatives --config editor
- Vue用の仮想環境を用意した。(割愛
- Node.js >= v10.19.0の用意(割愛
- fork, clone, yarn install
terminal# 対象をforkしておく git clone https://github.com/oriverk/covid19.git cd covid19terminal# yarnが入ってなかったのでinstallした後。 yarn install yarn dev
yarn dev
の処理?が終わった後、http://localhost:3000/
にアクセスすると
What I did
First
最初に書いた通り、Vue(と言うかJS)何もわからない人であり、コード修正は厳しいと考えたので、表示される自然言語の修正をする事にした。因みにどのファイルがどこにあるかも判らなかったので、commit履歴から探しました(^^;
- 表示言語選択メニューバー: covid19/nuxt-i18n.config.ts
- 他言語表示用json: covid19/assets/locales/
Main
東京都verから宮崎県verにした際の地域表記変更の漏れであり、1文字の修正だった。
CODE_OF_CONDUCT.md# 33行目:都庁の人だけではなく => 県庁の人だけでなくterminalgit add . # pre-commit.shでエラーが出たので(触ってない git commit -m "都庁を県庁に修正" --no-verify git push コピーしてきたURL developmenthow to pull request
無職&&個人で勉強なのでPRの機会は初めてで、ここをよく見ながらしましたが、怖かった
無事にpull requestがmergeされ、OSS活動(?)の実績解除となりました。
What I wanna do form now
- 表示自然言語の部分を中心に修正改善
- Warningと出ている部分の修正
- 恐らくTypescriptの型由来の警告なので、ドキュメントと格闘しながら。
序に
PCを触ってない時は、これを動かしてます。
#拡散希望RTお願いします
— ASRock Japan (@AsrockJ) March 26, 2020
ASRockerでもそうでない人も
リソース余ってたり、stresstestしたい自作erかもーん!
新型コロナ解析で分散処理プロジェクト「Folding@home」https://t.co/4EheevUKsK @pc_watchさんから pic.twitter.com/pUtQyuQgPp
- 投稿日:2020-03-29T08:40:53+09:00
クライアントサイド系を歴史も踏まえて振り返る
概要
- クライアントサイド系の技術を改めて背景・歴史振り返る
- クロスプラットフォーム系とJSフレームワーク系を中心に記載
- 気になっている技術はFlutter/Electron
- Angular/Vueは引き続きウォッチ
背景や歴史
スマホが出てくる前はマルチブラウザ対応が種だったが、Android/iOSも含めてクロスプラットフォームでの開発が必須な時代で、その状況に準じた製品が様々出てきている。
JQueryみたいなプリミティブなものは2010年前後に主流だった記憶があるが、その後AngularJSなどのデータバインティングとか少しずつ大きめの機能を持つようなJSフレームワークが出てきたように感じる。
※なお、RIA(Rich Internet Application)と言う言葉で総称され、Apach FlexやJavaFXやSilverlightなど様々なクライアントサイド技術の流行もあった。
クロスプラットフォームフレームワーク
こちらの記事を参考にさせていただきました。個人的にはElectronも気になっている。
- Flutter
- React Native
- Xamarin
- Unity
JSフレームワーク
- 2014年頃の御三家イメージ
- Angular
- Backbone
- Knockout
- 2020年における御三家イメージ※参考:比較ページ
- Aunglar
- Vue
- React
※JSフレームワーク事情2020年始めやThe State of JavaScript 2019を参考
長期政権のAngular
クライアントサイドといえば最大3年もあれば、次の技術になっているのが通例なイメージがある一方で、Angularは2012/6にAngularJS v1.0.0がリリース。その後2016/9に1系から2系になったかと思えば、リリースサイクルが変わり、2020/2にバージョン9が出ている。
多大な貢献のJQuery
併せてJQueryも以前は随分お世話になった。個人的には最近は触っていないが、利用しているプロジェクトもまだまだあるのではないかなと想像。
AltJS
もう最近はこの言い方をしないのかもしれないけれど、コンパイルするとJavaScriptが出力される言語。言わずもがなTypeScriptが前提となるケースが多い。例えば、AngularもTypeScriptが推奨となっている。CoffeScriptなども流行した。
AltJSはJavaScriptの人気を示していると感じる。少しずれるがJVM言語系のScalaやGroovyも同様にJavaの人気を示しているなと。
サーバーサイドJavaScript
今回主としては取り上げないが、node.jsなどのサーバサイドJavaScriptも流行しており、JavaScriptの人気を改めて示している。
まとめ
現時点では以下の形でまとめられることが多いように感じるけれど、時代背景に応じて数年後にはまた違った形になっていくかもしれない。例えば、今流行しているAIとかその辺の技術につられて何か出てくるとか。
- クロスプラットフォーム
- JSフレームワーク
- AltJS
- 投稿日:2020-03-29T07:01:05+09:00
Rails + heroku App でpage speed insightをあげる
レンダリングを妨げるリソースの除外
全部のjavascriptにレンダリングしてるところに
defer: true
をつけた
3秒から0.45秒に<%= javascript_pack_tag 'faq', defer: true %>オフスクリーン画像の遅延読み込み
遅延読み込みという技術は、Webサイトに表示される画像を一度に読み込まず、必要に応じて必要な分だけ読み込むというものです。不必要な画像の読み込みを後回しにして、画像以外のCSSやJSファイルの読み込みが先に行われます。そうすることで、表示速度を速くすることができます。
6秒消えた
yarn add v-lazy-image
<v-lazy-image :src="item.image_url" /> import VLazyImage from "v-lazy-image" export default { components: { VLazyImage } }テキスト圧縮の有効化
https://qiita.com/Oakbow/items/ec13c3a57327cc5f3197
gem 'heroku-deflater' bundle install静的なアセットと効率的なキャッシュポリシーの配信
- 投稿日:2020-03-29T05:01:50+09:00
Vue Routerでquery書き換えができなかった(凡ミス)
試行錯誤の段階を記載しているので急いでいる方は下へ。
問題
Vue Routerを使用してqueryを書き換えようとすると次のように怒られた。
Uncaught (in promise) NavigationDuplicated { _name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/blog?page=3") is not allowed", stack: "Error↵ at new NavigationDuplicated (webpack-int…node_modules/vue/dist/vue.runtime.esm.js:1853:26)" } Error at new NavigationDuplicated (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2013:14) at HTML5History.confirmTransition (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2129:18) at HTML5History.transitionTo (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2073:8) at HTML5History.replace (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2416:10) at eval (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2829:22) at new Promise (<anonymous>) at VueRouter.replace (webpack-internal:///./node_modules/vue-router/dist/vue-router.esm.js:2828:12) at Proxy.set (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/blog/article_list.vue?vue&type=script&lang=ts&:190:20) at callback (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"900baf1e-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/blog/article_list.vue?vue&type=template&id=ee03622c&scoped=true& (http://localhost:8080/js/article_list.js:23:1), <anonymous>:115:29) at invokeWithErrorHandling (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26)" at invokeWithErrorHandling (webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1853:26)"どうやら同じrouteに遷移しようとしていると思われているらしい。
実際はちゃんと別routeへ動こうとしているので、何かがおかしい。ソース
問題のソースはこれ。(抜粋)(変更)
let query = this.$route.query; query["page"] = page.toString(); this.$router.push({ query });GitHubを調査
https://github.com/vuejs/vue-router/issues/2872
それっぽいissueをみつけた。
解決方法としては、Exceptionを握りつぶすというもの。
(わざと同じルートへ動こうとしたときのissueだろうか?)ただ、同じルートへ遷移しようとすると無視するという仕様のもとでエラーを無視したところで得られるものは何もなく…
ソースを観察
stacktraceをもとに元凶であると思われるファイルへ。(
node_modules/vue-router/dist/vue-router.esm.js:2129
)vue-router.esm.jsif ( isSameRoute(route, current) && // in the case the route map has been dynamically appended to route.matched.length === current.matched.length ) { this.ensureURL(); return abort(new NavigationDuplicated(route)) }
isSameRoute
の定義を見にいく。vue-router.esm.jsfunction isSameRoute (a, b) { if (b === START) { return a === b } else if (!b) { return false } else if (a.path && b.path) { return ( a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && a.hash === b.hash && isObjectEqual(a.query, b.query) ) } else if (a.name && b.name) { return ( a.name === b.name && a.hash === b.hash && isObjectEqual(a.query, b.query) && isObjectEqual(a.params, b.params) ) } else { return false } }しっかりqueryが異なることを確認しているように見える。
実験
先の
isSameRoute
の冒頭でa,bの中身をconsole.logで確認する。a: { name: "article_list", meta: {}, path: "/blog", hash: "", query: {page: "3"}, params: {}, fullPath: "/blog?page=3", matched: [{…}], __proto__: Object } b: { name: "article_list", meta: {}, path: "/blog", hash: "", query: {page: "3"}, params: {}, fullPath: "/blog?page=2", matched: [{…}], __proto__: Object }queryとfullPathで齟齬が出ていることがわかった。
原因を考えてみると、問題のソースの1行目、オブジェクトを参照でコピーしていることに気づいた。let query = this.$route.query;これを次のように変更することで修正できた。
let query = Object.assign({}, this.$route.query);まとめ
JSのオブジェクトの代入には細心の注意を。参照渡しで痛い目にあいます。
(問題の核心はVue-routerとは少し離れますが、関係はしていたのでタグ付けしました)
- 投稿日:2020-03-29T01:46:08+09:00
忙しい人向けVue.jsプロジェクト作成
開発環境
Visual Studio Code (Onlineでも良い)
拡張機能 Vetur(便利です。)叩くコマンド
プロジェクト作成
>vue create <project名>
⇒必要なオプションを選択する(今回はTypeScriptや画面遷移させるRouter等を追加)
※追記 tsc -v でバージョン見るとなぜか古いが、内部的には入ってるっぽい。>C:\Users\<username>>npm ls -g typescript C:\Users\<username>\AppData\Roaming\npm `-- @vue/cli@4.2.3 `-- @vue/cli-ui@4.2.3 `-- typescript@3.7.5ここで内部的にnpmからモジュール持ってきたり、設定したり全部自動でやってくれている。(テストに出ます!)
開発サーバーの起動(出てきたURLに飛べばVueの初期画面が表示されているはずです)
>npm run serve
本番用ビルド
>npm run build
Vue CLI プラグインが超優秀らしい
やってくれること
・npmモジュールの導入
・設定ファイル等の追加
・Vueインスタンスへのロードetc例えば HTTP クライアントであるaxiosの導入(APIが作れる)
これだけでasyncとかawaitとかもう使える>vue add axios
UIをカッコよくしたい場合に使うライブラリ
・Vuetify - マテリアルデザイン、コンポーネントが多い。
・Element - 管理画面向けコンポーネントが豊富
・Buefy - BulmaというCSSフレームワークがベースで軽い
・Onsen UI - モバイルアプリ向けGitにあげときましょう。
Githubに新規リポジトリを追加
https://qiita.com/sodaihirai/items/caf8d39d314fa53db4db参考