フロントエンドアレルギーを少しでも治すべく「手を動かして学ぶ!Vue.js」を片手にVue.jsに入門し始めた。
今までの人生でほとんどJavaScriptやってきたことないマンでもわかりやすい良書。
今回はこの書籍と公式ドキュメントから学んだ内容で、Vue.js公式Webサイトのサンプルに載っているMarkdown Editorの仕組みを理解していく。
コード全体
え、これだけでMarkdownエディタ書けるの。Vue.jsマジやばい(語彙力
<!DOCTYPE html> <html> <head> <title>Markdown Editor</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/marked@0.3.6"></script> <script src="https://unpkg.com/lodash@4.16.0"></script> <link rel="stylesheet" type="text/css" href="/style.css" /> </head> <body> <div id="editor"> <textarea :value="input" @input="update"></textarea> <div v-html="compiledMarkdown"></div> </div> <script> new Vue({ el: "#editor", data: { input: "# hello" }, computed: { compiledMarkdown: function() { return marked(this.input, { sanitize: true }); } }, methods: { update: _.debounce(function(e) { this.input = e.target.value; }, 300) } }); </script> </body> </html>
テンプレート
Markdownエディタを描画しているのはこの4行。
<div id="editor"> <textarea :value="input" @input="update"></textarea> <div v-html="compiledMarkdown"></div> </div>
Vue.jsの「el」とは? - JavaScript勉強会
とあるように、editor
という名前でdivタグの内側を「Vueの表示をはめ込む場所」として定義。
textarea要素
次にtextarea要素。
<textarea :value="input" @input="update"></textarea>
:value="input"
:value
はv-bind:value
の省略形。「手を動かして学ぶ!Vue.js」50ページ目には
要素の属性をデータで指定するときは、v-bind
とある。つまり、textarea要素のvalue属性にinput
というデータを指定する、ということになる。
いや、でもtextarea要素にvalue属性なくね?
<textarea> - HTML: HyperText Markup Language | MDN
Vue.js公式ドキュメントでは複数行テキストに対してはv-model
を使っている。Vue.jsなにもわからない。
試しに v-bind:input
と v-models
で書いてみた。しかしどちらも動作する。やっぱりVue.jsなにもわからない。
https://jsfiddle.net/nao_y/rj28vd7t/5/
@input="update"
@input
はv-on:input
の省略形。「手を動かして学ぶ!Vue.js」50ページ目には
イベントとメソッドをつなぐときは、v-on
とある。つまり入力というイベントとupdate
というメソッドを繋いでいる。入力が発生すると、update
メソッドが動く、というイメージだろうか?
div要素
<div v-html="compiledMarkdown"></div>
v-html
はHTMLとして表示する。ここではcompiledMarkdown
プロパティをHTMLとして表示する。
Vueインスタンス
new Vue({ el: "#editor", data: { input: "# hello" }, computed: { compiledMarkdown: function() { return marked(this.input, { sanitize: true }); } }, methods: { update: _.debounce(function(e) { this.input = e.target.value; }, 300) } });
elオプション
el
オプションは#editor
となっている。ここはHTML部分の<div id="editor">
と対応していて、このdiv要素の中にVueの表示をはめ込む。
dataオプション
data
オプションのinput
プロパティはtextarea
要素の:value="input"
と紐付いていて、input
プロパティのデータがtextarea
要素に表示される。
computedオプション
computed
オプション(算出プロパティ)compiledMarkdown
プロパティが定義されている。このプロパティには関数が指定されている。
function() { return marked(this.input, { sanitize: true }); }
Marked.jsライブラリを使って、input
プロパティの内容をHTMLにパースしている。
パースしたHTMLは<div v-html="compiledMarkdown"></div>
で表示される。
ところで、{ sanitize: true }
となっていてHTMLのサニタイズをしているけど、Marked.jsのUsageには以下のようにある。
Warning: 🚨 Marked does not sanitize the output HTML. Please use a sanitize library, like DOMPurify (recommended), sanitize-html or insane on the output HTML! 🚨
どうやら出力するHTMLをサニタイズしないのでDOMPurifyなどを使うことが推奨されている。
DOMPurifyを使って書き直すとこんな感じになる。
function() { var html = marked(this.input); return DOMPurify.sanitize(html); }
methodsオプション
methods
オプションでは、イベントに対して処理を行う。
methods: { update: _.debounce(function(e) { this.input = e.target.value; }, 300) }
ここでは入力(@input="update"
)に対して、update
というメソッドが働く。
_.debounce()
はlodashというライブラリの関数。なんか便利関数の集まり的なライブラリらしい。
Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked.
https://lodash.com/docs/4.17.15#debounce
指定された秒数(ミリ秒)待った後、関数を呼び出す、というような役割。
_.debounce()
に渡される関数ではe
という引数がある。これはイベントのことを指すのだろうか。Pythonしかわからないマンなのでe
と言われると例外を連想してしまう。
ここでは300ミリ秒待ったあとに、this.input = e.target.value;
でinputプロパティに対してイベント(入力)の値(?)を代入する。
ここで更新されたinput
プロパティはcomputed
オプションのcompliedMarkdown
プロパティでHTMLへとパースされる。
つまり、update
メソッドの働きによってMarkdownエディタへの入力から300ミリ秒後にプレビューが更新される。
やってみた
Django Girls TutorialのブログにMarkdownエディタを実装してみた。
これがMarkdownエディタを実装した投稿画面。
投稿画面のtemplateは以下。省略形を元の形に直したり、サニタイズをDOMPurifyにやらせたり、プレビューへの反映待ち時間を150ミリ秒に縮めたりしている。