LoginSignup
4

More than 1 year has passed since last update.

[解説]Parcel+Hyperapp+markedでシンプルなマークダウンエディタを作る[JS]

Last updated at Posted at 2018-03-16

色々と雑ですがお付き合いください。また、解説は自分のリポジトリのものと少し違います(Linter系バッサリ吹き飛んでます)。

こちら、結果リポジトリ公開したものです。

基本情報

  • Parcelとは
    • 思考停止で(何の設定もなしに)モジュールをバンドルしてくれるマン。
  • Hyperappとは
    • 超軽量データバインドマン。
  • Markdownとは
    • Qiita書くときに使うやつ。
  • markedとは
    • MarkdownからHTMLに変換してくれるモジュール

書いていきます

とりあえず必要なものをyarn add(npm install)します。

terminal
yarn global add parcel-bundler
yarn add hyperapp marked node-sass
# これらは以下とほぼ同義です
# npm i -g parcel-bundler
# npm i hyperapp marked node-sass

parcelhyperappは先に書いたとおりです。

  • marked

    • Markdown→HTML変換器くん。
  • node-sass

    • Sass/SCSS→CSS変換器くん。

index.htmlをつくる

最低限のもののみ、HTML5で省略推奨されているタグは含めていません。

index.html
<!DOCTYPE html>
<title>markdown-editor with Hyperapp</title>
<body>
  <script src='app.js'></script>
</body>

ここで、app.jsという空ファイルを作っちゃいます。
その後、

terminal
parcel index.html

localhost:1234を開くととりあえずタイトルが「markdown-editor with Hyperapp」の真っ白のWebページが見えました。
Parcelのすごいところは、とりあえずindex.htmlを引数にして実行すればそこからsrcしてるファイルが全て自動でコンパイル&ブラウザ側でライブリロードされる点ですね。

hello Hyperapp

では、app.jsを編集します。

app.js
import { h, app } from 'hyperapp'

const state = {
  'output': 'hello, Hyperapp'
}

const actions = {
}

const view = (state, actions) => (
  <main id='app'>
    {state.output}
  </main>
)

app(state, actions, view, document.body)

1行目はモジュールのインポートですね。hはどうやらappの実行に必要なようなので一緒に入れてます(一回エラー見ました)。
次に、Hyperappではstateactionsviewそれらを適用させる場所(app)が初期化で求められるので、それらを書いてます。

stateは一言で言うと変数です。
actionはそれを編集するためのメソッド、関数を記述します。
viewは表示する中身をほぼそのまんま書いていきます。中身はJSX記法と呼ばれるもので、{}で変数の中身を表示できます

ので、これを保存した頃にはブラウザ側では「hello, Hyperapp」と表示されているはずです。

textareaの中身をリアルタイムで表示する

流れとしては、viewを編集して<textarea>タグを追加、それが編集されたらactionからメソッドを呼んでstateのoutputをいじって表示する感じです。

で、ハマったのはtextareaの中身をどうやって取得するかです。Vue.jsだと、v-modelとか便利なものがあるんですが、Hyperappに似たようなものがあるかと思ったんだが。

見つかりませんでしたので、生Javascriptで書いてます。誰か知ってたら教えてください。
先程書いたコードの一部を書き換えていくような形で書いていきます。

app.js
const view = (state, actions) => (
  <main id='app'>
    <textarea id='editor' oninput={e => actions.setOutput(document.getElementById('editor').value)} />
    <div>{state.output}</div>
  </main>
)

oninputで入力を感知したら、document.getElementById('editor').valueで得られた文字列(=textareaの内容)を引数にactions.setOutputを実行します。
では、actions.setOutputを定義します。

app.js
const actions = {
  setOutput: (input) => state => ({ output: input })
}

アロー関数式=>を使っているので混乱させてしまったら申し訳ないですが、つまり引数をinputとして扱ってstate.outputを変更するというものです。

この状態で、ブラウザに表示されているtextareaに文字を入れたとき、その内容がそのまま表示されていれば成功です。

textareaの中身でリアルタイムHTMLプレビューする

さっきまでの文字列はあくまで文字列として扱われ、HTMLタグはプレビューできてません。
上で話した通り、モジュール「marked」はMarkdownをHTMLに変換するため、HTMLプレビューが必要でした。

この変更はviewを編集するだけで秒解決です。ただ、Vue.jsで言うところのv-htmlのような機能も見当たらなかったので、直書きします。

app.js
const view = (state, actions) => (
  <main id='app'>
    <textarea id='editor' oninput={e => actions.setOutput(document.getElementById('editor').value)} />
    <div id='preview' innerHTML={state.output}></div>
  </main>
)

innerHTMLに入れたら解決しました。やったぜ。

仕上げの時間だ。Markdownプレビューしますよ

さて、もうここまでくるとoutputの中にmarkdownから変換されたHTMLを代入するだけですね。
上に書いたとおり、Markdown→HTML変換器くんのmarkedを使います。

まずmarkedのimport文の追加。

app.js
import marked from 'marked'

で、actionの編集。

app.js
const actions = {
  setOutput: (input) => state => ({ output: marked(input) })
}

marked超便利。marked超便利。marked超便利。
はい、これでとりあえず終了ですね。

見栄えをどうにかする

これに関しては最初にnode-sassを導入してるのでSCSSやSass、もしくはCSSを書いて下さい。僕の場合はこんな感じでした。
今回はJavascriptの解説だったので、あえてこの解説は省きます。

style.sass

style.sass
html, body
  height: 100%
  width: 100%
  margin: 0

#app
  display: flex
  height: 100%

textarea, #preview
  flex: 1 1 0
  overflow-y: scroll

textarea
  padding: 1em
  font-size: 12px
  background-color: #eee
  line-height: 1.5em
  font-family: courier, monospace
  border: 0

#preview
  padding: 1em

pre
  padding: 15px
  background-color: #282c34
  color: #EEE
  margin-right: 10px

@media print
  textarea
    display: none
  #preview
    overflow-y: visible

で、それをimportします。

app.js
import './style.sass'

おまけ:Highlight.jsでcodeをいい感じに表示する

Highlight.jsはcodeタグ内の各種コードをいい感じの色にしてくれるものです。

yarn add(npm install)

terminal
yarn add highlight.js
app.js
import highlight from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css'

marked.setOptions({
  highlight: function (code, lang) {
    return highlight.highlightAuto(code, [lang]).value
  }
})
style.sass
.hljs
  all: unset

以上です。お疲れ様でした。

一分説明が雑になってしまったので申し訳ないですが、以上になります。
Parcelは設定ファイルを作成しなくて良いHyperappは非常に少ないコードで書けるというそれぞれの強みが有ります。
逆に、これらは細かい設定が出来ない、複雑なものが作りにくいという弱みに直結しますが、使えるところでは使えるということを体感して頂けたら幸いです。

あ、Parcelで静的データ書き出ししちゃいましょう。

terminal
parcel build src/index.pug --public-url ./

これでdistの中に公開できる状態のデータが生成されているはず。
はやい。
かんたん。
Parcelだいすき。
何か質問、ご指摘等有りましたらなんでもお願いします。
では。

修正・補足

  • 180322
    • parcel-bundlerをグローバルインストールに変更しました。グローバルを汚したくない人はnpm scriptsをよしなに書いて下さい。

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
4