programing

vuejs2 데이터에 하위 구성 요소 동적 삽입($compile 또는 v-html 남용 없음)

randomtip 2022. 7. 10. 20:30
반응형

vuejs2 데이터에 하위 구성 요소 동적 삽입($compile 또는 v-html 남용 없음)

새로운 vuejs 컴포넌트를 미리 정의하지 않은 HTML 블록 내의 임의의 지점에 즉시 삽입하고 싶습니다.

다음은 제가 하려는 일을 보여주는 약간 조작된 예입니다.

Vue.component('child', {
  // pretend I do something useful
  template: '<span>--&gt;<slot></slot>&lt;--</span>'
})

Vue.component('parent', {
  data() {
    return {
      input: 'lorem',
      text: '<p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p>'
    }
  },
  template: `<div>
      Search: <input type='text' v-model="input"><br>
      <hr>
      This inserts the child component but doesn't render it 
      or the HTML:
      <div>{{output}}</div>
      <hr>
      This renders the HTML but of course strips out the child component:
      <div v-html="output"></div>
      <hr>
      (This is the child component, just to show that it's usable here: 
      <child>hello</child>)
      <hr>
      This is the goal: it renders both the input html 
      and the inserted child components:
      TODO ¯\_(ツ)_/¯
    </div>`,
  computed: {
    output() {
      /* This is the wrong approach; what do I replace it with? */
      var out = this.text;
      if (this.input) {
        this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
        var regex = new RegExp(this.input, "gi");
        out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
      }
      return out;
    }
  }
});

new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
  <parent></parent>
</div>

위의 토막에서 다음과 같이 가정합니다.data.text는 sanitized HTML 입니다.<child>유용한 기능을 하는 서브 컴포넌트입니다.이러한 컴포넌트들을 정리하면data.text미리 알 수 없는 것들이죠.(input데모용입니다.이 MCVE는 제가 구축 중인 코드와 실제로 유사하지는 않습니다. 단지 제가 어떤 상황에 처해 있는지를 보여주는 예입니다.)

그럼, 어떻게 해야 할까요?output함수 또는 부모 컴포넌트의 템플릿으로 HTML이 모두input및 삽입된<child>템플릿이 올바르게 렌더링되고 있습니까?

내가 시도한 것

  • Vue 1에서는 이에 대한 답은 간단합니다.$compile. vuejs2를 사용하고 있습니다.$compile(XSS의 취약성을 순진하게 도입하는 것이 너무 쉬워졌다는 정당한 우려 때문에)

  • v-module은 공급된 데이터를 삭제하여 하위 구성 요소를 제거합니다.물론 이 방법은 아닙니다.(이 페이지에서는 파셜을 대신 사용하는 것이 권장되지만 어떻게 이 상황에 적용할 수 있을지 모르겠습니다.어쨌든 vue2에서도 파셜이 삭제되었습니다.)

  • 나는 의 결과를 통과시키려고 노력했다.output()다른 컴포넌트로 변환하여 템플릿으로 사용합니다.이것은 유망한 접근 방식인 것 같습니다만, 그 보조 컴포넌트의 템플릿을 변경하는 방법을 알 수 없습니다. template다른 많은 컴포넌트 속성과 같은 함수가 아닌 문자열만 받아들이기 때문에 템플릿html을 예를 들어 프롭에 전달할 수 없습니다.리라이트 같은 거this.template안에서.beforeMount()또는bind()그랬으면 좋겠지만, 거기에서도 즐거움은 없어요.컴포넌트를 마운트하기 전에 템플릿 문자열을 대체할 수 있는 다른 방법이 있습니까?

  • 와는 달리template, 컴포넌트의render()기능...하지만 그 HTML 문자열을 네스트된 상태로 해석해야 하는 상황이 여전히 계속되고 있습니다.createElement기능들.애초에 Vue가 사내에서 하고 있는 일이 바로 이것입니다.여기서 직접 재창조하는 것 외에 다른 방법이 있을까요?

Vue.component('foo', {
  props: ['myInput'],
  render(createElement) {
    console.log(this.myInput); // this works...
    // ...but how to parse the html in this.myInput into a usable render function?
    // return createElement('div', this.myInput);
  },
})
  • 수 .<foo inline-template>{{$parent.output}}</foo>{{output}}돌이켜보면 그것은 명백해야 했지만 시도해 볼 가치가 있었다.

  • 즉석에서 비동기 컴포넌트를 구축하는 것이 해답일까요?이렇게 하면 임의의 템플릿이 있는 컴포넌트를 생성할 수 있지만 부모 컴포넌트에서 컴포넌트를 적절하게 호출하여 공급하려면 어떻게 해야 할까요?output (의 인스턴스가 동시에 이 있어 싱글톤이 다른할 수 .)(글로벌이나 싱글톤 없이 여러 인스턴스를 동시에 표시할 수 있는 다른 입력으로 재사용할 수 있어야 합니다).

  • 난 심지어 바보같은 생각까지 해봤어output()<child>메인 템플릿에서 다음과 같은 작업을 수행합니다.

  ...
  <template v-for="chunk in output">
      <span v-html="chunk"></span>
      <child>...</child>
  </template>
  ....

번거로우시다면 가능합니다.자녀의 슬롯에 있는 것도 다른 어레이로 분할하여 v-for 중에 인덱스로 가져와야 합니다만, 그렇게 할 수 있습니다.만약 input할 때 각 가 있는 . HTML을 분할할 때 나는 종종 각각의 태그에 불균형한 태그가 붙게 된다.chunk이 될 수 v-html 어쨌든 이 에 더 방법이 입니다.어쨌든 이 모든 전략은 잘못된 해킹처럼 느껴집니다. 더 나은 방법이 있을 것입니다.

  • 를 「에 드롭 도 있습니다.v-html그런 다음 (어떤 식으로든) 사후 DOM 조작을 통해 자 컴포넌트를 적절한 위치에 삽입할 수 있습니까?이 옵션은 해킹처럼 느껴지기 때문에 자세히 알아보지 못했습니다. 을 사용법 하지만 다른 모든 것이 실패하면 어떻게 될까요?

몇 가지 선제적 면책 사항

  • 는 XSS에 잘 있습니다.$compile- 거 - 작전 같은 거. - 작전 같은 거.어떤 방법으로든 비위생적인 사용자 입력은 포함되지 않습니다.사용자는 임의의 컴포넌트 코드를 삽입하지 않고 컴포넌트가 사용자 정의 위치에 하위 컴포넌트를 삽입해야 합니다.
  • XY의 문제가 아니라 컴포넌트를 즉시 삽입할 필요가 있다고 확신하고 있습니다(실패한 횟수와 막다른 골목에서 이 문제에 대해 충분히 생각해 보았습니다).하지만 비슷한 결과를 가져올 수 있는 다른 접근법이 있다면, 나는 열심히 귀를 기울인다.중요한 점은 어떤 컴포넌트를 추가해야 하는지 알고 있지만 어디에 추가해야 할지 미리 알 수 없다는 것입니다.그 결정은 런타임에 이루어집니다.
  • vue-cli하고 있습니다가 아닙니다.Vue.component()위의 샘플과 같이.그 구조에서 크게 벗어나지 않는 답이 선호되지만, 효과가 있는 것은 무엇이든 효과가 있습니다.

진행!

@에서 @BertEvans가 하고 있습니다.Vue.compile()존재한다고는 믿을 수 없고 놓쳐버린 존재라는 거죠

그러나 이 문서처럼 글로벌 변수에 의존하지 않고서는 여전히 사용하기 어렵습니다.그러면 템플릿이 글로벌하게 렌더링되지만 하드코드로 변환됩니다.

var precompiled = Vue.compile('<span><child>test</child></span>');
Vue.component('test', {
  render: precompiled.render,
  staticRenderFns: precompiled.staticRenderFns
});

"Error in render function: "Error in Render function: "는 정의되어 있지 않습니다.참조 에러: 는 정의되어 있지 .참조:staticRenderFns 안 있다render★★★★★★★★★★★★★★★★★★?

Vue.component('test', {
  props: ['input'],
  render() { return Vue.compile(this.input).render()},
  staticRenderFns() {return Vue.compile(this.input).staticRenderFns()}
});

의 다른 (2개의)가 .compile()---내부 프리 합니다.beforeMount()후한 오류가 발생합니다.)render' staticRenderFns'는 staticRenderFns'로 반환됩니다.

이것은 정말 올바른 방향으로 가고 있는 것처럼 느껴지지만, 나는 단지 어처구니없는 구문 오류 같은 것에 사로잡혀 있을 뿐이다.

위 댓글에서 말씀드렸듯이$compile 「」는 되어 있습니다.Vue.compile는 특정 빌드에서 사용할 수 있습니다.아래를 사용하는 것은 몇 가지 경우를 제외하고 당신이 의도한 대로 작동합니다.

Vue.component('child', {
  // pretend I do something useful
  template: '<span>--&gt;<slot></slot>&lt;--</span>'
})

Vue.component('parent', {
  data() {
    return {
      input: 'lorem',
      text: '<div><p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p></div>'
    }
  },
  template: `<div>
      Search: <input type='text' v-model="input"><br>
      <hr>
      <div><component :is="output"></component></div>
    </div>`,
  computed: {
    output() {
      if (!this.input)
         return Vue.compile(this.text)
      /* This is the wrong approach; what do I replace it with? */
      var out = this.text;
      if (this.input) {
        this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
        var regex = new RegExp(this.input, "gi");
        out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
        out = Vue.compile(out)
      }
      return out;
    }
  }
});

new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
  <parent></parent>
</div>

웹 팩을 사용하여 빌드한다고 하셨는데, 컴파일러가 없는 빌드의 기본값은 Vue라고 생각하기 때문에 다른 빌드를 사용하려면 수정해야 합니다.

하다, 다이나믹하다를 했습니다.component이치노

★★text에는 여러 루트가 있기 때문에 유효한 템플릿이 아닙니다.을 했다.div유효한 템플릿으로 만듭니다.

하는 경우 합니다.text예를 들어 "i" 또는 "di" 또는 "p"를 입력하면 결과가 예상과 다르며 특정 조합이 컴파일에 오류를 발생시킵니다.

Vert Evans . 파일을 로 이 .Vue.component()때 이 수 을 올립니다

적절한 Vue 빌드를 입수

2 1에서 vue-cli 2( 1 1)를 합니다.에서는Vue.compile빌드에서 할 수 . 하세요.확인해 주세요.webpack.base.conf.js츠키다

'vue$': 'vue/dist/vue.esm.js' // or vue/dist/vue.common.js for webpack1

'vue/dist/vue.runtime.esm.js'.( 」vue init webpack완전한 스탠드아론 빌드가 이미 있습니다.또한 "webpack-simple" 템플릿은 완전한 스탠드아론 빌드를 설정합니다.)

Vue-cli 3은 약간 다르게 동작하며 기본적으로는 Vue.compile을 사용할 수 없습니다.여기서 Vue.compile을 추가해야 합니다.runtimeCompiler의 판결을 내리다vue.config.js:

module.exports = {
    /* (other config here) */
    runtimeCompiler: true
};

컴포넌트

"자녀" 컴포넌트는 일반 .vue 파일일 수 있습니다.특별한 것은 없습니다.

"부모" 컴포넌트의 베어본 버전은 다음과 같습니다.

<template>
    <component :is="output"></component>
</template>
<script>
import Vue from 'vue';
import Child from './Child'; // normal .vue component import

export default {
  name: 'Parent',
  computed: {
    output() {
      var input = "<span>Arbitrary single-root HTML string that depends on <child></child>.  This can come from anywhere; don't use unsanitized user input though...</span>";
      var ret = Vue.compile(input);
      ret.components = { Child };  // add any other necessary properties similarly
      ret.methods = { /* ... */ }  // like so
      return ret;
    }
  }
};
</script>

과 웹 팩 후를 (「」 「」 「」 「」)ret.components: {Child}」를 참조해 주세요.

언급URL : https://stackoverflow.com/questions/44369839/dynamically-insert-child-components-inside-vuejs2-data-without-compile-or-abus

반응형