145
136

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue2のディレクトリ構成を考える

Last updated at Posted at 2017-03-01

はじめに

Vue2が良さそうなので、実装しながらディレクトリ構成について考えてみたいと思います。
Vuexも使います。

環境構築

Vue.jsではvue-cliが用意されており、最初の開発環境が簡単に作れます。
手っ取り早く動く環境を作りたいときなど試してみると良いです。

npm install -g vue-cli
vue init webpack my-project
cd my-project
npm install
npm run dev

またvue-cliは[ Webpack / Browserify ]の両方に対応されており、以下の4つのテンプレートが用意されています。

  • browserify
  • browserify-simple
  • webpack
  • webpack-simple

vue-cliはWebpackの2系に移行済みです!

ディレクトリ構成

こちらが現在のディレクトリ構成になります。
規模が大きくなってきても耐えれる構成にしたいということを意識して考えました。

├── assets
├── dist
├── gulp
├── gulpfile.js
├── package.json
├── src
│   ├── App.vue
│   ├── components
│   │   └── Sample
│   │       ├── Sample.vue
│   │       ├── style.css
│   │       └── template.html
│   │   └── globals
│   │       └── Header
│   │           ├── Header.vue
│   │           ├── header.html
│   │           └── header.scss
│   ├── constants
│   │   └── constant.js
│   ├── index.html
│   ├── index.js
│   ├── pages
│   │   └── RouteSample
│   │       ├── RouteSample.vue
│   │       ├── style.css
│   │       └── template.html
│   ├── routes.js
│   ├── utils
│   └── vuex
│       ├── actions
│       │   └── sample.js
│       ├── getters
│       │   └── sample.js
│       ├── modules
│       │   └── sample.js
│       ├── store.js
│       └── types.js
├── test
│   └── unit
│       ├── coverage
│       ├── karma.conf.js
│       └── specs
│           ├── components
│           │   └── Sample.spec.js
│           └── vuex
│               ├── actions
│               │   └── sample.spec.js
│               ├── getters
│               │   └── sample.spec.js
│               └── modules
│                   └── sample.spec.js
└── yarn.lock

assets

静的ファイル

dist

公開ディレクトリ

gulp

Gulpタスク及び、Webpack関連

App.vue

vue-routerのrouter-viewを記載します。
ルーティング外のコンポーネントもこちらに記載します。

App.vue
<template>
  <div id="app">
    <header-view></header-view>
    <router-view></router-view>
  </div>
</template>
<script>
import HeaderView from './components/globals/Header/Header'

export default {
  components: {
    HeaderView
  }
}
</script>

components

こちらには親コンポーネントからprops経由で値を受け取り、表示するコンポーネントを配置します。
直接Vuexにアクセスしないように作る想定です。
通常はHTML, JS, CSSを1ファイルでvueファイルとして記述しますが、規模が大きい画面だと
vueファイルが膨大になるのを避けたかったので、それぞれファイルを分けてsrcでインポートしています。
つまり以下のような書き方です。

Sample.vue
<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>

このようにすることで、HTML, JS, CSSを分割して書くことができます。

※scriptを分けるとWebpackのeslint-loaderが効いてくれず、現状はvueファイル内にscriptを書いてます。

Sample.vue
<template src="./template.html"></template>
<style src="./style.css"></style>
<script>
export default {
  props: {
    word: {
      type: String,
      required: true
    }
  }
}
</script>

globals

こちらにはルーティング外のコンポーネントを置きます。
ヘッダーやフッターなどが該当します。

constants

定数を定義

index.html

公開HTML

index.js

エントリポイントとなるJSです。
ここでやっていることは
・vue-routerを初期化する
・vuexのstoreをセットする
です。

index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
import store from './vuex/store'
import App from './App'

Vue.use(VueRouter)

const router = new VueRouter({
  mode: 'history',
  base: __dirname,
  routes
})

/* eslint-disable no-new */
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
/* eslint-enable no-new */

pages

ルーティングに対応するコンポーネントをここに置くようにします。
ここにあるコンポーネントが親コンポーネントとなり、getters経由でstateを取得し、
components配下の子コンポーネントにprops経由でstateを渡すようにしています。

