Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用render渲染函数,它比模板更接近编译器
。
在Vue2中,render函数是一个可选的、用于生成虚拟DOM的特殊函数。它是Vue实例的一个选项,允许开发者使用JavaScript编写模板,而不是使用HTML模板。render函数接收一个h函数(h函数是createElement的别名。很多人喜欢用createElement,形参不过是个名称而已)作为参数,该函数用于创建虚拟节点(VNode)。
下面是一个render函数简单示例:
render(h) {
return h('h1', 'Hello, Vue!')
}
上面是一个简单写法,完整来说,h函数接受三个参数:
第一个参数:可以是字符串(表示HTML标签名或组件名)、组件对象或函数。如果是自定义组件,需要确保组件已经在当前实例或全局注册。
第二个参数:属性对象,包含传递给子组件的props、attrs、class、style等。
第三个参数:子节点,可以是字符串、数组或对象。
当第一个参数为HTML标签名的时候,常用配置如下:
const vnode = h('div', {
id: 'my-id', // 设置id属性
class: 'my-class', // 设置class属性
style: { color: 'red' }, // 设置内联样式
onClick: function() { // 设置点击事件处理程序
console.log('Clicked!');
}
}, [
h('p', { class: 'text' }, 'Hello, World!') // 子节点
]);
当第一个参数为组件名称时,配置如下:
const vnode = h('my-component', {
// 组件的类名 使用同 :class 语法
class: {
aaa: true
},
// 组件的内联样式 使用同 :style 语法
style: {
color: 'blue'
},
// 一个对象,包含组件所需的属性。这些属性将被传递给组件,并在组件内部作为props进行访问。
props: {
msg: 'Hello, World!',
},
//一个对象,包含组件的非prop属性(例如,class、style等)。这些属性将被添加到组件的根元素上。
attrs: {
class: 'my-class',
style: { color: 'red' }
},
// 一个对象,包含组件的事件处理程序。
on: {
click() {
console.log('Clicked!!!')
}
},
// 一个对象,包含组件的原生事件处理程序。与on类似,但用于监听原生DOM事件。
nativeOn: {
click() {
console.log('Clicked!!!')
}
},
// 一个对象,包含组件的DOM属性。
domProps: {
innerHTML: 'Hello, World!'
},
// 一个对象,包含组件的作用域插槽。
scopedSlots: {
default: () => h('h1', null, '我是标题1')
},
// 一个字符串或数组,表示组件的插槽内容
slot: 'slot container',
// 一个字符串或数字,表示组件的唯一标识符。
key: 'unique-key',
// 一个字符串,表示组件的引用标识符。
ref: 'myComp',
// 一个函数,返回组件实例的数据对象。
data() {
return {
name: 'wft'
}
},
// 一个对象,包含组件的指令。
directives: [{ name: 'my-directive', value: 'Hello, World!' }],
// 一个对象,包含组件的生命周期钩子。
hooks: {
created() {
console.log('created!!')
},
mounted() {
console.log('mounted!!')
}
},
// 一个布尔值,表示是否将父组件的属性继承到子组件。
inheritAttrs: true,
// 一个字符串,表示组件的真实标签名。这在渲染组件时非常有用,因为它允许Vue跳过验证过程。
is: 'my-component'
}, {
default: () => h('p', null, '我是默认插槽')
});
我们在给子组件传递数据的时候,也就是父传子,一般写在props中, 其实也可以写在attrs中,如果写在attrs中,我们也可以在子组件中props中接受attrs中的变量,如果props中没写的变量都会在attrs中,我们在组件中通过this.$attrs中可以拿到。
下面是一个示例:
先使用render函数封装一个组件,注意写了render函数就不要写template了,不然render函数不起作用
@/components/BasicRender.vue
<script>
import { deepClone } from '@/utils'
export default {
props: {
tag: {
type: String,
default: 'div'
},
option: {
type: Object,
default: () => null
},
children: {
type: [String, Array, Object],
default: ''
}
},
render(h) {
const opt = deepClone(this.option)
console.log(opt, 'opt-->.')
return h(this.tag, opt, this.children)
}
}
</script>
上面是一个vue组件,也可以写一个.js文件,写render函数,那么它也可以跟组件一样使用(注册使用):
@/components/Render.js
import { deepClone } from '@/utils'
export default {
props: {
tag: {
type: String,
default: 'div'
},
option: {
type: Object,
default: () => null
},
children: {
type: [String, Array, Object],
default: ''
}
},
render(h) {
const opt = deepClone(this.option)
console.log(opt, 'opt 对象 obj -->.')
return h(this.tag, opt, this.children)
}
}
然后是main.vue,引入封装的render组件
<template>
<div class="wft-main">
<BasicRender
:tag="tag"
:option="option"
:children="children"
/>
</div>
</template>
<script>
// import BasicRender from '@/components/Render'
import BasicRender from '@/components/BasicRender'
export default {
data() {
return {
// tag: 'div',
// option: {
// id: 'WFT',
// class: 'wft',
// style: {
// border: '1px solid red',
// width: '350px',
// height: '350px',
// display: 'flex',
// justifyContent: 'center',
// alignItems: 'center'
// },
// onClick() {
// console.log('onClicked!!!')
// },
// attrs: {
// msg: '456 msg'
// }
// },
// children: '呵呵呵'
tag: 'G1',
option: {
class: {
'G1-class': true
},
style: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
props: {
title: 'wwwfffttt',
age: 18,
info: {
name: 'wft'
}
},
attrs: {
customData: {
id: '0000',
name: 'JAY'
}
},
scopedSlots: {
default: () => (<BasicRender tag={'h1'} children={'66611'} />),
content: () => '我是content'
}
},
children: {
default: '666',
content: '我是content插槽内容'
}
}
},
components: { BasicRender }
}
</script>
<style scoped>
.wft-main {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center
}
</style>
这是加载组件G1,注意:我们加载的组件一定是引入、注册之后的,我这里全局注册了,
然后是G1组件:
<template>
<div class="wft-w-h-100">
<slot></slot>
<h1>我是G1 全局组件</h1>
<h3>{{ title }}--{{ age }}--{{ info }}</h3>
<slot name="content"></slot>
</div>
</template>
<script>
export default {
data() {
return {
}
},
props: {
customData: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: ''
},
age: {
type: Number,
default: 0
},
info: {
type: Object,
default: () => ({})
}
}
}
</script>
<style scoped>
.wft-w-h-100 {
width: 100%;
height: 100%;
}
</style>
下面是运行的效果图: