Full examples of functional components

Keywords: Vue IE

Previously created components were relatively simple, with no management or monitoring of any state passed to them, and no lifecycle approach. It's just a function that receives parameters.
In the following example, we mark the component as functional, which means that it is stateless (no responsive data) and no instance (no this context).

A functional component is like this:

Vue.component('my-component', {
  functional: true,
  // Props optional
  props: {
    // ...
  },
  // To compensate for the lack of examples
  // Provide a second parameter as context
  render: function (createElement, context) {
    // ...
  }
})

Note: In versions prior to 2.3.0, the props option is required if a functional component wants to accept props. In versions 2.3.0 or above, you can omit the props option, and all properties on the components are automatically resolved to props.

In versions 2.5.0 and above, if you use Single file component Then template-based functional components can declare as follows:

<template functional>
</template>

Everything a component needs is passed through context, including:

  • props: Provides all prop objects
  • Children: Array of VNode child nodes
  • slots: A function that returns all slotted objects
  • data: passed to components data object As the second parameter of createElement, the incoming component
  • Parent: A reference to the parent component
  • listeners: (2.3.0+) An object that contains all event listeners registered on the parent component. This is just an alias to data.on.
  • injections: (2.3.0+) If used inject Options, then the object contains attributes that should be injected.

After adding function: true, simple updates between render functions of anchor header components add context parameters, this.$slots.default updates to context.children, and then this.level updates to context.props.level.  

Because a functional component is only a function, the rendering overhead is much lower. However, the lack of persistent instances also means that functional components do not appear. Vue devtools In the component tree.

Among the properties of the above contexts, context.props, context.data and context.children are used more frequently. context.props is used to transfer data. context.data transfers attributes such as class, id, etc. context.children refers to the default value of the slot, this.$slots.default.

Example 1

<body>
    <div id="app">
        <my-function-button v-bind:class="className" id="btn1">button</my-function-button>
    </div>
    <script src="js/vue.js"></script>
    <script>
   
     Vue.component("my-function-button",{
        functional:true,
        render:function(createElement,context){
            return createElement("button",context.data,context.children)
        }
     })

    new Vue({
        el: '#app',
        data: {
            className:"btnname"
        },
    });
    //context.data is used to transfer attributes, and context.props is used to transfer data.
    </script>
</body>

Example two

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>AdminLTE 2 | Morris.js Charts</title>
    <!-- Tell the browser to be responsive to screen width -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <!-- Morris charts -->

</head>

<body>
    <div id="app">
        <smart-list :items="items" v-bind:number="1" class="specialColor">hi</smart-list>
    </div>
    <script src="js/vue.js"></script>
    <script>
    // Module 1
    var EmptyList = {
        template: "<p>Empty list</p>"
    }
    // Module 2
    var Tablist ={
        props:["chuandiData"],
        template:`
           <ul>
               <li v-for="item in chuandiData">{{item.name}}</li>
           </ul>
        `
    }

    // Module 3
    var component3 = {
        template:` 
        <div> this is component3 </div>
        `
    }
    Vue.component("smart-list", {
        functional: true,
        props: {
            items: {
                type: Array,
                required: true
            }
        },
        render: function(createElement, context) {
            console.log(context)
            function appropriateListComponent() {
                var items = context.props.items
                if (items.length === 0) {
                    return EmptyList
                }
                if (items.length == 3) return Tablist
            }
            return createElement(
                appropriateListComponent(),
                {
                    props:{
                        chuandiData:context.props.items
                    }
                }
               
            )
        }
    })


    new Vue({
        el: '#app',
        data: {
            items: [{
                    name: 'a',
                    id: 0
                },
                {
                    name: 'b',
                    id: 1
                },
                {
                    name: 'c',
                    id: 2
                }
            ]

        },

    });
    //context.data is used to transfer attributes, and context.props is used to transfer data.
    </script>
</body>

</html>

Example three

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>AdminLTE 2 | Morris.js Charts</title>
    <!-- Tell the browser to be responsive to screen width -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <!-- Morris charts -->

