解決v-html無法理解vue模版的問題-動態獲取模版,動態插入app並使用當下app狀態數據需求


很多情況下,我們需要使用動態的html作為某個dom元素的inner html,如果這個內容是標准的html的話,則v-html能夠圓滿滿足需求,而如果內容包含了vue組件,則使用v-html就不能達到你希望的目標了。

我研究有兩種方案來解決,一種原生使用v-if提供的compile和mount特性;第二種類則使用render函數帶來的特殊能力實現這一點。

其中render函數的方案有一個現成的vue組件作為參考. v-runtime-template.

該組件產生的背景:

在項目開發中需要從server上接收template string.比如,即允許用戶自己通過拖拽來設計接口風格的程序中,通常被保存為vue template code,而該code會使用由前端工程師開發好的預定義組件。這些組件將通過api調用請求數據並且填充頁面的一個部分。

正如上面所說的,v-html很大程度上能夠部分滿足需求:

<template>
  <div>
    <div v-html="template"></div>
  </div>
</template>

<script>
export default {
  data: () => ({
    template: `
      <h2>Howdy Yo!</h2>
      <a href="croco-fantasy">Go to the croco-fantasy</a>
    `
  }),
};
</script>

上面的template字符串可以從server ajax call中獲得。問題是,如果該template string中包含了vue template code,則無能為力!

export default {
  data: () => ({
    template: `
      <app-title>Howdy Yo!</app-title>
      <vue-router to="/croco-fantasy">Go to the croco-fantasy</vue-router>
    `
  }),
};

v-html並不能理解vue-router這個tag,因此將會丟棄很多重要功能。

這時作者就需要開發一個能夠解決該類問題的"proxy"組件

v-runtime-template的實現原理是: 自動獲得v-runtime-template的父組件的context並且使得vue編譯並且attach.

render(h) {
    if (this.template) {
      const parent = this.parent || this.$parent
      const {
        $data: parentData = {},
        $props: parentProps = {},
        $options: parentOptions = {}
      } = parent;
      const {
        components: parentComponents = {},
        computed: parentComputed = {},
        methods: parentMethods = {}
      } = parentOptions;
      const {
        $data = {},
        $props = {},
        $options: { methods = {}, computed = {}, components = {} } = {}
      } = this;
      const passthrough = {
        $data: {},
        $props: {},
        $options: {},
        components: {},
        computed: {},
        methods: {}
      };

      //build new objects by removing keys if already exists (e.g. created by mixins)
      Object.keys(parentData).forEach(e => {
        if (typeof $data[e] === "undefined")
          passthrough.$data[e] = parentData[e];
      });
      Object.keys(parentProps).forEach(e => {
        if (typeof $props[e] === "undefined")
          passthrough.$props[e] = parentProps[e];
      });
      Object.keys(parentMethods).forEach(e => {
        if (typeof methods[e] === "undefined")
          passthrough.methods[e] = parentMethods[e];
      });
      Object.keys(parentComputed).forEach(e => {
        if (typeof computed[e] === "undefined")
          passthrough.computed[e] = parentComputed[e];
      });
      Object.keys(parentComponents).forEach(e => {
        if (typeof components[e] === "undefined")
          passthrough.components[e] = parentComponents[e];
      });

      const methodKeys = Object.keys(passthrough.methods || {});
      const dataKeys = Object.keys(passthrough.$data || {});
      const propKeys = Object.keys(passthrough.$props || {});
      const templatePropKeys = Object.keys(this.templateProps);
      const allKeys = dataKeys.concat(propKeys).concat(methodKeys).concat(templatePropKeys);
      const methodsFromProps = buildFromProps(parent, methodKeys);
      const finalProps = merge([
        passthrough.$data,
        passthrough.$props,
        methodsFromProps,
        this.templateProps
      ]);
      const provide = this.$parent._provided;

      const dynamic = {
        template: this.template || "<div></div>",
        props: allKeys,
        computed: passthrough.computed,
        components: passthrough.components,
        provide: provide
      };

      return h(dynamic, { props: finalProps });
    }
  }

以上代碼就是v-runtime-template的核心render函數.

<template>
  <div>
    <v-runtime-template :template="template"/>
  </div>
</template>

<script>
import VRuntimeTemplate from "v-runtime-template";
import AppTitle from "./AppTitle";

export default {
  data: () => ({
    template: `
      <app-title>Howdy Yo!</app-title>
      <vue-router to="/croco-fantasy">Go to the croco-fantasy</vue-router>
    `
  }),
components: {
    AppTitle
  }
};
</script>

上面就是使用v-runtime-template核心用法代碼。有一點需要額外指出的是,它還可以訪問父組件的scope,意味着任何通過data,props,computed或者methods能夠訪問的都能用.這樣你就可以擁有dynaimc templates的能力:可被父組件訪問的活躍reactive數據.

比如:

export default {
  data: () => ({
    animal: "Crocodile",
    template: `
      <app-title>Howdy {{animal}}!</app-title>
      <button @click="goToCrocoland">Go to crocoland</button>
    `
  }),
  methods: {
    goToCrocoland() {
      // ...
    }
  }

如果你需要動態地應用從server端返回的template,並能插入vue app中獲得對應的scope context訪問能力,那么v-runtime-template是一個非常不錯的選擇!~

https://alligator.io/vuejs/v-runtime-template/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM