【Vue.js】HTMLかけない人でも静的ファイルを読み込んでいい感じの記事を書けるようにする

# HTMLかけない人でも静的ファイルを読み込んでいい感じの記事を書けるようにする
## 動機
* HTMLかけない人でもいい感じの記事やテキストページを書けるようにしたい
* HTMLかける人でもテキストページくらいは労力減らして書けるようにしたい
* 利用者自身で好きに編集できるようにしたい
* 内容をDBに保存したりとかAPI連携するとか考えたくない
* 管理画面ちゃんと作るのめんどくさい


## 実現したもの
* Markdown使う
* .mdの静的ファイルを読み込むようにしたのでファイル上書きすれば更新可能

### デモ
![mdsampleGif - Google Chrome 2020-02-24 22-18-03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236151/2dbeed6f-5202-5e90-a3c1-58a7dfb669ec.gif)

#### インポートしてるmdファイルの中身
* markdown.md 

``` markdown
# 見出し1
## 見出し2
 `くつしたねこ`, `ハチワレねこ`, または `ジト目ねこ`
+ 哺乳類
  - ねこ
    * メインクーン
    + ノルウェージャンフォレストキャット
    - サバンナキャット
+ Very cute!

好きな柄順

1. 長さの違う靴下
2. 長さがだいたい同じ靴下
3. 全部


:smile:
> 猫
>> ねこ
> > > 🐈

\```
ねこ
ねこ
ねこ
\```


| 種類 | 詳細 |
| ------| -----------|
| メインクーン | かわいい |
| ノルウェージャンフォレストキャット | かわいい|
| サバンナキャット | かわいい |

## Links

[wiki:ネコ](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%82%B3)

[ホバーで説明が出るリンク](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%82%B3 "ねこかわいい")



![ねこ](./img/cat1.jpg)


 19^th^
- H~2~O

++Inserted text++

==Marked text==


Footnotes

Footnote 1 link[^first].

Footnote 2 link[^second].

Inline footnote^[Text of inline footnote] definition.

Duplicated footnote reference[^second].

[^first]: Footnote **can have markup**

    and multiple paragraphs.

[^second]: Footnote text.


### Abbreviations

This is HTML abbreviation example.

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

* [HTML]: Hyper Text Markup Language
```

画像はpublicのフォルダ配下に配置する。
今回はpublicの下にimgフォルダを作って画像を置いているので
` http://localhost:8080/img/cat1.jpg `
で画像にアクセスできる

