【Vue】QuillエディターでWYSIWYGを実装

Quill
目次

Vue.jsでWYSIWYGを実装する

WYSIWYGはたくさんパッケージがありますが、シンプルで無料なQuillを今回利用することにしました。

Quillの公式ドキュメントを翻訳したのは下記になります。

今回はVue.jsということもあり、QuillをVueパッケージ化させたvue-quill-editor を利用していきます。

環境

  • Laravel8
  • Vue2

インストール

npmインストールの場合

npm install vue-quill-editor --save

// or
yarn add vue-quill-editor

CDNの場合

<link rel="stylesheet" href="path/to/quill.core.css"/>
<link rel="stylesheet" href="path/to/quill.snow.css"/>    // テーマ:snow
<link rel="stylesheet" href="path/to/quill.bubble.css"/>  // テーマ:bubble
<script type="text/javascript" src="path/to/quill.js"></script>
<script type="text/javascript" src="path/to/vue.min.js"></script>
<script type="text/javascript" src="path/to/dist/vue-quill-editor.js"></script>
<script type="text/javascript">
  Vue.use(window.VueQuillEditor)
</script>

Mount

グローバルでマウント

import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'

import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme

Vue.use(VueQuillEditor, /* { default global options } */) // 共通オプション設定が不要な場合、第二引数は不要。componentでオーバーライド可能

Laravelのapp.jsの場合は下記になります。

import Vue from "vue";

// Quill
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme

Vue.use(VueQuillEditor) // importとこれだけで利用可能

ローカルでマウント

import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

import { quillEditor } from 'vue-quill-editor'

export default {
  components: {
    quillEditor
  }
}

モジュールの登録

import Quill from 'quill'
import yourQuillModule from '../yourModulePath/yourQuillModule.js'
Quill.register('modules/yourQuillModule', yourQuillModule)

Component

<template>
  <!-- Two-way Data-Binding -->
  <quill-editor
    ref="myQuillEditor"
    v-model="content"
    :options="editorOption"
    @blur="onEditorBlur($event)"
    @focus="onEditorFocus($event)"
    @ready="onEditorReady($event)"
  />

  <!-- Or manually control the data synchronization -->
  <quill-editor
    :content="content"
    :options="editorOption"
    @change="onEditorChange($event)"
  />
</template>

<script>
  // You can also register Quill modules in the component
  import Quill from 'quill'
  import someModule from '../yourModulePath/someQuillModule.js'
  Quill.register('modules/someModule', someModule)
  
  export default {
    data () {
      return {
        content: '<h2>I am Example</h2>',
        editorOption: {
          // Some Quill options...
        }
      }
    },
    methods: {
      onEditorBlur(quill) {
        console.log('editor blur!', quill)
      },
      onEditorFocus(quill) {
        console.log('editor focus!', quill)
      },
      onEditorReady(quill) {
        console.log('editor ready!', quill)
      },
      onEditorChange({ quill, html, text }) {
        console.log('editor change!', quill, html, text)
        this.content = html
      }
    },
    computed: {
      editor() {
        return this.$refs.myQuillEditor.quill
      }
    },
    mounted() {
      console.log('this is current quill instance object', this.editor)
    }
  }
</script>

実例

<template>
  <quill-editor
    name="body"
    ref="myQuillEditor"
    v-model="State.body"
    :options="editorOption"
  />
</template>
<script>
import { Store } from '@/stores/Store.js';

export default {
  data () {
    return {
      State: Store.state,
      editorOption: {
        theme: 'snow',
        debug: false,
        placeholder: 'お知らせの本文を入力してください',
        modules: {
          toolbar: [
            ['bold', 'italic', 'underline', 'strike'], // ボタンの表示オプション
            [{ 'color': [] }, { 'background': [] }],   // 選択肢が必要なカラーと背景色の表示オプション
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }], // Hタグの表示オプション
            [{ 'size': ['small', false, 'large', 'huge'] }], // 文字サイズの表示オプション
            [{ 'align': [] }], // 中央寄せなどの文字並びの表示オプション
          ]
        },
      }
    }
  },
  computed: {
    editor() {
      return this.$refs.myQuillEditor.quill
    }
  },
}
</script>

XSS対策をしよう

さてここから問題です。

WYSIWYGはHTMLタグを許可しているので、XSS対策をしないといけません。

blade側での対策

blade側では特定のHTMLタグだけを許可し、他は文字として扱うようにしましょう。

例えば下記のようにHTMLタグのエスケープを解除したとしまよう。

<div>{!! $wysiwygText !!}</div>

HTMLのエスケープを解除しただけだと、scriptタグを入れることができてしまいます。

そこから好きなように悪さできてしまいます。

そこで、許可するhtmlタグを指定しましょう

<div>{!! strip_tags($wysiwygText, ['p', 'a', 'span', 'u', 'em', 'strong']) !!}</div>

validationでDB保存を防ぐ

先ほどblade側に表示するHTMLタグを制限しましたが、とはいえ何かの拍子にコードを変えて、scriptタグを許可してしまう瞬間があるかもしれません。

そうなると場合によっては情報流出になりかねません。

ということで、そんな爆弾を抱えないように、validationで弾いてしまいましょう!

PHPでは正規表現による判定は、preg_match()を利用します。

preg_match('/正規表現のルール/', '対象の文字');

引数などの詳細は公式ドキュメントにて確認をしてください。

それでは<script>をvalidationで弾きたいと思います。

preg_match('/<\s*script/', '<script');

少し分解して解説すると、下記になります。

< = <
\s = スペースキー
* = 一つ前のものが0回以上の繰り返し
script = script

これによる、<scriptを検知しました。

じゃあ、これを実際にvaliationに組み込むと検知されません。

'not_regex:/<\s*script/'

なぜvadalidationで弾けないのでしょうか?

それは<の表示が異なるためです。

<&lt;として投稿されます 。

なので、&lt;に合わせて正規表現のルールを変えなければなりません。

'not_regex:/\<\s*script/'

これだとscriptタグを弾くことができます。

ぎゅう
WEBエンジニア
渋谷でWEBエンジニアとして働く。
LaravelとVue.jsをよく取り扱い、誰でも仕様が伝わるコードを書くことを得意とする。
先輩だろうがプルリクにコメントをして、リファクタしまくる仕様伝わるコード書くマン
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次
閉じる