作成日:2021/04/12 更新日:2023/03/03

#1 Vue.jsをインストールして動作確認

vue.jsについて

Vue.jsビュー ジェイエスとはJavaScriptで作られたフレームワークで、 ページ遷移せんい(ページを移動すること)などを、 ページ全体をリロードせずに変えたい部分の一部だけを切り替えて動的に表示させる機能を持っており、 S P Aシングル ページ アプリケーションと呼ばれています。
ページの内容がすぐに変わることや、マイクロインタラクション(動き)の実装ができ、 ユーザーの動きに合わせたストレスのないサイトの制作が実現できるため、 UI、UX*1に対して人気のフレームワークです。
* 1) ユーザーにとって使い勝手が良く、操作ストレスが少ないサイトを作ることを「UI/UX設計」「UI/UXデザイン」と呼びます。

vue-cliをインストールする

開発環境
OSMacOS Catalina 10.15.7
node16.13.0
npm6.14.8
vue-cli4.5.15
vue2.6.12
vue-cliはvue.jsのテンプレートを、コマンドラインよりインストールできます。ターミナルを利用します。

HomebrewとNodebrew(node.js)はインストール済みであることが前提ですすめます。
まだの方はこちら

$ brew update
$ nodebrew install-binary stable
Homebrewをアップデートしておきます。必要であればNodebrew(node.js)のバージョンも上げておきます。


$ npm i -g @vue/cli
npmでvue/cliとvue/cli-servie-globalをグローバルにインストールします。
i = installインストール  -g = globalグローバル の略です。 vue本体は、stgにある各プロジェクトフォルダ(ローカル)ではなく、グローバル( /Users/ユーザー名/.nodebrew/v16.13.0/lib/node_modules/ )にインストールします。

$ npm list -g
↓
/Users/ユーザー名/.nodebrew/node/v16.13.0/lib
├── @vue/cli@4.5.15
├── corepack@0.10.0
└── npm@8.1.0

$ npm list -g vue
↓
/Users/ユーザー名/.nodebrew/node/v16.13.0/lib
└─┬ @vue/cli@4.5.15
  └── vue@2.6.14
nodeがインストールされたディレクトリが表示されます。


$ vue -V
インストールされたvueのバージョンを確認してみましょう。
@vue/cli 4.5.15
このような表示が出ればOKです。

vue.jsのアップデートやその他コマンド

▼必要なときに見てみましょう

##npmアップデート
$ npm -v
$ npm i -g npm   //グローバル
$ npm update npm //ローカル

##vueアップデート
$ npm update -g @vue/cli


#パッケージバージョン確認
$ npm list --depth=0
$ npm info パッケージ名 version     //インストール済みパッケージのバージョン
$ npm info パッケージ名 versions    //全バージョン確認
$ npm update パッケージ名@バージョン //例)vue@2.6.13


#パッケージアップデート
$ npm outdated        //最新版が存在するパッケージを表示
  //↓表示が出たら
$ npm install -g npm-check-updates //バージョンアップパッケージをインストール
$ ncu -u      //package.jsonをアップデート
$ npm i       //アップデートインストール
  //↓うまく行かないときは...
$ npm i --save --legacy-peer-deps `パッケージ名`

#モジュール削除と再インストール
$ rm -rf node_modules package-lock.json && npm i
  ↓これを1行にしたもの
  $ rm -r node_modules
  $ rm package-lock.json
  $ npm i
作業を進めていく中で必要なアップデートやモジュールのアップデート、再インストール方法です。

ERESOLVEについて

npm ERR! code ERESOLVE                              ##error solve(エラー解決)
npm ERR! ERESOLVE unable to resolve dependency tree ##出来なかった...
npm ERR! 
npm ERR! While resolving: 02-move-comp2@0.1.0       ##プロジェクトフォルダ内で
npm ERR! Found: webpack@4.46.0                      ##このパッケージを見つけたけど
npm ERR! node_modules/webpack
npm ERR!   webpack@"^4.46.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer webpack@"^5.0.0" from sass-loader@11.1.1  ##sass-loader11.1.1はwebpack@5以上で動くよ
・・・
こんな感じのエラーもたまに出くわします。
このケースではwebpackのバージョンを上げられなかったので、sass-loaderのバージョンを下げることで対応しました。

パッケージの脆弱性について

$ npm audit                 //レポート出力
$ npm audit fix --force     //脆弱性を自動修正

//それでも以下のようなエラーが出た...
                      === npm audit security report ===                        
