函数式组件
一、函数式组件
1.函数式组件
当一个组件不需要状态(即响应式数据)、不需要任何生命周期场景、只接受一些props来显示组件时,我们可以将其标记为函数式组件。
为了弥补缺少的实例,render 函数提供第二个参数context作为上下文。context包括如下字段:
- props:提供所有 prop 的对象
- slots: 一个函数,返回了包含所有插槽(非作用域)的对象
- scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
- data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
- parent:对父组件的引用
- listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
- injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。
- children: VNode 子节点的数组,包含了所有的非作用域插槽和非具名插槽。
1.props和slots
//App.vue
<template>
<div id="app">
<base-level :level='1'>标题</base-level>
</div>
</template>
<script>
import BaseLevel from './components/BaseLevel';
export default {
name: 'App',
components: {
BaseLevel
}
}
</script>
// /components/BaseLevel.vue
<script>
export default {
functional: true,//函数式组件
props: {
//尽量写
level: {
type: Number,
required: true
}
},
render (h,context){
const {
props,slots } = context;
const tag = "h"+props.level;
return (
<tag>
{
slots().default }
</tag>
)
}
}
</script>
2.scopedSlots
//App.vue
<template>
<div id="app">
<base-level :level='1'>
<template #default="{ text }">//传递数据
标题 {
{
text }}
</template>
</base-level>
</div>
</template>
<script>
import BaseLevel from './components/BaseLevel';
export default {
name: 'App',
components: {
BaseLevel
}
}
</script>
// /components/BaseLevel.vue
<script>
export default {
functional: true,
props: {
level: {
type: Number,
required: true
}
},
render (h,context){
const {
props,slots,scopedSlots } = context;
const tag = "h"+props.level;
return (
<tag>
{
// slots().default
}
{
scopedSlots.default({
text: '默认插槽' }) }//也能实现插槽,还可传递数据
</tag>
)
}
}
</script>
3.data
// /components/BaseLevel.vue
<script>
export default {
functional: true,
//没有props
render (h,context){
const {
props,slots,scopedSlots,data } = context;
console.log(data);//看看data
const tag = "h"+props.level;
return (
<tag>
{
// slots().default
}
{
scopedSlots.default({
text: '默认插槽' }) }
</tag>
)
}
}
</script>
在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。(但还是推荐书写,一是可读性好,二是可维护性高)
4.listeners
// App.vue
<template>
<div id="app">
<base-level :level='1' @click='handleClick'>
<template #default="{ text }">
标题 {
{
text }}
</template>
</base-level>
</div>
</template>
<script>
import BaseLevel from './components/BaseLevel';
export default {
name: 'App',
components: {
BaseLevel
},
methods: {
handleClick(){
console.log('xxxx');
}
}
}
</script>
// /components/BaseLevel.vue
<script>
export default {
functional: true,
render (h,context){
const {
props,slots,scopedSlots,data,listeners } = context;
console.log(listeners);
const tag = "h"+props.level;
return (
<tag>
{
// slots().default
}
{
scopedSlots.default({
text: '默认插槽' }) }
</tag>
)
}
}
</script>
5.injections
<script>
import BaseLevel from './components/BaseLevel';
export default {
name: 'App',
components: {
BaseLevel
},
provide(){
/*****/
return {
name: 'jimo'
}
},
methods: {
handleClick(){
console.log('xxxx');
}
}
}
</script>
<script>
export default {
functional: true,
inject: ['name'],/*****/
props: {
level: {
type: Number,
required: true
}
},
render (h,context){
const {
props,slots,scopedSlots,data,listeners,injections } = context;
console.log(injections);
const tag = "h"+props.level;
return (
<tag>
{
// slots().default
}
{
scopedSlots.default({
text: '默认插槽' }) }
</tag>
)
}
}
</script>
6.children
<template>
<div id="app">
<base-level :level='1' @click='handleClick'>
<div>p</div>
<p>p</p>
</base-level>
</div>
</template>
<script>
export default {
functional: true,
inject: ['name'],
props: {
level: {
type: Number,
required: true
}
},
render (h,context){
const {
props,slots,scopedSlots,data,listeners,injections,children } = context;
console.log(children);
const tag = "h"+props.level;
return (
<tag>
{
// slots().default
}
{
scopedSlots.default({
text: '默认插槽' }) }
</tag>
)
}
}
</script>
2.slots() VS children
示例1:
<base-level :level="1" @click="handleClick">
<template v-slot:header>
<div>我是头部</div>
</template>
<div>div</div>
<p>p</p>
<template>template</template>
</base-level>
slots()的结果为:
{
default:[<div>div</div>, <p>p</p>, template],
header: [<div>我是头部</div>]
}
children的结果为:
[<div>div</div>, <p>p</p>, template]
示例2:
<base-level :level="1" @click="handleClick">
<template v-slot:header>
<div>我是头部</div>
</template>
<template v-slot:default>
<div>div</div>
</template>
<p>p</p>
<template>template</template>
</base-level>
slots()的结果为:
{
default:[<div>div</div>],
header: [<div>我是头部</div>]
}
children的结果为:
[<div>div</div>, <p>p</p>, template]
3.基于模板的函数式组件
在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
<template functional>
</template>