Render function
Vue recommends using templates to create your HTML in most cases. However, in some scenarios, you really need the full programming power of JavaScript. In this case you can use render functions, which are closer to the compiler than templates.
Nodes, trees, and virtual DOM
<div>
<h1>My title</h1>
Some text content
<! --TODO: Add tagline -->
</div>
Copy the code
Each element is a node. Each paragraph of text is also a node. Even comments are nodes. A node is a part of a page. Just like a family tree, each node can have child nodes (that is, each part can contain other parts).
Updating all of these nodes efficiently can be difficult, but fortunately you don’t have to do this manually. You just need to tell Vue what HTML you want on the page, which can be in a template:
<h1>{{ blogTitle }}</h1>
Copy the code
Or in a render function:
render: function (createElement) {
return createElement('h1'.this.blogTitle)
}
Copy the code
In both cases, Vue automatically keeps the page updated even if the blogTitle changes.
Virtual DOM
Vue tracks how it changes the real DOM by creating a virtual DOM. Take a closer look at this line of code:
return createElement('h1'.this.blogTitle)
Copy the code
What exactly does createElement return? It’s not an actual DOM element. It might be more accurately called createNodeDescription, because it contains information that tells the Vue what nodes need to be rendered on the page, including descriptions of their children. We describe such a node as a “virtual node”, often abbreviated to “VNode”. “Virtual DOM” is the name we use for the entire VNode tree built from the Vue component tree.
// @returns {VNode}
createElement(
// {String | Object | Function}
// An HTML tag name, component option object, or
Resolve an async function of any of the above. Required fields.
'div'.// {Object}
// A data object corresponding to the attribute in the template. Optional.
{
// (see next section for details)
},
// {String | Array}
// Child virtual nodes (VNodes), built from 'createElement()',
// You can also use strings to generate "text virtual nodes". Optional.
[
'Write some words first.',
createElement('h1'.'A headline'),
createElement(MyComponent, {
props: {
someProp: 'foobar'}})])Copy the code
2.1 Dive into data objects
In rendering functions, some template attributes have top-level fields in the Vnode data object, which also allows binding of ordinary attributes as well as DOM properties such as innerHTML (which overrides the V-HTML directive).
{
// Same API as' v-bind:class ',
// Takes a string, object, or array of strings and objects
'class': {
foo: true.bar: false
},
// Same API as' v-bind:style ',
// Accepts a string, object, or array of objects
style: {
color: 'red'.fontSize: '14px'
},
// Plain HTML attribute
attrs: {
id: 'foo'
},
/ / component prop
props: {
myProp: 'bar'
},
// DOM property
domProps: {
innerHTML: 'baz'
},
// The event listener is in 'on',
// Modifiers such as' V-on :keyup.enter 'are no longer supported.
// keyCode needs to be checked manually in the handler.
on: {
click: this.clickHandler
},
// Only for components, for listening on native events, not for internal component use
// 'vm.$emit' triggers the event.
nativeOn: {
click: this.nativeClickHandler
},
// Custom instruction. Note that you cannot bind 'oldValue' in 'binding'
// Assign, because Vue has automatically synchronized for you.
directives: [{name: 'my-custom-directive'.value: '2'.expression: '1 + 1'.arg: 'foo'.modifiers: {
bar: true}}].// The format of the scope slot is
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props= > createElement('span', props.text)
},
// If the component is a child of another component, specify a name for the slot
slot: 'name-of-slot'.// Other special top-level properties
key: 'myKey'.ref: 'myRef'.// If you apply the same ref name to multiple elements in a render function,
// then '$refs.myRef' becomes an array.
refInFor: true
}
Copy the code
2.2 Complete Examples
Codesandbox. IO/s/pensive – b…
2.3 the constraint
The VNode must be unique
All VNodes in the component tree must be unique. This means that the following render functions are not valid:
render: function (createElement) {
var myParagraphVNode = createElement('p'.'hi')
return createElement('div'[// error - Duplicate VNode
myParagraphVNode, myParagraphVNode
])
}
Copy the code
If you really need to repeat elements/components a lot, you can use factory functions to do it. For example, the following render function renders 20 identical paragraphs in a perfectly legal way:
render: function (createElement) {
return createElement('div'.Array.apply(null, { length: 20 }).map(function () {
return createElement('p'.'hi')}}))Copy the code
Third, inRender
The template function in the function
3.1 v-if
和 v-for
Vue’s rendering functions do not provide proprietary alternatives to what can easily be done in native JavaScript. For example, v-if and v-for are used in templates:
<ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
Copy the code
These can be overridden in the render function using JavaScript’s if/else and map:
props: ['items'].render: function (createElement) {
if (this.items.length) {
return createElement('ul'.this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p'.'No items found.')}}Copy the code
3.2 v-model
There is no direct counterpart to the V-Model in the rendering function — you have to implement the logic yourself:
props: ['value'].render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}
Copy the code
That’s the price of going deep, but it gives you a lot more control over the interaction details than a V-Model.
3.3 Event & key modifier
For.passive,.capture, and.once event modifiers, Vue provides prefixes that can be used with on:
The modifier | The prefix |
---|---|
.passive |
& |
.capture |
! |
.once |
~ |
.capture.once 或 .once.capture |
~! |
Such as:
on: {
'! click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~! mouseover': this.doThisOnceInCapturingMode
}
Copy the code
For all other modifiers, private prefixes are not required, because you can use event methods in event handlers:
The modifier | Handle equivalent operations in functions |
---|---|
.stop |
event.stopPropagation() |
.prevent |
event.preventDefault() |
.self |
if (event.target ! == event.currentTarget) return |
Keys:.enter .13. |
if (event.keyCode ! == 13) return For other key modifiers, it can be13 Instead ofAnother key code) |
Modifier keys:.ctrl ..alt ..shift ..meta |
if (! event.ctrlKey) return (will bectrlKey Change toaltKey ,shiftKey ormetaKey ) |
Here is an example using all modifiers:
on: {
keyup: function (event) {
// If the element that triggers the event is not an element of the event binding
/ / is returned
if(event.target ! == event.currentTarget)return
// If you press not Enter or
// Did not press shift at the same time
/ / is returned
if(! event.shiftKey || event.keyCode ! = =13) return
// Prevent events from bubbling
event.stopPropagation()
// Block the element's default keyup event
event.preventDefault()
// ...}}Copy the code
3.4 slot
You can access the contents of static slots via this.$slots, where each slot is an array of vNodes:
render: function (createElement) {
// `<div><slot></slot></div>`
return createElement('div'.this.$slots.default)
}
Copy the code
You can also access the scope slots via this.$scopedSlots. Each scope slot is a function that returns a number of VNodes:
props: ['message'].render: function (createElement) {
// `<div><slot :text="message"></slot></div>`
return createElement('div'[this.$scopedSlots.default({
text: this.message
})
])
}
Copy the code
To pass a scope slot to a child component using a render function, you can use the scopedSlots field in the VNode data object:
render: function (createElement) {
// `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
return createElement('div', [
createElement('child', {
// Pass 'scopedSlots' in the data object
/ / format for {name: props = > VNode | Array < VNode >}
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}
Copy the code
3.5 example
import { CreateElement, RenderContext } from 'vue/types/umd'
export default {
functional: true.props: {
row: Object.render: Function.index: Number.column: {
type: Object.default: null}},render: (h: CreateElement, ctx: RenderContext) = > {
const params: any = {
row: ctx.props.row,
index: ctx.props.index
}
if (ctx.props.column) params.column = ctx.props.column
return ctx.props.render(h, params)
}
}
Copy the code