实验了两种方式。vue模板组件异步渲染组件和函数式组件异步渲染组件。模板组件借用了slot插槽。

第一种方式,vue模板组件

建立一个vue文件 ,假如名字叫mountElement.vue

<template>
  <div id="mountedDiv"></div>
</template>
<script>
import { isVNode } from "./VnodeUtil";
import TempSlot from "./TempSlot";
import Vue from "vue";
export default {
  methods: {
    //渲染
    render(componentArr) {
      let self = this;
      let TempSoltConstructor = Vue.extend(TempSlot);
      let instance = new TempSoltConstructor();

      let vnodes = self.createVnode(componentArr);
      if (isVNode(vnodes)) {
        instance.$slots.default = [vnodes];
      }
      instance.$mount("#mountedDiv");
    },
     //生成虚拟dom
    createVnode(elementArr) {
      let self = this;
      const h = self.$createElement;
      return h(
        "div",
        {
          style: {
            height: "100vh",
            overflowY: "auto",
            width: "100vw",
            overflowX: "hidden"
          }
        },
        elementArr.map((element, index) => {
          return h(element.name, element, element.slot);
        })
      );
    }
  }
};
</script>

其中VnodeUtil为

const hasOwnProperty = Object.prototype.hasOwnProperty;

export function noop() {};

export function hasOwn(obj, key) {
  return hasOwnProperty.call(obj, key);
};
export function isVNode(node) {
    return node !== null && typeof node === 'object' && hasOwn(node, 'componentOptions');
};

TempSlot为

<template>
    <div>
        <slot>
        </slot>
    </div>
</template>

父组件引入mountElement.vue 并使用ref,渲染的时候,http请求(或其他异步方法)返回的dom结构(必须包含name,其他的内容参考createElement中参数),使用this.$ref.mountElement.render(需要渲染的dom结构)。

 

第二种方式,采用函数式组件

这里,全局注册了一个函数式组件


Vue.component('element-list', {
        functional: true,
        props: {
            componentArr: {
                type: Function,
                required: true
            }
        },
        render: function (createElement, context) {
            console.log("context=", context);
            //这里让传入的componentArr是函数,执行结果返回数组,避开不能直接传Observer观察对象问题
            var componentArr = context.props.componentArr();
            /**
             * 如果存在data.__ob__,说明data是被Observer观察的数据
             * 不能用作虚拟节点的data
             * 需要抛出警告,并返回一个空节点
             *
             * 被监控的data不能被用作vnode渲染的数据的原因是:
             * data在vnode渲染过程中可能会被改变,这样会触发监控,导致不符合预期的操作
             * Avoid using observed data object as vnode data:
             * Always create fresh vnode data objects in each render!
             */
            return createElement('div',
                {
                    style: {
                        height: "100vh",
                        overflowY: "auto",
                        width: "100vw",
                        overflowX: "hidden"
                    }
                },
             getElements(createElement, componentArr)
                /** componentArr && componentArr.length > 0 ? componentArr.map((element, index) => {
                    return createElement(
                        element.name,
                        element,
                        element.slot
                    )
                }) : "" */
            )
        }
    })

    function getElements(createElement, root) {
        return root ? (root instanceof Array && root.length > 0 ? root.map((element, index) => {
            return createElement(
                element.name,
                element,
                getElements(createElement, element.children)
            )
        }) : root.slot) : root
    }

在父组件中调用

<element-list :componentArr="componentArr"></element-list>

其中

data() {
    return {
      componentArr: () => [] //这个参数一定要是个函数
    };
  },

异步渲染的时候,只要改变componentArr的值,这里 componentArr要赋值给一个函数。比如 

componentArr =()=>{

    return [{

        name:"div",

        slot:"测试div"

        },{

        name:"a",

        slot:"测试a"}

    ]
}

 

Logo

前往低代码交流专区

更多推荐