vuejs内から生のhtmlをレンダリングする

Aug 17 2020

https://codepen.io/AnonymousCaptain/pen/eYZZOyO

これを作成しましたが、「{{data.body}}」をHTMLとして解釈する必要があります。

私はまったくの初心者です。誰かが私を助けてくれることを願っています。

これまでのところ、v-htmlと呼ばれるディレクティブを見て、デジタルの壁に頭をぶつけてきました...しかし、それが私が使用することになっているものかどうかはわかりません。

私はまた人々が次のようなことをするのを見ました:

Vue.component( "app"、{テンプレート: HTML

https://www.digitalocean.com/community/tutorials/vuejs-raw-html-binding これは私が必要なものかもしれませんか?

解決策を教えてください:)

new Vue({
  el: '#magiccardapp',
  data: {
    message: 'Hello',
    tabs: {
      'Title 1': {
        subtitle: 'some text here',
        title: 'Awesome Title',
        body: 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ab nam alias architecto officia, dolores animi qui debitis incidunt eius temporibus nostrum nihil soluta commodi molestiae necessitatibus ducimus amet. Suscipit, saepe!',
        img_1: 'https://placedog.net/320/180/?random',
        img_2: 'https://placedog.net/320/180/?random'
      },
      'Title 2': {
        subtitle: 'some cool text here',
        title: 'This is great',
        body: 'Lorem ipsasdfasdfasd alias architecto officia, dolores animi qui debitis incidunt eius temporibus nostrum nihil soluta commodi molestiae necessitatibus ducimus amet. Suscipit, saepe!',
        img_1: 'https://placedog.net/320/180/',
        img_2: 'https://placedog.net/320/180/'
      },
      'Title 3': {
        subtitle: 'some epic text here',
        title: 'Look I\'m a title!',
        body: 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ab nam aliasdfasdfaas architecto officia, dolores animi qui debitis incidunt eius temporibus nostrum nihil soluta commodi molestiae necessitatibus ducimus amet. Suscipit, saepe!',
        img_1: 'https://cdn.mos.cms.futurecdn.net/QjuZKXnkLQgsYsL98uhL9X-320-80.jpg',
        img_2: 'https://aldf.org/wp-content/uploads/2018/06/sad-dog-1846066_1920-320x180.jpg'
      },
      'Title 4': {
        subtitle: 'some other text here',
        title: 'LOREM IPSUM?',
        body: 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ab nam alias architecto officia, dolores animi qui debitis incidunt eius temporibus nostrum nihil soluta commodi molestiae necessitatibus ducimus amet. Suscipit, saepe!',
        img_1: 'https://cdn.mos.cms.futurecdn.net/QjuZKXnkLQgsYsL98uhL9X-320-80.jpg',
        img_2: 'https://aldf.org/wp-content/uploads/2018/06/sad-dog-1846066_1920-320x180.jpg'
      },
    },
    activeTab: 'Title 1',
  },
  computed:{
    tabContent() {
      return this.tabs[this.activeTab];
    },
  },
  methods: {
    setTabActive(tab) {
      this.activeTab = tab; 
    }
  },
  components:{
    'TabContent': {
      props: {
        data: Object,
      },
    }
  },
})
/*tabs*/

magiccarddeck {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  grid-gap: 1rem 1rem;
  align-items: center;
  max-width: 1000px;
  padding: 1rem 1rem;
  font-family: "Open Sans",
        sans-serif;
  text-align: center;
}

magiccard:hover {
  transform: scale(1.05);
  border-radius: 0.25rem;
  overflow: hidden;
  transition: 1s;
  -webkit-transition: 1s;
}

.line {
    height: 2px;
    width: 100%;
    margin: auto;
    background-color: #c38e3d;
}

magiccard {
    background: ghostwhite;
    border-radius: 0.25rem;
    overflow: hidden;
    transition: 1s;
    -webkit-transition: 1s;
    cursor: pointer;
    height: 100%;
    color: black;
    border: 0.3rem solid #c38e3d;
    height: max-content;
    filter: brightness(50%);
}

magiccard:hover {
  filter: brightness(100%);
}

magiccard.active {
  cursor: unset;
  transform: scale(1.05);
  filter: brightness(100%);
}

magiccard h3 {
  justify-content: center;
  letter-spacing: 4px;
  margin: 0px;
  color: black;
  font-weight: 400;
  font-size: 20px;
  line-height: 90%;
  padding-left: 0%;
  padding-top: 3%;
  padding-bottom: 3%;
  border-radius: 20px;
}

magiccard p {
  line-height: 1.6;
}




/* BOTTOM */

section {
    font-family: "Open Sans",
        sans-serif;
    font-weight: 800;
    color: ghostwhite;
    font-size: 12px;
}

#workshopgrid {
    padding: 1rem 1rem;
    max-width: 1000px;
    justify-self: center;
}

