vue-cli with Semantic UI on webpack

<img src="https://qiita-image-store.s3.amazonaws.com/0/47035/232a8053-7eda-20f6-992e-b02062bdd20e.png" alt="Vue with Semantic-UI on webpack" width="985">

- __[Semantic UI](https://semantic-ui.com/)__

Bootstrap もよいですが、 Semantic UI がとてもよくできています。「これはいい！」と意気込んでみたものの、 webpack で扱うのにつまずきました。調べたところ世界で多くの方たちが悩まれているようです。ここらでひとつ真面目に取り組んでみるかということで、なんとか動作した設定に至るまでの流れを残しておきます。

ちなみに Vue を前提に話を進めていますが、 React であれば Semantic UI React がオフィシャルに提供されているのでそれを素直に使うのがよいでしょう。


## 前提

```bash
$ date
2017年 3月28日 火曜日 21時12分35秒 JST

$ node -v
v7.7.4

$ npm -v
4.5.0
```

## vue-cli

- [vuejs/vue\-cli: Simple CLI for scaffolding Vue\.js projects](https://github.com/vuejs/vue-cli)

```bash
$ npm install -g vue-cli
```
```bash
$ vue -V
2.8.1
```

ポイントを簡潔に示すために webpack-simple テンプレートを選択しています。

```bash
$ vue init webpack-simple my-project

  This will install Vue 2.x version of the template.

  For Vue 1.x use: vue init webpack-simple#1.0 my-project

? Project name my-project
? Project description A Vue.js project
? Author mail@example.com
? Use sass? No
```
```bash
$ cd my-project

$ npm install
```

## Semantic UI

- [Getting Started \| Semantic UI](https://semantic-ui.com/introduction/getting-started.html)

※ 以下の操作は結局のところダウンロードした Semantic UI ソースを Gulp を使って js, css, components, themes をビルドし配置しているに過ぎません。カスタマイズのつもりもなくデフォルトのテーマでよい場合は上記リンク先に記載の __Simpler Setup__ の Download Zip から入手すればOK。

```bash
$ npm install -g gulp
```

```bash
$ npm install semantic-ui --save
```

```bash
[21:50:18] Starting 'run setup'...
? Set-up Semantic UI Automatic (Use defaults locations and all components)
?     
    We detected you are using NPM. Nice!  
    
    Is this your project folder?
    /Users/syon/repo/my-project 
    
 Yes

? Where should we put Semantic UI inside your project? semantic/
[21:52:26] Finished 'run setup' after 2.13 min
[21:52:26] Starting 'create install files'...
```

```bash
$ cd semantic/

$ gulp build
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/47035/7a280b01-055a-ab1c-dd39-f1e102222a09.png" alt="Semantic UI dist directory" width="490">


## Import Semantic UI to webpack

Semantic UI の依存パッケージである jQuery をインストールします。また、ビルドした CSS を webpack で読み込むための style-loader もインストールします。

```bash
$ npm install jquery --save

$ npm install style-loader --save-dev
```

### webpack.config.js

Semantic UI の CSS を webpack に読み込ませ style タグに定義し、Webフォントもファイルとして読み込んでいます。ここで `name=semantic/dist/themes/default/assets/fonts/[name].[ext]` に着目。CSS ファイルが参照しているパスに合わせる必要があります。 jQuery は webpack グローバルで使えるようにプラグインとして登録します。これで各コンポーネントで毎回 import する必要がなくなります。

```diff
diff --git a/webpack.config.js b/webpack.config.js
index beb773d..d91f610 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -30,9 +30,23 @@ module.exports = {
         options: {
           name: '[name].[ext]?[hash]'
         }
+      },
+      {
+        test: /\.css$/,
+        use: ['style-loader', 'css-loader']
+      },
+      {
+        test: /\.(eot|svg|ttf|woff|woff2)$/,
+        loader: 'file-loader?name=semantic/dist/themes/default/assets/fonts/[name].[ext]'
       }
     ]
   },
+  plugins: [
+    new webpack.ProvidePlugin({
+      $: 'jquery',
+      jQuery: 'jquery'
+    })
+  ],
   resolve: {
     alias: {
       'vue$': 'vue/dist/vue.esm.js'
```

### src/main.js

```diff
diff --git a/src/main.js b/src/main.js
index 385fcfe..5f7d4e4 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,6 +1,9 @@
 import Vue from 'vue'
 import App from './App.vue'
 
+import '../semantic/dist/semantic.css'
+import '../semantic/dist/semantic.js'
+
 new Vue({
   el: '#app',
   render: h => h(App)
```

## App.vue (Sample)

冒頭の画像のソースコードです。 `mounted`や`updated`のタイミングでスクリプトを呼んでいます。

- [Vue\.js オプション-ライフサイクルフック](https://jp.vuejs.org/v2/api/#オプション-ライフサイクルフック)

```html:App.vue
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <div class="ui container">
      <div class="ui labeled button" tabindex="0">
        <div class="ui red button">
          <i class="heart icon"></i> Like
        </div>
        <a class="ui basic red left pointing label">
          1,048
        </a>
      </div>
      <div class="ui labeled button" tabindex="0">
        <div class="ui basic blue button">
          <i class="fork icon"></i> Forks
        </div>
        <a class="ui basic left pointing blue label">
          1,048
        </a>
      </div>
      <div class="ui yellow progress" data-percent="60">
        <div class="bar"></div>
      </div>
      <div class="ui olive progress" data-percent="30">
        <div class="bar"></div>
      </div>
      <div class="ui green progress" data-percent="85">
        <div class="bar"></div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'app',
  mounted () {
    $(this.$el).find('.progress').progress();
  },
  updated () {
    $(this.$el).find('.progress').progress();
  },
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<style>
...
</style>
```

## Dependencies

```json:package.json(抜粋)
  "dependencies": {
    "jquery": "^3.2.1",
    "semantic-ui": "^2.2.9",
    "vue": "^2.2.1"
  },
  "devDependencies": {
    "babel-core": "^6.0.0",
    "babel-loader": "^6.0.0",
    "babel-preset-latest": "^6.0.0",
    "cross-env": "^3.0.0",
    "css-loader": "^0.25.0",
    "file-loader": "^0.9.0",
    "style-loader": "^0.16.1",
    "vue-loader": "^11.1.4",
    "vue-template-compiler": "^2.2.1",
    "webpack": "^2.2.0",
    "webpack-dev-server": "^2.2.0"
  }
```

Enjoy!