美文网首页
Vue2之virtual dom,render函数化组件

Vue2之virtual dom,render函数化组件

作者: yancolin | 来源:发表于2018-04-28 11:35 被阅读43次

    Vue.js提供了一个functional的布尔值选项,设置为true可以使组件无状态和无实例,也就是没有data和this上下文。这样用render函数返回虚拟节点可以更容易渲染,因为函数化组件只是一个函数,渲染开销要小很多。
    使用函数化组件时,Render函数提供了第二个参数context来提供临时上下文。组件需要的data,props,slots,children,parent都是通过这个上下文来传递的,比如this.level要改写成context.props.level,this.$slolts.default改写成context.children。

    下面示例用函数化组件展示了一个根据数据智能选择不同组件的场景:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
    <div id="app">
        <smart-item :data="data"></smart-item>
        <button @click="change('img')">切换为图片组件</button>
        <button @click="change('video')">切换为视频组件</button>
        <button @click="change('text')">切换为文本组件</button>
    </div>
    <script>
        // 图片组件选项
        var ImgItem = {
            props:['data'],
            render:function (createElement) {
                return createElement('div',[
                    createElement('p','图片组件'),
                    createElement('img',{
                        attrs:{
                            src: this.data.url
                        }
                    })
                ]);
            }
        }
        // 视频组件选项
        var VideoItem = {
            props:['data'],
            render:function (createElement) {
                return createElement('div',[
                    createElement('p','视频组件'),
                    createElement('video',{
                        attrs:{
                            src: this.data.url,
                            controls: 'controls',
                            autoplay: 'autoplay'
                        }
                    })
                ]);
            }
        }
        // 纯文本组件选项
        var TextItem = {
            props:['data'],
            render:function (createElement) {
                return createElement('div',[
                    createElement('p','纯文本组件'),
                    createElement('p',this.data.text)
                ]);
            }
        }
        Vue.component('smart-item',{
            // 函数化组件
            functional: true,
            render: function (createElement,context) {
                // 根据传入的数据,智能判断显示哪种组件
                function getComponent(){
                    var data = context.props.data;
                    // 判断prop:data的type字段是属于哪种类型的组件
                    if( data.type === 'img' ) return ImgItem;
                    if( data.type === 'video' ) return VideoItem;
                    return TextItem;
                }
                return createElement(
                    getComponent(),
                    {
                        props:{
                            // 把smart-item的prop:data传给上面智能选择的组件
                            data: context.props.data
                        }
                    },
                    // 相当于this.$slots.default
                    context.children
                );
            },
            props:{
                data:{
                    type: Object,
                    required: true
                }
            }
        });
    
        var app = 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: '这是一段纯文本'
                        }
                    }
                }
            },
            created: function () {
                // 初始化时,默认设置图片组件的数据
                this.change('img');
            }
        });
    </script>
    </body>
    </html>
    

    逐步分析一下实现内容。ImgIten,VideoItem,TextItem这3个对象分别是图片组件、视频组件和纯文本组件的选项,它们都接收一个prop:data。在函数化组件smart-item里,也有props:data,通过getComponent函数来判断其字段type的值,选择这条数据适合渲染的组件。通过createElement把getComponent()返回的对象设置为第一个参数,然后通过第二个参数把smart-item的data传递到选择的组件里的prop:data,组件渲染出不同的内容。
    根实例app中的方法change用来生成不同的数据,通过3个button来切换。
    该实例难理解的地方在于smart-item和3个功能组件都有prop:data,它们的传递顺序和原理看起来比较含糊。
    函数化组件在业务中并不是很常用,而且也有其他类似的方法来实现,比如上例也可以用组件的is特性来动态挂载。总结起来,函数化组件主要适用于以下两个场景:
    1、程序化地在多个组件中选择一个。
    2、在将children,props,data 传递给子组件之前操作它们。

    效果图:


    用组件的is特性实现
    <div id="app">
        <component :is="view"></component>
        <button @click="view='component1'">切换为图片组件</button>
        <button @click="view='component2'">切换为视频组件</button>
        <button @click="view='component3'">切换为文本组件</button>
    </div>
    <script>
        Vue.component('component1',{
            template:'<div><p>图片组件</p><img src="https://raw.githubusercontent.com/iview/iview/master/assets/logo.png"></div>'
        });
        Vue.component('component2',{
            template: '<div><p>视频组件</p><video src="http://vjs.zencdn.net/v/oceans.mp4" controls="controls" autoplay="autoplay"></video></div>'
        });
        Vue.component('component3',{
            template: '<div><p>纯文本组件</p><p>这是一段纯文本</p></div>'
        });
        var app = new Vue({
            el: '#app',
            data: {
                view: 'component1'
            }
        });
    </script>
    

    相关文章

      网友评论

          本文标题:Vue2之virtual dom,render函数化组件

          本文链接:https://www.haomeiwen.com/subject/vtfglftx.html