RouteSample.vue
<template src="./template.html"></template>
<style src="./style.css"></style>
<script>
import { mapActions, mapGetters } from 'vuex'
import { CLICK } from '../../vuex/types'
import Sample from '../../components/Sample/Sample'

export default {
  methods: {
    ...mapActions({
      click: CLICK
    })
  },
  computed: {
    ...mapGetters([
      'word'
    ])
  },
  components: {
    Sample
  }
}
</script>
template.html
<sample
  :click="click"
  :word="word">
</sample>

routes.js

ルーティングが増えたらこちらに記載します。

route.js
const RouteSample = require('./pages/RouteSample/RouteSample')

export default [
  {
    path: '/sample',
    component: RouteSample
  }
]

utils

ユーティリティー

actions

機能ごとにアクションを分けています。
公式ではアクションは1ファイルに書くことになっていますが、
規模が大きくなると1ファイルに書くのは厳しいと判断しました。

sample.js
import { CLICK, OTHER_CLICK } from '../types'

export default {
  [CLICK] ({ dispatch, commit }, param) {
    commit(CLICK, param) // mutationsを呼ぶ
    dispatch(OTHER_CLICK, param) // 他のアクションを呼ぶ
  }
}

getters

アクションに合わせてこちらも機能ごとに分けています。
ただし、gettersに定義する変数は全体で重複があってはならない為、注意が必要です。
つまり以下のような書き方です。

sample.js
export default {
  word: state => {
    return state.word
  }
}
sample2.js
export default {
  word: state => {
    return state.keyword
  }
}

これをビルドして動作させると、以下のようなエラーが出ます。
[vuex] duplicate getter key: word

※gettersを1ファイルにしてしまうのもありだと思います。

modules

Vuexのactions, getters, state, mutationsをまとめてexportしています。
gettersと違いstateは機能ごとに持てる為、機能内で重複しなければ大丈夫です。

sample.js
export default {
  state: {
    word: 'before'
  },
  mutations: {
    [CLICK] (state, payload) {
      state.word = payload.word
    }
  }
}
sample2.js
export default {
  state: {
    word: false
  },
  mutations: {
    [OTHER_CLICK] (state, payload) {
      state.word = payload.word
    }
  }
}

上記のような書き方をしても、以下のようにstateを持ってくれます。

state {
  sample {
    word: 'before'
  },
  sample2 {
    word: false
  }
}

store.js

modulesを読み込みstoreを作ります。
ここでstoreを作る際にstrict: trueを設定しています。(厳密モード)
これを設定しておくと、stateの予期せぬ変更(ミューテーションハンドラの外部で変更)に対して、エラーを投げてくれます。
ただし、ステージングや本番環境ではパフォーマンス劣化の原因となるので、falseを設定してください。

store.js
import Vue from 'vue'
import Vuex from 'vuex'
import sample from './modules/sample'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'staging'

export default new Vuex.Store({
  modules: {
    sample
  },
  strict: debug
})

types.js

mutation-typeを定義しています。全体で一意の値を設定します。

test

上述したvue-cliの中にも単体テストとE2Eテストが組み込まれているので、参考になります。
vue-cliは以下で構築されています。
karma + mocha + chai + phantomJS
・カバレッジ計測にistanbul
・スタブJSにsinon

その他のライブラリを検討するならば、
Jasmine(もしくはJest
power-assert
辺りも検証してもいいのではないでしょうか。

ESLint

Vue.jsのLintはeslint-plugin-vue,eslint-config-vueを使いました。
一旦、これでやってみようと思ってます。

.eslintrc
"root": true,
"parser": "babel-eslint",
"parserOptions": {
  "sourceType": "module"
},
"extends": "vue",
"plugins": [
  "html"
]

さいごに

Vue.jsを少し書いてみて、全体的にシンプルでライブラリも使いやすくて良いと感じました!
後、日本語ドキュメントも豊富です。
vue-devtoolsもいい感じでした。

145
136
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
145
136

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?