┌──────────────────────────────────────────────────────────────────────────────┐
│                                Manual Review                                 │
│            Some vulnerabilities require your attention to resolve            │
│                                                                              │
│         Visit https://go.npm.me/audit-guide for additional guidance          │
└──────────────────────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate      │ Regular expression denial of service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ glob-parent                                                  │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in    │ >=5.1.2                                                      │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ @vue/cli-plugin-babel [dev]                                  │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ @vue/cli-plugin-babel > webpack > watchpack >                │
│               │ watchpack-chokidar2 > chokidar > glob-parent                 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1751                            │
└───────────────┴──────────────────────────────────────────────────────────────┘
・・・
それでもエラーが出る場合は、一つ一つ潰していきます。
上の例ではglob-parentというパッケージが5.1.2より小さいとダメだよという警告です。

$ npm i glob-parent@6.0.1
か
$ npm i --legacy-peer-deps glob-parent@6.0.1  ##依存関係を無視してインストール
上記のように、指定されたバージョンをインストールします(package.jsonに記載される)。
それでも解消しない場合は、
package-lock.json内でglob-parentが出てくる箇所を探して、低いバージョンになっているものを 現在のインストールされている(npm info パッケージ名 versionで確認できる)、 指定以上のバージョンの数字に書き換えます。package-lock.jsonを保存して もう一度npm auditすればエラーが無くなっています。

もしくは
$ npm ls glob-parent
これでファイルの関係を詳しく調べましょう。
不明な場合は一緒に出てくるURLに飛べば、解決方法も記載されています。英文ですが翻訳プラグインを使って頑張って読み解きましょう。

開発用フォルダに移動し、vueプロジェクトを作成する


$ cd ~/job/stg/vue/
例ではjob/stgフォルダ内にvueフォルダを作りました。vueのプロジェクトはここに入れていきます。

$ vue create project-name
vueコマンドでvueプロジェクトを作成します。project-nameは適当な名前をつけましょう。 ここでは01-vueとしました。

Vue3が2020年9月にリリースされましたが、まだ日本語のマニュアルがありません。学習するために今回はVue2を選択しましょう。 カーソルキーで下矢印を押して、エンターキーで決定します。

・
・
・
65 packages are looking for funding
  run `npm fund` for details

⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project 01-vue.
👉  Get started with the following commands:

 $ cd 01-vue
 $ npm run serve
インストールが完了するとこのような表示になります。下から2行に次のコマンドが表示されます。

$ cd 01-vue
まずこのコマンドを実行して、作成したプロジェクトフォルダに移動します。

$ npm run serve
このコマンドを入力してエンターを押すとサーバーが起動しはじめます。 $ npm run serve -- --port 8081 とオプションを付けてポートを変更することもできます

INFO  Starting development server...  //開発サーバーを起動しています。。。
98% after emitting CopyPlugin

 DONE  Compiled successfully in 2622ms  //成功しました!


  App running at:
  - Local:   http://localhost:8080/       //このアドレスをブラウザで表示してね
  - Network: http://192.168.10.***:8080/

  Note that the development build is not optimized.  //開発ビルドは最適化されていないことに注意してください。
  To create a production build, run npm run build.   //本番ビルドを作成するには、npm run buildを実行します。
このような表示が流れたあとwebサーバが起動しブラウザで表示できるようになります。※MAMPなどは不要です。
表示にあるようにhttp://localhost:8080/にアクセスしてみましょう。
Ctr+cでサーバを終了させることができます。

フォルダの内容を確認

インストール直後のディレクトリ/ファイル構成です。

├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── node_modules
├── public
│    ├── favicon.ico
│    └── index.html
└── src
     ├── App.vue
     ├── assets
     │    └── logo.png
     ├── components
     │    └── HelloWorld.vue
     └── main.js
各ファイルの詳しい内容
package.json

/* package.jsonの一部 */
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-template-compiler": "^2.6.11"
  },
ライブラリの管理をするためのファイル。インストールしたものが追記される。
node_modules

800以上のライブラリが最初にインストールされています。追加されたモジュールもここに入ります。 必要に応じてモジュールを追加していきます。
public
ファビコンなど公開用の静的なファイルをおいておきます。
src
vueアプリケーションのコードをおいておく場所です。
main.js、App.vue
main.js = main.jsはページが表示されて最初に実行されるjsファイルです。いろんな初期設定を記載しておきます。
App.vue = ページを表示させるためのファイルです。
src/assets
jsや画像、scssファイルをおいておく場所です。asset = 資産
src/components
vueのコンポーネントファイルをおく場所です。

ライブラリのインストール

サイトを作るために最低限必要な、ルーティングに関するライブラリとSCSSを扱えるようにするためのライブラリをインストールしておきます。

$ npm i -D ライブラリ名
インストールコマンドです。
iinstallインストールの略。 逆はuninstallアン インストール(削除)です。
-Dはpackage.jsonのdevDependencies:デブ ディペンデンシーに記載され、 省くとdependencies:ディペンデンシー(和訳:依存関係)にライブラリが記載されます。
開発・制作のみで使うものはdevDependencies:に記載します。
たまに

