20200809のvue.jsに関する記事は11件です。

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.7

Javaのインストール

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 Testing

Routerが未選択状態なので、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.vue

AWS Amplify ハンズオン 認証認可機能の追加 - AWS 怠惰なプログラマ向けお手軽アプリ開発手法 2019 に従い、追記する。

$ amplify serve

ローカルの画面から「Create account」でアカウントを作って認証の動作確認を実施。メールアドレスによる認証コードの機能もこれで利用可能に。

サインアウトボタンの表示

$ vim src/App.vue

AWS 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 detected

Hostingが追加される。

$ 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 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
```

「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? Yes

design環境を反映する。

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に向けてプルリクエストを作成すると、自動的に環境が構築される。

環境はこれで準備完了です。

開発をしていきましょう。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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なので変更したらすぐ反映される便利。
image01.png
こんな画面が表示されます。

今回他に入れるライブラリを追加

// 今回必要なライブラリとか入れる(随時追加)
yarn add csv-parse 
yarn add vue-chartjs chart.js

csv-parseはcsvを扱うためのライブラリで、vue-chart.jsはグラフを扱うライブラリです。
今回データソースをCSVにしたのは使ってるネット証券会社から、自分の投資しているものをCSV形式でダウンロードできるのでそれを使おうかなーと思ったためです。
gridsomeは色んなデータソースを使えるので(markdown・jsonやAPIなど)、他の形式使いたい方はこちらを参照
https://gridsome.org/docs/fetching-data/

ページの追加とメニューの変更

新しい静的なページなど追加するにはpagesディレクトリに追加していきます。
image02.png
お次は共通の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サイトの閲覧が非常に高速になります。

image03.png
こんな感じでメニューとか変更しました。あとは中身を作ってかないと。

あとがき

やっぱhot reload便利。
そして調べてるうちにlive reloadとhot reloadの違いを初めて知った。。

ライブリロードは、ファイルが変更されたときにアプリケーション全体をリロードまたはリフレッシュします。たとえば、ナビゲーションに4つのリンクがあり、変更を保存した場合、ライブの再読み込みによってアプリが再起動され、アプリが最初のルートに戻されます。
ホットリロードは、アプリケーションの状態を失うことなく変更されたファイルのみをリフレッシュします。たとえば、ナビゲーションに4つのリンクがあり、一部のスタイリングに変更を保存した場合、状態は変更されませんが、新しいスタイルはページ上に表示されます。同じページにいてください。

あと全然関係ない話ですが、半沢直樹は面白いけど顔芸が濃すぎて話しが入ってこなくなると思った日曜日でした。
あと一週間なのでGridsomeをどんどん学んでいこう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:vue

Vue.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!"と表示されていれば、
導入と読み込みは上手くいっています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

情報ソース

実際に触ってみる

今回はreactiverefについて見ていきます。

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すると以下のように表示されます。
FireShot Capture 013 - vue3-test - localhost.png

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プラスされてます。

Screenshot from 2020-08-09 18-08-5.png

では、refで宣言したものを再度reactiveで使ったらどうなるでしょうか?
以下のコードで試してみます。

App.vue
const count = ref(0)
const state = reactive({
  count
})

console.log(state.count)

state.count = 1
console.log(count.value) 

変更してコンソールを見てみると、このようにcountに代入されています。
参照渡しされているっぽいですね。

Screenshot from 2020-08-09 18-08-5.png

refreactiveの違いは?

refreactiveはどちらもリアクティブなデータを実現するという点では一緒です。
公式のページを見てみると、

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) とほぼ同じです。リアクティブを使ってオブジェクトを作成するだけで、それだけです。
しかし、リアクティブのみにする場合の問題は、合成関数の消費者がリアクティブを保持するためには、返されたオブジェクトへの参照を常に保持していなければならないということです。オブジェクトを破壊したり、拡散したりすることはできません。

まだまだ理解ができていないので、これからもっと調べて試していきたいと思います。

おわりに

自分自身、調べた結果を忘れないために書いたので、間違いなどがあると思います。
もし「ここ違うんじゃ?」などあったら、教えていただけると幸いです。

 参考にした記事や引用元

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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 buildnuxt 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 buildnuxt generateの出力の違いについて紹介します。
なお、Nuxtは2.14.1を利用します。

NuxtにおけるSSR・SSG・SPAの設定方法について

Nuxtではnuxt.config.jsmodeプロパティtargetプロパティによってアプリケーションのモードが管理されています。
modeにはuniversalspatargetにはserverstaticの設定値が用意されています。

modespaにするとSPAになります。
modeuniversalにした場合、targetstaticにすればSSG、targetserverにすればSSRになります。

Nuxtアプリケーションのモードの初期設定は、新規作成時の対話の回答によって決定されます。
? Rendering mode:modeの設定値に関する質問、? Deployment target:targetの設定値に関する質問です。

image.png
image.png

検証に利用するサンプルアプリケーションについて

SSR・SSG・SPAにおけるnuxt buildnuxt generateの挙動を確認するにあたり、今回はNuxt公式ドキュメントで紹介されているCustom Routesを利用します。

Custom Routesはユーザー一覧画面と各ユーザーの詳細画面が用意されているシンプルなアプリケーションです。

スクリーンショット 2020-08-09 17.11.51.png

スクリーンショット 2020-08-09 17.11.56.png

各パターンにおける実行結果の違いについて

2パターンのmodeuniversalspa)と2パターンのtargetserverstatic)を組み合わせた、計4パターンでnuxt buildnuxt 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)やってます。フォローしてもらえるとうれしいです!

参考記事


  1. SSGは『静的化』や『静的ファイル生成』などとも呼ばれることがあります。 

  2. NUXTJS『コマンド一覧』 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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
                |
                |--Dockerfile
Dockerfile
FROM 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 300
docler-compose.yml
version: "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.yml
    command:>
      cd front    #追記
      npm run dev

サーバー起動

$ docker-compose up

#エラー出力

違うエラー....

解決法

そうか、じゃあ最初から起動する場所を指定すればいいじゃん。
ということで、

Dockerfile
FROM 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

嬉しみいいいいい(起動した。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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が呼び出されると、反転したshowshowに代入されます。
ちなみに、thisというのは、jsファイルのappを指しています。

ここでの処理内容は、ボタンをクリックするとtoggleBtnの処理が呼び出されて、showデータのtruefalseが切り替わる仕組みになっています。この切り替えによって、名前の表示/非表示ができるようになるというわけです。

おわりに

はじめてQiitaに記事を書いたのですが、こんなに簡単な処理内容でも文字にして説明するとなると全然スムーズにいかず、かなり頭を使いました。人に教えることに慣れていないので、これからもっと練習して人に教えるスキルとプログラミングスキルを向上させていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 -v

MongoDBのインストール

  • 今回はローカル環境でアプリケーションが動くことを確認したいので、まず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.js
const 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.js
const 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 start
  • この時点でhttp://localhost:3000/mydbに接続するとデータベースに登録した本の名前が出ています。 image.png

API通信部分と画面の作成

API通信部分の作成

  • frontendにaxiosをインストールします。
$ cd frontend
$ npm install --save axios
  • frontendにapiフォルダを作成します。以下のようにindex.jsとmethods.jsを書き、通信部分を作ります。
api/index.js
import axios from "axios";

export default () => {
  return axios.create({
    baseURL: `http://localhost:3000/`
  });
};
api/methods.js
import 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.js
import 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
  • http://localhost:8080/に接続すると、読んだ本の名前がリストになっています。 image.png

