基于 Vue.js 使用 Render 函数实现留言列表功能

1,125 阅读1分钟

本文使用Render 函数来完成一个留言列表的功能。 按照惯例,先初始化各个文件

<head>
	<meta charset="utf-8">
	<title>留言列表</title>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
	<div id="app" v-cloak>
		<div class="message">
			<v-input v-model="username"></v-input>
			<v-textarea v-model="message"></v-textarea>
			<button @click="handleSend">发布</button>
		</div>
		<list :list="list" @reply="handleReply"></list>
	</div>
	<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
	<script src="input.js"></script>
	<script src="list.js"></script>
	<script src="index.js"></script>
</body>

// 发布一条留言,需要的数据有昵称和留言内容,发布操作应该在根实例app内完成。留言列表的数据也是从app获取的。所以在index.js中添加这3项数据:

var app = new Vue({

el: '#app',
data: {
	username: '',
	message: '',
	list: []
},
methods: {

	// 数组list储存了所有的留言内容,通过函数handleSend给list添加一项留言数据,添加成功后,把textarea文本框清空。

	handleSend: function () {
		// 提交留言前,做非空判断
		if (this.username === '') {
			window.alert('请输入昵称');
			return;
		},
		if (this.message === '') {
			window.alert('请输入留言内容');
			return;
		},
		this.list.push({
			name: this.username,
			message: this.message
		});
		this.message = '';
	},
	handleReply: function (index) {
		var name = this.list[index].name;
		this.message = '回复@'+ name + ':';
		this.$refs.message.focus();
	}
}

});

// 先由外至内构建出大致的DOM结构 // 动态绑定value,并监听input事件,把输入的内容$emit('input')派发到父组件

input.js: Vue.component('vInput', {

props: {
	value: {
		type: [String, Number],
		default: ''
	}
},
render: function (h) {
	var _this = this;
	return h('div', [
		h('span', '昵称:'),
		h('input', {
			attrs: {
				type: 'text'
			},
			domProps: {
				value: this.value
			},
			on: {
				input: function (event) {
					_this.value = event.target.value;
					_this.$emit('input', event.target.value);
				}
			}
		})
	]);
}

});

Vue.component('vTextarea', {

props: {
	value: {
		type: String,
		default: ''
	}
},
render: function (h) {
	var _this = this;
	return h('div', [
		h('span', '留言内容:'),
		h('textarea', {
			attrs: {
				placeholder: '请输入留言内容'
			},
			domProps: {
				value: this.value
			},
			ref: 'message',
			on: {
				input: function (event) {
					_this.value = event.target.value;
					_this.$emit('input', event.target.value);
				}
			}
		})
	]);
}
// 点击回复按钮后,文本框立即聚焦;
methods: {
	focus: function () {
		this.$refs.message.focus();
	}
}

});

list.js: Vue.component('list', {

props: {
	list: {
		type: Array,
		default: function () {
			return [];
		}
	}
},
render: function (h) {
	var _this = this;
	var list = [];
	// this.list.forEach相当于template里的v-for指令,遍历出每条留言。句柄handleRely直接向父组件派发一个事件reply,
	// 父组件(app)接受后,将当前list-item的昵称提取,并设置到v-textarea内。
	this.list.forEach(function (msg, index) {
		var node = h('div', {
			attrs: {
				class: 'list-item'
			}
		}, [
			h('span', msg.name + ': '),
			h('div', {
				attrs: {
					class: 'list-msg'
				}
			}, [
				h('p', msg.message),
				h('a', {
					attrs: {
						class: 'list-reply'
					},
					on: {
						click: function () {
							_this.handleReply(index);
						}
					}
				}, '回复')
			]) 
		])
		list.push(node);
	});
	// 列表数据list为空时,渲染一个“列表为空”的信息提示节点;不为空时,每个list-item应包含昵称,留言内容和回复按钮3个子节点。
	if (this.list.length) {
		return h('div', {
			attrs: {
				class: 'list'
			},
		}, list);
	} else {
		return h('div', {
			attrs: {
				class: 'list-nothing'
			}
		}, '留言列表为空');
	}
},
methods: {
	handleReply: function (index) {
		this.$emit('reply', index);
	}
}

})