$ npm i --save ライブラリ名
--saveいった記述を見かけます。dependenciesで登録するためですが、 npmのv5より-Dがなければ自動でdependenciesに登録されるため、このや--save--save-devコマンドは必要ありません。

vue-routerをインストールしてみる


$ npm i vue-router@3
このコマンドを実行してインストールしてみましょう。
vue-routerの最新はv4になりますが、今回のvueはv2なのでvue-routerはv3をインストールします。 ※サーバを起動しているときはCntorolキー + Cでサーバを停止して、コマンドラインツールでコマンドを実行します。

package.jsonの"dependencies": {} 内に

"dependencies": {
  "core-js": "^3.6.5",
  "vue": "^2.6.11",
  "vue-router": "^3.5.3"
},
"vue-router": "^3.5.3"が追記されていることが確認できます。

vue-routerの設定

router/index.jsを作成します
テキストエディタで新規ファイルを作り、以下の内容を記述します。
srcディレクトリ内にrouterディレクトリを作成し、その中にindex.jsという名前で保存します。

import Vue from 'vue'
import VueRouter from 'vue-router'

import News from '@/components/PostNews'    /*後ほど作成するファイルです*/
import About from '@/components/PostAbout'  /*後ほど作成するファイルです*/

Vue.use(VueRouter)

const routes = [
  { path: '/news', component: News },
  { path: '/about', component: About }
]
const router = new VueRouter({
  mode: 'history',
  routes
})
export default router
▼解説

import VueRouter from 'vue-router'
vue-routerを読み込みます


import News from '@/components/PostNews.vue'
import About from '@/components/PostAbout.vue'
作ったテンプレートPostNews.vueとPostAbout.vueを使うので読み込む設定をします。
読み込んだファイルに名前をつけます。(PostNews.vueはNewsという名前に)
※@はsrcディレクトリのエイリアスになります。


Vue.use(VueRouter)
useコマンドでvue-routerを使えるようにします。


const routes = [
  { path: '/news', component: News },    /* どのURLで(path)、どのコンポーネントを読み込むか(component) */
  { path: '/about', component: About }
]
const router = new VueRouter({
  mode: 'history',  /* ヒストリーモードに変更(URLに'#'がつかない)*/
  routes            /* `routes: routes` の短縮表記 */
})
export default router
ルーティングの設定


export default new VueRouter({
  routes: [
    { 
      path: '/news',
      component: News
    },
    {
      path: '/about',
      component: About
    }
  ]
})
ルーティングの設定は上記のように書いてある例もあります。
一度変数`router`に入れているか、直接書くかの違いです。
src/main.jsに2ヶ所追記します

import Vue from 'vue'
import App from './App.vue'
import router from './router'  /* ←追加 */

Vue.config.productionTip = false

new Vue({
  router,      /* ←追加 */
  render: h => h(App),
}).$mount('#app')
router/index.jsをインポートさせて、設定します。

テスト用にcomponentsフォルダに2つのコンポーネントファイル作成

router/index.jsにも指定した、テスト用ファイルの PostNews.vuePostAbout.vueをcomponentsディレクトリ内に作成します。
ファイル名が大文字で始まることに注意してください。

// PostNews.vue
<template>
  <div class="News">これはNewsの内容</div>
</template>

// PostAbout.vue
<template>
  <div class="about">これはAboutの内容</div>
</template>

テスト用にApp.vueに追記・編集


// App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <!-- ここから追記 -->
    <header>
      <ul class="nav">
        <li><router-link to="/">Home</router-link></li>
        <li><router-link to="/news">News</router-link></li>
        <li><router-link to="/about">About</router-link></li>
     </ul>
    </header>
    <router-view/>
    <!-- ここまで追記 -->
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#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;
}
/*追加*/
.nav li{
  display: inline-block;
  margin:10px;
}
</style>
App.vueはページの大枠を構成するファイルです。この範囲の中でデータを描画させます。

vueのコンポーネントファイルは <template>テンプレート<script>スクリプトとと <style>スタイルとの3つのタグを記述します。 templateに1ヶ所、styleに1ヶ所追加してみましょう。

<div id="app">内で<router-link to="/">というvue独自のタグを使ってリンクさせています。 テスト用で作ったコンポーネントを読み込んで、クリックしたときに、表示やURLがどのように変わるかみてみましょう。
ターミナルで$ npm run serveして、ブラウザで確認します。

<script>タグ内で、HelloWorld.vueはimportされていますが、 PostAbout.vueや、PostNews.vueはimport指示がされてません。ではどうして表示されたのでしょうか?
About.vueや、News.vueはrouter/index.js側で読み込まれており、ルーター機能により、<template>タグ内の <router-view/>タグの箇所に描画されるという仕組みになっています。

ここまでのファイル構成

├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
└── src
    ├── App.vue
    ├── assets
    │   └── logo.png
    ├── components
    │   ├── HelloWorld.vue
    │   ├── PostAbout.vue
    │   └── PostNews.vue
    ├── main.js
    └── router
        └── index.js

liリンクをクリックするとコンテンツ内容が変わり、URLも変わります。

生成されたHTMLはこのようになります。

#2 静的なページをvue.jsを使って構築する

過去に作ったデータをvue.js化してみよう

基礎1 #7 レスポンシブコーディングでサイト制作① で作成したレスポンシブデザインをscss化したデータを使って、 同じものをvue.jsで作ってみましょう。
先程、#1で作ったvueプロジェクト01-vueを編集していきます。
手順としては、まずいくつかのコンポーネント(部品)vueファイルを作成して、jsファイルでルーティングの設定をしていきます。※サーバは停止しておいてください。

最初に部品としてのコンポーネント(部品)を読み込む、土台としてのHome.vueを作ります。 これがURLに対応する1ページ単位となります。https://hoge.com/news/にはNews.vue。https://hoge.com/about/にはAbout.vueというイメージです。

このファイルはcomponentsディレクトリではなくviewsディレクトリに配置します。
srcディレクトリ内にviewsディレクトリを作成します。

├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── node_modules
├── public
│   ├── favicon.ico
│   └── index.html
└── src
    ├── App.vue
    ├── assets
    │   ├── images
    │   │   ├── 画像
    │   └── scss
    │       ├── _variables.scss
    │       └── main.scss
    ├── components
    │   ├── MyFooter.vue
    │   ├── MyHeader.vue
    │   ├── base.vue
    │   ├── MainMenu.vue
    │   ├── MainVision.vue
    │   └── Navi.vue
    ├── main.js
    ├── router
    │   └── index.js
    └── views
        └── Home.vue
完成したときのファイル配置

Home.vueの作成


// Home.vue
<template>
  <div class="home">
    <MyHeader/>
    <Navi/>
    <Base/>
    <MyFooter/>
  </div>
</template>

<script>
import MyHeader from '@/components/MyHeader.vue'
import Navi from '@/components/Navi.vue'
import Base from '@/components/base.vue'
import MyFooter from '@/components/MyFooter.vue'

export default {
  name: 'Home',
  components: {
    MyHeader,
    Navi,
    Base,
    MyFooter,
  }
}
</script>

<style lang="scss" scoped>
.home {
  background: #fff;
}
</style>
template
描画するコンポーネント(部品)をタグの形式で配置させます。必ずdivなどのセレクタで囲います。
script
描画するvueファイルをインポートし、
export defaultでこのtemplate内で使えるようにコンポーネントを登録
style
lang="scss"で、scss形式を記述できます。
scopedは、このコンポーネント内(ローカル)でしか適応させたくない場合に指定します。 その場合、必ず.homeなどのclassかidで指定します。

コンポーネント(部品)ファイルの作成

以前作った基礎1 #7 レスポンシブコーディングでサイト制作① のhtmlやscssファイルを必要なセクションごとに切り出していきます。1枚のhtmlやscssファイルを、 headerやfooterやボタンなど特定の場所ごとに切り分けていくイメージです。

<template>タグの内容にはhtml部分を、<style>タグの内容はscssファイルから、それぞれ必要なセレクタを切り出してコピーしていきます。
これらのファイルはcomponentsディレクトリに入れていきます。

以前は1枚のhtmlやscssファイルにheaderからfooterまですべてのセレクタを記述していましたが、 vueではコンポーネント(部品)ごとに、そのなかで登場するセレクタだけを記述していきます。
該当するscssを変更したいときにはscssファイルではなく、コンポーネントファイルに記述してあるstyleを編集できるので管理が容易になります。

また、存在するhtmlタグ名(main, nav, articleなど)はコンポーネント名として使えません。 なるべく2単語でPascalCaseでファイル名も作成しましょう。(※今回は割愛...)
MyHeader.vue

//MyHeader.vue
<template>
  <header>
    <div class="header__contents">
      <div class="header__contents--text text-right">
        Professional Skills in<br>the Engineering Curriculum.
      </div>
      <div class="header__contents--logo">
        <p class="header__contents--logo-mark"><img src="../assets/images/logo.png" alt="CBC logo"></p>
        <p><img src="../assets/images/logo-bagde.png" alt="CRI BOOT CAMP"></p>
      </div>
      <div class="header__contents--text">
        プロフェッショナルになるための<br>“挫折しない”トレーニングメソッド
      </div>
    </div>
    <div class="header__image">
      <img src="../assets/images/badge.png">
    </div>
  </header>
</template>

<script>
export default {
  name: 'MyHeader'
}
</script>

<style lang="scss" scoped>
header {
  border-top:20px solid $mainColor;
  /* widthをpx指定している部分がレスポンシブ化のポイントになります */
  .header__contents{
    width:1200px;
    margin:43px auto 88px;
    display:flex;
    justify-content:center;
    @include mq('max','md'){
      display:block;
      margin-top:240px;
      position:relative;
    }
    @include mq('max','lg'){
      width:100%;
    }
    &--text{
      align-self:flex-end;
      margin:0 30px;
      width:25%;
      border-top:1px solid $mainColor;
      border-bottom:1px solid $mainColor;
      @include mq('max','md'){
        margin:0 auto;
        width:90%;
      }
    }
    .text-right{
      text-align:right;
      @include mq('max','md'){
        text-align:left;
        border-bottom:none;
      }
    }
    &--logo{
      text-align:center;
      @include mq('max','md'){
        position: absolute;
        top:-228px;
        left: 0;
        right: 0;
        margin: auto;
      }
      &-mark{
        margin-bottom:20px;
      }
    }

  }
  .header__image{
    position:relative;
    height:600px;
    background:$mainColor url('../assets/images/bg_header.jpg')no-repeat right center / cover;
    @include mq('max','md'){
      position:relative;
      height:40vh;
      background:$mainColor url('../assets/images/bg_header.jpg')no-repeat right center / cover;
    }
    img{
      position:absolute;
      top:-60px;
      right:5vw;
    }
  }
}
</style>
MyFooter.vue

//MyFooter.vue
<template>
  <footer>
  <div class="footer">
    <div class="footer__image"></div>
    <h2>Location & Contact</h2>

    <div class="footer__column">
      <div class="footer__column--form">
        <form action="send.php" method="POST">
          <p>ご予約やお問い合わせはこちらのフォームを<br>ご利用ください。個人情報保護方針について</p>
          お名前<br>
          <input type="text" name="inputName">
          メールアドレス<br>
          <input type="text" name="inputMail">
          問い合わせ内容<br>
          <textarea name="toiawase"></textarea>
          <input type="submit" value="送信">
        </form>
      </div>
      <div class="footer__column--map">
        <iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d810.4161765212042!2d139.66673032925627!3d35.660631688388975!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x6018f4ab6ef28945%3A0xed98990ae6e1597b!2z5qCq5byP5Lya56S-44Kv44Oq44Ko44Kk44OG44Kj44OW44Oq44K944O844K544Kk44Oz44K544OG44Kj44OB44Ol44O844OI!5e0!3m2!1sja!2sjp!4v1571382479874!5m2!1sja!2sjp" frameborder="0" style="border:0;" allowfullscreen=""></iframe>
        <div>
        〒000-0000 東京都世田谷区 0-0-0<br>
        TEL. 03-0000-0000<br>
        OPENING HOURS: 12:00 PM – 11:00 PM
        </div>
      </div>
    </div>
  </div>
  <div class="footer__copy">© 20XX Created by CBC</div>
  </footer>
</template>

<script>
export default {
  name: 'MyFooter'
}
</script>

<style lang="scss" scoped>
footer {
  text-align:center;
  .footer {
    color:#fff;
    padding:60px 0 70px;
    background-color:$mainColor;
    h2{
      font-size:50px;
      margin:30px auto 80px;
      @include mq('max','md'){
        font-size:2em;
      }
    }
    &__image{
      width:100%;
      height:600px;
      background:$mainColor url('../assets/images/bg_footer.jpg')no-repeat center center / cover;
      @include mq('max','md'){
        height:40vh;
      }
    }
    &__column{
      width:1200px;
      display:flex;
      margin:40px auto 100px;
      @include mq('max','md'){
        display:block;
      }
      @include mq('max','lg'){
        width:100%;
      }
      &--form{
        flex:1;
        width:60%;
        @include mq('max','md'){
          flex:1;
          width:100%;
        }
        form{
          width:60%;
          margin:10px auto 0;
          display:flex;
          flex-direction:column;
          text-align:left;
          @include mq('max','md'){
            width:80%;
          }
          p{
            font-size:0.9em;
            margin-bottom:15px;
          }
          input[type="text"]{
            margin: 0 0 20px 0;
            padding:5px 10px;
            font-size:1.2em;
            border-radius:5px;
            border:none;
            background-color:#fff; //リセット用のress.cssが効いて背景色になるので、白色を指定
          }
          textarea{
            margin: 0 0 20px 0;
            padding:10px;
            height:8em;
            font-size:1.3em;
            border-radius:5px;
            border:none;
            background-color:#fff; //リセット用のress.cssが効いて背景色になるので、白色を指定
          }
        }
      }
      &--map {
        flex:1;
        text-align:left;
        width:500px;
        height:450px;
        @include mq('max','lg'){
          margin:0 auto;
          width:97%;
        }
        iframe{
          width:500px;
          height:450px;
          @include mq('max','md'){
            width:90%;
            margin:50px auto 0;
            text-align:center;
          }
          @include mq('max','lg'){
            width:40vw;
            height:450px;
          }
        }
      }
    }
    &__copy{
      font-size:0.8em;
      padding:20px 0;
      color:#aaa;
    }
  }
}
</style>
Navi.vue