</head>

<body>
    <div id="app">
        <smart-list :items="items" v-bind:number="1" class="specialColor">hi</smart-list>
    </div>
    <script src="js/vue.js"></script>
    <script>
    // Module 1
    var EmptyList = {
        template: "<p>Empty list</p>"
    }
    // Module 2
    var Tablist = {
        template: `
         <ul>
             <li><slot></slot></li>
         </ul>
        `
    }
    Vue.component("smart-list", {
        functional: true,
        props: {
            items: {
                type: Array,
                required: true
            }
        },
        render: function(createElement, context) {
            console.log(context)
            function appropriateListComponent() {
                var items = context.props.items
                if (items.length === 0) {
                    return EmptyList
                }
                if (items.length == 3) return Tablist
            }
            return createElement(
                appropriateListComponent(),
                context.children
            )
        }
    })


    new Vue({
        el: '#app',
        data: {
            items: [{
                    name: 'a',
                    id: 0
                },
                {
                    name: 'b',
                    id: 1
                },
                {
                    name: 'c',
                    id: 2
                }
            ]

        },

    });
    //context.data is used to transfer attributes, and context.props is used to transfer data.
    </script>
</body>

</html>

Example four

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>AdminLTE 2 | Morris.js Charts</title>
    <!-- Tell the browser to be responsive to screen width -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <!-- Morris charts -->
    <link rel="stylesheet" href="../../dist/css/Basic.css">
    <link rel="stylesheet" href="../../dist/css/lanrenzhijia.css">
</head>

<body>
    <div id="app">
        <smart-item :data1="data">hi</smart-item>
        <button @click="change('img')">Switch to Pictures as Components</button>
        <button @click="change('video')">Switch to Video as Component</button>
        <button @click="change('text')">Switch to Text Component</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
    // Picture Component Options
    var ImgItem = {
        props: ['data2'],
        render: function(createElement) {
            return createElement('div', [
                createElement('p', 'Picture component'),
                createElement('img', {
                    attrs: {
                        src: this.data2.url
                    }
                })
            ]);
        }
    }
    // Video component
    var VideoItem = {
        props: ['data2'],
        render: function(createElement) {
            return createElement('div', [
                createElement('p', 'Video component'),
                createElement('video', {
                    attrs: {
                        src: this.data2.url,
                        controls: 'controls',
                        autoplay: 'autoplay'
                    }
                })
            ]);
        }
    };
    /*Plain text component*/
    var TextItem = {
        props: ['data2'],//2. Receiving props objects from 1
        render: function(createElement) {
            return createElement('div', [
                createElement('p', 'Plain text component'),
                createElement('p', this.data2.content)
            ]);
        }
    };

    Vue.component('smart-item', {
        functional: true,
        render: function(createElement, context) {
            function getComponent() {
                var data = context.props.data1;
                if (data.type === 'img') return ImgItem;
                if (data.type === 'video') return VideoItem;
                return TextItem;
            }
            return createElement(
                getComponent(), {
                    props: {
                        data2: context.props.data1 //getComponent() returns components, and props is used to pass data to the returned components, such as the TextItem component.
                    }
                }
                ,context.children  //Can not need
            )
        },
        props: {
            data1: {
                type: Object,
                required: true
            }
        }
    });
    new Vue({
        el: '#app',
        data: {
            data: {}
        },
        methods: {
            change: function(type) {
                if (type === 'img') {
                    this.data = {
                        type: 'img',
                        url: 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'
                    }
                } else if (type === 'video') {
                    this.data = {
                        type: 'video',
                        url: 'http://vjs.zencdn.net/v/oceans.mp4'
                    }
                } else if (type === 'text') {
                    this.data = {
                        type: 'text',
                        content: 'This is a plain text.'
                    }
                }
            }
        },
        created: function() {
            this.change('text');
        }
    });
	//
    </script>
</body>

</html>

Posted by navarre on Thu, 16 May 2019 03:02:24 -0700