workshopbox {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    border: 0.3rem solid #c38e3d;
    border-radius: 0.5rem;
    background: ghostwhite;
    padding: 1rem 2rem;
    grid-template-areas:
        'text'
        'img';
}

@media (min-width: 800px) {
    workshopbox {
        grid-template-columns: 3fr 1fr;
        grid-template-areas:
            'text img';
    }

    workshopdescription {
        border-right-style: groove;
        padding-right: 2%;
    }

    workshopimg {
        padding-left: 1rem;
    }
}

@media (max-width: 799px) {
    workshopbox {}

    workshopdescription {
        border-bottom-style: groove;
    }

    workshopimg {
        padding-top: 1rem;
    }
}



.workshoptext {
    grid-area: 1 / 1 / 2 / 2;
    grid-area: text;
    margin: 0px;
    color: black;
    font-weight: 400;
    font-size: 15px;
    line-height: 100%;
    padding-top: 3%;
    padding-bottom: 2%;
}

workshopdescription {
    grid-area: 1 / 1 / 2 / 2;
    grid-area: text;
    margin: 0px;
    color: black;
    font-weight: 400;
    font-size: 15px;
    line-height: 100%;
    padding-top: 3%;
    padding-bottom: 2%;
}

workshopdescription h1 {
    line-height: initial;
}

workshopimg {
    grid-area: 1 / 2 / 2 / 3;
    grid-area: img;
    display: grid;
    grid-gap: 1rem;
    color: black;
    align-content: center;
    /* border-left: 1px solid; */
    justify-content: center;
}

/*transitions*/

.fade-enter-active > *,
.fade-leave-active > * {
  transition-duration: 200ms;
  transition-property: opacity, transform;
  transition-timing-function: cubic-bezier(0.6, 0.15, 0.35, 0.8);
}

.fade-enter > *,
.fade-leave-to > * {
  opacity: 0;
  transform: translateY(40px);
}

.fade-enter-active > *:nth-child(2) {
  transition-delay: 100ms;
}
.fade-enter-active > *:nth-child(3) {
  transition-delay: 200ms;
}

.fade-leave-active > *:nth-child(1) {
  transition-delay: 200ms;
}
.fade-leave-active > *:nth-child(2) {
  transition-delay: 100ms;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="magiccardapp">

  <magiccarddeck>
    <magiccard v-for="(tab, tabName) in tabs" :key="tabName" @click="setTabActive(tabName)" :class="{'active': tabName === activeTab}">

      <h3 class="tab-copy">{{ tabName }}</h3>
      <div class="line"></div>
 {{tab.subtitle}}
    </magiccard>
  </magiccarddeck>
  
  <section id="workshopgrid">
    <workshopbox>
      <workshopdescription>
        <transition name="fade" mode="out-in" appear :duration="500">
          <tab-content v-for="(tabContent, t) in tabs" :data="tabContent" :key="'content'+t" v-if="t === activeTab" inline-template>
            <div class="content">
              <h1>{{data.title}}</h1>
              <p>{{data.body}}</p>
            </div>
          </tab-content>
        </transition>

      </workshopdescription>
      <tab-content v-for="(tabContent, t) in tabs" :data="tabContent" :key="'workshopimg'+t" v-if="t === activeTab" inline-template>
      <workshopimg>
        <img :src="data.img_1">
        <img :src="data.img_2">
      </workshopimg>
        </tab-content>
    </workshopbox>
  </section>

</main>

回答

1 zuchmanski Aug 17 2020 at 19:29

v-html次のように使用できます。

<p v-html="data.body"></p>

<p>タグ内にhtmlをレンダリングします。

s4k1b Aug 17 2020 at 19:31

vueでhtmlとしてテキストを使用したい場合は、次を使用できます。 v-html

ただし、v-htmlこれを使用するとアプリがXSS攻撃を受けやすくなるため、使用しないように警告が表示されます。

この警告は常に表示され、正当な理由で表示されます。XSS攻撃を防ぐには、生で挿入するhtml文字列をサニタイズする必要があります。このサニテーションプロセスは、HTMLからスクリプトの実行につながる可能性のあるものをすべて取り除き、XSS攻撃を防ぎます。そのため、を使用してHTMLノードの完全なセットをDOMに安全に挿入できますv-html

さまざまなセキュリティ専門家によってレビューされているDOMPurifyは、生のhtmlをサニタイズするのに適したライブラリです。