//Navi.vue
<template>
  <nav>
    <ul>
      <li><a href="">home</a></li>
      <li><a href="">beginner</a></li>
      <li><a href="">basic</a></li>
      <li><a href="">advanced</a></li>
      <li><a href="">design</a></li>
      <li><a href="">performance</a></li>
      <li><a href="">kids</a></li>
    </ul>
  </nav>
</template>

<script>
export default {
  name: 'Navi'
}
</script>

<style lang="scss" scoped>
nav {
  background-color:$mainColor;
  ul{
    width:1200px;
    margin:0 auto;
    display:flex;
    justify-content: space-around;
    @include mq('max','md'){
      flex-wrap:wrap;
    }
    @include mq('max','lg'){
      width:100%;
    }
    li{
      width:120px;
      height:60px;
      @include mq('max','md'){
        margin:6px 0;
      }
      a{
        display:flex;
        justify-content: center;
        align-items: center;
        height:100%;
        color:#fff;
        @include mq('max','md'){
          border:1px solid #fff;
        }
      }
    }
  }
}
</style>
base.vue

//base.vue
<template>
<main>
  <MainMenu/>
  <MainVision/>
</main>
</template>

<script>
import MainMenu from '@/components/MainMenu.vue'
import MainVision from '@/components/MainVision.vue'

export default {
  name: 'Menu',
  components: {
    MainMenu,
    MainVision
  }
}
</script>
MainMenu.vue

//MainMenu.vue
<template>
<section class="menu">
  <div class="menu__content">
    <div class="menu__content--text">
      <img src="../assets/images/title_cording.png" alt="cording">
      <h1>Title</h1>
      <p>ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。</p>
      <div class="btn"><a href="">MENU</a></div>
    </div>
    <div class="menu__content--image img-cording"></div>
  </div>
  <div class="menu__content">
    <div class="menu__content--image img-design"></div>
    <div class="menu__content--text">
      <img src="../assets/images/title_design.png" alt="design">
      <h1>Title</h1>
      <p>ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。</p>
      <div class="btn"><a href="">MENU</a></div>
    </div>
  </div>
</section>
</template>

<script>
export default {
  name: 'MainMenu'
}
</script>

<style lang="scss" scoped>
.menu {
  width:1200px;
  margin:0 auto;
  @include mq('max','md'){
    margin:30px auto 0;
  }
  @include mq('max','lg'){
    width:100%;
  }
  &__content{
    display:flex;
    text-align:center;
    @include mq('max','md'){
      display:block;
      position:relative;
    }
    &--text{
      flex:1;
      @include mq('max','md'){
        position: absolute;
        top:30px;
        left: 0;
        right: 0;
        margin: auto;
        z-index:99;
      }
      img{
        margin-top:80px;
        text-align:center;
      }
      h1{
        margin:20px 0 50px;
        font-size:1.3em;
      }
      p{
        width:65%;
        margin:0 auto 20px;
      }
    }
    &--image{
      flex:1;
      height:600px;
      @include mq('max','md'){
        position: relative;
        margin-bottom:2px;
        &::before{
          height:600px;
          background-color: rgba(0,0,0,0.6);
          position: absolute;
          top: 0;
          right: 0;
          bottom: 0;
          left: 0;
          content: ' ';
        }
      }
    }
    .img-cording{
      background:url('../assets/images/menu_cording.jpg')no-repeat center center / cover;
    }
    .img-design{
      background:url('../assets/images/menu_design.jpg')no-repeat center center / cover;
    }
  }
}
</style>
MainVision.vue

//MainVision.vue
<template>
<section class="vision">
  <div class="vision__image">
    <h1>ブラウザやテキストエディタ等ツールのインストールや、<br>html・CSSとはなにかを学び、<br>簡単なサイトを作りながらコーダーを目指します。</h1>
    <p><img src="../assets/images/be-pro.png" alt="Be Professional"></p>
  </div>
  <div class="vision__columun">
    <div class="vision__columun--item">
      <p class="vision__columun--item-img"><img src="../assets/images/contents_cording.png" alt="cording image"></p>
      <p>
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      </p>
    </div>
    <div class="vision__columun--item">
      <p class="vision__columun--item-img"><img src="../assets/images/contents_design.png" alt="design image"></p>
      <p>
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      </p>
    </div>
    <div class="vision__columun--item">
      <p class="vision__columun--item-img"><img src="../assets/images/contents_analytics.png" alt="analytics image"></p>
      <p>
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      ここはコンテンツの説明を入力していきます。魅力的な文章を追加して、ユーザーにコンテンツの内容を理科してもらいましょう。
      </p>
    </div>
  </div>
