vue的函数式组件


一、函数式组件的简介

1. 什么是函数式组件?

我们可以把函数式组件想象成组件里的一个函数,入参是渲染上下文(render context),返回值是渲染好的HTML

对于函数式组件,可以这样定义:

  • Stateless(无状态):组件自身是没有状态的;
  • Instanceless(无实例):组件自身没有实例,也就没有this。

由于函数式组件拥有这两个特性,我们就可以把它用作高阶组件(High order components),所谓高阶,就是可以生成其他组件的组件。

2. 函数式组件的特点

  • 没有管理任何状态 ;
  • 没有监听任何传递给它的状态 ;
  • 没有生命周期方法 ;
  • 只接收一些prop的函数;

3. 函数式组件的优点

渲染开销低,因为函数式组件只是函数。

4. 为什么要使用函数式组件

速度快

因为函数式组件没有状态,所以我们不需要像vue的响应式系统一样需要经过额外的初始化。

函数式组件仍然会对响应的变化做出 响应式变化 ,比如新传入props。但是在组件本身中,它无法知道数据什么时候发生变化,因为它不维护自身状态。

对于大型应用程序,在使用 函数式组件 之后,我们会看到DOM的渲染和更新有很大的改进。

5. 函数式组件的适用场景

函数式组件可能不适用很多情况,因为使用JavaScript框架的目的是构建响应式的应用程序。

适用场景:

  • 一个简单的展示组件,例如:button组件,pills,tags,cards,甚至整个页面都是静态文本,比如详情说明页面;
  • “ 高阶组件 ” 用于接收一个组件作为参数,返回一个被包装过的组件;
  • v-for 循环中的每个子组件。

二、函数式组件的使用

1. 如何声明函数式组件

渲染函数:将组件标记为 functional : true

export default {
    name: 'funcBtn',
    // 标记为函数式组件
    functional: true,
    // 必写,functional:true加上render才是函数式组件
    render(createElement, context) {
        return createElement('button', 'click me')
    }
}

单文件组件:在template标签上 functional

<template functional>
  <div>
    <button>click me</button>
  </div>
</template>

<script>
export default {
  name: 'funcBtn'
}
</script>

2. context 参数

为了弥补缺少的实例,在render中提供了第二个参数作为上下文,组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象:

  • props:提供所有 prop 的对象;
  • children:VNode 子节点的数组;
  • slots:一个函数,返回了包含所有插槽的对象;
  • scopedSlots:一个暴露传入的作用域插槽的对象,也以函数形式暴露普通插槽;
  • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件;
  • parent:对父组件的引用;
  • listeners: 一个包含了所有父组件为当前组件注册的事件监听器的对象,这是 data.on 的一个别名;
  • injections:如果使用了 inject 选项,则该对象包含了应当被注入的 property。
    在这里插入图片描述

现在创建一个父组件App.vue来引入上面的函数式组件:

<template>
    <FuncBtn>click me</FuncBtn>
</template>
<script>
import FuncBtn from '../components/FuncBtn'
export default {
  name: 'App',
  components: {
    FuncBtn
  }
}
</script>

上面的 click me 就是 FuncBtn 的 children 属性,所以FuncBtn组件可以写为:

<script>
export default {
  name: 'FuncBtn',
  functional: true,
  render (createElement, context) {
    return createElement('button', context.children);
  }
}
</script>

this.$slots.default => context.children
this.level => context.props.level

使用 es6参数 的解构,用 {children} 来代替 context :

<script>
export default {
  name: 'FuncBtn',
  functional: true,
  render (createElement, {children) {
    return createElement('button', children);
  }
}
</script>

3. 事件定义

函数式组件没有实例,事件只能由父组件传递。下面在app.vue上定义一个最简单的click事件:

<template>
  <FuncBtn @click="handler">click me</FuncBtn>
</template>

<script>
import FuncBtn from '../components/FuncBtn'
export default {
  name: 'App',
  components: {
    FuncBtn
  },
  methods: {
    handler () {
      alert(1)
    }
  }
}
</script>

FuncBtn组件:

<script>
export default {
  name: 'FuncBtn',
  functional: true,
  render (createElement, {props, listeners, children}) {
  	return createElement('button', {
	  attrs: props,
	  on: {
		click: listeners.click
	  }
	}, children)
  }
}
</script>

页面效果:
在这里插入图片描述
点击按钮,执行点击事件:
在这里插入图片描述
简单写法

vue中设计的api比较人性化,我们可以将props、listeners统一集中在data中。

FuncBtn组件:

<script>
export default {
  name: 'FuncBtn',
  functional: true,
  render (createElement, {data, children}) {
  	// data属性:将所有attribute和事件监听器传下去,并将同名属性替换或智能合并
  	return createElement('button', data, children)
  }
}
</script>

FuncBtn组件中所有的 attribute事件监听器 都通过 data属性 传递下去了,以至于这些事件并不要求 .native 修饰符。

如果使用基于模板的函数式组件,那么还需要手动添加 attribute监听器
可以使用 data.attrs 传递 HTML attribute ,使用 listeners 传递 事件监听器 。

FuncBtn组件:

<script>
<template functional>
  <button v-bind="data.attrs" v-on="listeners">
	<slot></slot>
  </button>
</template>

export default {
  name: 'FuncBtn'
}
</script>

效果也是一样的。

4. context 参数中 slots() 和 children 的对比

在上述示例中,我们都是通过 children 来获取父组件的插槽内容的,你可能想知道既然 children 可以获取到,为什么还同时需要 slots() 呢?slots().default 不是和 children 类似的吗?在一些场景中,是这样 —— 但如果是带有 具名插槽 的函数式组件呢?

父组件 App.vue:

<template>
  <div>
    <FuncText :text="text" @click="handler">
      <template v-slot:foo>first</template>
      <template>second</template>
    </FuncText>
  </div>
</template>

<script>
import FuncText from '../components/FuncText'
export default {
  name: 'App',
  components: {
    FuncText
  },
  data () {
    return {
      text: 'click me'
    }
  },
  methods: {
    handler () {
      alert(1)
    }
  }
}
</script>

子组件 FuncText.vue:

使用html模板,代码如下:

<template functional>
  <div>
    <p>
      <slot name="foo"></slot>
    </p>
    <p>
      <slot></slot>
    </p>
    <button v-bind="data.attrs" v-on="listeners">{{props.text}}</button>
  </div>
</template>

为了对比出 slots()children 差别,我们这里使用render渲染函数来完成子组件:

<script>
export default {
  name: 'FuncText',
  functional: true,
  props: {
    text: {
      type: String,
      default: ''
    }
  },
  render (createElement, {props, data, slots, children}) {
    return createElement('div', {}, [createElement('p', slots().foo), 
    createElement('p', slots().default), 
    createElement('button', data, props.text)])
  }
}
</script>

对于这个组件,children 会给你两个段落标签,而 slots().default 只会传递第二个匿名段落标签,slots().foo 会传递第一个具名段落标签。同时拥有 childrenslots() ,可以选择让组件感知某个插槽机制,还是简单地通过传递 children,移交给其它组件去处理。


总结

参考链接: vue函数式组件
官网地址: 函数式组件

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值