Functional component
A functional Component is a component that does not hold state data, instance this, or a lifecycle.
Functional components do not have data, lifecycle, or this. Functional components are also called stateless components.
Template definition:
<template functional>
<div>
<h1>{{ props.title }}</h1>
</div>
</template>
<script>
export default {
name: 'FunOne'.props: {
title: [String,}}</script>
<style></style>
Copy the code
Render function definition
export default {
name: 'FunTwo'.functional: true.props: {
title: [String],},render(h, { props }) {
return h('div', {}, [h('h1', {}, props.title)])
},
}
Copy the code
It cannot be defined as:
<template>
<div>
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: 'FunOne'.functional: true.props: {
title: [String,}}</script>
<style></style>
Copy the code
Use the Render function to define the input box
MyInput.jsx
export default {
name: 'MyInput'.functional: true.props: {
value: {
type: [String.Number].default: ' ',}},// NOTE functional components do not have this
render(h, context) {
const { props, listeners, data } = context
return h('input', {
/ / DOM attributes
domProps: {
value: props.value,
},
on: {
input: ({ target }) = > {
data.on['my-change'] (Math.random().toString(36))
// Listeners are aliens of data.on
listeners['my-input'](target.value)
listeners.input(target.value)
},
},
})
},
}
Copy the code
Use MyInput in the render function
import MyInput from './MyInput'
export default {
name: 'MyInputExample'.data() {
return { value: ' '}},render(h) {
// MyInput is a component object option
return h(MyInput, {
model: {
value: this.value,
callback: value= > {
console.log('model', value)
this.value = value
},
},
on: {
// NOTE listens for the event-name event on MyInputExample.
// Listeners in the functional component
// there will be an event-name method
// Used to send data to the outside world
'my-input': value= > {
console.log('my-input', value)
},
'my-change': value= > {
console.log('my-change', value)
},
},
})
},
}
Copy the code
Listen for event-name events on MyInputExample of the parent component, and there is an event-name method on the Listeners object of the functional component.
Use Render to define functional components
Vue provides context in the second argument to the Render function, which accesses properties like props and slots:
Props: component props object. Data: The component's data object, the second argument to H. Listeners are listeners to 'event-name' on the component, and the function values the event name. Data can be thrown to the parent component using parameters of the function. Listeners are aliases of 'data.on'. Slots: function that returns an object containing all slots. ScopedSlots: object, each property of which is a VNode function that returns slots, passing arguments. Children: an array of children that can be passed directly to the third argument of the function 'h'. Parent: A parent component that modifies the parent's data or calls its methods. Injection: Injection object.Copy the code
Props, like props of common components, is not mandatory. However, after being declared, the type of the props can be restricted, and the interface of the component is clearer.
What’s the difference between slots() and children?
Slots () returns objects for all slots. Children is a VNode array that does not contain V-slots on the template
<FunTwo>
<p slot="left">left</p>
<span style="color:red;">button</span>
<template v-slot:right>
<div>right</div>
</template>
<template slot="middle">
<span>left</span>
</template>
</FunTwo>
Copy the code
Children contains p, span, and span, but not div.
At the same time, you decide who to render.
What’s the difference between slots and scopedSlots?
Slots is a function that returns the VNode object containing all slots. The attribute is the name of the slot. No arguments can be passed.
ScopedSlots is an object with the attribute of the slot name. It is a function that returns a VNode for the slot and can be passed as an argument.
ScopedSlots is more powerful.
Who does children, slots, scopedSlots use?
External components use V-slot to specify slots, preferentially using scopedSlots because it can pass parameters.
The data object
<FunTwo
:class="'fun-com'"
class="class2"
:style="{ 'background-color': '#ccc', color, padding: '20px' }"
style="font-size:20px;"
:title="title"
dataKey="title"
@click="onClick"
/>
Copy the code
Contains the following attributes:
The on attribute is an object, the key is an event that is listened on the component, and on.click(params) can emit params to the parent component, i.e. this.$emit(‘click’,params).
Attrs non-props attribute.
Static staticClass, static staticStyle, dynamic style, vue will normalize the style.
How do I trigger custom events in a functional component?
Data. on[‘event-name’](params) event-name is the name of the event listened on the component,
listeners['event-name'](params)
.
Event-name is the name of the event listened on the component, and params is the argument to the event.
If the parent component listener does not listen for the event, it does not have this property.
injection
The parent component provides instances:
provide() {
return { parent: this}},Copy the code
Introduce in child components:
inject: ['parent'].Copy the code
Injection is an object that contains the parent property.
Custom events cannot be emitted with injection.parent.$emit(). $emit will bind this internally, and the functional component has no instance.
How do I use computed and methods
Functional components are not reactive and cannot use computed properties and methods like normal components.
A template-defined functional component has a way of defining functions directly and then calling them in the template.
<template functional>
<div>
<h1>{{ props.title }} {{ $options.fullName(props) }}</h1>
</div>
</template>
<script>
export default {
name: 'FunOne'.props: {
title: [String],},fullName(props) {
return props.title + 'jack' + 'chou'}},</script>
Copy the code
Function components defined by Render cannot be declared at props; they can be declared inside Render.
export default {
name: 'FunButton'.functional: true.props: {
title: [String],},render(h, { data, props, children, slots, scopedSlots, injections, parent }) {
const fullName = props= > {
return props.title + 'jack' + 'chou'
}
return h('div', data, [h('button', {}, fullName(props)), props.title])
},
}
Copy the code
Define a functional component MyButton
MyButton.jsx
export default {
name: 'MyButton'.functional: true.props: {
person: {
type: Object.default: () = > ({ name: 'jack'.age: 23})}},render(h, { props, scopedSlots, listeners }) {
// NOTE default is a keyword that needs to be renamed
const { left, right, default: _defaultSlot } = scopedSlots
const defaultSlot = (_defaultSlot && _defaultSlot({ person: props.person })) || <span>button</span>
const leftSlot = (left && left()) || ' '
const rightSlot = right && right(props.person)
const button = h(
'button',
{
on: {
click: () = > {
listeners.click && listeners.click(props.person)
},
},
},
[defaultSlot]
)
return (
<div>
{leftSlot}
{button}
{rightSlot}
</div>)}},Copy the code
Template definition
<template functional>
<div>
<slot name="left"></slot>
<button @click="listeners['click'] && listeners['click'](props.person)">
<slot :person="props.person">
<span>button</span>
</slot>
</button>
<slot name="right" :age="props.age"></slot>
</div>
</template>
<script>
export default {
name: 'MyButton'.props: {
person: {
type: Object.default: () = > ({ name: 'Functional component'.age: 24})},}},</script>
Copy the code
What are the advantages of functional components
Functional components are not readable. Why are they?
Fast, that is, good performance.
Functional components have no state, and no additional initialization is required for Vue reactive systems, etc.
The component itself does not know when its data changes because it does not maintain its own state, that is, it does not have data.
Which scenario is appropriate to use functional components
- Pure presentation components, which tend to be logically simple.
- In many cases, the V-for loop extracts this part of the code into functional components.
- Dynamically select one of several components to render.
- Manipulate children, props, and data before passing them to the child components —-
This is equivalent to using a functional component as its parent to encapsulate it twice
. - High order component (HOC) – receives a function that returns VNode via props,
The first argument to the function is h
In aFunctional component
The functional component is a higher-order component.
Examples of higher-order components:
export default {
name: 'Container'.functional: true.render(h, { props }) {
return props.renderContainer(h, props.data)
},
}
Copy the code
Using higher-order components in templates:
<template>
<div class="zm-form-table">
<ul>
<li
v-for="(item, index) in titleList"
:key="index"
>
<Container v-if="typeof item.prop === 'function'" :renderContainer="item.prop" :data="data" />
<span v-else>
{{
![null, void 0, ''].includes(data[item.prop] &&
data[item.prop] ||''
}}
</span>
</div>
</li>
</ul>
</div>
</template>
<script>
import Container from './container.js'
export default {
name: 'FormTable'.components: {
Container,
},
props: {
titleList: {
type: Array.default: () = > {
return [
// NOTE test data
/* {title: 'title 3', prop: 'key3'}, {title:' title 3', // prop can be a string or a function prop: (h, data) = > {return (< el - button size = 'mini' type = "primary" > button < / el - button >)},}, * /]}},data: {
type: Object.default: () = > {
return {
// Test data
/* key1: 'test 1', key2:' test 2', key3: 'test 3', */}},},},}</script>
Copy the code
Pass props like this:
<template>
<FormTable :titleList="titleList" :data="detail" />
</template>
<script>
export default {
name: 'BaseInfo'.data() {
return {
detail: {},
titleList: [{title: 'Parental identity'.// NOTE data is passed in from the Render function of the Container
prop: (h, data) = > {
const options = [
{ label: 'daddy'.value: 'father' },
{ label: 'mother'.value: 'mother' },
{ label: 'grandfather'.value: 'grandpa' },
{ label: 'grandma'.value: 'grandma' },
{ label: 'grandfather'.value: 'grandfather' },
{ label: 'grandma'.value: 'grandmother' },
{ label: 'other'.value: 'other'},]const identity = options.find(item= > data[key] === item.value)
return <span>{identity && identity.label}</span>}, {},title: 'Parents wechat'.prop: 'weiXin',},],}},created() {
setTimeout(() = > {
// The interface returns
this.detail = {
identity: 'father'.weiXin: 'jack8848',}},1000)}},</script>
Copy the code
This pattern of passing the Render function to the component through props is extremely useful and will be seen later in component encapsulation.
High-order components in React: Components act as input arguments. New components act as functions that return values. Vue can also implement such components, but it’s a bit more cumbersome. See here: Explore Vue higher-order components
Problems with functional components
- Style scoped behaves differently for functional and state components.
A scoped-style functional component is treated as a child component with the same CSS selector. The parent component’s style is in effect, that is, the child component’s scoped is not in effect.
reference
Scoped styles inconsistent between functional and stateful components
Renderless Components in Vue.js
Vue. Js functional components: What, why & When?