Vue 中的渲染函数和递归组件

Posted

技术标签:

【中文标题】Vue 中的渲染函数和递归组件【英文标题】:Render Functions and Recursive Components in Vue 【发布时间】:2020-09-11 11:22:28 【问题描述】:

我在理解递归组件时遇到了一些麻烦,我相信对于我想要完成的事情,这可能是最好的方法。这是我目前所处的位置的小提琴,将在下面解释。

https://jsfiddle.net/wp0hon7z/2/

我正在尝试遍历嵌套的 JSON,它本质上是在模仿 DOM。每个“节点”看起来像这样。您可以打开 fiddle 以查看更多嵌套的 JSON 和

"tagName": "section",
"classes": ["container", "mx-auto"],
"attrs": "id":"main",
"textNode": "",
"children": []

目前我能够递归地遍历并将每个节点创建到一个组件中,并将它们放入一个组件数组中,我在 Vue 实例中填充该数组。

问题是,子组件需要显示在父组件内。我在想也许用组件对象创建一个对象,然后使用递归组件来解析这些,但我不知道该怎么做。

另一个想法可能是创建一个带有父 ID 的平面组件数组?然后可能以某种方式使用它?

有关如何进行此操作的一些指导会很棒,我认为递归组件会有所帮助,但不确定除了创建元素/渲染函数之外我还能如何使用它。每个节点都需要与类列表、属性列表、on 等进行 2 路绑定。我计划跟踪这些并使用状态/存储进行编辑,可能是 vuex。

目前在 Fiddle 中看到的代码会显示 JSON 中的所有组件,但没有嵌套,所以只是一个接一个。

    const jsonData = [
        
            "tagName": "section",
            "classes": ["container","mx-auto"],
            "attrs": ,
            "textNode": "",
            "children": [
                
                   "tagName": "div",
                    "classes": ["flex","flex-wrap"],
                    "attrs": ,
                    "textNode": "",
                    "children": [
                        
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": ,
                            "textNode": "Hello"
                        ,
                        
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": ,
                            "textNode": "Goodbye"
                        
                    ]
                
            ]
        
    ];



    let Components = [];
    let uuid = 0;
 

    function recurse()  
            recursiveInitialize(jsonData)
      
    

 function recursiveInitialize(j) 

        
        if (Array.isArray(j)) 
            return j.map((child) => recursiveInitialize(child))
        

        if (j.children && j.children.length > 0) 


             initializeComponent(j)

            console.log("Hi I am " + j["tagName"] + " and a parent")


            j.children.forEach((c) => 
                console.log("Hi I am " + c["tagName"] + " and my parent is " + j["tagName"])
                recursiveInitialize(c)
            );


        

        else 
            console.log("Hi, I dont have any kids, I am " + j["tagName"])
            initializeComponent(j)
        
    
    

  function initializeComponent(jsonBlock)
        let tempComponent = 
        		name: jsonBlock["tagName"]+ uuid.toString(),
            methods: 
                greet() 
                    store.setMessageAction(this)
                
              ,
            data: function() 
                return 
                    tagName: jsonBlock["tagName"],
                    classes: jsonBlock["classes"],
                    attrs: jsonBlock["attrs"],
                    children: jsonBlock["children"],
                    textNode: jsonBlock["textNode"],
                    on: click: this.greet,
                    ref: uuid,
                
            ,
            beforeCreate() 
                this.uuid = uuid.toString();
                uuid += 1; 
                
            
            ,
            render: function(createElement) 
                  return createElement(this.tagName, 
                    class: this.classes,
                    on: 
                        click: this.greet
                    ,
                    attrs: this.attrs,
                , this.textNode);
            ,
            mounted() 
                // example usage
                console.log('This ID:', this.uuid);
            ,
        
        Components.push(tempComponent);
        return tempComponent
    
 
    const App = new Vue(
        el: '#app',

        data: 
            children: [
                Components
            ],
        ,
        beforeCreate() 
            recurse();
            console.log("recurseRan")
        ,

        mounted() 
            this.populate()
        ,

        methods: 
            populate() 
                let i = 0;
                let numberOfItems = Components.length;

                for (i = 0; i < numberOfItems; i++) 
                    console.log("populate: " + Components[i])
                    this.children.push(Components[i]);
                

            ,
        
    );
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


                        <div id="app">
                            <template v-for="(child, index) in children">
                                <component :is="child" :key="child.name"></component>
                            </template>
                        </div> 