</section>
</template>

<script>
export default {
  name: 'MainVision'
}
</script>

<style lang="scss" scoped>
.vision {
  position: relative;
  @include mq('max','md'){
    margin-top:50px;
  }
  &__image{
    border-top: 60px solid $mainColor;
    height:600px;
    background:$mainColor url('../assets/images/bg_contents.jpg')no-repeat center center / cover;
    display:flex;
    justify-content: center;
    align-items: center;
    flex-direction:column;
    @include mq('max','md'){
      font:bold 1.6em/1.9em sans-serif;
    }
    /*背景画像に黒いカバーをかける方法*/
    &::before{
      height:600px;
      background-color: rgba(0,0,0,0.6);
      position: absolute;
      top: 60px;
      right: 0;
      bottom: 0;
      left: 0;
      content: ' ';
    }
    h1{
      font:bold 31px/2.3em sans-serif;
      text-align:center;
      color:#fff;
      z-index:99;
    }
    p{
      margin-top:75px;
      z-index:99;
    }
  }

  &__columun{
    width:1200px;
    margin:80px auto 150px;
    display:flex;
    justify-content: space-between;
    @include mq('max','md'){
      width:90%;
      margin:10px auto;
      display:block;
    }
    @include mq('max','lg'){
      width:97%;
    }
    &--item{
      width:30%;
      text-align:left;
      @include mq('max','md'){
        width:90%;
        margin-top:40px;
        text-align:left;
      }
      &-img{
        margin:0 0 50px;
        text-align:center;
        @include mq('max','md'){
          margin:0 0 10px;
      text-align:center;
        }
        img{
          height:200px;
        }
      }
    }
  }
}
</style>
※ここでは、コンポーネントBaseを土台として、さらにコンポーネントMainMenuMainVisionを読み込ませるように設計してみました。
※自分で過去に作成していたファイルを切り出す場合、画像のリンクパスが変更になります。templateとstyleの../images/../assets/images/に変更しましょう。

scssファイルの作成

scssファイルはassetsディレクトリにsassディレクトを作成しその中に入れていきます。
具体的なセレクタではない共通セレクタ(bodyやaタグなど)はmain.scssに記述します。

/* main.scss */
body {
  font:normal 16px/1.8em Arial,Helvetica,sans-serif;;
  color: $mainColor;
}
a {
  text-decoration: none;
}
.btn a,
input[type="submit"]{
  display:inline-block;
  margin:0 auto;
  padding:10px 0;
  width:130px;
  cursor:pointer;
  font-size:1.3em;
  color:#58847F;
  text-align:center;
  border: 10px solid #58847F;
  background-color:transparent;
}
また以前作成した、変数をまとめた_variables.scssはそのままassets/scssディレクトリに入れておきましょう。
※変数をまとめたstyleなどの、別のファイルに読み込ませることを目的としたscssファイル名にアンダーバーをつけてわかりやすようにします。
リセット系css

$ npm i ress
いままでreset.cssを作って読み込んでいましたが、それもコマンドラインツールでress.scssをインストールしましょう。 後ほどimport設定します。

scssファイルのインポート

変数をまとめた共通のscssを、各コンポーネントに毎回importするのは大変なので、importなしで使えるようにします。

