6

ユーザーがショートコードを含むテキストを入力できる管理領域にフォームがあります。

[温度 c=200] でオーブンを加熱します。

私が望むのはtemp、フロントエンドに表示されるときにショートコードが解析され、Vue 2 コンポーネントに変換されることです。

単純化されたTemperature.vueコンポーネントを次に示します。

<template>
    <span class="temperature">
        {{ celsius }}&deg;C
    </span>
</template>

<script>
    import { mapGetters, mapActions } from 'vuex'

    export default {
        props: ['celsius'],
        name: 'temperature'
    }
</script>

Instructions.vueそして、ショートコードを使用してテキストを解析およびレンダリングする単純化されたコンポーネントを次に示します。

<template>
    <div v-html="parseShortcodes(text)"></div>
</template>

<script>
    import ShortcodeParser from 'meta-shortcodes'
    import Temperature from 'Temperature.vue'

    export default {

        data: () => {
            return {
                text: 'Heat oven at [temp c=200]',
                parser: ShortcodeParser()
            }
        },
        components: [
            Temperature
        ],
        methods: {
            parseShortcodes(text) {
                return this.parser.parse(text)
            }
        },
        created() {
            this.parser.add('temp', function(options) {
                return '<temperature :celsius="'+options.c+'"></temperature>'
            })
        }
    }
</script>

解析は正常に機能し、置換がフロントエンドに出力されます。しかし、<temperature>タグは文字どおりにレンダリングされ、Vue コンポーネントとしてではなく、ある程度予想されます。

オーブンを <temperature :celsius="200"></temperature> で加熱します

頭を包み込めないように見えるのは、Temperature定義したコンポーネントに実際に変換するためにどのような手順を実行する必要があるかです。それは可能ですか?私が見逃しているこれを行うためのより正統な方法はありますか?

v-htmlを使用してテキストをレンダリングすることには、セキュリティ上の懸念もあります。必ずしもこれが必要なわけではありませんがTemperature、エスケープされた文字列からコンポーネントが表示されることを期待するのは、さらに無理があると思います。v-htmlデータベースに挿入する前にいつでもサニタイズできますが、可能であれば避けたいと思います。

4

3 に答える 3

7

サンプルを機能させるには、Instructions コンポーネントの render 関数を使用する必要があります。レンダリング関数では、新しい Vue コンポーネントを作成できます。たとえば、'intruction' としましょう。これに、ショートコード解析から得られた文字列を渡して、テンプレートとして使用します。そのコンポーネント宣言では、Temperature コンポーネントを子として追加し、渡した prop をレンダリングします。以上です。例は次のとおりです。

Instructions.vue

<script>
    import ShortcodeParser from 'meta-shortcodes'
    import Temperature from 'Temperature.vue'

    export default {

        data: () => {
            return {
                text: 'Heat oven at [temp c=200]',
                parser: ShortcodeParser()
            }
        },
        methods: {
            parseShortcodes(text) {
                return this.parser.parse(text)
            }
        },
        render (createElement) {

            const template = this.parseShortcodes(this.text) //returns something like this <div class="intruction">'Heat oven at <temperature :celsius="200"></temperature>'</div>

            var component = Vue.component('instruction', {
                template: template,
                components: {
                    'temperature': Temperature
                }
            })

            return createElement(
                'div',
                {
                    class: {
                        instructions: true
                    }
                },
                [
                    createElement(component)
                ]
            )
       }
    }
</script>
于 2016-11-16T14:08:15.480 に答える
2

これが私のアプローチですが、それがあなたが求めているものかどうかはわかりません。動的にレンダリングするための中間コンポーネントを導入します。また、テスト用に Instructions.vue を少し変更しました。ショートコード パーサーに合わせて変更できます。

Instructions.vue

<template>
  <div>
    <input type="text" @input="parseInput" />
    <DynamicComponent :type="parsedType" :props="parsedProps"></DynamicComponent>
  </div>
</template>

<script>
import DynamicComponent from './DynamicComponent';


export default {
  components: {
    DynamicComponent
  },
  data() {
    return {
      parsedType: '',
      parsedProps: ''
    };
  },
  methods: {
    parseInput(e) { // parse the input
      if (e.target.value === '[temp c=200]') { // change this to use your parser
        this.parsedType = 'Temperature';
        this.parsedProps = { celsius: 200 };
      } else {
        this.parsedType = '';
        this.parsedProps = '';
      }
    }
  }
};
</script>

DynamicComponent.vue

<script>
import Temperature from './Temperature';
// import all other dynamically rendered components


export default {
  components: {
    // let it know what components are there
    Temperature
  },
  props: ['type', 'props'],
  render(h) {
    return h(this.type, {
      props: this.props
    }, this.$slots.default);
  }
};
</script>
于 2016-11-16T04:49:43.137 に答える
0

現在、テンプレートをコンパイルするステップがないため、テキストとしてレンダリングされます。テキストをコンポーネントに置き換えるには、次のことができると思います

  1. 温度が見つかったら、それをhtml要素にラップします
  2. JavaScript で新しい Vue インスタンスを作成します (文字列内の html テンプレートとしてではありません)
  3. el オプションまたは $mount を使用して、その要素の上部に vue インスタンスをマウントします
  4. v-html の代わりに補間を使用して XSS を防止します https://vuejs.org/v2/guide/syntax.html#Text
于 2016-11-13T11:57:31.373 に答える