Quasar v1メモ

# [Quasar (v1)](https://github.com/quasarframework/quasar)

vueのUIというような位置づけで紹介されているところが多いが、プロジェクト作成時に基本的なフォルダ構成も作成してくれてVueRouterやVuexが使える構造になっており、SPAやSSRを指定してビルドする機能があるのでUIというよりははじめから豊富なコンポーネントを持っているNuxtのような位置づけだと思った。

## [ロードマップ](https://quasar.dev/start/roadmap)

## [ドキュメント](https://quasar.dev/introduction-to-quasar)

見やすいが、アクセスできなくなってる事が多い気がするのが辛すぎる

## [Vueコンポーネント](https://quasar.dev/vue-components)
## [Vueディレクティブ](https://quasar.dev/vue-directives)

奇麗で豊富なので楽しい。

### 個人的に気になったコンポーネントとディレクティブ

* [carousel](https://quasar.dev/vue-components/carousel)
* [drawer](https://quasar.dev/layout/drawer)
* [dialog](https://quasar.dev/vue-components/dialog)　フルスクリーン
* [time](https://quasar.dev/vue-components/time)
* [date](https://quasar.dev/vue-components/date)
* [infinite-scroll](https://quasar.dev/vue-components/infinite-scroll)
* [knob](https://quasar.dev/vue-components/knob)　ドラッグで変更できる
* [parallax](https://quasar.dev/vue-components/parallax)
* [popup-edit](https://quasar.dev/vue-components/popup-edit)
* [splitter](https://quasar.dev/vue-components/splitter)　左右の幅をドラッグ
* [stepper](https://quasar.dev/vue-components/stepper)
* [tab-panels](https://quasar.dev/vue-components/tab-panels)
* [timeline](https://quasar.dev/vue-components/timeline)
* [tree](https://quasar.dev/vue-components/tree)
* [virtual-scroll](https://quasar.dev/vue-components/virtual-scroll)
* [mutation](https://quasar.dev/vue-directives/mutation) redo undo

## [プロジェクト作成方法](https://quasar.dev/start/pick-quasar-flavour)

[Quasar CLI Installation](https://quasar.dev/quasar-cli/installation)

### 基本的な構造

| Path  | メモ | リンク | 
| ---- | ---- | ---- |
| quasar.conf.js | ビルド設定等 | [quasar-conf-js](https://quasar.dev/quasar-cli/quasar-conf-js) |
| src/index.template.html | html、head、bodyタグが記載された一番外枠のファイル（configの[htmlVariables](https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-htmlVariables)で変数を使用したりしている) | |
| src/App.vue | | |
| src/boot | Vueがインスタンス化される前に実行 | [boot-files](https://quasar.dev/quasar-cli/cli-documentation/boot-files) |
| src/store | ここにstoreを記述（Vuex） | [vuex-store](https://quasar.dev/quasar-cli/cli-documentation/vuex-store) |
| src/router/routes.js | ここにルーティングを記述（VueRouter） | [routing](https://quasar.dev/quasar-cli/cli-documentation/routing) |
| src/components | 自作コンポーネント等（使わないでpagesのみで作るのでもOK） | |
| src/layouts | 画面用コンポーネント（pagesを読み込む用） | |
| src/pages | 画面用コンポーネント | |
| src/assets | vueファイルからの相対パス./でアクセス可能なもの | [App Handling Assets](https://quasar.dev/quasar-cli/cli-documentation/handling-assets) |
| src/statics | ビルド時にdistのstaticsにデプロイされる | [App Handling Assets](https://quasar.dev/quasar-cli/cli-documentation/handling-assets) |
| src/css | プロジェクト作成時に設定した形式（Pick your favorite CSS preprocessor）のcssファイルが入る（SCSSやSASS等） | |


### プロジェクト作成後の整備（とりあえずこれを設定しておけば使えるようになりそうというもの）

#### historyモードに変更

```js:quasar.conf.js
vueRouterMode: 'history',
```

#### テーマカラーを変更

```sass:src/css/quasar.variables.sass
$primary   : #027BE3
$secondary : #26A69A
$accent    : #9C27B0

$positive  : #21BA45
$negative  : #C10015
$info      : #31CCEC
$warning   : #F2C037
```

#### FontAwesomeを使えるようにする

```diff:quasar.conf.js
    // https://github.com/quasarframework/quasar/tree/dev/extras
    extras: [
      // 'ionicons-v4',
      // 'mdi-v4',
-     // 'fontawesome-v5',
+     'fontawesome-v5',
      // 'eva-icons',
      // 'themify',
      // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!

      'roboto-font', // optional, you are not bound to it
      'material-icons' // optional, you are not bound to it
    ],
```

```html:FontAwesomeで使用するアイコンのclassをそのまま設定することで使用可能
<q-icon name="fas fa-question-circle" />
```

#### axiosを設定する

```js:src/boot/axios.js
import Vue from 'vue';
import axios from 'axios';

const axiosInstance = axios.create({
  baseURL: 'http://example.com',
  'Content-Type': 'application/json',
  Accept: 'application/json'
});

// *.vueからはthis.$axiosで呼び出せます
Vue.prototype.$axios = axiosInstance;

export { axiosInstance };
```

```js:quasar.conf.js
boot: [
  'axios'
],
```

#### VueRouterにナビゲーションガードを設定してログイン判定等を行う

```js:src/boot/auth.js
import { axiosInstance } from 'boot/axios';

export default ({ router, store, Vue }) => {
  router.beforeEach((to, from, next) => {
    to.matched.some(async (record) => {
      // https://router.vuejs.org/ja/guide/advanced/meta.html
      if (!record.meta.requiresAuth) {
        // ログイン不要
        next();
      } else {
        // ログイン必要
        let postData = new FormData();
        postData.append('name', 'value');

        await axiosInstance.post('endpoint', postData)
          .then(response => {
            next();
          })
          .catch(error => {
            if (error.response.status === 401) {
              next({ name: 'login' });
            } else {
              next({ name: 'home' });
            }
          })
          .finally(() => {
            // finally
          });
      }
    });
  });
};
```

```js:quasar.conf.js
boot: [
  'axios',
  'auth'
],
```

#### lodash等のツールを使えるようにする

```js:src/boot/lodash.js
import _ from 'lodash';

export default ({ app, router, Vue }) => {
  window._ = _;
};
```

```js:quasar.conf.js
boot: [
  'axios',
  'auth',
  'lodash'
],
```

#### mixinを使う

```js:src/mixins/api.js
import { axiosInstance } from 'boot/axios';

export default {
  methods: {
    post: (url, data) => {
      // return Promise
      return axiosInstance.post(url, data);
    }
  }
};
```

```vue:src/pages/XXXXXX.vue
<template>
  <q-page class="flex flex-center">
    <img @click="callMixin" alt="Quasar logo" src="~assets/quasar-logo-full.svg">
  </q-page>
</template>

<script>
import Api from '../mixins/api';

export default {
  name: 'XXXXXX',
  mixins: [Api],

  methods: {
    async callMixin () {
      let response = await this.post('endpoint', {});
    }
  }
};
</script>
```

#### Vuexを使う

**exampleストアのモジュールを作る**

```js:src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';

import example from './example';

Vue.use(Vuex);

/*
 * If not building with SSR mode, you can
 * directly export the Store instantiation
 */

// *.vueからはthis.$storeで呼び出せます
// how to access state: this.$store.state.example.XXXXXX
// how to access getter: this.$store.getters.example.someGetter
// how to call mutation: this.$store.commit('example/someMutation', val)
// how to call action: this.$store.dispatch('example/someAction', val)

export default function (/* { ssrContext } */) {
  const Store = new Vuex.Store({
    modules: {
      example
    },

    // enable strict mode (adds overhead!)
    // for dev mode only
    strict: process.env.DEV
  });

  return Store;
}
```

```js:src/store/example/index.js
import state from './state';
import * as getters from './getters';
import * as mutations from './mutations';
import * as actions from './actions';

export default {
  namespaced: true,
  getters,
  mutations,
  actions,
  state
};
```

#### Actions

```js:src/store/example/actions.js
// action
// trriger: store.dispatch('someAction')
// action method is Includes asynchronous processing

export function someAction (context) {
//   context.commit('someMutation'); // call mutation
//   context.dispatch('someAction'); // call action
//   context.state.XXXXXX; // access store.state
//   context.getters.someGetter; // access store.getter
}
```

#### Getters

```js:src/store/example/getters.js
// getter
// trriger: store.getters.someGetter
// Accessible as Computed Properties

export function someGetter (state) {
//   return state.XXXXXX + state.XXXXXX;
}
```

#### Mutations

```js:src/store/example/mutations.js
// mutation
// trriger store.commit('methodName')
// Mutation methods must be synchronous

export function someMutation (state) {
//  state.example = 'example';
}
```

#### State

```js:src/store/example/state.js
export default {
  example: ''
};
```