@use 'variables' as v;    /* ← 全ファイルにこれを書くのは保守性が悪い... */
body {
  color: v.$Color;
  ・・・
他のscssファイル、例えばmain.scssなどで_variables.scssで使おうとするとこのようにimportさせますが、 すべてのscssファイルでわざわざimportせずに使えるようにするには、ルートにvue.config.jsを作成し、 以下のように記述します。srcディレクトリと同じ場所に配置します。

// vue.config.js

module.exports = {
  css: {
    loaderOptions: {
      scss: {
        additionalData: `@import "@/assets/scss/_variables.scss";`
      }
    }
  }
};
module.exports = { }でwebpackローダーの拡張設定をしています。
sass-loader v9.0以上は、prependDataは、additionalDataに変更されました。

scssファイルのコンパイル

このままではsassをコンパイル(css形式に変換)できません。Webpackを使ってコンパイルさせるライブラリをインストールします。

$ npm install -D sass-loader sass
sass-loaderとsass(dart-sass)をインストールします。2021年9月時点では以下のバージョンです。
"sass": "^1.41.1",
"sass-loader": "^12.1.0",

Node.js v16などでインストール時にエラーが出る場合は、は低いバージョンを指定してみましょう。

$ npm install -D sass-loader@10.2.0 sass
これでインストールできると思います。

imagesフォルダをコピー

以前作成したサイトのimagesフォルダを、src/assets/ディレクトリの中にコピーします。

main.jsの編集

main.jsはページが表示されて最初に実行されるjsファイルです。

import Vue from 'vue'
import App from './App.vue'
import router from './router'

import 'ress';                     /* npm経由でインストールしたress.cssをインポート */
import '@/assets/scss/main.scss'   /* コンポーネントに取り込んでいないscssファイル */

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
styleファイルを読み込むための記述をします。

ルーティングの設定(router/index.js)


import Vue from 'vue'
import VueRouter from 'vue-router'

import Home from '@/views/Home.vue'
Vue.use(VueRouter)

const routes = [
  { path: '/', component: Home }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
export default router
4行目でHome.vueをインポートします。


const routes = [
  { path: '/', component: Home }
]
path:'' でURLを指定します。path: '/news' といった感じになります。また、viewsの中に作ったvueコンポーネントファイルを指定します。

const routes = [
  { path: '/', component: Home },
  { path: '/news', component: News },
  { path: '/about', component: About }
]
複数指定するとこのようになります。

base: process.env.BASE_URL,
ルーティングに関わる設定です。base: 〜でベーストラルURLの設定。
process.env.XXXというデータで、このアプリの`環境に関するデータ(環境変数)`が取得できます。

App.vueの編集

App.vueを編集します。

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>
<template>内の<router-view/>のヶ所で、router/index.jsで記述したコンポーネント (URLが'/'の場合、Home.vue)がルーティング機能により表示されます。

完成品の確認とサーバ公開

ブラウザで確認


$ npm run serve
http://localhost:8080/
ブラウザを確認して、画面が表示されていればOKです。

エラーが出る場合はエラー内容を確認しながら、以下の内容を確認してみましょう。
  • ・imagesフォルダ含め、ファイルが全て揃っているか
  • ・sass-loaderがnpm経由でインストールされているか
  • ・存在しないコンポーネントを読み込んではいないか
  • ・ファイルのパスが間違っていないか
  • ・ターミナルを再起動してコマンド入力してみる

webサーバーで公開するためビルドする

ローカルの開発環境での表示が確認できたところで、レンタルサーバやAWSなどの公開サーバにアップロードしてみましょう。
Ctr+cでサーバを停止し、ビルドコマンドを実行します。

$ npm run build
Building for production...という表示のあと、 ルートディレクトリにdistフォルダが作成されたはずです。
distはdistribution(配布する)の略です。

├── css
│   ├── app.db43c444.css
│   └── chunk-vendors.501fb1dc.css
├── favicon.ico
├── img
│   └── 画像
├── index.html
└── js
    ├── app.8019827c.js
    ├── app.8019827c.js.map            //ソースマップ
    ├── chunk-vendors.fc849a72.js
    └── chunk-vendors.fc849a72.js.map  //ソースマップ
このような内容になっています。
詳しい説明は省きますが、このフォルダの中身をサーバーのルートディレクトリに入れて、ブラウザで確認すると同じ表示になっていることが確認できます。

公開用ディレクトリが違う場合などのオプションはvue.config.jsに記述します。

/* vue.config.js */

const path = require('path')

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? '/test/' : '/',

  productionSourceMap: process.env.NODE_ENV === 'production' ? false : true,

  devServer: {
    port: 8082,
    https: false
  },

  css: {
    loaderOptions: {
      scss: {
        additionalData: `@import "@/assets/scss/_variables.scss";`
      }
    }
  }
}
publicPathでディレクトリを指定します。

  publicPath: './',
①ルートの場合


  publicPath: '/test/',
②testディレクトリだった場合


publicPath: process.env.NODE_ENV === 'production'
? '/test/'
: '/',
③Webpackで公開用(グローバル)か開発用(ローカル)かのモードを判断させてディレクトリを変える場合
productionは公開用のことです。
/test/とした場合は、サーバのルートにtestディレクトリを作成し、distフォルダの中身をすべてアップロードします。 その後https://xxxx.com/test/にアクセスします。

■そのほか

productionSourceMap: process.env.NODE_ENV === 'production' ? false : true,
公開用にソースマップが不要な場合。process.env.NODE_ENVというデータに、公開用か、開発用かの情報が入ってます。

devServer: {
  port: 8082,
  https: false
},
開発用のポートを変えたい場合は、ここで指定できます。 下のようにオプションでポートを指定することもできます。

$ npm run serve -- --port 8082