This is the third day of my participation in Gwen Challenge
1. Introduction to render function
To put it simply, in Vue we use template HTML syntax to build the page, using the render function we can use JS language to build the DOM. Since vue is a virtual DOM, it also needs to be translated into a VNode function when it gets the template. Using the render function to build the DOM, Vue avoids the translation process. When the render function is used to describe the virtual DOM, vue provides a function that is the tool needed to build the virtual DOM. It’s called createElement on its website. And the convention is called h.
1.1 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.
1.2 Parameter accepted by createElement
// @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 attributes in the template. Optional.
{
// (see 1.3 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
1.3 Use of render function
render:(h) = > {
return h('div', {// Bind div to the class attribute
class: {
child: true.more: false},// bind styles to div
style: {width:'200px'.height:'200px',},// bind the click event to div
on: {
click: () = > {
console.log('Click event')}},})}Copy the code
1.4 Drill down into the Render function data object
Just as V-bind :class and V-bind :style are treated specially in template syntax, they also have corresponding top-level fields in VNode data objects. This object also allows you to bind normal HTML attributes, as well as DOM attributes such as innerHTML (which overrides v-HTML directives).
{
// 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 attributes
domProps: {
innerHTML: 'baz'
},
// The event listener is in the 'on' property,
// 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 attributes
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
1.4 the constraint
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
2. Application of render function
2.1 Render a simple element
// app.vue (root component)
<template>
<div id="app">
<myRender></myRender>
</div>
</template>
<script>
import myRender from './components/myRender'
export default {
components:{
myRender
}
}
</script>
Copy the code
// myRender.vue
<script>
export default {
render:(h) = > {
return h('div', {class: {
child: true.more: false
},
attrs: {
id: 'foo'.name: 'child'},style: {width:'100%'.height:'200px',},domProps: {
innerHTML: 'I'm a child of the Render'
}
})
}
}
</script>
<style lang="stylus" scoped>
.child {
background: pink
font-size 24px
letter-spacing 2px
}
.more {
background: red
}
</style>
Copy the code
2.2 Adding subLabels
<script>
export default {
render:(h) = > {
return h('div',
{
class: 'wrapper'.attrs: {
id: 'wrapper',},style: {width:'100%'.height:'250px'}, },[ h('h2'.'title'),
h('div', {class: 'content'.attrs: {
id: 'content',},style: {width:'800px'.height:'100px'
},
domProps: {innerHTML:'I am content'
}
})
]
)
}
}
</script>
<style lang="stylus" scoped>
.wrapper
background: pink
letter-spacing 2px
.content
margin 0 auto
background: red
color #ffffff
font-size 24px
</style>
Copy the code
2.3 Use JavaScript instead of templates
Vue’s rendering functions do not provide proprietary alternatives to what can easily be done in native JavaScript.
- In the v-if and V-for template syntax:
<ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
<script>
export default {
data(){
return{
items: [1.2.3]}}}</script>
Copy the code
Render function implementation:
<script>
export default {
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.')}},data(){
return{
items: [1.2.3]
}
}
}
</script>
Copy the code
- v-model
<script>
export default {
render:function(createElement) {
var self = this
return createElement('div',[
createElement('div', {class: 'showContent'},self.inputValue),
createElement('input', {class: 'content'.domProps: {value:self.inputValue
},
on: {input:function(event){
self.inputValue = event.target.value
}
}
})
]
)
},
data(){
return{
inputValue:' '}},watch: {inputValue:function(){
console.log(this.inputValue)
}
},
}
</script>
<style lang="stylus" scoped>
.showContent
font-size 32px
letter-spacing 2px
.content
margin 10px auto
color blue
font-size 24px
</style>
Copy the code
2.4 Static Slot
The use of the enclosing $slots
- The parent component
<template>
<div id="app">
<myRender>
<template v-slot:header>
<div >The head</div>
</template>
<template #footer>
<div >The foot</div>
</template>
</myRender>
</div>
</template>
<script>
import myRender from './components/myRender'
export default {
components:{
myRender
}
}
</script>
Copy the code
- Child components
<script>
export default {
render:function(createElement) {
let childHeader = this.$slots.header
let childFooter = this.$slots.footer
return createElement(
'div',
{
class: 'showContent'.style: {width:'100%'
}
},
[
createElement('div', {class:'childHeader'},childHeader),
createElement('div',childFooter),
]
)
},
}
</script>
<style lang="stylus" scoped>
.showContent
letter-spacing 2px
background-color red
.childHeader
color blue
font-size 24px
</style>
Copy the code
2.5 Scope slot
The use of the enclosing $scopedSlots
- The parent component
<template>
<div id="app">
<myRender :myLayout="layout">
<template slot-scope="childMsg">
<div >
{{childMsg.text}}
</div>
</template>
</myRender>
</div>
</template>
<script>
import myRender from './components/myRender'
export default {
data(){
return{
layout: {header:'head'.footer:'feet'}}},components:{
myRender
}
}
</script>
Copy the code
- Child components
<script>
export default {
render:function(createElement) {
let self = this
return createElement(
'div',
{
style: {width:'100%'
},
},[
self.$scopedSlots.default({
text: this.myLayout.header
})
]
)
},
props: {myLayout:Object
}
}
</script>
Copy the code