この記事では npx create-nuxt-app
で作成してTypeScript環境を導入したNuxt.jsプロジェクトにCoreUIを組み込む。
なお、Nuxt.jsにCoreUIを組み込んだ非公式のテンプレートが存在するけれど、今回この非公式テンプレートも参考にしながらすべて手動でCoreUIを組み込みこんだ結果わかったのは、この非公式テンプレのどこにCoreUIの要素があるのか全然わからんということだった。非公式テンプレのことは最後にまたすこし書く。
公式テンプレート
https://github.com/coreui/coreui-free-vue-admin-template
↑このリポジトリから取得できるものを、以下、公式テンプレートと呼ぶことにする。基本的にはこれを参考にしながら、同じことをNuxtプロジェクトで実現するにはどうすればよいかを考えていく。
とりあえずインストール
まずは次の2つのモジュールをインストールする。
npm i @coreui/vue @coreui/coreui
これらをインストールしろという指示は、coreui-vueのREADMEにある。ついでに、公式テンプレートのpackage.jsonを覗いて依存関係を見てみると、次のような感じ。
"dependencies": {
"@coreui/coreui": "~3.0.0",
"@coreui/icons": "~1.0.1",
"@coreui/utils": "~1.2.2",
"@coreui/vue": "~3.0.0",
"@coreui/vue-chartjs": "~1.0.3",
"vue": "~2.6.11",
"vue-router": "~3.1.5",
"vuex": "~3.1.2"
}
インストール手順に記載のない @coreui/utils
と @coreui/icons
は必須ではなさそう。でもiconsのほうは、名前のとおりCoreUIの独自アイコンが収められており、それらを利用するためにこの記事のあとの方でまたすこし触れる。
CoreUIコンポーネントを使ってみる
CoreUIのコンポーネントをひとつ使ってみよう。公式テンプレートの src/views/base/Cards.vue
を参考に、自分のプロジェクトの pages/index.vue
に次のような要素を加えてみた。
<div>
<CCard>
<CCardHeader>
カードのタイトル
<div class="card-header-actions">
<a
href="https://coreui.io/vue/docs/components/card-components"
class="card-header-action"
rel="noreferrer noopener"
target="_blank">
<small class="text-muted">docs</small>
</a>
</div>
</CCardHeader>
<CCardBody>
カードの内容
</CCardBody>
</CCard>
</div>
でも、CCardというコンポーネントがVueに登録されるような処理をどこにも書いていないので、このままでは当然エラーがでる。
[Vue warn]: Unknown custom element: <CCard> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
これを解決するには、CoreUIをVueにプラグインとして登録すればよい。以下では、それをNuxt.jsプラグインとして実現する。
次の内容で plugins/coreui.ts
というファイルを作成し、
import Vue from 'vue'
import CoreUI from '@coreui/vue'
Vue.use(CoreUI)
この(Nuxt.jsとしての)プラグインを読み込むよう nuxt.config.ts
を編集。
export default {
// ...
plugins: [
'~/plugins/coreui'
],
// ...
}
これで、コンポーネントが見つからないというエラーは消えるけど、まだスタイルが適用されていない。公式テンプレートでは src/assets/scss
下に共通のスタイル定義 style.scss
, _custom.scss
, _variables.scss
が置かれているので、これらを自分のプロジェクトの assets/scss
下にコピーし、グローバルなスタイル定義として nuxt.config.ts
に記載しておく。
export default {
// ...
css: [
'~/assets/scss/style.scss'
],
// ...
}
これでカードがきれいに表示されるようになるはず。
アイコンを使う
アイコン表示につかうコンポーネント CIcon
は @coreui/icons-vue
で定義されているが、これは @coreui/vue
から依存されているので、すでにインストールされている。
$ npm ls
my-project@1.0.0 /Users/user/my-project
├── @coreui/coreui@3.0.0
├─┬ @coreui/vue@3.0.0
│ ├── @coreui/icons@1.0.1
│ ├─┬ @coreui/icons-vue@1.3.0
│ │ └── vue@2.6.11 deduped
たとえば pages/index.vue
に先ほど追加したカードのヘッダにアイコンを表示するなら、次のように書けばよいようだけど、いまのまま CIcon
コンポーネントを使っても、エラーは出ないけれどアイコンは表示されない。
<CCardHeader>
カードのタイトル
<div class="card-header-actions">
<a
href="https://coreui.io/vue/docs/components/card-components"
class="card-header-action"
rel="noreferrer noopener"
target="_blank"
>
<CIcon name="cil-envelope-open" /> <!-- これを追加した! -->
<small class="text-muted">docs</small>
</a>
</div>
</CCardHeader>
CIcon
コンポーネントがどのようにアイコンを参照しているかというと、公式テンプレートの src/main.js
にある次の記述。この中でインポートされている icons.js
は、 @coreui/icons
モジュールで定義されているアイコンやプロジェクト固有のアイコン(テンプレではサンプルとしてロゴを読み込んでいる)をexportしており、それをルートコンポーネントのオプションとして設定している。ちなみに、 icons.js
がエクスポートする各アイコンはアイコンサイズと表示用のHTMLタグを文字列で保持する長さ2のstring配列データ。
// src/main.js
import { iconsSet as icons } from './assets/icons/icons.js'
new Vue({
el: '#app',
router,
store,
icons,
template: '<App/>',
components: {
App
}
})
Nuxt.jsプロジェクトでこの main.js
と同じことをやるには、ルートとなっているコンポーネントの生成箇所を、ハックするか、生成直後に次のようなことができればよいのだろうけど、いくら調べてもそれができる適切な方法を見つけることができなかった。誰か教えてほしい。
import { iconsSet as icons } from 'myicons.ts'
$root.$options.icons = icons
仕方がないので、ルートの直下に配置されるレイアウト用コンポーネントの作成タイミングにこのロジックを挿入することにしてみた。以下、その手順。
プロジェクトをTypeScript化しているので、まずは型定義を追加する。 @types/coreui-icons.d.ts
ファイルを次の内容で作成し、それを型定義ファイルとして tsconfig.json に追加。
// @types/coreui-icons.d.ts
import Vue from 'vue'
type IconsSet = { string: string[] }
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
icons?: IconsSet
}
}
// tsconfig.json
"types": [
"@types/node",
"@nuxt/types",
"./@types/coreui-icons" // <- これ
]
つぎに公式テンプレートの src/assets/icons
ディレクトリ下のファイル icons.js
と logo.js
を、プロジェクトの assets/icons
ディレクトリにコピーする。TypeScript化するので、 icons.js
改め icons.ts
がエクスポートしてるiconsSetに型指定しておく。
// assets/icons.ts
import { IconsSet } from '~/@types/coreui-icons'
export const iconsSet: IconsSet = Object.assign( // ...略
ちなみに、公式テンプレートの icons.js
は @coreui/icons
が提供しているすべてのアイコンをインポートしているわけではないので、必要なアイコンがあればあとで足していけばよいと思う。
最後に layouts/default.vue
にコンポーネントを定義して created
メソッドを実装する。
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import { iconsSet as icons } from '~/assets/icons/icons'
@Component
export default class DefaultLayout extends Vue {
created() {
this.$root.$options.icons = icons
}
}
</script>
これでアイコンが表示されるようになったはず。 icons.ts
をうまく組めば CIcon
コンポーネントでfont-awesomeなどサードパーティのアイコンも表示できると思う。
レイアウト
次に、公式テンプレートと同じように、コンテンツのまわりにヘッダー・サイドメニュー・フッターを配置するレイアウトを作る。そこまでできればCoreUIを利用する準備が整ったと言えそう。けど、疲れたので続きは改めて。
CoreUIはサイドメニューの開閉ステータス管理にVuexを使っているので、レイアウトと合わせてVuexまわりの設定についても触れる。vue-routerまわりについては特に問題なくサイドメニューなどを作成できる。ただしCoreUIは当然 <nuxt-link>
を使ってはいないので、そこにこだわる場合ツラいことになりそう。
→ 書いた。 Nuxt.js + TypeScript のプロジェクトに CoreUI を手動クリーンインストール【後編】
非公式テンプレートについて
リンク再掲。
https://github.com/muhibbudins/nuxt-coreui
非公式テンプレートのpackage.jsonは次のとおりで、Nuxt関連のはさておくとして、bootstrap-vue 以下、公式テンプレートの依存関係に存在しないライブラリを用いている。一方、CoreUI関連モジュールへの依存はない(え?)。
"dependencies": {
"cross-env": "^5.2.0",
"nuxt": "^2.0.0",
"express": "^4.16.3",
"@nuxtjs/axios": "^5.0.0",
"@nuxtjs/style-resources": "^0.1.1",
"bootstrap-vue": "^2.0.0-rc.1",
"chart.js": "^2.7.1",
"flag-icon-css": "^2.9.0",
"font-awesome": "^4.7.0",
"simple-line-icons": "^2.4.1",
"vue-chartjs": "^3.1.1"
},
非公式テンプレートが実際に何のコンポーネントを使ってデモサイトを作っているかというと、 bootstrap-vueっぽい。アイコンも依存関係にあるとおり、CoreUIとは関係のない製品を組み合わせている様子。
こんな記事を書いてるけれど別にCoreUI推しというわけではないので、Nuxt.jsにUIフレームワークとしてbootstrap-vueなどを組み合わせたテンプレートとして、これはこれでいいと思うのだけど、なぜこれをNuxt + CoreUI projectと称しているのかが理解できず、頭にハテナが付いてる状態。