更新日 2021.10.14

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

vue.jsについて

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

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

開発環境
OSMacOS Catalina 10.15.7
vue-cli4.5.9
vue2.6.12
npm6.14.8
node14.15.0
webpack4.44.2
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.6.0/lib/node_modules/ )にインストールします。

$ npm list -g
↓
/Users/ユーザー名/.nodebrew/node/v16.6.0/lib
└── npm@7.19.1

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


$ vue -V
インストールされたvueのバージョンを確認してみましょう。
@vue/cli 4.5.9
このような表示が出れば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.239:8080/

  Note that the development build is not optimized.
  To create a production build, run 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 xxx
--saveいった記述を見かけます。dependenciesで登録するためですが、 npmのv5より-Dがなければ自動でdependenciesに登録されるため、このや--save--save-devコマンドは必要ありません。

vue-router


$ npm i vue-router
package.jsonの"dependencies": {} 内に"vue-router": "^3.*.*"が追記されていることが確認できます。 srcディレクトリにrouterディレクトを作成し、その中にindex.jsを作成して、以下の内容を記述します。
router/index.jsを作成します

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

import News from '@/components/News.vue'
import About from '@/components/About.vue'

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/News.vue'
import About from '@/components/About.vue'
テスト用でNews.vueとAbout.vueを使うので読み込む設定をします。@はsrcディレクトリのエイリアスになります。

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

const routes = [
  { path: '/news', component: News },    /* どのURLで、どのコンポーネントを読み込むか */
  { 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
    }
  ]
})
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にも指定した、テスト用ファイルのNews.vueAbout.vueをcomponentsディレクトリ内に作成します。
ファイル名が大文字で始まることに注意してください。

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

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






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されていますが、 About.vueや、News.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
    │   ├── About.vue
    │   ├── HelloWorld.vue
    │   └── News.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なしで使えるようにします。

@import "./_variables.scss";  /* ← 全ファイルにこれを書くのは保守性が悪い... */
body {
  color: $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
コピーしました
RSS https://cbc-study.com/rss.xml 
質問などあればSlackで! 誰でも無料でできます
cbc-study.slack.com