【问题讨论】:

【参考方案1】:

你有没有尝试过类似的事情

// MyRecursiveComponent.vue
<template>
  <div>
    <!-- node content -->
    <div v-for="childNode" in jsonData.children">
      <MyRecursiveComponent :jsonData="childNode" />
    </div>
  </div>
<template

【讨论】:

那么我应该在哪里调用渲染函数来实际编译将替换它的 html【参考方案2】:

    const jsonData = [
        
            "tagName": "section",
            "classes": ["container","mx-auto"],
            "attrs": ,
            "textNode": "",
            "children": [
                
                   "tagName": "div",
                    "classes": ["flex","flex-wrap"],
                    "attrs": ,
                    "textNode": "",
                    "children": [
                        
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": ,
                            "textNode": "Hello"
                        ,
                        
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": ,
                            "textNode": "Goodbye"
                        
                    ]
                
            ]
        
    ];



    let Components = [];
    let uuid = 0;
 

    function recurse()  
            recursiveInitialize(jsonData)
      
    

 function recursiveInitialize(j) 

        
        if (Array.isArray(j)) 
            return j.map((child) => recursiveInitialize(child))
        

        if (j.children && j.children.length > 0) 


             initializeComponent(j)

            console.log("Hi I am " + j["tagName"] + " and a parent")


            j.children.forEach((c) => 
                console.log("Hi I am " + c["tagName"] + " and my parent is " + j["tagName"])
                recursiveInitialize(c)
            );


        

        else 
            console.log("Hi, I dont have any kids, I am " + j["tagName"])
            initializeComponent(j)
        
    
    

  function initializeComponent(jsonBlock)
        let tempComponent = 
        		name: jsonBlock["tagName"]+ uuid.toString(),
            methods: 
                greet() 
                    store.setMessageAction(this)
                
              ,
            data: function() 
                return 
                    tagName: jsonBlock["tagName"],
                    classes: jsonBlock["classes"],
                    attrs: jsonBlock["attrs"],
                    children: jsonBlock["children"],
                    textNode: jsonBlock["textNode"],
                    on: click: this.greet,
                    ref: uuid,
                
            ,
            beforeCreate() 
                this.uuid = uuid.toString();
                uuid += 1; 
                
            
            ,
            render: function(createElement) 
                  return createElement(this.tagName, 
                    class: this.classes,
                    on: 
                        click: this.greet
                    ,
                    attrs: this.attrs,
                , this.textNode);
            ,
            mounted() 
                // example usage
                console.log('This ID:', this.uuid);
            ,
        
        Components.push(tempComponent);
        return tempComponent
    
 
    const App = new Vue(
        el: '#app',

        data: 
            children: [
                Components
            ],
        ,
        beforeCreate() 
            recurse();
            console.log("recurseRan")
        ,

        mounted() 
            this.populate()
        ,

        methods: 
            populate() 
                let i = 0;
                let numberOfItems = Components.length;

                for (i = 0; i < numberOfItems; i++) 
                    console.log("populate: " + Components[i])
                    this.children.push(Components[i]);
                

            ,
        
    );
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


                        <div id="app">
                            <template v-for="(child, index) in children">
                                <component :is="child" :key="child.name"></component>
                            </template>
                        </div> 

【讨论】:

嗨,Maks,我认为您发布的代码与我的相同。

以上是关于Vue 中的渲染函数和递归组件的主要内容,如果未能解决你的问题,请参考以下文章

Vue组件递归渲染

Vue组件递归渲染

Vue组件递归渲染

JS使用Vue自定义组件实现动态多层数据渲染+递归+踩坑

JS使用Vue自定义组件实现动态多层数据渲染+递归+踩坑

JS使用Vue自定义组件实现动态多层数据渲染+递归+踩坑