解决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/

posted @ 2019-08-14 16:56  世有因果知因求果  阅读(2740)  评论(0编辑  收藏  举报