【问题标题】:Elegantly inject pre-rendered element into Vue template优雅地将预渲染元素注入 Vue 模板
【发布时间】:2020-07-04 15:19:17
【问题描述】:

我正在尝试在 Vue 组件中呈现现有的 HTMLElement,如下所示:

const someElement = document.createElement('p'); // for instance

Vue.component('my-component', {
  data: () => ({ someElement }),
  template: '<div>{{ someElement }}</div>',
  // The above doesn't work; {{ moustache }} tags turn the element into a string.
});

有几种部分解决方案,但没有一个是完美的:

  1. 我可以使用v-html,它适用于某些元素,但无法保留,例如事件监听器。

    Vue.component('my-component', {
      data: () => ({ someElement }),
      template: '<div v-html="someElement.outerHTML"></div>',
    });
    
  2. 我可以在mounted() 钩子上手动注入元素,这会起作用,但有点难看并且感觉不习惯。

    Vue.component('my-component', {
      data: () => ({ someElement }),
      template: '<div></div>',
      mounted() {
        this.$el.appendChild(someElement);
      },
    });
    
  3. 如果我能以某种方式将元素包装在一个组件中,我就可以渲染该包装组件。但目前尚不清楚如何做到这一点。我可以使用manual render() function,但render() 似乎必须返回VNode 实例,而且我看不到将HTMLElement 对象转换为VNode 实例的方法。鉴于VNode 表示虚拟 DOM,而HTMLElement 是实际 DOM 的一部分,因此不应期望存在一种方式。

    Vue.component('element-wrapper', {
      render() {
        return someElement;  // can't, not a VNode
      },
    });
    Vue.component('my-component', {
      template: '<div><element-wrapper></element-wrapper></div>',
    });
    
  4. 我可以使用:is,但:is 需要一个组件选项对象,这意味着这个选项与#3 具有所有相同的困难。

    Vue.component('my-component', {
      template: '<div><component :is="elementAsComponent"></component></div>',
      data: () => ({
        elementAsComponent: /* again need to wrap element in component */,
      }),
    });
    

有没有优雅的方法来做到这一点?

【问题讨论】:

    标签: javascript vue.js


    【解决方案1】:

    这可以通过将自定义元素包装在一个非常薄的 Vue 组件中来实现,该组件只是将其根元素替换为挂载时的自定义元素。

    像这样(fiddle):

    const myCustomElement = document.createElement('p');
    myCustomElement.innerText = 'click me';
    myCustomElement.addEventListener('click', () => {
        myCustomElement.style.color = 'red';
    })
    
    /* Takes an HTMLElement and returns a wrapping Vue component options object */
    function liftToVue(element) {
      return {
        template: '<div></div>',
        mounted() {
            this.$el.replaceWith(element);
        },
        // If element is or may be a Promise, you can do:
        // async mounted() { this.$el.replaceWith(await element); }
      };
    }
    
    Vue.component('my-component', {
      data: () => ({ myCustomElementAsComponent: liftToVue(myCustomElement) }),
      template: '<div><component :is="myCustomElementAsComponent" /></div>',
    })
    

    不过,很有可能是an XY problem。看看你是否可以首先使用 Vue 组件而不是生成 HTMLElement 实例。

    【讨论】:

      猜你喜欢
      • 2017-11-22
      • 1970-01-01
      • 2023-03-02
      • 2020-06-12
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 2018-08-10
      • 2017-11-27
      相关资源
      最近更新 更多