- 投稿日:2020-08-09T23:58:40+09:00
Amplify + Vue + GitHubでWebアプリの自動デプロイ環境を構築
Amplify + Vue + GitHubでWebアプリの自動デプロイ環境を構築を構築します。
手動デプロイ、自動デプロイ、ブランチ名規則による自動デプロイ、プルリクエストによる自動デプロイの設定をします。
前提
- AWSアカウントは所持している
ローカル環境準備
Node.js/npmのインストール
Amplify CLIではNode.jsで10.x、npmでは6.x以降のバージョンが推奨されています
なので、環境を確認。インストールされていなかったりバージョンが古い場合はアップデートする。
$ node -v; npm -v v14.7.0 6.14.7Javaのインストール
Amplify MockingはOpenJDK 1.8 以降のJavaのランタイムを必要とします
なので、環境を確認。インストールされていなかったりバージョンが古い場合はアップデートする。
$ java -version openjdk version "1.8.0_265" OpenJDK Runtime Environment Corretto-8.265.01.1 (build 1.8.0_265-b01) OpenJDK 64-Bit Server VM Corretto-8.265.01.1 (build 25.265-b01, mixed mode)Amplify CLIのインストール
$ npm install -g @aws-amplify/cli@4.16.1 (略)Vue CLIのインストール
$ npm install -g @vue/cli@4.0.5 (略)Amplify CLIの設定
$ amplify configure Follow these steps to set up access to your AWS account: Sign in to your AWS administrator account: https://console.aws.amazon.com/AWSのログイン画面がブラウザで開かれるので、ログインする。
ログイン後、ターミナルに戻って、Enter。
Press Enter to continueリージョンを選択する。
Specify the AWS Region ? region: eu-west-1 eu-west-2 eu-central-1 ❯ ap-northeast-1 ap-northeast-2 ap-southeast-1 ap-southeast-2 (Move up and down to reveal more choices)IAMユーザーを作成する。
Specify the username of the new IAM user: ? user name: amplify-hoge Complete the user creation using the AWS console https://console.aws.amazon.com/iam/home?region=undefined#/users$new?step=final&accessKey&userNames=amplify-hoge&permissionType=policies&policies=arn:aws:iam::aws:policy%2FAdministratorAccess Press Enter to continueブラウザで開かれるので、画面に従って作成。
- ユーザー名を確認し、アクセスの種類で「プログラムによるアクセス」のチェックを確認して「次のステップ:アクセス権限」へ
- 「AdministratorAccess」のチェックを確認して「次のステップ:タグ」へ
- タグを追加する場合は追加して「次のステップ:確認」へ
- 内容を確認し「ユーザーの作成」
- 画面は閉じずにそのまま保持
ターミナルに戻り、先程の画面のIDやアクセスキーの値を入力。
Press Enter to continue Enter the access key of the newly created user: ? accessKeyId: ********** ? secretAccessKey: ******************** This would update/create the AWS Profile in your local machine ? Profile Name: amplify-hoge Successfully set up the new user.Vueプロジェクトの作成
セットアップ
$ vue create hoge ? Your connection to the default yarn registry seems to be slow. Use https://registry.npm.taobao.org for faster installation? Yes接続の確認が出たら「Y」。
? Please pick a preset: default (babel, eslint) ❯ Manually select features「Manually select features」を選択してEnter。
? Please pick a preset: Manually select features ? Check the features needed for your project: ◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ❯◉ Router ◯ Vuex ◯ CSS Pre-processors ◉ Linter / Formatter ◯ Unit Testing ◯ E2E TestingRouterが未選択状態なので、Routerを選択肢てスペースキーで選択状態にしてEnter。
残りの選択肢はデフォルトのままEnter。
アプリの起動確認
$ cd hoge $ npm run serve起動するとURLが表示されるので、ブラウザで確認。
App running at: - Local: http://localhost:8080/ - Network: http://192.168.3.5:8080/Ctrl + C で停止。
UIコンポーネントのインストール
それっぽい見た目になるよう、UIコンポーネントを利用する。
$ npm install vue-material element-ui --save (略)バックエンドのセットアップ
プロジェクトのディレクトリにて実行する。
$ amplify init Note: It is recommended to run this command from the root of your app directory ? Enter a name for the project hoge ? Enter a name for the environment dev ? Choose your default editor: Vim (via Terminal, Mac OS only) ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using vue ? Source Directory Path: src ? Distribution Directory Path: dist ? Build Command: npm run-script build ? Start Command: npm run-script serve Using default provider awscloudformation For more information on AWS Profiles, see: https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html ? Do you want to use an AWS profile? Yes ? Please choose the profile you want to use amplify-hoge空の表が出力されることを確認。
$ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------- | --------- | --------------- |アプリケーション全体で使用するスタイルの作成
任意のCSSを記述する。
$ touch src/assets/style.css
$ vim src/assets/style.css
ソースコードを配置
AWS Amplify ハンズオン アプリケーションの雛形を作成 - AWS 怠惰なプログラマ向けお手軽アプリ開発手法 2019 のソースコードを利用して配置する。
- src/router/index.js
- src/App.vue
- src/main.js
- src/Chat.vue
- src/AI.vue
動作確認
先程はnpm経由で確認したものを、amplify経由でローカル確認する。
$ amplify serve (略) ERROR Failed to compile with 7 errors 13:23:11 These dependencies were not found: * core-js/modules/es.object.to-string in ./src/router/index.js * vue in ./src/main.js, ./src/App.vue and 4 others To install them, you can run: npm install --save core-js/modules/es.object.to-string vueとなる場合、以下で回避。
$ vi babel.config.js設定を修正。
module.exports = {
presets: [
//'@vue/cli-plugin-babel/preset'
["@vue/app", { useBuiltIns: "entry" }]
]
}
$ npm install --save vue再度実行し、URLを取得。
$ amplify serve (略) App running at: - Local: http://localhost:8080/ - Network: http://192.168.3.5:8080/ Note that the development build is not optimized. To create a production build, run yarn build.Amplify Vueモジュールのインストール
$ npm i aws-amplify aws-amplify-vue (略)Auth機能のセットアップ
$ amplify add auth選択肢はすべてデフォルトのままEnter。
$ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Auth | hoge7f13904xxxxxxx | Create | awscloudformation |Authが追加されていることを確認。
これをamplify pushでクラウドに反映する。「Are you sure you want to continue?」はデフォルトのYを選択し、クラウドに反映されるのを待つ。
$ amplify push ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Auth | hoge7f13904xxxxxxx | Create | awscloudformation | ? Are you sure you want to continue? Yes ⠏ Updating resources in the cloud. This may take a few minutes... (略)ここまでで、Amazon Cognitoの準備が完了。
サインインフォームの表示
$ vim src/main.js$ vim src/App.vueAWS Amplify ハンズオン 認証認可機能の追加 - AWS 怠惰なプログラマ向けお手軽アプリ開発手法 2019 に従い、追記する。
$ amplify serveローカルの画面から「Create account」でアカウントを作って認証の動作確認を実施。メールアドレスによる認証コードの機能もこれで利用可能に。
サインアウトボタンの表示
$ vim src/App.vueAWS Amplify ハンズオン 認証認可機能の追加 - AWS 怠惰なプログラマ向けお手軽アプリ開発手法 2019 に従い、追記する。
$ amplify serveローカルの画面で動作確認。
ウェブホスティング
手動デプロイ
$ amplify add hosting ? Select the plugin module to execute Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment) ? Choose a type Manual deployment You can now publish your app using the following command: Command: amplify publishデフォルトの選択肢でEnter。
$ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Hosting | amplifyhosting | Create | awscloudformation | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation | No amplify console domain detectedHostingが追加される。
$ amplify publish ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Hosting | amplifyhosting | Create | awscloudformation | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation | ? Are you sure you want to continue? Yes (略)「Are you sure you want to continue?」はデフォルトの「Y」を選択し、反映を待つ。末尾にURLが表示されるので、そのURLにアクセスするとホスティングされたアプリを確認可能。
環境の追加
自動ホスティングにより、GitHubを介してそれぞれの環境に自動反映されるよう、まずは各環境を用意する。
$ amplify env list | Environments | | ------------ | | *dev |今は、最初に作成した開発環境の「dev」のみ。
ここに「staging」環境を追加する。
```
$ amplify env add
Note: It is recommended to run this command from the root of your app directory
? Do you want to use an existing environment? No
? Enter a name for the environment staging
Using default provider awscloudformationFor more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use amplify-hoge
```「Do you want to use an existing environment?」はデフォルトではなく「No」にする。「Enter a name for the environment staging」は「staging」、その他はデフォルト。
$ amplify env list | Environments | | ------------ | | dev | | *staging |stagingが追加されている。なお、「*」がついているものが現在利用している環境。
$ amplify env checkout dev ✔ Initialized provider successfully. Initialized your environment successfully. $ amplify env list | Environments | | ------------ | | *dev | | staging |環境の切り替えは「amplify env checkout」で行う。
自動デプロイ
GitHubと連携した自動デプロイをしたい場合はこちらの設定を行う。
ここまでで用意したソースコードをGitHubにPushしておく。
ここまでの手動手順で設定した既存のHostingを解除する必要があるので、解除する。
$ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation | | Hosting | amplifyhosting | No Change | awscloudformation | Amplify hosting urls: ┌──────────────┬───────────────────────────────────────────┐ │ FrontEnd Env │ Domain │ ├──────────────┼───────────────────────────────────────────┤ │ dev │ https://dev.xxxxxxxxxxxxxx.amplifyapp.com │ └──────────────┴───────────────────────────────────────────┘ $ amplify remove hosting ? Are you sure you want to delete the resource? This action deletes all files related to this resource from the backend directory. Yes Successfully removed resource $ amplify status Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Hosting | amplifyhosting | Delete | awscloudformation | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation |Hosting解除の設定を反映する。
$ amplify push ✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Hosting | amplifyhosting | Delete | awscloudformation | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation | ? Are you sure you want to continue? Yes次に、Amplify ConsoleでHostingするための設置を追加する。
「Choose a type」で「Continuous deployment (Git-based deployments)」を選択。
すると、ブラウザで設定画面が開かれる。
「GitHub」を選択し、「Connect branch」。
GitHubの「Authorize AWS Amplify」の画面が表示されるので、権限許可。
再び、Amplify Consoleの画面に戻るので、自動デプロイをさせたいリポジトリとブランチを設定し、「次へ」。
「ビルド設定の構成」の「ビルド設定の追加」にある、「Select a backend environment」にて反映させたい環境を選択する。
その下にある「Create new role」をクリックし「Amplify - Backend Deployment」の権限を付与。
「次へ」、「保存してデプロイ」と進む。
しばらくすると、デプロイの進捗が画面に表示されるので確認し、コンソールに戻ってEnter。ホスティングされたURLも表示されている。
この状態で、src/App.vueの表示されている文言を変更してみるなど、GitHubの指定したブランチにPushしてみると自動デプロイされることが確認できる。
この状況ではURLが分かれば誰でもアクセスできてしまうので、Basic認証を設定する。
Amplify Consoleのメニューにある「アクセスコントロール」の「アクセス管理」を選択。設定する対象のブランチの行にある「Access setting」の値を「制限 - パスワードが必須です」にし、ID/PWを設定し、「Save」。
design環境を追加
デザイナーがUI/UX実装時に確認できる環境を追加する。複数のフロントエンドを試したいが、バックエンドは共通の1つのstaging環境を利用し、フロントエンドのみ、デザイナーが開発しているソースコードになるようにする。
ブランチ名が「design/」で始まるものはすべて既存のstaging環境のバックエンドに向くようにする。
$ amplify env add Note: It is recommended to run this command from the root of your app directory ? Do you want to use an existing environment? No ? Enter a name for the environment design「Do you want to use an existing environment?」は「No」を選択。
$ amplify env list | Environments | | ------------ | | dev | | staging | | *design |design環境が追加されていることを確認。
$ amplify push ✔ Successfully pulled backend environment design from the cloud. Current Environment: design | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Auth | hoge7f13904xxxxxxx | Create | awscloudformation | | Hosting | amplifyhosting | No Change | | ? Are you sure you want to continue? Yesdesign環境を反映する。
GitHubにdesign-baseブランチを作成してPushしておく。
$ git add . $ git commit -m "add design-base env" $ git checkout -b design-base $ git push --set-upstream origin design-base次に、Amplify Consoleからdesign環境とdesign-baseブランチの紐付けの設定を行う。
アプリのトップ画面から「ブランチの接続」を選択。
ブランチは「design-base」、Backend environmentで「design」を選択して「次へ」、「保存してデプロイ」。
デプロイが完了したら、「amplify status」でURLの確認が可能。
$ amplify status Current Environment: design | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Auth | hoge7f13904xxxxxxx | No Change | awscloudformation | | Hosting | amplifyhosting | No Change | | Amplify hosting urls: ┌──────────────┬───────────────────────────────────────────────────┐ │ FrontEnd Env │ Domain │ ├──────────────┼───────────────────────────────────────────────────┤ │ design-base │ https://design-base.xxxxxxxxxxxxxx.amplifyapp.com │ ├──────────────┼───────────────────────────────────────────────────┤ │ master │ https://master.xxxxxxxxxxxxxx.amplifyapp.com │ └──────────────┴───────────────────────────────────────────────────┘次に、Amplify Consoleから 「desgin/」から始まるブランチ名の時にdesign環境を使用するよう設定する。
アプリのトップ画面から「全般」、「編集」を選択。
編集画面で以下の設定を設定。
- Branch autodetection: Enabled
- Branch autodetection - patterms: design/**
- Branch autodetection - backend environment: Point all branches to existing environment
- Branch autodetection branch environment: design
- Branch autodetection access control: Enabled
- 任意のusernameとpassword
ここまでの設定で、「design/」から始まるブランチ名がPushされた際にはブランチごとにdesign環境のホスティングが実行される。
試しに2つの環境がホスティングされることを確認する。
$ git checkout -b design/alpha $ git push --set-upstream origin design/alpha $ git checkout -b design/beta $ git push --set-upstream origin design/betaプルリクエストをホスティング
プルリクエストのソースコードを自動的にホスティングするPreview機能を設定する。
アプリのトップ画面から「Preview」、「Enable Previews」を選択。
「Install GitHub app」を選択すると、GitHubの画面が開かれるので、対象のリポジトリを選択して「Install」。
Amplify Consoleの画面に戻ると、対象リポジトリのブランチ一覧が表示される。
ここでは、masterブランチを選択し、「Manage」を選択。
「Pull Request Preview」を「Enabled」にして、「Pull Request Preview - backend environment」で「Point all Pull Requests for this branch to an existing environment」を選択して「dev」を指定して「確認」。
これで、masterに向けてプルリクエストを作成すると、自動的に環境が構築される。
環境はこれで準備完了です。
開発をしていきましょう。
参考
- 投稿日:2020-08-09T23:05:01+09:00
Gridsome入門 SPAを作ってみよう 【2日目 環境構築編】
スケジュール
前提条件
- node.js v8.3以上
- yarn or npmが入っている(Document見るとyarnの方が推奨とのこと)
環境構築手順
CLI入れる
まずはGridsomeのCLIを入れます。vue-cliを使ったことある人ならほぼ一緒なのですぐできちゃいそう。
直感的でわかりやすいです。cli便利ー。// GridsomeのCLIを入れる yarn global add @gridsome/cli // project作成 gridsome create asset-management-app // projectのフォルダに移動 cd asset-management-app/ // ローカルサーバーの立ち上げ gridsome develop
gridsome develop
でlocalhost:8080で確認することができます。しかもhot reloadなので変更したらすぐ反映される便利。
こんな画面が表示されます。今回他に入れるライブラリを追加
// 今回必要なライブラリとか入れる(随時追加) yarn add csv-parse yarn add vue-chartjs chart.jscsv-parseはcsvを扱うためのライブラリで、vue-chart.jsはグラフを扱うライブラリです。
今回データソースをCSVにしたのは使ってるネット証券会社から、自分の投資しているものをCSV形式でダウンロードできるのでそれを使おうかなーと思ったためです。
gridsomeは色んなデータソースを使えるので(markdown・jsonやAPIなど)、他の形式使いたい方はこちらを参照
https://gridsome.org/docs/fetching-data/ページの追加とメニューの変更
新しい静的なページなど追加するにはpagesディレクトリに追加していきます。
お次は共通のLayoutsを変更src/layouts/Default.vue<template> <div class="layout"> <header class="header"> <strong> <g-link to="/">{{ $static.metadata.siteName }}</g-link> </strong> <nav class="nav"> <g-link class="nav__link" to="/portfolio/">Portfolio</g-link> <g-link class="nav__link" to="/asset-allocation/">Asset allocation</g-link> <g-link class="nav__link" to="/asset-transition/">Asset transition</g-link> </nav> </header> <slot/> </div> </template>g-linkはgridsomeが用意してくれているページリンク用のコンポーネントで、このコンポーネントのおかげでgridsomeは高速にページ遷移することを可能しています。以下はdocumentから引用
<g-link>は、リンクが表示されているときにIntersectionObserverを使用してリンクされたページをプリフェッチします。これにより、クリックしたページが既にダウンロードされているため、Gridsomeサイトの閲覧が非常に高速になります。
こんな感じでメニューとか変更しました。あとは中身を作ってかないと。あとがき
やっぱhot reload便利。
そして調べてるうちにlive reloadとhot reloadの違いを初めて知った。。ライブリロードは、ファイルが変更されたときにアプリケーション全体をリロードまたはリフレッシュします。たとえば、ナビゲーションに4つのリンクがあり、変更を保存した場合、ライブの再読み込みによってアプリが再起動され、アプリが最初のルートに戻されます。
ホットリロードは、アプリケーションの状態を失うことなく変更されたファイルのみをリフレッシュします。たとえば、ナビゲーションに4つのリンクがあり、一部のスタイリングに変更を保存した場合、状態は変更されませんが、新しいスタイルはページ上に表示されます。同じページにいてください。あと全然関係ない話ですが、半沢直樹は面白いけど顔芸が濃すぎて話しが入ってこなくなると思った日曜日でした。
あと一週間なのでGridsomeをどんどん学んでいこう。
- 投稿日:2020-08-09T21:43:06+09:00
RailsにVue.jsを導入する準備
はじめに
バージョン
Ruby '2.6.5'
Rails '6.0.3'
Vue.js '2.6.11'Railsは6.0以降。
Vue.jsの導入
Webpackerを用いてインストール
ターミナルrails webpacker:install:vueVue.jsを読み込む
app/view/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title>Title</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all' %> <%= javascript_pack_tag 'application' %> <%# 以下の一行を追加 %> <%= javascript_pack_tag 'hello_vue' %> </head> <body> <%= yield %> </body> </html>localhost:3000へアクセスして、"Hello Vue!"と表示されていれば、
導入と読み込みは上手くいっています。
- 投稿日:2020-08-09T21:22:10+09:00
Vue Composition APIについて調べてみた件
夏休み記事1件目です
(ホントはchefの記事を書いていたのですが、どうにもエラーが解決できなかったので先にこちらです)Vue3について調べたり試したりしたことを忘れないように書き留めました。
間違ってるところがありましたら、優しくご指摘していただけると幸いです。composition APIって?
関数ベースで提供されるAPI郡
まもなくやってくるvue.js3 23ページよりつまり、用意された関数を使っていろいろできる
なんでこれが必要なの?
いままでのVueでは以下のことが問題でした。
- コンポーネントが大きくなるとコードの可読性が落ちる
- テストしにくい
- コード再利用のパターン化がしにくい
- TypeScriptによる型のサポートが受けにくい
今まで、変数の宣言は
data()
でやって、処理する関数は、methods()
で書いていて、コンポーネントを上下に行ったり来たりしていたものが、これで改善されるかんじですかね?提供される主なAPIの種類
- リアクティブ状態の操作
- コンポーネントの新エントリーポイント
- ライフサイクルフック
- Dependency injection(依存性の注入)
リアクティブとは?
ある変数を書き換えた時に、事前に定めた関係性を元に、他の変数が適切に更新されたり、事前に定めた動作が発動することを「リアクティブである」と言います
きたるべきvue-nextのコアを理解する - そもそも「リアクティブ」とは?リアクティブ状態の操作
API名 内容 reactive オブジェクトを取得し、元のオブジェクトの反応型プロキシを返す。2.xの Vue.observable()
と同じ。ref 内側の値を取得して、反応性があり、変更可能な ref オブジェクトを返す。 readonly オブジェクトまたはrefを取得し、読み込み専用のプロキシを元のオブジェクトに返す。 computed getter関数を受け取り、getterから返された値に対して不変の反応型 ref オブジェクトを返す。 watch watch は特定のデータソースを監視し、別のコールバック関数を適用する。2.x の this.$watch と同じ。 watchEffect 依存関係を反応的に追跡しながら即座に関数を実行し、依存関係が変更された場合はいつでも再実行する。 その他にもAPIがあるので気になる方はVue composition APIを見てみてください
ライフサイクルフック
コンポーネントの新エントリーポイントはあとで
setup
でちょこっと触れるので省略します。
今までのものとcomposition APIのライフサイクルフックの対応は以下のようになっています
Vue 2.x Compostion API beforeCreate setup created setup beforeMount onBeforeMount beforeUpdate onBeforeUpdate beforeDestroy onBeforeUnmount destroyed onUnmounted errorCaptured onErrorCaptured 実際に触ってみる
今回は
reactive
とref
について見ていきます。vueのプロジェクトを作成してvue3の各機能が使えるように
vue-next
を追加しますvue create try-composition cd try-composition vue add vue-next
早速composition APIを使ってみたいと思います
App.vue<template> <div>{{ count }} {{ object.foo }}</div> </template> <script> import { ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) // expose to template return { count, object } } } </script>
setup
関数はコンポーネント内でコンポジションAPIを使うためのエントリーポイントとして機能するようです。
return
したものがテンプレートのレンダリングコンテキストになるそうです。これで
npm run serve
すると以下のように表示されます。
ref
を使った場合value
という内部値を指すプロパティーがあります。
これをつかって値の操作ができます。App.vue<template> <div></div> </template> <script> import { ref } from 'vue' export default { setup () { const count = ref(0) console.log(count.value) count.value++ console.log(count.value) // expose to template return { } } } </script>変更してコンソールを見てみるとこのようになっています。
しっかり値が1プラスされてます。では、refで宣言したものを再度reactiveで使ったらどうなるでしょうか?
以下のコードで試してみます。App.vueconst count = ref(0) const state = reactive({ count }) console.log(state.count) state.count = 1 console.log(count.value)変更してコンソールを見てみると、このように
count
に代入されています。
参照渡しされているっぽいですね。
ref
とreactive
の違いは?
ref
とreactive
はどちらもリアクティブなデータを実現するという点では一緒です。
公式のページを見てみると、The difference between using ref and reactive can be somewhat compared to how you would write standard JavaScript logic:
(翻訳)ref と reactive の違いは、標準的な JavaScript ロジックの書き方と比較すると、多少の違いがあります。具体的には以下のコードが紹介されています。
// style 1: 別々の変数 let x = 0 let y = 0 function updatePosition(e) { x = e.pageX y = e.pageY } // --- compared to --- // style 2: 1つのオブジェクト const pos = { x: 0, y: 0 } function updatePosition(e) { pos.x = e.pageX pos.y = e.pageY }
対象 該当箇所 内容 ref style 1に該当する 個別に変数宣言する reactive style 2に該当する オブジェクトでまとめて宣言する 読んで試した結果、オブジェクトなのか、別々の変数なのかというかんじなのでしょうか?
公式の説明の翻訳は以下のようです。
ref を使用する場合は、スタイル (1) を refs を使用してより冗長な同等のものに大部分を変換しています (プリミティブ値をリアクティブにするため)。
reactive を使うことは、スタイル (2) とほぼ同じです。リアクティブを使ってオブジェクトを作成するだけで、それだけです。
しかし、リアクティブのみにする場合の問題は、合成関数の消費者がリアクティブを保持するためには、返されたオブジェクトへの参照を常に保持していなければならないということです。オブジェクトを破壊したり、拡散したりすることはできません。まだまだ理解ができていないので、これからもっと調べて試していきたいと思います。
おわりに
自分自身、調べた結果を忘れないために書いたので、間違いなどがあると思います。
もし「ここ違うんじゃ?」などあったら、教えていただけると幸いです。参考にした記事や引用元
- 投稿日:2020-08-09T19:26:56+09:00
【Nuxt】SSR・SSG・SPAにおける『nuxt build』と『nuxt generate』の実行結果の違いまとめ
NuxtにはSSR(Server Side Rendering)、SSG(Static Site Generator)1、SPA(Single Page Application)の3種類のモードが用意されています。
また、本番環境でNuxtアプリケーションを実行するにあたり
nuxt build
とnuxt generate
の2つのコマンドが用意されています。
nuxt build
はアプリケーションをWebpackでビルドし、JSとCSSをミニファイするコマンド2です。ビルドファイルの出力先は.nuxt
配下です。
nuxt generate
は静的ウェブサイトへデプロイする静的ファイルを生成するコマンド2です。静的ファイルの出力先はdist
配下です。
静的ウェブサイトのホスティングサービスではNetlifyやAmazon S3などが有名です。SSRでアプリケーションを運用する場合、
nuxt build
でファイルをビルド後、nuxt start
でNuxtアプリケーションをサーバー上で起動という流れになります。
一方、SSGやSPAではnuxt generate
で静的ファイルを生成後、静的ウェブサイトにアップロードするという流れになります。では、SSGで
nuxt build
を実行したり、SSRでnuxt generate
を実行したりするとどうなるでしょうか。今回はSSR・SSG・SPAにおける
nuxt build
とnuxt generate
の出力の違いについて紹介します。
なお、Nuxtは2.14.1
を利用します。NuxtにおけるSSR・SSG・SPAの設定方法について
Nuxtでは
nuxt.config.js
のmodeプロパティとtargetプロパティによってアプリケーションのモードが管理されています。
mode
にはuniversal
とspa
、target
にはserver
とstatic
の設定値が用意されています。
mode
をspa
にするとSPAになります。
mode
をuniversal
にした場合、target
をstatic
にすればSSG、target
をserver
にすればSSRになります。Nuxtアプリケーションのモードの初期設定は、新規作成時の対話の回答によって決定されます。
? Rendering mode:
はmode
の設定値に関する質問、? Deployment target:
はtarget
の設定値に関する質問です。検証に利用するサンプルアプリケーションについて
SSR・SSG・SPAにおける
nuxt build
とnuxt generate
の挙動を確認するにあたり、今回はNuxt公式ドキュメントで紹介されているCustom Routesを利用します。Custom Routesはユーザー一覧画面と各ユーザーの詳細画面が用意されているシンプルなアプリケーションです。
各パターンにおける実行結果の違いについて
2パターンの
mode
(universal
・spa
)と2パターンのtarget
(server
・static
)を組み合わせた、計4パターンでnuxt build
とnuxt generate
を実行しました。以下にパターンごとの結果を掲載します。
『mode: universal』『target: server』の場合
SSRに該当するパターンです。
nuxt build
の実行ログは以下の通りです。ℹ Production build ℹ Bundling for server and client side ℹ Target: server ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 5.36s ✔ Server Compiled successfully in 539.09ms Hash: 5a5cd4d0c2a79cef9011 Version: webpack 4.44.1 Time: 5358ms Built at: 2020/08/08 15:59:26 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.83 KiB [emitted] LICENSES 389 bytes [emitted] app.dce2e9b.js 55.3 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 2 hidden assets Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.dce2e9b.js Hash: 583b6fd0b31f116a80e2 Version: webpack 4.44.1 Time: 540ms Built at: 2020/08/08 15:59:26 Asset Size Chunks Chunk Names pages/index.js 6.63 KiB 1 [emitted] pages/index pages/users/_id.js 6.59 KiB 2 [emitted] pages/users/_id server.js 86.6 KiB 0 [emitted] app server.manifest.json 307 bytes [emitted] + 3 hidden assets Entrypoint app = server.js server.js.map ℹ Ready to run nuxt start ✨ Done in 9.20s.実行ログをもとに、SSRモードの
nuxt build
の内容をまとめると以下のようになります。
- サーバーサイドとクライアントサイドのビルドが実行される
- Targetはserverに設定される
nuxt generate
の実行ログは以下の通りです。ℹ Production build ℹ Bundling for server and client side ℹ Target: static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 5.25s ✔ Server Compiled successfully in 578.63ms Hash: 413f8a0717bc47e1c0fb Version: webpack 4.44.1 Time: 5253ms Built at: 2020/08/08 16:01:35 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.83 KiB [emitted] LICENSES 389 bytes [emitted] app.c20583b.js 55.5 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 2 hidden assets Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.c20583b.js Hash: cef8c098609baff3310b Version: webpack 4.44.1 Time: 579ms Built at: 2020/08/08 16:01:35 Asset Size Chunks Chunk Names pages/index.js 6.63 KiB 1 [emitted] pages/index pages/users/_id.js 6.59 KiB 2 [emitted] pages/users/_id server.js 86.6 KiB 0 [emitted] app server.manifest.json 307 bytes [emitted] + 3 hidden assets Entrypoint app = server.js server.js.map ℹ Generating output directory: dist/ ℹ Generating pages ✔ Generated route "/" ✔ Generated route "/users/3" ✔ Generated route "/users/5" (略) ✔ Client-side fallback created: 200.html実行ログをもとに、SSRモードの
nuxt generate
の内容をまとめると以下のようになります。
- サーバーサイドとクライアントサイドのビルドが実行される
- Targetはstaticに設定される
- dist配下に各ページの静的ファイルが作成される
『mode: universal』『target: static』の場合
SSGに該当するパターンです。
nuxt build
の実行ログは以下の通りです。ℹ Production build ℹ Bundling for server and client side ℹ Target: full static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 6.34s ✔ Server Compiled successfully in 561.19ms Hash: 009f5cce6a4034eb2970 Version: webpack 4.44.1 Time: 6345ms Built at: 2020/08/08 15:51:05 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.86 KiB [emitted] LICENSES 389 bytes [emitted] app.edced78.js 58.5 KiB 0 [emitted] [immutable] app node_modules/commons.501805f.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.fe29326.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.7d0c948.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.715f042.js 2.35 KiB 4 [emitted] [immutable] runtime + 2 hidden assets Entrypoint app = runtime.715f042.js node_modules/commons.501805f.js app.edced78.js Hash: b97105c83cc5f7b09c53 Version: webpack 4.44.1 Time: 562ms Built at: 2020/08/08 15:51:06 Asset Size Chunks Chunk Names pages/index.js 6.63 KiB 1 [emitted] pages/index pages/users/_id.js 6.59 KiB 2 [emitted] pages/users/_id server.js 87.7 KiB 0 [emitted] app server.manifest.json 307 bytes [emitted] + 3 hidden assets Entrypoint app = server.js server.js.map ℹ Ready to run nuxt generate ✨ Done in 12.29s.実行ログをもとに、SSGモードの
nuxt build
の内容をまとめると以下のようになります。
- サーバーサイドとクライアントサイドのビルドが実行される
- Targetはfull staticに設定される
nuxt generate
の実行ログは以下の通りです。✔ Skipping webpack build as no changes detected ℹ Generating output directory: dist/ ℹ Generating pages with full static mode ✔ Generated route "/" ✔ Generated route "/users/4" ✔ Generated route "/users/6" (略) ✔ Client-side fallback created: 200.html ✨ Done in 3.19s.実行ログをもとに、SSGモードの
nuxt generate
の内容をまとめると以下のようになります。
- dist配下に各ページの静的ファイルが作成される
『mode: spa』『target: server』の場合
デプロイ先をサーバーに指定したSPAです。
nuxt build
の実行ログは以下の通りです。ℹ Production build ℹ Bundling only for client side ℹ Target: static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 5.23s Hash: 286e8db275c753c4ebd5 Version: webpack 4.44.1 Time: 5227ms Built at: 2020/08/08 16:04:10 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.79 KiB [emitted] LICENSES 389 bytes [emitted] app.43c0549.js 55.4 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 1 hidden asset Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.43c0549.js ℹ Generating output directory: dist/ ℹ Generating pages ✔ Generated route "/" ✔ Client-side fallback created: 200.html ✨ Done in 8.36s.実行ログをもとに、SPA(target: server)モードの
nuxt build
の内容をまとめると以下のようになります。
- クライアントサイドのビルドが実行される
- Targetはstaticに設定される
- dist配下にルート(/)の静的ファイル(index.html)が作成される
nuxt generate
の実行ログは以下の通りです。ℹ Production build ℹ Bundling only for client side ℹ Target: static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 5.15s Hash: 286e8db275c753c4ebd5 Version: webpack 4.44.1 Time: 5155ms Built at: 2020/08/08 16:05:27 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.79 KiB [emitted] LICENSES 389 bytes [emitted] app.43c0549.js 55.4 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 1 hidden asset Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.43c0549.js ℹ Generating output directory: dist/ ℹ Generating pages ✔ Generated route "/" ✔ Client-side fallback created: 200.html ✨ Done in 7.95s.実行ログをもとに、SPA(target: server)モードの
nuxt generate
の内容をまとめると以下のようになります。
- クライアントサイドのビルドが実行される
- Targetはstaticに設定される
- dist配下にルート(/)の静的ファイル(index.html)が作成される
『mode: spa』『target: static』の場合
デプロイ先を静的ウェブサイトに指定したSPAです。
nuxt build
の実行ログは以下の通りです。ℹ Production build ℹ Bundling only for client side ℹ Target: static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 5.23s Hash: 286e8db275c753c4ebd5 Version: webpack 4.44.1 Time: 5236ms Built at: 2020/08/08 16:06:27 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.79 KiB [emitted] LICENSES 389 bytes [emitted] app.43c0549.js 55.4 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 1 hidden asset Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.43c0549.js ℹ Ready to run nuxt generate ✨ Done in 7.75s.実行ログをもとに、SPA(target: static)モードの
nuxt build
の内容をまとめると以下のようになります。
- クライアントサイドのビルドが実行される
- Targetはstaticに設定される
nuxt generate
の実行ログは以下の通りです。ℹ Doing webpack rebuild because nuxt.config.js modified ℹ Production build ℹ Bundling only for client side ℹ Target: static ✔ Builder initialized ✔ Nuxt files generated ✔ Client Compiled successfully in 6.29s Hash: 47d36e3c6f1bf3363c94 Version: webpack 4.44.1 Time: 6296ms Built at: 2020/08/08 16:07:19 Asset Size Chunks Chunk Names ../server/client.manifest.json 7.79 KiB [emitted] LICENSES 389 bytes [emitted] app.ef33196.js 55.4 KiB 0 [emitted] [immutable] app node_modules/commons.48315ec.js 168 KiB 1 [emitted] [immutable] node_modules/commons pages/index.9ce75e2.js 1.51 KiB 2 [emitted] [immutable] pages/index pages/users/_id.5722ee2.js 1.45 KiB 3 [emitted] [immutable] pages/users/_id runtime.84feac3.js 2.35 KiB 4 [emitted] [immutable] runtime + 1 hidden asset Entrypoint app = runtime.84feac3.js node_modules/commons.48315ec.js app.ef33196.js ℹ Generating output directory: dist/ ℹ Generating pages ✔ Generated route "/" ✔ Client-side fallback created: 200.html ✨ Done in 8.93s.実行ログをもとに、SPA(target: static)モードの
nuxt generate
の内容をまとめると以下のようになります。
- クライアントサイドのビルドが実行される
- Targetはstaticに設定される
- dist配下にルート(/)の静的ファイル(index.html)が作成される
まとめ
実行結果をまとめると以下のようになります。
- プロセスは異なるものの、
nuxt build
ではビルドファイル、nuxt genearte
では静的ファイルが生成される結果は同じ- SPA(target: server)のときに限り、
nuxt build
でビルドファイルだけでなく静的ファイルも生成されるtarget
が異なっていてもmode
が同じであれば成果物は変わらないmode
が異なる、つまりSPAかSPAじゃない(SSR or SSG)かで成果物が異なるnuxt build
による成果物は、SSR/SSGの場合はサーバーとクライアント、SPAの場合はクライアントのみのビルドファイルnuxt generate
による成果物は、SSR/SSGの場合は各画面、SPAの場合はルートのみの静的ファイルTwitter(@nishina555)やってます。フォローしてもらえるとうれしいです!
参考記事
SSGは『静的化』や『静的ファイル生成』などとも呼ばれることがあります。 ↩
- 投稿日:2020-08-09T18:48:03+09:00
【Docker】仮想環境で、Nuxt.jsを立ち上げる時に起こるエラー【エラーNo2】
エラー内容
状況としては、docker-composeを使ってイメージとアプリを作っていて、サーバーを立ち上げようとしたら、次のようなエラーが出て、立ち上がらなかった。
app_front | npm ERR! code ENOENT app_front | npm ERR! syscall open app_front | npm ERR! path /app/package.json app_front | npm ERR! errno -2 app_front | npm ERR! enoent ENOENT: no such file or directory, open '/app/package.json' app_front | npm ERR! enoent This is related to npm not being able to find a file. app_front | npm ERR! enoent app_front | app_front | npm ERR! A complete log of this run can be found in: app_front | npm ERR! /app/.npm/_logs/2020-08-08T13_35_16_656Z-debug.log app_front exited with code 254参考対象者
- Dockerを使って、Nuxt.jsアプリを立ち上げたい方
環境
$ docker -v Docker version 19.03.12エラー発生
ディレクトリ構成
environment--/app | |--docker-compose.yml |--/front | |--DockerfileDockerfileFROM node:12.5.0-alpine ENV HOME="/app" \ LANG=C.UTF-8 \ TZ=Asia/Tokyo WORKDIR ${HOME} RUN apk update && \ apk upgrade && \ npm install -g npm && \ npm install -g @vue/cli ENV HOST 0.0.0.0 EXPOSE 300docler-compose.ymlversion: "3" services: front: container_name: app_front build: front command: npm run dev tty: true volumes: - ./front:/app ports: - 8080:3000 depends_on: - backコンテナ作成から、サーバー起動
ビルドしてイメージを作成する。
$ docker-compose build
作成したイメージから、Nuxt.jsのアプリをつくていく。
$ docker-compose run --rm front npx create-nuxt-app sample_appアプリが作成できたら、コンテナを立ち上げてみる(=サーバーを立ち上げる)
$ docker-compose up
すると、冒頭にあったエラーが、、、
Starting app_front ... done Attaching to app_front app_front | npm ERR! code ENOENT app_front | npm ERR! syscall open app_front | npm ERR! path /app/package.json app_front | npm ERR! errno -2 app_front | npm ERR! enoent ENOENT: no such file or directory, open '/app/package.json' app_front | npm ERR! enoent This is related to npm not being able to find a file. app_front | npm ERR! enoent app_front | app_front | npm ERR! A complete log of this run can be found in: app_front | npm ERR! /app/.npm/_logs/2020-08-08T13_35_16_656Z-debug.log app_front exited with code 254解決への試行錯誤
app_front | npm ERR! path /app/package.json app_front | npm ERR! errno -2 app_front | npm ERR! enoent ENOENT: no such file or directory, open '/app/package.json'この辺のエラーを見ると、
/app/package.json
とかディレクトリ構成が違ってんだよなぁーとか思いつつ、そこを中心にせめていくことにした。まずは、本来のディレクトリ構成である
/app/front/package.json
でコンテナを起動するように、コマンドでディレクトリ移動させるようにする。docker-compose.ymlcommand:> cd front #追記 npm run devサーバー起動
$ docker-compose up #エラー出力違うエラー....
解決法
そうか、じゃあ最初から起動する場所を指定すればいいじゃん。
ということで、DockerfileFROM node:12.5.0-alpine ENV HOME="/app/feederin" \ #HOMEの値を変更 LANG=C.UTF-8 \ TZ=Asia/Tokyo WORKDIR ${HOME} RUN apk update && \ apk upgrade && \ npm install -g npm && \ npm install -g @vue/cli ENV HOST 0.0.0.0 EXPOSE 3000更新したDockerfileから、イメージを再構築する
$ docker-compose up -d --buildそして、コンテナを起動すると、、、
$ docker-compose up -d Starting app_front ... done嬉しみいいいいい(起動した。)
- 投稿日:2020-08-09T18:17:58+09:00
Vue.jsで名前の表示/非表示を切り替える
はじめに
ボタンを押すと画面上にある名前の表示と非表示の切り替えができるプログラムを書いていきます。
ソースコード
全体のソースコードは以下の通りになります。(headタグやvueの読み込み箇所は省きます。)
HTMLファイルに書くソースコード
<div id="app"> <button @click="toggleBtn">クリックしてね</button> <p v-if="show"> {{name}} </p> </div>jsファイルに書くソースコード
var app = new Vue({ el: "#app", data: { name: "Naoki", show: true, }, methods: { toggleBtn: function () { this.show = !this.show; }, }, });ソースコードの解説
コードを書く順番で見ていきます。
条件分岐で名前を表示させる
<!-- 条件分岐 --> <p v-if="show"> {{name}} <!-- nameの出力 --> </p>
show
がtrueの場合、name
のデータが出力されます。//保持しているデータ data: { name: "Naoki", show: true, },
name
に"Naoki"という文字データとshow
にブーリアン型の true が入っている状態です。ここでの処理内容は
v-if="show"
で条件分岐が行われ、今のshow
のデータの中身はtrueで条件を満たしているので、name
に入っている"Naoki"という文字データが画面上に出力されることになります。
※コメント欄に補足説明があるので、よろしければご覧ください。クリックで名前の表示/非表示を切り替える
<!-- クリックイベントでの関数の呼び出し --> <button @click="toggleBtn">クリックしてね</button>ボタンをクリックすると
toggleBtn
の処理が呼び出されます。// 関数の設定 methods: { toggleBtn: function () { this.show = !this.show; }, },toggleBtnが呼び出されると、反転した
show
がshow
に代入されます。
ちなみに、this
というのは、jsファイルのapp
を指しています。ここでの処理内容は、ボタンをクリックすると
toggleBtn
の処理が呼び出されて、show
データのtrue
とfalse
が切り替わる仕組みになっています。この切り替えによって、名前の表示/非表示ができるようになるというわけです。おわりに
はじめてQiitaに記事を書いたのですが、こんなに簡単な処理内容でも文字にして説明するとなると全然スムーズにいかず、かなり頭を使いました。人に教えることに慣れていないので、これからもっと練習して人に教えるスキルとプログラミングスキルを向上させていきたいと思います。
- 投稿日:2020-08-09T18:14:50+09:00
Vue.js+Node.js+MongoDBで簡単なクライアントサーバシステムを作ってみた
この記事は株式会社富士通システムズウェブテクノロジーが企画するいのべこ夏休みアドベントカレンダー 2020の10日目の記事です。本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません。
はじめに
Vue.jsとNode.jsを使い、MongoDBに登録された今までに読んだ本の名前をブラウザで確認できるアプリケーションを作りました。
どんな本を読んだか把握しやすくすることで今後の勉強に役立てたいと思います。環境
以下は使用するソフトウェアのバージョンです。
OSはWindows10 Enterprise バージョン1809です。$ node -v v12.16.1 $ npm -v 6.13.4 $ vue -V @vue/cli 4.4.6 $ mongo >db.version() 4.2.8事前準備
- Node.jsとMongoDBのインストールをして、データベースに本のデータを登録します。
Node.jsのインストール
- Node.js公式サイトからNode.jsをダウンロードします。 コマンドプロンプトで以下のコマンドを入力し、バージョン情報が出てきたら問題なくインストールされています。
$ node -v
- Node.jsと一緒にnpmもインストールされるので、以下のコマンドで確認します。
$ npm -vMongoDBのインストール
- 今回はローカル環境でアプリケーションが動くことを確認したいので、まずWindows環境にMongoDBをインストールします。
- MongoDB公式サイトからServerのダウンロードを選択し、ダウンロードしたインストーラーを起動してください。
- Windowsのシステム環境変数Pathに以下のパスを追加します。 ※バージョンによって、4.4の部分は変わります
C:\Program Files\MongoDB\Server\4.4\bin\
- Windowsの再起動後にコマンドプロンプトで以下のコマンドを入力してバージョン情報を確認します。
$ mongo > db.version();データベースの準備
- MongoDB shellでデータベースとコレクションの作成を行います。
$ mongo > use mydb switched to db mydb > db.createCollection('mylist'); { "ok" : 1 } > show collections mylist
- 以下のコマンドでデータベースに読んだ本の名前を登録します。
$ mongo > use mydb switched to db mydb > db.mylist.insert({name:'アジャイルサムライ'}); WriteResult({ "nInserted" : 1 }) > db.mylist.insert({name:'エクストリームプログラミング'}); WriteResult({ "nInserted" : 1 }) > db.mylist.insert({name:'テスト駆動開発'}); WriteResult({ "nInserted" : 1 }) > db.mylist.insert({name:'スクラム現場ガイド'}); WriteResult({ "nInserted" : 1 })クライアントサイドの作成
- 以下のコマンドでクライアントサイドとサーバーサイドをまとめるディレクトリ、クライアントサイドの作業ディレクトリを作成します。 ※質問形式でプロジェクトの作成を行う部分がありますが、Install vue-router?はyesとしてください。
$ mkdir my_project $ cd my_project $ vue init webpack frontend $ cd frontend $ npm run dev
- この状態で
http://localhost:8080
に接続すると、Welcome to Your Vue.js Appと出力された画面を確認できます。サーバーサイドの作成
- 以下のコマンドでサーバーサイドの作業ディレクトリを作成し、必要なモジュールをインストールします。
$ cd my_project $ mkdir backend $ cd backend $ npm init $ npm install --save express body-parser cors mongoose
- 作成されたpackage.jsonのscriptsの部分を以下のように編集します。
package.json{ "name": "backend", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js" }, "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", "mongoose": "^5.9.27" } }APIの作成
- backendにindex.jsを作成し、データベースに登録した本の名前を取得するメソッドを書きます。 ※connectOptionはmongooseでデータベースに接続する際に出る警告を避けるため書いています。
backend/index.jsconst express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const mongoose = require("mongoose"); const BooksList = require("./models/module"); const connectOption = { useNewUrlParser: true, useUnifiedTopology: true, }; mongoose.connect("mongodb://localhost/mydb", connectOption); const app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(cors()); app.get("/mydb", (req, res) => { BooksList.find(function (err, result) { if (!err) { return res.json(result); } else { return res.status(500).send("post mylist faild"); } }); }); app.listen(process.env.PORT || 3000);
- modelsフォルダとmodule.jsを作成し、データベースのスキーマ設定をします。
models/module.jsconst mongoose = require("mongoose"); const Schema = mongoose.Schema; const BooksListSchema = Schema({ name: String }, { collection: "mylist" }); module.exports = mongoose.model("BooksList", BooksListSchema);
- コマンドプロンプトでnpm startとコマンドを入力し、データベースとAPIの確認をします。
$ cd backend $ npm startAPI通信部分と画面の作成
API通信部分の作成
- frontendにaxiosをインストールします。
$ cd frontend $ npm install --save axios
- frontendにapiフォルダを作成します。以下のようにindex.jsとmethods.jsを書き、通信部分を作ります。
api/index.jsimport axios from "axios"; export default () => { return axios.create({ baseURL: `http://localhost:3000/` }); };api/methods.jsimport Api from "./index"; export default { getBooksData() { return Api().get("/mydb"); } };画面の作成
- src/componentsフォルダにBooksList.vueを作成します。デフォルトで作られていたHelloWorld.vueは削除しました。
src/components/BooksList.vue<template> <div class="bookslist"> <h1 class="title">読んだ本リスト</h1> <div class="books"> <li v-for="book in books" :key="book.index">{{book.name}}</li> </div> </div> </template> <script> import Methods from "@/api/methods"; export default { name: "BooksList", data() { return { books: "" }; }, created() { this.getData(); }, methods: { async getData() { let books_data = await Methods.getBooksData(); this.books = books_data.data; }, }, }; </script> <style> .bookslist { max-width: 300px; margin: 0 auto; text-align: center; font-family: Century; } .books { display: inline-block; text-align: left; } .title { color: #5b5b5b; background: linear-gradient(transparent 70%, #6088c6 70%); } </style>
- frontend/router/index.jsに先ほど削除したHelloWorld.vueがコンポーネントとして登録されているので、BooksList.vueに書き換えます。
frontend/router/index.jsimport Vue from "vue"; import Router from "vue-router"; import BooksList from "../components/BooksList"; Vue.use(Router); export default new Router({ routes: [ { path: "/", name: "BooksList", component: BooksList } ] });
- 画面上にデータベースに登録した本の名前が出ているか確認します。
- backendとfrontendでそれぞれ
npm start
とコマンド入力をします。$ cd backend $ npm start$ cd frontend $ npm startさいごに
今回はMongoDBに直接データを入力し、画面上に表示するアプリケーションを作成しました。
今後は画面上からデータを入力できるようにする機能や、本のカテゴリー別の表を作って表示するなどの機能も追加していきたいと思っています。参考
- 投稿日:2020-08-09T18:02:46+09:00
vue-youtubeを使用して、複数のモーダル上で動画を再生させる
実装結果
git映像
実装サイトURL
https://miwa-vue-youtube.netlify.app/
実装コード
- ディレクトリ
src ├── App.vue ├── assets │ └── scss │ └── main.scss ├── components │ ├── MovieButton.vue │ └── MovieModal.vue ├── main.js └── plugins └── vue-youtube.js
- バージョン
"@vue/cli": "4.4.6", "vue": "^2.6.11", "vue-youtube": "^1.4.0"
コードの内容を見る
- src/App.vue
<template> <div id="app"> <movie-button :btn-num="1" :movie-id="movieData[0]" @modal-open="modalOpen($event)" /> <movie-button :btn-num="2" :movie-id="movieData[1]" @modal-open="modalOpen($event)" /> <movie-button :btn-num="3" :movie-id="movieData[2]" @modal-open="modalOpen($event)" /> <movie-modal :modal-content="modalContent" :is-open="isOpen" @modal-close="modalClose()" /> </div> </template> <script> import MovieButton from "./components/MovieButton"; import MovieModal from "./components/MovieModal"; export default { name: "App", components: { MovieButton, MovieModal }, data() { return { isOpen: false, modalContent: {}, movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"] }; }, methods: { modalOpen(event) { console.log(event); this.modalContent = event; this.isOpen = true; }, modalClose() { this.isOpen = false; } } }; </script> <style lang="scss"> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
- src/components/MovieButton.vue
<template> <transition name="fade" mode="out-in"> <div class="modal" v-if="isOpen"> <div class="modal__overlay" @click="onClick()"></div> <div class="modal__body"> <p>モーダル</p> <div class="youtube__wrapper"> <youtube :video-id="modalContent.movieId" ref="youtube"></youtube> </div> <button type="button" @click="onClick()">ボタン閉じる</button> </div> </div> </transition> </template> <script> export default { props: { modalContent: { type: Object }, isOpen: { type: Boolean } }, methods: { onClick() { this.$emit("modal-close"); } } }; </script> <style lang="scss" scoped> .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; } .modal__overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #0008; } .modal__body { position: relative; top: 50%; right: 0; bottom: 0; left: 0; max-width: 1000px; margin: auto; background: #fff; transform: translateY(-50%); } .fade-enter-active, .fade-leave-active { transition: opacity 0.3s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
- src/components/MovieModal.vue
<template> <button type="button" @click="onClick()">動画{{ btnNum }}</button> </template> <script> export default { props: { btnNum: { default: 0, type: Number }, movieId: { type: String } }, methods: { onClick() { this.$emit("modal-open", { movieId: this.movieId }); } } }; </script> <style lang="scss" scoped></style>
- src/plugins/vue-youtube.js
import Vue from "vue"; import VueYoutube from "vue-youtube"; Vue.use(VueYoutube);
- src/main.js
import Vue from "vue"; import App from "./App.vue"; import "./plugins/vue-youtube.js"; Vue.config.productionTip = false; require("@/assets/scss/main.scss"); new Vue({ render: h => h(App) }).$mount("#app");
- src/assets/main.scss
.youtube__wrapper { position: relative; width: 100%; margin: 0 auto; height: 0; padding-bottom: 56.25%; overflow: hidden; background: #aaa; iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } }実装手順
1. vue-youtubeインストール
$ npm install vue-youtube // or yarn add vue-youtube2. vue-youtube記載
- src/plugins/vue-youtube.js
import Vue from "vue"; import VueYoutube from "vue-youtube"; Vue.use(VueYoutube);
- src/main.js
import "./plugins/vue-youtube.js";※Nuxt.jsの場合、pluginsに記載
export default{ plugins: ["~plugins/vue-youtube.js"] }参考リンク
3. ボタン追加
- src/components/MovieButton.vue
<template> <button type="button" @click="onClick()">動画{{ btnNum }}</button> </template> <script> export default { props: { btnNum: { default: 0, type: Number }, movieId: { type: String } }, methods: { onClick() { this.$emit("modal-open", { movieId: this.movieId }); } } }; </script> <style lang="scss" scoped></style>
- src/App.vue
<template> <div id="app"> <movie-button :btn-num="1" :movie-id="movieData[0]" @modal-open="modalOpen($event)" /> <movie-button :btn-num="2" :movie-id="movieData[1]" @modal-open="modalOpen($event)" /> <movie-button :btn-num="3" :movie-id="movieData[2]" @modal-open="modalOpen($event)" /> </div> </template> <script> import MovieButton from "./components/MovieButton"; export default { components: { MovieButton }, data() { return { isOpen: false, modalContent: {}, movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"] }; }, methods: { modalOpen(event) { this.modalContent = event; this.isOpen = true; } } }; </script>
movie-button
について
btn-num
ボタンの番号を識別させるためのもの(今回の実装では必要なし)movie-id
動画のidで動画の内容を識別させるためのもの(管理しやすくするため、data内のmovieDataに登録)@modal-open="modalOpen($event)"
子コンポーネントのクリックイベントを検知して、実行(モーダルを開くためのもの)参考
4. モーダル実装
- src/components/MovieModal.vue
<template> <transition name="fade" mode="out-in"> <div class="modal" v-if="isOpen"> <div class="modal__overlay" @click="onClick()"></div> <div class="modal__body"> <p>モーダル</p> <div class="youtube__wrapper"> <youtube :video-id="modalContent.movieId" ref="youtube"></youtube> </div> <button type="button" @click="onClick()">ボタン閉じる</button> </div> </div> </transition> </template> <script> export default { props: { modalContent: { type: Object }, isOpen: { type: Boolean } }, methods: { onClick() { this.$emit("modal-close"); } } }; </script> <style lang="scss" scoped> .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; } .modal__overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #0008; } .modal__body { position: relative; top: 50%; right: 0; bottom: 0; left: 0; max-width: 1000px; margin: auto; background: #fff; transform: translateY(-50%); } .fade-enter-active, .fade-leave-active { transition: opacity 0.3s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
- src/App.vue
<template> <div id="app"> <movie-modal :modal-content="modalContent" :is-open="isOpen" @modal-close="modalClose()" /> </div> </template> <script> import MovieModal from "./components/MovieModal"; export default { name: "App", components: { MovieModal }, data() { return { isOpen: false, modalContent: {}, movieData: ["r8bECyGsw6Q", "2MqjzMeD3Uo", "4Vsi174LRgg"] }; }, methods: { modalClose() { this.isOpen = false; } } }; </script>
- src/assets/main.scss
.youtube__wrapper { position: relative; width: 100%; margin: 0 auto; height: 0; padding-bottom: 56.25%; overflow: hidden; background: #aaa; iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } }
movie-modal
について
modal-content
モーダルに送る情報(今回はmovieId
のみ)is-open
モーダルが開いているか判定させるもの@modal-close="modalClose()"
子コンポーネントのクリックイベントを検知して、実行(モーダルを閉じるためのもの)参考リンク
+α
1. モーダルを開いたときに自動で再生を行う
PCのみ自動再生を行いたい場合
autoplay = 1
を追加<template> <youtube :video-id="modalContent.movieId" ref="youtube" :player-vars="playerVars" ></youtube> </template> <script> export default { data() { return { playerVars: { autoplay: 1 } }; }, } </script>参考リンク
autoplay = 1
のみだとSPなどのデバイスで対応することができないため、SPでも対応させたい場合、無音でインライン再生に変更を行うことで自動再生を行うことができる。<template> <youtube :video-id="modalContent.movieId" ref="youtube" :player-vars="playerVars" @ready="ready" ></youtube> </template> <script> export default { data() { return { playerVars: { playsinline: 1 } }; }, methods: { async fetchYoutube() { await (this.isOpen = ture); this.$refs.youtube.fetchData(); }, ready() { const youtubePlayer = this.$refs.youtube.player youtubePlayer.mute() youtubePlayer.playVideo() } } }; </script>参考リンク
- events - vue-youtube - npm
- iframe 組み込みの YouTube Player API リファレンス | YouTube IFrame Player API
- パラメータ一覧 - iframe 組み込みの YouTube Player API リファレンス
- スマホ(iOS)でも動画を自動再生させよう!(iFrame Player API、videoタグ対応) | 株式会社 エヴォワークス -EVOWORX-
2. 字幕を自動で追加を行う
cc_lang_pref = 1
に設定を行うことで字幕を追加できるexport default { data() { return { playerVars: { cc_lang_pref: 1 // cc_lang_prefを1に行う } }; }, }参考リンク
詰まった部分について
1. youtubeのスタイルが一部ずれる
- vueコンポーネント内に
.youtube__wrapper { position: relative; width: 100%; margin: 0 auto; height: 0; padding-bottom: 56.25%; overflow: hidden; background: #aaa; iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } }のような記載を行うと
iframe
内のコンポーネントにスタイルが当たらないため以下のような状態になる。
また、スタイルを当てていないとモーダルを閉じる時、動画の高さがなくなり、以下のような状態になる。
なので、グローバルcssの場所に以下を記載
.youtube__wrapper { position: relative; width: 100%; margin: 0 auto; height: 0; padding-bottom: 56.25%; overflow: hidden; background: #aaa; iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } }参考リンク
2. v-ifを用いると
this.$refs
が取得できない
v-if
を用いるとコンポーネントが描画されていない状態でthis.$refsの内容を取得しようとするので、取得できないエラーになる。
なので、async/await
を用いることになり、コンポーネント描画 → $refs取得を行うことができるようにするasync fetchYoutube() { await (this.isOpen = ture); // isOpenフラグがtrueになったら this.$refs.youtube.fetchData(); // this.$refs.youtubeの取得を行う },参考リンク
3. モーダルが上下中央によらない
.modal__body { position: absolute; top: 0; right: 0; bottom: 0; left: 0; max-width: 1000px; margin: auto; background: #fff; }のような状態で配置した場合、
のような状態で、高さが取得されないため、
position: relative
とtop: 50%
などで変更を行う.modal__body { position: relative; /* relativeに変更 */ top: 50%; /* 50%に変更 */ right: 0; bottom: 0; left: 0; max-width: 1000px; margin: auto; background: #fff; transform: translateY(-50%); /* -50%を追加 */ }
- 投稿日:2020-08-09T16:24:53+09:00
Vue-CLI 3でnpm run serveが失敗するときの解決方法
事象
Dockerコンテナ内の環境にて、
npm run serve
を実行すると、下記のようなエラーが発生していました。ERROR Failed to compile with 2 errors This relative module was not found: * ./src/main.js in multi (webpack)-dev-server/client/index.js (webpack)/hot/dev-server.js ./src/main.js, multi (webpack)/hot/dev-server.js (webpack)-dev-server/client/index.js ./src/main.js環境
- Microsoft Windows 10 Pro
- Docker for Windows
- Visual Studio Code
- node.js : 12.18.2
- npm : 6.14.5
原因
筆者の場合、
npm install
を実行してもpackage.json
内に含まれているdevDependencies
がインストールされておらず、必要なライブラリ(ここでは@vue/cli-service
)が存在していなかったというのが原因でした。解決策
node.js側の環境変数である
NODE_ENV
をdevelopment
(あるいはdev
)にしてからnpm install
を実行する必要がありました。
NODE_ENV
を設定する方法は、①.コマンドラインからexport
する方法と、docker-composeを使っている場合は②.docker-compose.xml
に記載する方法があります。①.
export
コマンドで設定する下記のコマンドで
NODE_ENV
に環境値を設定できます。# NODE_ENVを設定 $ export NODE_ENV=development # 設定した値を確認する $ echo $NODE_ENV development②.
docker-compose.xml
のenvironmentキーに設定するdocker-composeでコンテナを立ち上げている場合、
environment
キーを使って設定することができます。version: '3' services: web: build: . ports: - "8080:8080" environment: - NODE_ENV=development tty: true
export
で設定したときと同様に、$NODE_ENV
をechoすると設定値を確認すると、development
が設定されているのが確認できるはずです。問題の原因を特定するには?
今回の場合、
package.json
に記載されているパッケージが正しくインストールできていると早とちりし、「パスが通っていない」か「windowsでシンボリックリンクが使えないのが悪さしている」と思い込んでいたのが、原因の特定が遅れてしまった原因でした。ちなみに、パッケージが正しくインストールされているかは、
npm list --depth=0
で確認することができます。# インストールされているパッケージの一覧(一階層のみ)を表示 $ npm list --depth=0 project@0.1.0 /app +-- core-js@3.6.5 `-- vue@2.6.11 # core-js と vue しか入ってないやん!もし、筆者と同様に
npm run serve
が正しく動作しない場合は、このコマンドで確認してみるといいかもしれません。
- 投稿日:2020-08-09T11:08:01+09:00
[vue]css-loaderを4.*にしたらesModule optionを切ろう
SSIA
私がパワプロ2020で遊んでる間にcss-loaderのv4.0.0がリリースされました。
そうとは知らずいつも通り1からプロジェクトを組んだ結果、vueコンポーネントに書いたsytleがあたらないという現象にぶち当たりました
どうして…(ここで猫になる)SSIAは「タイトルで言うこと全部言ってますよ」という意味です
ではここでcss-loaderのv4.0.0のリリースノートを見てみましょう
- minimum required Node.js version is 10.13.0
- minimum required webpack version is 4.27.0
- the esModule option is true by default
- default value of the sourceMap option depends on the devtool option
......
はい
- the esModule option is true by default
- the esModule option is true by default
- the esModule option is true by default
- the esModule option is true by default
- the esModule option is true by default
ここが原因です
切りましょうこれに気づくのに一晩かかりました
webpack.config.js{ // 前略 module: { rules: [ { test: /\.vue$/, loader: "vue-loader", options: { hotReload: true, }, }, { test: /\.css$/i, use: [ "vue-style-loader", { loader: "css-loader", // ココ options: { esModule: false, }, }, ], }, ], }, // 後略 }参考
あったらこんなに苦労しなかったのに…強いて言うなら公式です
リリースから日が浅いか自力でwebpack書いてる人が少ないからか騒いでる人は見当たりませんでした未解決事項
- これはvueを使う場合に限った問題だろうか?
- css-loaderのesModuleを切らずにvueや
/^vue(-style)?-loader$/
側をesModuleに対応させることで解決できないだろうか?
- loaderの読み込み順からして対応させるならvue-style-loaderと推察されるがこちらにはesModuleスタイルに切り替えるオプションは見当たりませんでした
- vue-loaderはv14時点で強制的にesModuleスタイルになっています