本文使用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);
}
}
})