[vue]渲染函数及函数式组件

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 10 天,点击查看活动详情

渲染函数 -- render

1、使用

通常我们都是使用模板创建html的,这也是vue推荐的方式,但也可以使用js完全去控制生成html。

渲染函数:

字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。

渲染函数接收一个函数参数,利用这个函数创建虚拟DOM, 最终返回一个虚拟DOM。

写法:

render: function (createElement) { 
    // createElement函数返回结果是VNode 
    return createElement( 
        tag, // 标签名称 
        data, // 传递数据 
        children // 子节点(VNode)数组 
    ) 
}
复制代码

使用demo

<heading level="1" title="123">哈哈哈</heading>
复制代码
Vue.component('heading', {
    // 考虑用户是否会传递level,可使用对象形式校验数据类型或设置为必填
    props: {
        level: {
            type: String,
            required: true
        }
    },
    // render接收的这个参数通常写为h,因为VNode在底层使用的算法叫snabbdom, 在这个算法里生成虚拟DOM的函数就叫做h
    render(h) {
        let vnode = h( 
            'h' + this.level, // 标签名称
            {
                attrs: {
                    id: 'foo'
                }
            }, // 传递数据;没有被定义为 prop 的 attribute ,它会自动添加到组件的根元素上
            this.$slots.default, // 子节点VNode数组
        )
        console.log(vnode)
        return vnode
    }
})
复制代码

3、createElement参数

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',

  // {Object}
  // 一个与模板中 attribute 对应的数据对象。可选。
  {
    // (详情见链接:深入数据对象)
  },

  // {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

复制代码

深入数据对象

// 使用 
// <heading level="1" title="fdsafsd" icon="zhongguoditu">render标题1</heading>

// 丰富组件
Vue.component('heading', {
    props: {
        level: {
            type: String,
            required: true
        },
        title: {
            type: String,
            default: ''
        },
        icon: {
            type: String,
            default: ''
        }
    },
    render(h) {
        // 子节点数组
        let children = [];

        // icon属性处理逻辑
        // <svg class="icon ">
        //     <use xlink:href="#icon-zhongguoditu"/>
        // </svg> 
        if(this.icon){
            children.push(h(
                'svg',
                {
                    class: 'icon',

                },
                [h(
                    'use',
                    {
                        attrs: {
                            'xlink:href': '#icon-'+ this.icon
                        }
                    }
                )]
            ))
        }
        // 再添加一个子节点:my-component组件,给其传递参数
        children.push(h(
            'my-component',
            {
                props: {
                    message: '组件啊'
                }
            }
        ))
        // 拼接子节点
        children = children.concat(this.$slots.default)

        let vnode = h( 
            'h' + this.level, // 标签名称
            {
                attrs: {
                    title: this.title
                },
                

            }, // 传递数据;给组件绑定的数据可以不用手动处理,它会自动移到这个根节点上
            children, // 子节点VNode数组
        )
        console.log(vnode)
        return vnode
    }
})


Vue.component('my-component', {
    props: ['message'],
    template: `
        <div>{{message}}</div>
    `
})
复制代码

函数式组件

1、渲染函数

没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。

画重点:

  • 没有状态,也就是函数内部没有定义data属性等
  • functional: true 将组件标记为函数式组件
  • 函数式组件没有实例,不能直接使用this访问上下文

将上面代码改造为函数式组件

// <heading level="1" title="fdsafsd" icon="zhongguoditu">render标题1</heading>


Vue.component('heading', {
    functional: true, // 标记为函数式组件
    // 在 2.3.0 之前的版本中,props 选项是必须的
    // 在 2.3.0 或以上的版本中, 可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
    props: {
        level: {
            type: String,
            required: true
        },
        title: {
            type: String,
            default: ''
        },
        icon: {
            type: String,
            default: ''
        }
    },

    // 不能用template, 使用render; 没有实例,不存在this, render通过context传递上下文;
    render(h, context) {
        // 属性获取的变化
        const {icon, level, title} = context.props;

        // 子节点数组
        let children = [];
        
        // icon属性处理逻辑
        // <svg class="icon ">
        //     <use xlink:href="#icon-zhongguoditu"/>
        // </svg> 
        if(icon){
            children.push(h(
                'svg',
                {
                    class: 'icon',

                },
                [h(
                    'use',
                    {
                        attrs: {
                            'xlink:href': '#icon-'+ icon
                        }
                    }
                )]
            ))
        }
        // 拼接子节点  将this.$slots.default更新成context.children
        children = children.concat(context.children)
        

        let vnode = h( 
            'h' + level, // 标签名称
            {
                attrs: {
                    title: title
                },
                

            }, // 传递数据;给组件绑定的数据可以不用手动处理,它会自动移到这个根节点上
            children, // 子节点VNode数组
        )
        
        return vnode
    }
})
复制代码
  • 小结
  1. 如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。

  2. props选项:
    在 2.3.0 之前的版本中,props 选项是必须的
    在 2.3.0 或以上的版本中, 可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。

  3. 函数式组件只是函数,渲染开销低很多

  4. slots()children区别:
    children就是指所有子节点;
    slots()可以让组件感知某个插槽机制, 如slots().default默认匿名内容

2、基于模板的函数式组件

基于模板的函数式组件可以这样声明:

<template functional>
</template>
复制代码
<!--函数式组件-->
<template functional>
  <div :title='data.attrs.title'>
    {{data.attrs}}
    <slot name='header'/>
    <slot/>
  </div>
</template>
复制代码
<!--使用函数式组件-->
<functional-temp :title='title'>
  <template v-slot:header>
      头部
  </template>
  内容
</functional-temp>
复制代码

猜你喜欢

转载自juejin.im/post/7086758554136215565
今日推荐