さいごに

今回はMongoDBに直接データを入力し、画面上に表示するアプリケーションを作成しました。
今後は画面上からデータを入力できるようにする機能や、本のカテゴリー別の表を作って表示するなどの機能も追加していきたいと思っています。

参考

vueとexpressでSPAを作る
MongoDB超入門
Node.jsからMongoDBに接続してみる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-youtubeを使用して、複数のモーダル上で動画を再生させる

実装結果

git映像

動画.gif

実装サイト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インストール

vue-youtube - npm

$ npm install vue-youtube // or yarn add vue-youtube

2. 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>

参考リンク

2. 字幕を自動で追加を行う

cc_lang_pref = 1に設定を行うことで字幕を追加できる

スクリーンショット 2020-08-09 17.52.34.png

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内のコンポーネントにスタイルが当たらないため以下のような状態になる。
スクリーンショット 2020-08-09 16.05.30.png

また、スタイルを当てていないとモーダルを閉じる時、動画の高さがなくなり、以下のような状態になる。

スクリーンショット 2020-08-09 16.06.07.png

なので、グローバル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;
}

のような状態で配置した場合、

スクリーンショット 2020-08-09 17.42.06.png

のような状態で、高さが取得されないため、 position: relativetop: 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%を追加 */
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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_ENVdevelopment(あるいは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が正しく動作しない場合は、このコマンドで確認してみるといいかもしれません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[vue]css-loaderを4.*にしたらesModule optionを切ろう

SSIA

私がパワプロ2020で遊んでる間にcss-loaderのv4.0.0がリリースされました。
そうとは知らずいつも通り1からプロジェクトを組んだ結果、vueコンポーネントに書いたsytleがあたらないという現象にぶち当たりました
無題.png
どうして…(ここで猫になる)

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スタイルに切り替えるオプションは見当たりませんでした:thinking:
    • vue-loaderはv14時点で強制的にesModuleスタイルになっています
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む