第一个参数(必须) - {String | Object | Function}
Vue.component('elem', {render: function(createElement) {return createElement('div');//一个HTML标签字符/*return createElement({template: '<div></div>'//组件选项对象});*//*var func = function() {return {template: '<div></div>'}};return createElement(func());//一个返回HTML标签字符或组件选项对象的函数*/}});
第二个参数(可选) - {Object}
Vue.component('elem', {render: function(createElement) {var self = this;return createElement('div', {//一个包含模板相关属性的数据对象'class': {foo: true,bar: false},style: {color: 'red',fontSize: '14px'},attrs: {id: 'foo'},domProps: {innerHTML: 'baz'}});}});
第三个参数(可选) - {String | Array}
Vue.component('elem', {render: function(createElement) {var self = this;// return createElement('div', '文本');//使用字符串生成文本节点return createElement('div', {},[//由createElement函数构建而成的数组createElement('h1', '主标'),//createElement函数返回VNode对象createElement('h2', '副标')]);}});
与template写法对比
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><ele></ele></div><script>/*Vue.component('ele', {template: '<div id="elem" :class="{show: show}" @click="handleClick">文本</div>',data: function() {return {show: true}},methods: {handleClick: function() {console.log('clicked!');}}});*/Vue.component('ele', {render: function(createElement) {return createElement('div', {'class': {show: this.show},attrs: {id: 'elem'},on: {click: this.handleClick}}, '文本');},data: function() {return {show: true}},methods: {handleClick: function() {console.log('clicked!');}}});new Vue({el: '#app'});</script>
</body>
</html>
this.$slots用法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><blog-post><h1 slot="header"><span>About Me</span></h1><p>Here's some page content</p><p slot="footer">Copyright 2016 Evan You</p><p>If I have some content down here</p></blog-post></div><script>Vue.component('blog-post', {render: function(createElement) {var header = this.$slots.header,//返回由VNode组成的数组body = this.$slots.default,footer = this.$slots.footer;return createElement('div', [createElement('header', header),createElement('main', body),createElement('footer', footer)])}});new Vue({el: '#app'});</script>
</body>
</html>
使用props传递数据
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><ele :show="show"></ele><ele :show="!show"></ele></div><script>Vue.component('ele', {render: function(createElement) {if (this.show) {return createElement('p', 'true');} else {return createElement('p', 'false');}},props: {show: {type: Boolean,default: false}}});new Vue({el: '#app',data: {show: false}});</script>
</body>
</html>
v-model指令
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><el-input :name="name" @input="val=>name=val"></el-input><div>你的名字是{{name}}</div></div><script>Vue.component('el-input', {render: function(createElement) {var self = this;return createElement('input', {domProps: {value: self.name},on: {input: function(event) {self.$emit('input', event.target.value);}}})},props: {name: String}});new Vue({el: '#app',data: {name: 'hdl'}});</script>
</body>
</html>
作用域插槽
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><ele><template scope="props"><span>{{props.text}}</span></template></ele></div><script>Vue.component('ele', {render: function(createElement) {// 相当于<div><slot :text="msg"></slot></div>return createElement('div', [this.$scopedSlots.default({text: this.msg})]);},data: function() {return {msg: '来自子组件'}}});new Vue({el: '#app'});</script>
</body>
</html>
向子组件中传递作用域插槽
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><ele></ele></div><script>Vue.component('ele', {render: function(createElement) {return createElement('div', [createElement('child', {scopedSlots: {default: function(props) {return [createElement('span', '来自父组件'),createElement('span', props.text)];}}})]);}});Vue.component('child', {render: function(createElement) {return createElement('b', this.$scopedSlots.default({text: '我是组件'}));}});new Vue({el: '#app'});</script>
</body>
</html>
函数化组件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>render</title><script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body><div id="app"><smart-item :data="data"></smart-item><button @click="change('img')">切换为图片为组件</button><button @click="change('video')">切换为视频为组件</button><button @click="change('text')">切换为文本组件</button></div><script>// 图片组件选项var ImgItem = {props: ['data'],render: function(createElement) {return createElement('div', [createElement('p', '图片组件'),createElement('img', {attrs: {src: this.data.url}})]);}}// 视频组件var VideoItem = {props: ['data'],render: function(createElement) {return createElement('div', [createElement('p', '视频组件'),createElement('video', {attrs: {src: this.data.url,controls: 'controls',autoplay: 'autoplay'}})]);}};/*纯文本组件*/var TextItem = {props: ['data'],render: function(createElement) {return createElement('div', [createElement('p', '纯文本组件'),createElement('p', this.data.text)]);}};Vue.component('smart-item', {functional: true,render: function(createElement, context) {function getComponent() {var data = context.props.data;if (data.type === 'img') return ImgItem;if (data.type === 'video') return VideoItem;return TextItem;}return createElement(getComponent(),{props: {data: context.props.data}},context.children)},props: {data: {type: Object,required: true}}});new Vue({el: '#app',data() {return {data: {}}},methods: {change: function(type) {if (type === 'img') {this.data = {type: 'img',url: 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'}} else if (type === 'video') {this.data = {type: 'video',url: 'http://vjs.zencdn.net/v/oceans.mp4'}} else if (type === 'text') {this.data = {type: 'text',content: '这是一段纯文本'}}}},created: function() {this.change('img');}});</script>
</body>
</html>
渲染动态表单用法
<template><div><form><div v-for="(field, index) in fields" :key="index"><component :is="field.type" :name="field.name" :label="field.label" :options="field.options" v-model="form[field.name]" /></div></form></div>
</template><script>
import Vue from 'vue';export default {data() {return {form: {},fields: [{ type: 'text', name: 'username', label: '用户名' },{ type: 'password', name: 'password', label: '密码' },{ type: 'select', name: 'gender', label: '性别', options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }] },{ type: 'checkbox', name: 'hobbies', label: '爱好', options: [{ value: 'reading', label: '阅读' }, { value: 'music', label: '音乐' }, { value: 'sports', label: '运动' }] },{ type: 'radio', name: 'status', label: '状态', options: [{ value: 'active', label: '激活' }, { value: 'inactive', label: '未激活' }] },],};},render(h) {const self = this;const fields = self.fields.map((field) => {const props = {props: {name: field.name,label: field.label,options: field.options,value: self.form[field.name],},on: {input: (value) => {self.form[field.name] = value;},},};return h(field.type, props);});return h('div', fields);},
};
</script><style>
/* 样式省略 */
</style>
在示例代码中,我们定义了一个表单组件,其中的 `fields` 数组包含了表单中的各个字段,每个字段都有一个 `type` 属性表示字段的类型,例如文本框、密码框、下拉框等。在 `render` 函数中,我们使用 `map` 方法遍历 `fields` 数组,根据每个字段的类型动态生成对应的组件,并将组件的属性和事件绑定到表单数据中。最后,我们将所有生成的组件包裹在一个 `div` 元素中返回。
需要注意的是,由于在 `render` 函数中动态生成了组件,因此在模板中无法直接使用这些组件。如果需要在模板中使用这些组件,可以通过在 `render` 函数中将生成的组件保存到一个数组中,然后在模板中使用 `v-for` 渲染这个数组。
希望这个示例能帮助您理解如何使用 Vue 的 render 函数渲染动态表单。如有任何问题,请随时提问。