说说如何利用 Render 函数来实现 Vue.js 的内置指令

445 阅读3分钟

因为在 Render 函数中无法使用 Vue 内置指令,比如 v-if 等,但我们可以在 Render 函数中,利用原生的 JavaScript 来实现这些指令。

1 v-if/v-else

html:

<div id="app">
    <e :is-show="isShow"></e>
    <button @click="isShow=!isShow">切换</button>
</div>

js:

Vue.component('e', {
	render: function (createElement) {
		if (this.isShow) {
			return createElement('p', 'isShow 为 true');
		} else {
			return createElement('p', 'isShow 为 false');
		}
	},
	props: {
		isShow: {
			type: Boolean,
			default: false
		}
	}
});

var app = new Vue({
	el: '#app',
	data: {
		isShow: false
	}
});

效果:

2 v-for

可以使用 for 循环来实现 v-for 指令。

html:

<div id="app2">
    <e2 :list="list"></e2>
</div>

js:

Vue.component('e2', {
	render: function (createElement) {
		var nodes = [];
		for (var i = 0; i < this.list.length; i++) {
			nodes.push(createElement('p', this.list[i]));
		}
		return createElement('div', nodes);
	},
	props: {
		list: {
			type: Array
		}
	}
});

var app2 = new Vue({
	el: '#app2',
	data: {
		list: [
			'人民心理学,实践出真知',
			'氢能源上游解析',
			'能救命的可穿戴设备都长什么样?'
		]
	}
});

渲染结果:

createElement 用于创建 DOM 节点,可以把它当作 JavaScript 的函数来理解。

3 v-if 加 v-for 综合示例

下面是综合利用 JavaScript 的 if 、else 和数组的 map 方法来渲染列表的示例。

html:

<div id="app3">
    <e3 :list="list"></e3>
    <button @click="click">显示</button>
</div>

js:

Vue.component('e3', {
	render: function (createElement) {
		if (this.list.length) {
			return createElement('ul', this.list.map(function (item) {
				return createElement('li', item);
			}));
		} else {
			return createElement('p', '无数据');
		}
	},
	props: {
		list: {
			type: Array,
			default: function () {
				return [];
			}
		}
	}
});

var app3 = new Vue({
	el: '#app3',
	data: {
		list: []
	},
	methods: {
		click: function () {
			this.list = [
				'人民心理学,实践出真知',
				'氢能源上游解析',
				'能救命的可穿戴设备都长什么样?'
			];
		}
	}
});

这里我们使用了 map() 方法,该方法会对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。

效果:

上述 Render 函数所对应的 template 编码方式为:

<ul v-if="list.length">
    <li v-for="item in list">{{item}}</li>
</ul>
<p v-else>无数据</p>

4 v-model

html:

<div id="app4">
    <e4></e4>
</div>

js:

Vue.component('e4', {
	render: function (createElement) {
		var that = this;
		return createElement('div', [
			createElement('input', {
				domProps: {
					value: this.value
				},
				on: {
					input: function (event) {
						that.value = event.target.value;
					}
				}
			}),
			createElement('p', '绑定值:' + this.value)
		])
	},
	data: function () {
		return {
			value: ''
		}
	}
});

var app4 = new Vue({
	el: '#app4'
});

效果:

这个 Render 函数所对应的 template 编码方式为:

<div>
    <input v-model="value">
    <p>绑定值:{{value}}</p>
</div>

5 事件与按键修饰符

事件与按键修饰符也可以自行实现:

修饰符 语句
.stop event.stopPropagation()
.prevent event.preventDefault()
.self if(event.target !== event.currentTarget) return;
.enter.13 if(event.keyCode !== 13) return;
.ctrl if(event.ctrlKey) return;
.alt if(event.altKey) return;
.shift if(event.shiftKey) return;
.meta if(event.metaKey) return;

有的事件修饰符, Vue.js 还提供了特殊前缀:

修饰符 前缀
.capture !
.once ~
.capture.once.once.capture ~!

假设我们希望实现一个留言上墙的功能,即在输入框中输入内容,内容会放在留言列表中。

html:

<div id="app5">
    <e5></e5>
</div>

js:

Vue.component('e5', {
	render: function (createElement) {
		var that = this;

		//渲染列表
		if (this.list.length) {
			var listNode = createElement('ul', this.list.map(function (item) {
				return createElement('li', item);
			}));
		} else {
			var listNode = createElement('p', '无内容');
		}

		return createElement('div', [
			listNode,
			createElement('input', {
				attrs: {
					placeholder: '想说的话……'
				},
				style: {
					width: '200px'
				},
				on: {
					keyup: function (event) {
						//如果不是回车键,直接返回
						if (event.keyCode !== 13) {
							return;
						}

						//把输入的内容新增到聊天列表
						that.list.push(event.target.value);

						//清空输入框
						event.target.value='';
					}
				}
			})
		])
	},
	data:function () {
		return {
			value:'',
			list:[]
		}
	}
});

var app5 = new Vue({
	el: '#app5'
});

效果:

6 未定义 slot 内容的默认值

slot 内容的默认值可以通过 this.$slots.default 来判定 ——

html:

<div id="app6">
    <e6></e6>
    <e6>
        <p>5亿年前的地球,磁场几乎全消失了,科学家:因祸得福</p>
    </e6>
</div>

js:

Vue.component('e6', {
	render: function (createElement) {
		if (this.$slots.default === undefined) {
			return createElement('div', '无消息');
		} else {
			return createElement('div', this.$slots.default);
		}
	}
});

var app6 = new Vue({
	el: '#app6'
});

效果:


本文示例代码