vuejs 내에서 원시 HTML 렌더링

Aug 17 2020

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

이 작업을 수행했지만 HTML로 해석하려면 "{{data.body}}"가 필요합니다.

나는 완전히 초보자이고 누군가가 나를 도울 수 있기를 바랍니다.

지금까지 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 공격을 방지하고 .NET을 사용하여 전체 HTML 노드 세트를 DOM에 안전하게 삽입 할 수 있습니다 v-html.

다양한 보안 전문가가 검토 한 DOMPurify 는 원시 HTML을 삭제하는 데 적합한 라이브러리입니다.