- 投稿日:2020-12-15T23:38:47+09:00
【Vue.js】Local Storageでデータを永続化させる
Local Storageとは
JavaScriptを使ってWebブラウザにデータを保存できる仕組みです
5~10MB程度のデータを永続化できます
保存された内容はChrome DevToolsで確認できます
今回はVue.jsで見ていきます
保存する
保存するには
localStorage.setItem()を使用します第1引数にキー
第2引数にバリュー
を渡します<template> <div class="container"> <button @click="set()">保存</button> </div> </template> <script> export default { methods: { set() { localStorage.setItem('name', '田中') } } } </script>簡単に「保存ボタン」が押されたらデータを保存するようにします
第2引数に複数の値を渡す場合は
JSON.stringify()を使用しJSON文字列へ変換する必要があります<template> <div class="container"> <button @click="set()">保存</button> </div> </template> <script> export default { methods: { set() { localStorage.setItem('obj', JSON.stringify({ id: 1, name: 'tanaka', age: 20 })) } } } </script>
取得する
データを取り出すには
localStorage.getItem()を使用します
現状JSON形式でデータが保存されているのでJSON.parse()でJavaScriptのオブジェクトに変換する必要があります
mountedにデータを取得する記述を用意しレンダリングします<template> <div class="container"> <button @click="set()">保存</button> <p>{{this.info.id}}</p> <p>{{this.info.name}}</p> <p>{{this.info.age}}</p> </div> </template> <script> export default { data() { return { info: {} } }, mounted() { this.info = JSON.parse(localStorage.getItem('obj')) }, methods: { set() { localStorage.setItem('obj', JSON.stringify({ id: 1, name: 'tanaka', age: 20 })) } } } </script>永続化されているのでリロードしても消えません
削除する
最後です
localStorage.removeItem();で引数に渡したキーに紐づく値を削除できます。また、
localStorage.clear();で全ての値を削除できますlocalStorage.removeItem('obj'); localStorage.clear();
- 投稿日:2020-12-15T23:29:41+09:00
【写真とコード付き】Vue.jsでフェードイン・フェードアウトする簡単は方法
はじめに
この記事はVue.jsの基本は抑えられている程で、話を進めていきます。もしVue.jsを初めて触ると言う方は、こちらの記事を参照していただければと思います。
⬇️【写真とコード付き】Vue.jsの構築から基本的な書き方まで1から解説【超初心者向け】
https://qiita.com/yuki4839/items/62f40564e3f4c8dbfc51
さて今回は、Vue.jsでフェードイン・フェードアウトの仕方について、ご紹介させていただこうと思います。
早速いきましょう。
実行環境
使用ツール、デバイスはこちら
- Google chrome
- Mac OS Catalina
- Visual Studio Code
また今回使用するディレクトリ階層はこちら。
ディレクトリ階層─ root(任意のディレクトリ) │ ├─ index.html │ ├─ css │ └ style.css │ └─ js └ main.js
各ファイルの初期値はこちら。
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./css/style.css"> </head> <body> <div id="web"> <p> {{ context }} </p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script src="./js/main.js"></script> </body> </html>style.css/* 出力結果を見やすくするためのスタイルです */ body { background-color: #add8e6; } #web { background-color: #fff; margin: 20px; padding: 20px; width: 300px; }main.jsconst web = new Vue({ el: '#web', data: { context: `Hello Vue.js!` } })
現時点での出力結果はこちら。
transition
単刀直入に言うと、フェードイン・フェードアウトを行うには、マウントしたHTMLタグ内で
transitionタグを利用することによって作成することができます。まずは実際のコードをご覧ください。
index.html<!-- headタグ省略 --> <body> <div id="web"> <button v-on:click="show=!show"> Click </button> <transition> <p v-show="show"> This sentence is a Transition. </p> </transition> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script src="./js/main.js"></script> </body>style.cssbody { background-color: #add8e6; } #web { background-color: #fff; margin: 20px; padding: 20px; width: 300px; } .v-enter-active, .v-leave-active { transition: 3s; } .v-enter, .v-leave-to { opacity: 0; }main.jsvar web = new Vue({ el: '#web', data: { show: false } })手順
①まず
v-on:click="show=!show"(falseとtrueの切り替え) でクリックイベントを作成。
②次にv-show="show"でクリックイベントの対象要素を指定。
③クリックイベントの対象要素をtransitionタグで囲む。
動作手順
①Vue.js の data が false に設定されているので、
pタグ には display: none;が当たっている。
②buttonタグ のボタンをクリックすると、pタグ の display: none;が外れる。
③display: none; が外れると同時に、transitionタグ の配下の要素(上記なら pタグ)に、class="v-enter-active v-enter"が当てられる。
④イベントが終了したら、class="v-enter-active v-enter"が外れる。
⑤再度 buttonタグ のボタンをクリックすると、transitionタグ の配下の要素(上記なら pタグ)に、class="v-leave-action v-leave-to"が当てられる。
⑥イベントが終了したら、class="v-enter-active v-enter"が外れ、display: none;が当てられる。
動作確認
まずデフォルトはこちら。
ボタンをクリックすると、ゆっくりフェードイン。
しばらくすると描画完了です。
再びボタンを押すと、ゆっくりフェードアウト。
しばらくすると描画完了です。
まとめ
今回は
Vue.jsでのフェードイン・フェードアウトの方法を解説いたしました。ぜひ実際にコードを書いて見てください!最後まで読んでいただき、ありがとうございました!
筆者:yuki|IT業界のリアルな転職事情など発信|元トップ営業マン(訪販)→未経験からエンジニア転職へ
Qiita:https://qiita.com/yuki4839
Twitter:https://twitter.com/yukifullstack
- 投稿日:2020-12-15T21:45:34+09:00
Jestでdocument.activeElementを使えるようにする方法
はじめに
document.activeElementのテストをJestで書いていた時に沼にハマったので備忘録として残す
モック方法
mountでtemplateを定義した後、attachToでdocument.bodyを定義することでdocument.activeElementを使う事が出来るようになる
component.spec.jsconst wrapper = ('component', () => mount({ template: `<div> <input type='text' /> <input type='text' /> <input type='text' /> </div>`, }, { attachTo: document.body, })参考
https://qiita.com/ykhirao/items/8e8a9547a693c677813c
https://vue-test-utils.vuejs.org/ja/api/options.html#attachtodocument
- 投稿日:2020-12-15T17:53:42+09:00
【自分用】VueがIE11で動かなかった
WebpackとBabelを通しているがIE11で何も表示されなくなった
SCRIPT1002: Syntax errorEval関数のところでエラーが出ていたので、webpackに以下の設定を追加してみたが、直らなかった
webpack.config.jsmodule.exports = { //... devtool: 'none' }原因はvueの読み込みところだった
script.jsimport Vue from 'vue/dist/vue.esm.browser.min.js'以下に変更
script.jsimport Vue from 'vue'
resolve.aliasの設定がされていないので、追加webpack.config.jsmodule.exports = { //... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', } } }
resolve.aliasの指定は以下の記事を参考にした
https://blog.websandbag.com/entry/2020/08/07/190655他のPJでは動いていたので、根本的な原因は別のところにあると思うが、とりあえず解決したのでここまで
- 投稿日:2020-12-15T16:41:06+09:00
Vue.js 3 入門 「ルーティング」(Vue Router)
はじめに
Vue.js 3 の ルーティング について、自分が学んだことを備忘録として記載します。
Vue.js に殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びますプロジェクトの作成
まずは Vue CLI を用いてプロジェクトを作成します。
Vue CLI についてはこちらの記事を参照してください。プロジェクトを作成するには、作成したいフォルダで以下のコマンドを実行します。
hello-routerはプロジェクト名です。任意のプロジェクト名を設定してください。cd 任意のフォルダ vue create hello-routerプリセットの選択
すると、以下のように利用するプリセット(プロジェクト設定)の選択を求められます。
まずは最低限の構成とするので「Manually select features」(手動で選択)を選択します。
versionはご自身のバージョンに読み替えてください。Vue CLI v4.5.9 ? Please pick a preset: Default ([Vue 2] babel, eslint) Default (Vue 3 Preview) ([Vue 3] babel, eslint) > Manually select featuresプロジェクトに組み込むモジュールを選択
プロジェクトに組み込むモジュールを選択します。
ここでBabelとLinterに加えて、Routerを選択します。
[Space]キーで選択することができ、[Enter]キーで確定となります。
Routerを選択することによって、Vue Routerというルーティング機能を提供するライブラリが組み込まれます。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: (*) Choose Vue version (*) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support >(*) Router ( ) Vuex ( ) CSS Pre-processors (*) Linter / Formatter ( ) Unit Testing ( ) E2E TestingVue.js のバージョンを選択
Vue.js のバージョンを選択します。
本記事では 3.x を選択します。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 2.x > 3.x (Preview)Historyモードの有効/無効を選択
Historyモードの有効/無効を選択します。
基本的にY(有効)で問題ありません。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) YLinter の設定を選択
Linterの設定を選択します。
今回は最低限のESLint with error prevention only(エラー防止のみ)を選択します。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: (Use arrow keys) > ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ESLint + Prettier続けて、Lintの実行タイミングの選択を求められます。
Lint on save(保存時)を選択します。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection) >(*) Lint on save ( ) Lint and fix on commit設定情報の格納先を選択
BabelとESLintの設定情報を個別の設定ファイルとするか、package.jsonにまとめるかを選択します。
個別の設定ファイルとしたほうが綺麗なのでIn dedicated config filesを選択します。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys) > In dedicated config files In package.json今回の設定を保存しておくかを選択
今回の設定を保存しておくかを選択します。
今回はあくまでお試しなのでN(保存しない)とします。Vue CLI v4.5.9 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? (y/N) Nプロジェクトの生成開始
ここまでの設定内容を元に、プロジェクトの生成が開始されるので、完了するまで待機します。
正常に完了すると、以下のような文言が表示されます。Vue CLI v4.5.9 Creating project in 任意のフォルダ\hello-router. Installing CLI plugins. This might take a while... 途中省略... Running completion hooks... Generating README.md... Successfully created project hello-router. Get started with the following commands: $ cd hello-router $ npm run serve生成されたフォルダを確認
カレントフォルダに、指定したプロジェクト名のフォルダが生成されています。
アプリの実行
早速実行してみましょう。
上記のプロジェクト生成完了時の文言(Get started with the following commands:)にある通り、以下のコマンドを実行します。
プロジェクトルートに移動して、開発用のサーバーを実行するコマンドです。cd hello-router npm run serve以下のような文言が表示されれば、開発用のサーバーが起動できています。
ブラウザを起動しhttp://localhost:8080にアクセスしてください。App running at: 途中省略... Note that the development build is not optimized. To create a production build, run npm run build.ページの表示
以下のような画面が表示されれば、プロジェクトの作成は成功です。
開発用サーバーは[Ctrl] + [C]で終了することができます。挙動を確認する
ここからルーティングの挙動を確認していきます。
http://localhost:8080にアクセスすると、以下のようにHome画面が表示されます。
つまり、Homeコンポーネント(/src/views/Home.vue)が呼び出されていることがわかります。ここでアドレスバーから手入力でURLを変更します。
変更前は以下のようになっているかと思います。http://localhost:8080アドレスバーから手入力でURLを以下のように変更してください。
末尾に/aboutを追加します。http://localhost:8080/about変更後、Enterを押下すると、以下のようにAbout画面が表示されると思います。
つまり、Aboutコンポーネント(/src/views/About.vue)が呼び出されました。ここで生じた2つの疑問について、仕組みを説明していきます。
http://localhost:8080にアクセスすると、なぜHomeコンポーネント(/src/views/Home.vue)が呼び出されるのか/aboutを追加すると、なぜAboutコンポーネント(/src/views/About.vue)が呼び出されるのかルーティング
クライアントから要求されたURLに応じて、処理を受け渡すコンポーネントを決定する仕組みのことです。
ルーティング機能を提供するライブラリのことをルーターと呼び、Vue.js では標準的なルーターとしてVue Routerが用意されています。ルーティングの流れ
リクエストのURLをアプリケーションが定義しているルート(Route)と照合します。
一致するルートが見つかった場合は、該当するコンポーネントが呼び出されます。http://localhost:8080/about従って
Aboutコンポーネントが呼び出されるのは、上記のURLをルートと照合した結果、一致するルートが見つかったからということになります。ルート
では、ルートはどこでどのように定義されているのでしょうか。
ルーティング情報として/src/route/index.jsに定義されています。import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default routerまず抑えておきたいのは
createRouterメソッドによって、ルーティング情報を扱うルーターが生成されている点です。const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes })createRouterメソッド
- history
- historyモードの基本情報
- ほぼ定型
createWebHistory(process.env.BASE_URL)- routes
- ルート
- ここでは
routes変数の値が割り当てられている実際のルートが定義されているのは、
routes変数です。const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } ]
- path
- リクエストパス
- name
- ルートの名前
- component
- ルーティングによって呼び出されるコンポーネント
従って、ここでは以下の2つのルートが定義されていることになります。
Homeルート
/でHomeコンポーネントが呼び出される
Aboutルート
/aboutでAboutコンポーネントが呼び出される結果として、
http://localhost:8080にアクセスするとHomeコンポーネントが呼び出され、/aboutを追加すると、Aboutコンポーネントが呼び出されることになります。ルーターの有効化
なお、ルーターは
/src/main.jsで有効化されています。import { createApp } from 'vue' import App from './App.vue' import router from './router' createApp(App).use(router).mount('#app')Vueインスタンスにライブラリを組み込む
useメソッドに、定義したルーター(router)を渡すことで有効化しています。ルートを追加してみる
コンポーネントを追加
src/views/Article.vueを追加
src/viewsフォルダには、ルーティングに関わるコンポーネントを配置します。
src/componentsフォルダも同様の役割ですが、こちらにはより細かい部品を配置します。<template> <div class="article"> <h1>This is an article page</h1> </div> </template>ルートを追加
src/router/index.jsを以下のように修正
Articleルートを追加します。const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, // ↓ 追加 { path: '/article', name: 'Article', component: () => import('../views/Article.vue') } ]ついでにTOPのリンクも追加
src/App.vueを以下のように修正ルーター経由のリンクは、
aタグではなく、router-linkを利用します(to属性でリンク先を指定)。
また、ルーター経由で呼び出されたコンポーネントは、router-viewの領域に反映されます。
※ルーターを利用する場合、router-viewによる表示領域の確保は必須<template> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link to="/article">Article</router-link> <!-- 追加 --> </div> <router-view/> </template>結果を確認
以下のURLにアクセス
http://localhost:8080Articleページへのリンクが追加されています。
以下のURLにアクセス または
Articleのリンクを押下http://localhost:8080/article
Articleコンポーネントが呼び出されていることが確認できました。以上となります。
ありがとうございました。
- 投稿日:2020-12-15T13:09:25+09:00
vue.js google adsense
vue.js で adsense を使いたい。
参考
https://www.ketancho.net/entry/2018/05/22/080000インスコ
npm install vue-adsense --saveコード改変
これをやらないとエラーが出る
vim node_modules/vue-adsense/main.jsをして、
以下の内容に変更node_modules/vue-adsense/main.jsimport VueAdsense from './VueAdsense.vue' export default VueAdsenseapp.js に追記
app.jsimport VueAdsense from 'vue-adsense' // Google Adsense 用 Vue.component('adsense', VueAdsense)コード挿入
使いたいコードに以下を追記して完了。
それぞれ、adsenseから拾ったデータを入れること。hoge.vue<adsense ad-client="ca-pub-xxx" ad-slot="2061149xxxx" ad-format="auto" full-width-responsive="true"> </adsense>
- 投稿日:2020-12-15T10:52:17+09:00
JSX etcの問題点
※この文章はベータ版です。
JSXの問題はロジックの中にHTMLなどのレイアウト用言語のコード片(orそのコード片を生成するコード)が埋め込まれてしまうことにあります。
(JSXはトランスパイラにより、JS的に問題のない専用の関数でHTMLなどのレイアウト用コードを組み立てるコードに変換されます。)昔からロジック中でHTMLを生成できるライブラリ・フレームワークが多数、作成されてきました。
現在もRails etcのWEBアプリケーションフレームワーク(WAF)のコントローラにおいて、用意された関数(orメソッド)群を用いてHTMLを組み立てるコードを書くことが可能になっています。
しかし、メジャーWAFでは、ロジックの中にHTMLなどのレイアウト用言語のコード片(orそれを組み立てるコード)を埋め込むことはメンテナンスetcの観点から、特定のケースを除いて回避することが推奨されてきました。
ロジックの中にHTMLなどのレイアウト用言語のコード片(orそれを組み立てるコード)を埋め込むことを、何の制限もなく、許可してしまうと、コード中にレイアウト用言語のコード片が散逸し、コードの見通しが悪く、レイアウトの変更が発生した場合etcに、ロジックを追いかけなければならない事態になります。
ほとんどの言語やフレームワークで、ロジックの中にHTMLなどのレイアウト用言語のコード片を埋め込むことは推奨されず、HTMLなどのレイアウト用言語で記述するテンプレートにロジック・コードを埋め込むスタイルが採用されてきました。
ロジックの中に埋め込むよりは、テンプレートとして切り出し、ロジックの埋め込みを可能にするほうが問題が少ないと判断されてきたからだと思います。
ロジック中に埋め込む場合に比べ、テンプレートにロジックを埋め込む場合は多少のオーバーヘッドがあることが多いですが、パフォーマンスに対する要求がハイレベルな場合を除き、ロジック中に埋め込む場合におこり得る問題の前では有意なオーバーヘッドではありません。
(PHPはレイアウト言語にロジック・コードを埋め込むスタイルの仕様でありながら、ロジックの中にレイアウト用コード片を埋め込むこむこともできてしまいます、が、PHPのモダンなフレームワークでは、view用テンプレートでレイアウト用言語にロジック・コードを埋め込み、ロジック中にレイアウト用コード片を埋め込まないスタイルを採用しています。)
(HamlやSlimなど、HTMLタグを対応した関数に置き換え、テンプレート自体をレイアウト組み立てロジックとして記述し、実行時にHTMLに変換するテンプレート・エンジンもありますが、エンジニア視点のみで作成されているもので、汎用性も乏しいので、個人的には邪道だと思っています。)
HTML仕様においては、scriptタグにロジックのJSコードを集約し、scriptタグ外では、タグのイベント用属性でJSコード片を埋め込める以外はJSコードを埋め込むことはできません。
HTML仕様自体が、デザイン(レイアウト)とロジックを分離する形で設計されています。
(HTMLは元々、デザイン(レイアウト)用の言語であり、プログラマブルではありませんでした。デザイン(レイアウト)を動的に変更・制御したいというニーズが発生し、それに応える形でJavascriptが誕生し、scriptタグが実装され、イベント用の属性が追加され、HTMLは後付けでプログラマブルになったのです。)
JSXの信奉者?による文章には、VueやAngularのコード例として、入門記事では見かけるものの実際はVueやAngularにおいてバッドなコード例とされているようなコードを引用して、JSXの優位性を主張しているモノがありますが、
VueやAngularにおいてHTMLorHTMLテンプレート用にビルトインで用意されているディレクティブは、それぞれのグッドなコード例のように書けば、見通しのよいスマートなHTMLテンプレートを記述できます。 (VueやAngularに用意されているディレクティブはHTMLのタグに属性を追加する形でHTMLを拡張して、デフォルトのHTMLにはない機能を擬似的にHTMLに追加しています。)
HTMLテンプレートによるデザインとロジックの分離は、デザイナーとエンジニアの協業だけでなく、コード全体の見通しも良く、してくれます。
HTMLなどのレイアウト用言語のコードはHTMLファイルorテンプレートに集約し、ロジック内に埋め込まないスタイルが望ましいと個人的には思います。
JSX etcでレイアウト用言語のコードをロジックに埋め込む際には、規約で埋め込み可能な箇所を限定し、レイアウト用言語のコード片がロジック中に散らばらないようにすべきです。
- 投稿日:2020-12-15T10:27:34+09:00
Vue.js Vuex編(シンプルなカウンターアプリケーション)
実行環境
macOS Catalina バージョン 10.15.7
MacBook Pro 13インチ
Vue 2.6.12カウンターアプリケーション
vuexを用いてカウンターアプリケーションを作成する。まず、プロジェクト内でvuexをインストールする。
npm install vuexその後、srcディレクトリの直下にstore.jsを作成する。シンプルなVuexStoreを記述する。(main.js内でstoreをインポートしてvueインスタンス内に記述しないとvuexを使うことができないので注意。)
store.js// vue,vuexをインポート import Vue from 'vue' import Vuex from 'vuex' // vuexプラグインの使用の宣言 Vue.use(Vuex) // シンプルなVuexStore const store = new Vuex.Store({ state: { count: 0, }, mutations: { increment(state) { state.count++ } } }) // storeをエクスポート export default storestateに格納されているデータは直接変更することはできない。ミューテーションをコミットすることによって値の更新をすることができる。ミューテーションの中で定義する関数の引数にはstateが必要となる。
次にApp.vueの子コンポーネントとしてCount.vueを作成する。Count.vue<template> <div> <h3>カウンターアプリケーション</h3> <button @click="increment">+1</button> <p>{{ count }}</p> </div> </template> <script> export default { computed: { count() { return this.$store.state.count }, }, methods: { increment() { this.$store.commit('increment') }, } } </script>コンポーネントから値を更新する際、this.$store.commit('increment')のようにしている部分がミューテーションをコミットしている部分である。
最後に
今回は非常にシンプルなカウンターアプリケーションを作成した。次の記事でgetters,mutations,actionsについてまとめようと思う。
- 投稿日:2020-12-15T10:22:50+09:00
Vue.jsでパンくずリスト(手動でページ設定)
はじめに
Vue.jsでパンくずリストを実装すべく調べながら進めたので、メモ書きとして残しておきます。
自動でリストを作成する内容ではありませんので、よろしくお願いします。仕様について
TOPページから一覧ページを挟んでとあるページに遷移します。
実装
呼び出し側の中身
Example.vue<div> <BreadCrumb :breadcrumbs="breadcrumbs"/> </div> <script> import BreadCrumb from "/components/BreadCrumb"; export default { components: { BreadCrumb }, data () { return { breadcrumbs: [ { name: 'TOP', path: '/' }, { name: 'Category', // 中間ページ path: '/category', }, { name: 'ExamplePage' // とあるページ } ] } }; <script>パンくずリストのコンポーネント中身
BreadCrumb.vue<div class="breadcrumb-area"> <div class="breadcrumb-item"> <div v-for="v in breadcrumbs"> <div v-if="v.path"> <router-link :to="v.path"> <span class="link">{{v.name}}</span> </router-link> <span class="space">></span> </div> <div v-else> {{v.name}} </div> </div> </div> </div>解説
例として最後のとあるページで記載しています。
子コンポーネントに渡すデータにそのページまでのnameとpathをdataに配列型に持たせることで、パンくずが増えていくイメージです。
とあるページでは、nameは保持させますが、pathを保持させないことで、v-ifを使ってリンクかただの文字を表示させるかを切り分けています。最後に
手動でページ設定をする方法でパンくずリストを作成してみました。
ちなみに、自動でページ毎に設定をさせる方法もあるみたいですので、大量のページに使う方などはそちらを参考にして頂いた方がいいかもしれません。https://hirakublog.com/code/218/
参考にさせていただきました。ありがとうございます。
- 投稿日:2020-12-15T09:12:19+09:00
脆弱性のある依存パッケージをnpm auditで監査する
フューチャーAdventCalendar2020 15日目です。
昨日は、iwamoto-san(@qiiwa)さんによる、PingでLinuxサーバの死活監視をするべきではないという話でした。
はじめに
こんにちは。2019年4月入社の市川です。
現在私はVue.jsでのSPA(Single Page Application)の開発業務に携わらせていただくことが多いのですが、プロジェクトで使用しているパッケージの脆弱性対応を業務で行いました。今回は私が調べながら脆弱性対応を行った内容を、こちらで紹介をしていきたいと思います。
環境
- npm 6.14.8
- node 14.9.0
現状把握
npm v.6.0.0より、そのプロジェクトで使用しているパッケージの依存関係を監査して脆弱性が存在するかどうかを確認できる、
npm auditコマンドが用意されています。(https://docs.npmjs.com/cli/v6/commands/npm-audit)まず今回はこちらを使用して自分のプロジェクトで使用しているパッケージの依存関係にどの程度の脆弱性があるのかを確認してみましょう。
実行すると、下記のような出力結果を得ることができます。
$ npm audit === npm audit security report === ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ Low │ Prototype Pollution │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ foo │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ bar │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ hoge │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/fuga │ └───────────────┴──────────────────────────────────────────────────────────────┘ ... ... ...(割愛) ... found 100 vulnerabilities (95 low, 5 high) in 200 scanned packages ・・・① run `npm audit fix` to fix 98 of them. ・・・② 2 vulnerabilities require manual review. See the full report for details. ・・・③(↑脆弱性件数の数値を少しいじってます)
npm auditによって下記のようなことがわかりました。
① プロジェクトで使用している200個のpackageのうち100件の脆弱性があり、
そのうち95件はlow level、5件はhigh levelであること
② 100件のうち98件の脆弱性に関してはnpm audit fixコマンドで脆弱性対応ができること
③ 2つの脆弱性については手動での確認が必要なことnpm audit fix での脆弱性対応
現状も把握できたので、脆弱性対応を実施していきます。
上記の出力結果にあるnpm audit fixコマンドはnpmのv6.1.0から使用でき、node_modulesに存在する大抵の脆弱性を自動修正してくれます。では、
npm audit fixを実行しましょう。$ npm audit fix + hoge@1.17.30 added 1 package from 1 contributor, removed 10 packages and updated 15 packages in 10.000s 63 packages are looking for funding run `npm fund` for details fixed 98 of 100 vulnerabilities in 200 scanned packages 2 vulnerabilities required manual review and could not be updated
npm audit fixにより、100個の脆弱性のうち98個に対応することができました。便利ですね。
ログに出力されている通り、パッケージの依存関係によってnpm audit fixで自動で修正しきれない脆弱性が残ってしまうことがあり、こちらは手動で解消しなくてはいけません。今回、手動で対応しなければならない脆弱性が2つ残っているのでその内容を確認します。
$ npm audit === npm audit security report === # Run npm update webpack --depth 2 to resolve 2 vulnerabilities ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ High │ Remote Code Execution │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ serialize-javascript │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ @vue/cli-plugin-babel [dev] │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ @vue/cli-plugin-babel > webpack > terser-webpack-plugin > │ │ │ serialize-javascript │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/1548 │ └───────────────┴──────────────────────────────────────────────────────────────┘ ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ High │ Remote Code Execution │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ serialize-javascript │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ @vue/cli-plugin-eslint [dev] │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ @vue/cli-plugin-eslint > webpack > terser-webpack-plugin > │ │ │ serialize-javascript │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/1548 │ └───────────────┴──────────────────────────────────────────────────────────────┘npm auditを実行すると、上記のような出力結果を得ました。
ログに出力されている、More infoのリンクにアクセスすると詳細な内容が記載されています。
(https://www.npmjs.com/advisories/1548)この2つの脆弱性に対応するためには、
npm update webpack --depth 2を実行してwebpackをupdateするよう記載があるので、それに従ってnpm update webpack --depth 2を実行し、npm auditで出力結果を確認します。$ npm update webpack --depth 2 ... ... $ npm audit === npm audit security report === found 0 vulnerabilities in 200 scanned packagesこちらで依存しているパッケージの脆弱性が0件となることが確認できました。
脆弱性対応を自動化する
ソフトウェアの世界では、日々脆弱性が発見されては修正されてを繰り返しています。
人間が常に脆弱性の情報をキャッチアップし、アップデート作業を行うのは少し億劫ですよねプライベートで使用しているGithubでは脆弱性が発見された時、自動で脆弱性対応を行うPull Requestを作成する設定を行っているのですが、業務の方ではあまり自動化できていないので、 gitlab-ciをなどを活用した脆弱性対応が行えないか後日検証してみたいと思います。
参考
- 投稿日:2020-12-15T08:01:39+09:00
大急ぎでAPI+SPA構成のアプリを立ち上げる(Nuxt.js&簡易認証編)
前回の記事
大急ぎでAPI+SPA構成のアプリを立ち上げる(Spring Boot&Heroku編)
あらすじ
こんなの(下図)を明日までに準備することになり、バックエンドとDBの構築までを終わらせました。
今回はフロントエンドを何とかします。Nuxt.jsについて
Nuxt.jsはフロントエンドアプリを構築するためのフレームワークです。
Vue.jsの知識は必要になりますが、最初からガッツリ出来上がったものが手に入るので、急いでいる時にはもってこいです。環境
OS
- macOS Catalina
ツール・ソフトウェア
- Node.js v10.15.3
- npm v6.14.8
- WebStorm 2020.2.4
(※バックエンド・Heroku関連は前回記事に記載)
雛形を作る
Get Startedに従い、雛形を作ります。
このコマンドを実行するとカレントディレクトリにソースが生成されるため、あらかじめ開発用のディレクトリに移動しておきます。npx create-nuxt-app my-rapid-app-front生成中にいろいろ聞かれますが、落ち着いて答えていきましょう。
■Project name, Project description, Author name
何でもOKです。適当に入れます。
■package manager
yarn か npm の2択です。
長い目で見ればyarnがオススメですが、未インストールならnpmでもOKかと。■UI framework, custom server framework, Nuxt.js modules, linting tools, test framework
たくさんあって迷っちゃいますね。全部Noneです。
Lintツールくらいは入れておいても損はないのですが、今回の記事では取り上げません。■rendering mode
サーバサイドレンダリング(SSR)かSPAの2択です。今回はSPA。
できました。
早速プロジェクトを開いてみましょう。何から何までご用意されています。最高ですね。
動作確認
「nuxt dev」コマンドを実行して、ローカルで開発用サーバを起動します。
このときホットリロードが有効になるため、コードを修正すると即時反映されます。npm run dev起動したら、ブラウザで
http://localhost:3000を開きます。適当に入れたメッセージが表示されてて恥ずかしいので、さっさと直していきましょう。
APIサーバと接続する(バックエンド側)
早速APIを叩いていきたいところですが、先にバックエンド側にCORSの設定を入れます。
どこから飛んできたリクエストを許容するか、あらかじめ決めておく必要があるわけです。
DemoApplication.javaに設定を追記したら、Herokuにpush。package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } // 以下を追記 @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { // localhostとフロントエンドのドメイン(予定)に対し、"/api/"で始まるURIへのリクエストを許可 // 末尾に/を入れると機能しないので注意 registry.addMapping("/api/**") .allowedOrigins( "http://localhost:3000", "https://my-rapid-demo-front.herokuapp.com" ); } }; } }git add . git commit -am "cors setting" git push heroku masterこれで準備完了です。
APIサーバと接続する(フロントエンド側)
ローカルからの接続先は
http://localhost:8080、Herokuからはhttps://***.herokuapp.comにしたいので、環境変数で制御できるように設定を入れます。
nuxt.config.jsを開き、末尾に以下を追加してください。env: { baseUrl: process.env.BASE_URL || 'http://localhost:8080' }これにより、変数
process.env.baseUrlに対し、環境変数BASE_URLが設定されていればその値が、されていなければhttp://localhost:8080が格納されるようになります。続いて
pages/index.vueを開き、<template>と<script>をごそっと編集。
<style>はとりあえずそのまま。<template> <div class="container"> <ul> <li v-for="employee in employees"> <span>{{ employee.name }} [{{ employee.department.name }}]</span> </li> </ul> </div> </template> <script> export default { data() { return { employees: [] } }, async created() { const response = await fetch(`${process.env.baseUrl}/api/employees`) if (response.ok) { this.employees = await response.json() } else { console.error(response.statusText) } } } </script>これがトップページになります。中身をざっくり解説すると、
- ページのインスタンスができたタイミング(created)で、Employee一覧取得APIを実行
- 変数(date)の
employeesにレスポンスボディを格納v-forを使い、employeesの要素数だけ<li>タグを生成という流れです。
画面を再度開き、以下のようになっていれば接続はOKです。
殺風景ですが、とりあえずヨシ。
デプロイする
静的ビルドしてNetlify等に置くだけでもOKなのですが、今回はフロントエンドもHeroku上で動かします。
公式のガイドを参考に進めていきましょう。まず、Heroku用の設定ファイルを作ります。
ファイル名は「Procfile」。ここに起動時のコマンドを設定します。echo 'web: nuxt start' > Procfileプロジェクト直下にProcfileができたことを確認したら、Herokuアプリケーションを作成。
heroku create my-rapid-demo-frontHerokuに設定を入れます。上2つがサーバの動作に必要な設定、
下が「process.env.baseUrl」に与えるための環境変数です。heroku config:set HOST=0.0.0.0 -a 【アプリ名】 heroku config:set NODE_ENV=production -a 【アプリ名】 heroku config:set BASE_URL=https://【バックエンドのアプリ名】.herokuapp.com -a 【アプリ名】ここで注意なのですが、npm/yarn使用時に生成される
package-lock.json/yarn.lockをコミットすると、ビルドに失敗してしまいます。
.gitignoreに追記して、コミット対象外になるようにしておきます。echo "package-lock.json" >> .gitignore echo "yarn.lock" >> .gitignoreコードをpush。
git add . git commit -am "initial commit" git push heroku masterpushが完了したら、
https://【アプリ名】.herokuapp.com/を開いてみましょう。
起動まで少し時間がかかるかもしれません。無事に成功しました。
しばらく待っても何も出てこない場合、git push時の出力、Herokuのログ、デベロッパーツールのコンソールを確認し、エラーが起きていないか確認してください。Done!
これで最低限の構築は完了です。
あとは必要に応じてAPIを増やしたり、テンプレートやUIフレームワークを見繕ってフロントエンドの見栄えを調整したりしていきましょう。
下のスクリーンショットはVuetifyで体裁を整えた画面です。簡易認証を入れる(おまけ)
さて、開発用とはいえAPIと画面が公開されているのは気になる…
ということで、最低限の認証機構を入れてみます。
- フロントエンドとバックエンドで共通のAPIキーを持たせてチェック
- 画面を開くときにユーザ名・パスワードの入力を求める
なお、これを導入したとてAPIキーが流出すればアクセスし放題なので、言うまでもなく気休めです。くれぐれも本番運用にはお使いになりませんよう…
1. APIキーのチェック
まずはバックエンド側から。
application.propertiesにAPI_KEY=dummyという行を追加します。
この値は環境変数で上書きされるため、環境変数があればその値、なければ「dummy」が適用されます。続いて、パッケージ
interceptorを新しく切り、中に以下のクラスを作成します。package com.example.demo.interceptor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class ApiInterceptor implements HandlerInterceptor { @Value("${API_KEY}") private String API_KEY; @Override public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { // preflightの場合はスキップ if (request.getMethod().equals(HttpMethod.OPTIONS.name())) return true; String authorizationHeader = request.getHeader("Authorization"); if (authorizationHeader == null || !authorizationHeader.equals("Bearer " + API_KEY)) { response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); return false; } return true; } }このpreHandleというメソッドは、リクエストがControllerに渡される前に実行されます。
Headerに設定された文字列を読み取り、キーが一致しなかったら401エラーを返して後続処理が行われないようにしています。続いて、
DemoApplication.javaを開き、先ほど作ったcorsConfigurerをまるっと書き換えます。@Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins( "http://localhost:3000", "https://my-rapid-demo-front.herokuapp.com" ); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(apiInterceptor) .addPathPatterns("/api/**"); } }; }
addInterceptorsメソッドを実装することで、先ほどのInterceptorが/api/以下のリクエストに適用されるようになります。続いてフロントエンド側。
先ほどのbaseUrlと同様に、nuxt.config.jsに変数を追加します。env: { baseUrl: process.env.BASE_URL || 'http://localhost:8080', apiKey: process.env.API_KEY || 'dummy' }そして、リクエスト送信時にヘッダを追加します。
const response = await fetch(`${process.env.baseUrl}/api/employees`, { headers: { Authorization: `Bearer ${process.env.apiKey}` } })これで実装は完了です。
試しに"dummy"を別の文字列に変えてみると、401エラーが返ってくるかと思います。Heroku上にはもう少し長めの文字列を付与してあげます。
なお、本格的に認証を行うのであればただの文字列ではなく、JWTトークンなどを検討するのが良いかと思います。heroku config:set API_KEY=【キー文字列】 -a 【フロントエンドアプリ名】 heroku config:set API_KEY=【キー文字列】 -a 【バックエンドアプリ名】2. 画面に認証ダイアログ
こちらはNuxt.js用の神モジュールを使わせて頂きます。
npm install nuxt-basic-auth-modulemodules: [ 'nuxt-basic-auth-module' ], basic: { name: process.env.BASIC_AUTH_USER || 'user', pass: process.env.BASIC_AUTH_PASSWORD || 'password', enabled: true },デプロイ後に画面を開き直すと、ダイアログが出てきました。
これですべて完了です。おわりに
ここまで読んでいただきありがとうございました。
リポジトリのリンクを置いておきますので、お急ぎの際はご自由にお使いください。GitHub
my-rapid-demo
my-rapid-demo-front参考ページ
- 投稿日:2020-12-15T06:56:58+09:00
Vue.js で Atomic Design のサイトテンプレートを作成
Atomic Designの勉強のために
Vue.js で簡単なサイトテンプレートを作成してみました。作ったもの
vue create ~ npm run serve
npm, vueバージョン
$ npm -v 6.14.8 $ vue --version @vue/cli 4.5.9プロジェクト作成
vue create vue_atomicプリセットはマニュアルを選択しました
? Please pick a preset: Default ([Vue 2] babel, eslint) Default (Vue 3 Preview) ([Vue 3] babel, eslint) > Manually select features初期インストールするプラグインは以下を選択しました。
? Check the features needed for your project: (*) Choose Vue version (*) Babel (*) TypeScript ( ) Progressive Web App (PWA) Support (*) Router (*) Vuex >(*) CSS Pre-processors (*) Linter / Formatter ( ) Unit Testing ( ) E2E TestingVue3 を選択
? Choose a version of Vue.js that you want to start the project with 2.x > 3.x (Preview)以下の2つはデフォルトにしました
? Use class-style component syntax? (y/N) y ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) nヒストリーモードを使用するようにしました。
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y以下の項目もデフォルトにしました。
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys) > Sass/SCSS (with dart-sass) Sass/SCSS (with node-sass) Less Stylus ? Pick a linter / formatter config: (Use arrow keys) > ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ESLint + Prettier TSLint (deprecated) ? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection) >(*) Lint on save ( ) Lint and fix on commit ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys) > In dedicated config files In package.jsonプリセットを保存はしませんでした。
? Save this as a preset for future projects? (y/N) N動作確認
cd vue_atomic npm run serveCSS Frameworkのインストール
2020/12/12現在ではBootstrap-vueが未対応の様でした。
フリーランス 技術調査ブログ
https://px-wing.hatenablog.com/entry/2020/11/25/082139を参考にさせていただいたところ
TailwindCSSが対応しているとのことだったので、
初めてですがこの機会に使用してみることにしました。TailWindCSS公式を参考に、
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9ルートディレクトリに
postcss.config.jsを作成し、以下を記述します。postcss.config.js/* eslint-disable @typescript-eslint/no-var-requires */ const autoprefixer = require('autoprefixer'); const tailwindcss = require('tailwindcss'); module.exports = { plugins: [ tailwindcss, autoprefixer, ], };index.cssを作成し以下を記述します。
(srcフォルダ直下にcssフォルダを作成して、配置しました)index.css/* ./src/index.css */ /*! @import */ @tailwind base; @tailwind components; @tailwind utilities;main.ts に以下の一文を記述します。
main.tsimport './css/index.css'これでTailWindCSSで記述できる準備ができました。
フォルダ構成
今回は以下のようにいたしました
src - assets - components - atoms - molecules - organisms - templates - css - pages - router - store App.vue main.ts初期状態だとviewsフォルダがあったのですが削除しました。
templatesは作らず、他の4つで構成しているという例をよく見たのですが、
今回は半ば無理やりtemplatesも作ってみました。main.ts
最終的なmain.ts.
main.tsimport { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' import './css/index.css' const app = createApp(App) /** * componentsフォルダの中のVueファイルを読み込み、 * グローバルコンポーネントとして登録。 */ const files = require.context('$comp', true, /\.vue$/) const components: { [key: string]: __WebpackModuleApi.RequireContext} = {} files.keys().forEach(key => { const str: string = key.replace(/(\.\/|\.vue)/g, '') components[str] = files(key).default }) // 読み込んだvueファイルをグローバルコンポーネントとして登録 Object.keys(components).forEach(key => { const nameArr = key.split('/') const name = nameArr[nameArr.length - 1] app.component(name, components[key]) }); app.use(store).use(router).mount('#app')最初、こう書いていたら型エラーが出ましたので、
const components: { [key: string]: string} = {}こう修正したところ、エラーが無くなりました。
const components: { [key: string]: __WebpackModuleApi.RequireContext} = {}各ファイルの作成
最初に作成されたHome.vueを参考にしながら各ファイルを作成していきました。
Home.vue<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/> </div> </template> <script lang="ts"> import { Options, Vue } from 'vue-class-component'; import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src @Options({ components: { HelloWorld, }, }) export default class Home extends Vue {} </script>App.vue
App.vue<template> <Layout-Default> <template v-slot:header> <The-Header></The-Header> </template> <template v-slot:main> <transition name="fade"> <router-view /> </transition> </template> <template v-slot:footer> <The-Footer></The-Footer> </template> </Layout-Default> </template>Atoms
CustomLink.vue<template> <router-link :to="{ name: link }" class="px-3 py-2 rounded-md text-sm font-medium" > {{ label }} </router-link> </template>CustomTitle.vue<template> <div class="mx-auto text-9xl"> <p> {{ label }} </p> </div> </template>Molecules
CustomCard.vue<template> <!-- This example requires Tailwind CSS v2.0+ --> <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-lg m-4 sm:align-middle sm:max-w-lg sm:w-full" > <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <div class="mt-3 text-center"> <h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-headline" > {{ title }} </h3> <div class="mt-2"> <p class="text-sm text-gray-500"> {{ text }} </p> </div> </div> </div> </div> </template>TailWindCSS公式にある、Modalの中身を少し変更したものになります。
Organisms
CustomTitleContainer.vue<template> <div key="title" class="container mx-auto self-center"> <slot name="contents"></slot> </div> </template>CustomContentsContainer.vue<template> <!-- This example requires Tailwind CSS v2.0+ --> <div key="content" class="md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-4 mx-auto"> <div class="flex"> <slot name="contents1"></slot> </div> <div class="flex"> <slot name="contents2"></slot> </div> <div class="flex"> <slot name="contents3"></slot> </div> <div class="flex"> <slot name="contents4"></slot> </div> </div> </template>テンプレートのメインコンテンツを入れ替えるためのコンテナになります。
コンテナはOrganismsを内包することもあるので、自身もOrganismsになるかと思うのですが
そもそもコンテナのようなものを要素として作るべきなのか、作ったとして別の要素になるのか、
いまいち理解できていません…TheHeader.vue<template> <nav class="bg-blue-800 shadow"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex items-center justify-between h-16"> <div class="flex items-center"> <div class="flex-shrink-0"> <Custom-Link class="text-gray-200 hover:text-white text-lg" label="HOME" link="Home"></Custom-Link> </div> <div class=""> <div class="ml-10 flex items-baseline space-x-4"> <Custom-Link class="text-gray-200 hover:text-white" label="PageA" link="PageA"></Custom-Link> <Custom-Link class="text-gray-200 hover:text-white" label="PageB" link="PageB"></Custom-Link> </div> </div> </div> </div> </div> </nav> </template>TheFooter.vue<template> <nav> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 "> <div class="flex items-center justify-between h-16"> <div class="flex items-center"> <div class="flex-shrink-0"> <Custom-Link class="text-sm text-gray-500 hover:text-gray-900" label="HOME" link="Home" ></Custom-Link> </div> <div> <p class="text-xs">© xxxxxxxx {{ year }}</p> </div> </div> </div> </div> </nav> </template>ヘッダーとフッター。
Vueの命名規則に則ってこちらの2つのみThe~ という名前にしています。Templates
LayoutDefault.vue<template> <div> <header> <slot name="header"></slot> </header> <div> <main class="container main-container mx-auto flex"> <slot name="main"></slot> </main> </div> <footer> <slot name="footer"></slot> </footer> </div> </template>やっぱり無理やり作った感じが否めません。
VueでAtomicDesignにする時にはTemplatesは作らなくても良さそうだと感じました。Pages
Home.vue<template> <Custom-Title-Container> <template v-slot:contents> <Custom-Title :label="label"></Custom-Title> </template> </Custom-Title-Container> </template>PageA.vue<template> <template> <Custom-Contents-Container> <template v-slot:contents1> <Custom-Card :title="title1" :text="text1"></Custom-Card> </template> <template v-slot:contents2> <Custom-Card :title="title2" :text="text2"></Custom-Card> </template> <template v-slot:contents3> <Custom-Card :title="title3" :text="text3"></Custom-Card> </template> <template v-slot:contents4> <Custom-Card :title="title4" :text="text4"></Custom-Card> </template> </Custom-Contents-Container> </template></template>各PagesはOrganismsに作ったコンテンツ表示用のコンテナを中身を入れ替えて表示するようにしました。
まとめ
AtomicDesignを採用したからといって問題が何でも解決するわけでもなく、
データの受け渡し方法など考慮しなければならないポイントが多くあることに気づけました。
Vue3やTypeScriptを使ってみたくて選んだのですが、特徴や良さを活かすことがまったくできなかったので反省です。TailWindCSSはClassの記述量が多くなるのが難点だと感じましたが、
AtomicDesignでCSSをメインに適用するコンポーネントとそうでないものを分離することで、
少しデメリットを吸収することができるのでは、と思いました。今回の制作物は以下のGitのリポジトリに置いております。



