## 実装
### 使ったもの
Vue.js
[vue-markdown](https://github.com/miaolz123/vue-markdown)
　VuiCLI3を使ってるので
　`npm install --save vue-markdown  @vue/cli` でインストールした
[axios](https://www.npmjs.com/package/axios)

### 階層
```
MarkdownSmaple
  ┠ public
  ┃  ┠ markdown.md  --importするmarkdownのファイル
  ┃  ┠ img
  ┃  ┗  ┗ cat1.jpg  --markdown内で使う画像ファイル
  ┠ src
  ┃  ┠ main.js
  ┃  ┠ assets
  ┃  ┠ components
  ┃  ┃ ┠ Markdown.vue
  ┃  ┃ ┠ md.css   --markdown用のcss
  ┗  ┗ ┗ md_table.css  --markdown用(table)のcss
```

### コード
* main.js
 
 axiosを使えるようにする

``` JavaScript
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' //追加

Vue.config.productionTip = false
Vue.prototype.$axios = axios //追加

new Vue({
  render: h => h(App)
}).$mount('#app')

```

* Markdown.vue

``` Vue
<template>
  <div>
    <v-row id="md">
      <v-col>
        <vue-markdown :source="source"></vue-markdown>
      </v-col>
    </v-row>
  </div>
</template>

<script>

import VueMarkdown from "vue-markdown";
export default {
  components: {
    VueMarkdown
  },
  data() {
    return { source: "" };
  },
  mounted: function() {
   /* publicのフォルダに置いたmdファイルを取得する
      public配下はbuildするとroot直下として扱われる
　　　assetsフォルダの下に置くと読み込まれないので注意　*/
    this.$axios
      .get("./markdown.md")
      .then(response => (this.source = response.data));
  }
};
</script>
<style>
/* cssファイルをインポート */
@import "./md.css";
@import "./md_table.css";
#md {
  position: relative;
  left: 5%;
}
</style>
```

* md.css

[ここ](https://kevinburke.bitbucket.io/markdowncss/)のcssを一部改修した
cssを変更すればデザインも変わるのでいい感じにしよう

``` css
@import url(http://fonts.googleapis.com/css?family=Ubuntu:bold);
@import url(http://fonts.googleapis.com/css?family=Vollkorn);


html { 
  font-size: 100%; 
  overflow-y: scroll; 
  -webkit-text-size-adjust: 100%; 
      -ms-text-size-adjust: 100%; 
}

body {
  max-width:42em;
  padding:1em;
  margin:auto;
  color:#444;
  line-height:1.5em;
  font-family:Vollkorn, Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif;
  font-size:12px;
  background:#fefefe;
}

a { 
  color: #337ab7; 
  text-decoration:none;
}

a:visited { color: #0b0080; }
a:hover   { color: #06e; }
a:active  { color:#faa700; }
a:focus   { outline: thin dotted; }
a:hover, a:active { outline: 0; }

::-moz-selection{ background: rgba(255,255,0,0.3); color: #000}
::selection{      background: rgba(255,255,0,0.3); color: #000}

a::-moz-selection{ background: rgba(255,255,0,0.3); color: #0645ad}
a::selection{      background: rgba(255,255,0,0.3); color: #0645ad}

p {
  margin: 1em 0;
}

img {
  max-width: 100%;
}

h1,h2,h3,h4,h5,h6 {
  font-family: 'Ubuntu';
  font-weight:normal;
  color:#111;
  line-height:1em;
}
h4, h5, h6 { font-weight: bold; }
h1 { font-size:2.5em; }
h2 { font-size:2em; }
h3 { font-size:1.5em; }
h4 { font-size:1.2em; }
h5 { font-size:1em; }
h6 { font-size:0.9em; }

blockquote {
  padding-left: 3em;
  margin: 0;
  color: #666666;
  border-left: 0.5em #EEE solid;
}

hr {
  padding: 0; 
  margin: 1em 0; 
  display: block; 
  height: 2px; 
  border: 0; 
  border-top: 1px solid #aaa;
  border-bottom: 1px solid #eee; 
}

pre, kbd, samp { 
  color: #000; 
  font-family: monospace, monospace; 
  _font-family: 'courier new', monospace; 
  font-size: 0.98em; 
  
}
 code{ 
	padding: 2px 4px;
    font-size: 90%;	
	color: #c7254e; 
	background-color: #f9f2f4;
    border-radius: 4px;
	font-family: monospace, monospace; 
	_font-family: 'courier new', monospace; 
	font-size: 0.98em; 
	
  }

pre { 
  white-space: pre; 
  white-space: pre-wrap; 
  word-wrap: break-word; 
  color: #333;
  background-color: #f5f5f5;
  border: 1px solid #ccc;
  border-radius: 4px;
}

pre code {
    padding: 0;
    font-size: inherit;
    color: inherit;
    white-space: pre-wrap;
    background-color: transparent;
    border-radius: 0;
}

b, strong { 
  font-weight: bold; 
}

dfn { font-style: italic; }

ins { color: #000;  text-decoration: underline; }

mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }

sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sup { top: -0.5em; }
sub { bottom: -0.25em; }

ul, ol { margin: 1em 0; padding: 0 0 0 2em; }
li p:last-child { margin:0 }
dd { margin: 0 0 0 2em; }

img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; }



@media only screen and (min-width: 480px) {
  body {
    max-width: 100%;
    font-size: 14px;
  }
}

@media only screen and (min-width: 768px) {
  body {
    max-width:42em;
    font-size: 16px;
  }
}

@media print {
  * { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; }
  body{font-size:12pt; max-width:100%;}
  a, a:visited { text-decoration: underline; }
  hr { height: 1px; border:0; border-bottom:1px solid black; }
  a[href]:after { content: " (" attr(href) ")"; }
  abbr[title]:after { content: " (" attr(title) ")"; }
  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
  pre, blockquote { border: 1px solid #999; padding-right: 1em; page-break-inside: avoid; }
  tr, img { page-break-inside: avoid; }
  img { max-width: 100% !important; }
  @page { margin: 0.5cm; }
  p, h2, h3 { orphans: 3; widows: 3; }
  h2, h3 { page-break-after: avoid; }
}
```

* md_table.css

長かったので外に出しただけ

``` css
table {
	border-spacing: 0;
	border-collapse: collapse;
  }

  td, th {
	padding: 0
  }
  thead {
	display: table-header-group
  }
  .table {
	width: 100%;
	max-width: 100%;
	margin-bottom: 20px
  }

  .table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th {
	padding: 8px;
	line-height: 1.42857143;
	vertical-align: top;
	border-top: 1px solid #ddd
  }

  .table>thead>tr>th {
	vertical-align: bottom;
	border-bottom: 2px solid #ddd
  }

  .table>caption+thead>tr:first-child>td, .table>caption+thead>tr:first-child>th, .table>colgroup+thead>tr:first-child>td, .table>colgroup+thead>tr:first-child>th, .table>thead:first-child>tr:first-child>td, .table>thead:first-child>tr:first-child>th {
	border-top: 0
  }

  .table>tbody+tbody {
	border-top: 2px solid #ddd
  }

  .table .table {
	background-color: #fff
  }

  .table-condensed>tbody>tr>td, .table-condensed>tbody>tr>th, .table-condensed>tfoot>tr>td, .table-condensed>tfoot>tr>th, .table-condensed>thead>tr>td, .table-condensed>thead>tr>th {
	padding: 5px
  }

  .table-bordered {
	border: 1px solid #ddd
  }

  .table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, .table-bordered>thead>tr>td, .table-bordered>thead>tr>th {
	border: 1px solid #ddd
  }

  .table-bordered>thead>tr>td, .table-bordered>thead>tr>th {
	border-bottom-width: 2px
  }

  .table-striped>tbody>tr:nth-of-type(odd) {
	background-color: #f9f9f9
  }

  .table-hover>tbody>tr:hover {
	background-color: #f5f5f5
  }

  table col[class*=col-] {
	position: static;
	display: table-column;
	float: none
  }

  table td[class*=col-], table th[class*=col-] {
	position: static;
	display: table-cell;
	float: none
  }

  .table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active {
	background-color: #f5f5f5
  }

  .table-hover>tbody>tr.active:hover>td, .table-hover>tbody>tr.active:hover>th, .table-hover>tbody>tr:hover>.active, .table-hover>tbody>tr>td.active:hover, .table-hover>tbody>tr>th.active:hover {
	background-color: #e8e8e8
  }

  .table>tbody>tr.success>td, .table>tbody>tr.success>th, .table>tbody>tr>td.success, .table>tbody>tr>th.success, .table>tfoot>tr.success>td, .table>tfoot>tr.success>th, .table>tfoot>tr>td.success, .table>tfoot>tr>th.success, .table>thead>tr.success>td, .table>thead>tr.success>th, .table>thead>tr>td.success, .table>thead>tr>th.success {
	background-color: #dff0d8
  }

  .table-hover>tbody>tr.success:hover>td, .table-hover>tbody>tr.success:hover>th, .table-hover>tbody>tr:hover>.success, .table-hover>tbody>tr>td.success:hover, .table-hover>tbody>tr>th.success:hover {
	background-color: #d0e9c6
  }

  .table>tbody>tr.info>td, .table>tbody>tr.info>th, .table>tbody>tr>td.info, .table>tbody>tr>th.info, .table>tfoot>tr.info>td, .table>tfoot>tr.info>th, .table>tfoot>tr>td.info, .table>tfoot>tr>th.info, .table>thead>tr.info>td, .table>thead>tr.info>th, .table>thead>tr>td.info, .table>thead>tr>th.info {
	background-color: #d9edf7
  }

  .table-hover>tbody>tr.info:hover>td, .table-hover>tbody>tr.info:hover>th, .table-hover>tbody>tr:hover>.info, .table-hover>tbody>tr>td.info:hover, .table-hover>tbody>tr>th.info:hover {
	background-color: #c4e3f3
  }

  .table>tbody>tr.warning>td, .table>tbody>tr.warning>th, .table>tbody>tr>td.warning, .table>tbody>tr>th.warning, .table>tfoot>tr.warning>td, .table>tfoot>tr.warning>th, .table>tfoot>tr>td.warning, .table>tfoot>tr>th.warning, .table>thead>tr.warning>td, .table>thead>tr.warning>th, .table>thead>tr>td.warning, .table>thead>tr>th.warning {
	background-color: #fcf8e3
  }

  .table-hover>tbody>tr.warning:hover>td, .table-hover>tbody>tr.warning:hover>th, .table-hover>tbody>tr:hover>.warning, .table-hover>tbody>tr>td.warning:hover, .table-hover>tbody>tr>th.warning:hover {
	background-color: #faf2cc
  }

  .table>tbody>tr.danger>td, .table>tbody>tr.danger>th, .table>tbody>tr>td.danger, .table>tbody>tr>th.danger, .table>tfoot>tr.danger>td, .table>tfoot>tr.danger>th, .table>tfoot>tr>td.danger, .table>tfoot>tr>th.danger, .table>thead>tr.danger>td, .table>thead>tr.danger>th, .table>thead>tr>td.danger, .table>thead>tr>th.danger {
	background-color: #f2dede
  }

  .table-hover>tbody>tr.danger:hover>td, .table-hover>tbody>tr.danger:hover>th, .table-hover>tbody>tr:hover>.danger, .table-hover>tbody>tr>td.danger:hover, .table-hover>tbody>tr>th.danger:hover {
	background-color: #ebcccc
  }

  .table-responsive {
	min-height: .01%;
	overflow-x: auto
  }

```

### まとめ
利用規約とか軽い日記とか労力かけたくないけど見栄えよくしたい場合にいいと思います。
HTMLかけなくてもコマンド使えればなんとかなる
コマンド使えない人も使えるようにするなら管理画面作ってあげたほうがいいけど、静的ファイル更新するだけなのでそこまで難しくなさそう