如果看完了Vue.js入门1-5,就可以很容易的理解这个小Demo,这个Demo我们来模拟一个留言表。
1.首先,我们写一个Demo需要把准备工作做好
1.1创建5个文件,如图:
1.2在index.html中编写如下代码:
<html>
<head>
<title>留言表</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="app" v-cloak style="width: 500px; margin: 0 auto;">
<div class="message">
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="input.js"></script>
<script src="list.js"></script>
<script src="index.js"></script>
</body>
</html>
1.3在index.js中编写如下代码:
var app = new Vue({
el:'#app',
});
Vue.component('vInput',{
});
Vue.component('vTextarea',{
});
其他文件先不用添加代码,用到的时候我们再说!
2.如果要发布一条留言,需要的数据有昵称和留言内容,这些操作应该在Vue的实例中完成,所以在index.js中添加以下代码:
var app = new Vue({
el:'#app',
data:{
username:'',
message:'',
list:[]
},
methods:{
handleSend:function(){
this.list.push({
name:this.username,
message:this.message
});
this.message = '';
}
}
});
2.1在index.html中使用v-model将username和message进行双向绑定
<div class="message">
<v-input v-model="username"></v-input>
<v-textarea v-model="message"></v-textarea>
</div>
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'
},
demProps:{
value:this.value
},
on:{
input:function(event){
_this.value = event.target.value;
_this.$emit('input',event.target.value);
}
}
})
]);
}
});
扩充:组件与Vue的实例比较相似,需注册后才可以使用,注册有全局注册和局部注册俩中方式。全局注册后,
任何Vue实例都可以使用。全局注册代码如下:
Vue.component('my-component',{
//选项
})
my_component就是注册的组件自定义的标签名称,要在父实例中使用这个组件,必须要在实例创建前注册,完后
就可以使用<my-component></my-component>来使用了,代码如下:
<div id="app">
<my-component></my-component>
</div>
<script>
Vue.component('my-component',{
});
var app = new Vue({
el:'app',
})
</script>
完后在组件中的选项那里添加template就可以显示内容了
Vue.component('my-component',{
//选项
template:'<div>组件的内容</div>'
});
渲染后的结果就是:
<div id="app">
<div>组件的内容</div>
</div>
$emit和JavaScript的观察者模式的dispatchEvent和addEventListener这俩个方法类似,
子组件$emit()用来触发事件,父组件$on()用来监听子组件的事件。父组件也可以直接在子组件的自定义标签上使用v-on来
监听子组件触发的自定义事件。
读完这段话,是不是对上面的代码又有了一个全新的认识呢!?
2.3列表数据list为空的时候,渲染一个”列表为空”的信息提示节点:不为空时,每个list-item要包含昵称,留言内容和恢复按钮3个子节点。list.js的render内容如下:
rander:function (h) {
var _this = this;
var list = [];
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);
});
if (this.list.length) {
return h('div', {
attrs: {
class: 'list'
},
}, list);
} else {
return h('div', {
attrs: {
class: 'list-nothing'
}
}, '留言列表为空!');
}
}
2.4handleReply直接向父组件派发一个事件reply,父组件(app)接收后,将当前list-item的昵称提取,并设置到v-textarea内,在list.js中增加代码如下:
methods: {
handleReply: function (index) {
this.$emit('reply', index);
}
}
2.5在index.html中添加
<button @click="handleSend">发布</button>
<list :list="list" @reply="handleReply"></list>
具体代码如下:
<div id="app" v-cloak style="width: 500px; margin: 0 auto;">
<div class="message">
<v-input v-model="username"></v-input>
<v-textarea v-model="message" ref="message"></v-textarea>
<button @click="handleSend">发布</button>
</div>
<list :list="list" @reply="handleReply"></list>
</div>
2.6因为需要判断一下用户是否输入的昵称和聊天的消息,所以我们在index.js中添加:
handleReply:function(index){
var name = this.list[index].name;
this.message = '回复@' + name +': ';
this.$refs.message.focus();
}
if (this.username === '') {
window.alert('请输入昵称!');
return;
}
if (this.message === '') {
window.alert('请输入消息!');
return;
}
2.7在input.js的vTextarea组件中添加:
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();
}
}
3.接下来我们就开始写css的样式了,在style.css中编写代码:
[v-cloak]{
display: none;
}
*{
padding: 0;
margin: 0;
}
.message{
width: 450px;
text-align: right;
}
.message div{
margin-bottom: 12px;
}
.message span{
display: inline-block;
width: 100px;
vertical-align: top;
}
.message input, .message textarea{
width: 300px;
height: 32px;
padding: 0 6px;
color: #657180;
border: 1px solid #d7dde4;
cursor: text;
outline: none;
}
.message input:focus, .message textarea:focus{
border: 1px solid #3399ff;
}
.message textarea{
height: 60px;
padding: 4px 6px;
}
.message button{
display: inline-block;
padding: 6px 15px;
border: 1px solid #39f;
border-radius: 4px;
color: #fff;
background-color: #39f;
cursor: pointer;
outline: none;
}
.list{
margin-top: 50px;
}
.list-item{
padding: 10px;
border-bottom: 1px solid #e3e8ee;
overflow: hidden;
}
.list-item span{
display: block;
width: 60px;
float: left;
color: #39f;
}
.list-msg{
display: block;
margin-left: 60px;
text-align: justify;
}
.list-msg a{
color: #9ea7b4;
cursor: pointer;
float: right;
}
.list-msg a:hover{
color: #39f;
}
.list-nothing{
text-align: center;
color: #9ea7b4;
padding: 20px
}
4.这样就可以实现一个留言板了,五个页面所有代码,完整的我再发一遍
index.heml代码:
<html>
<head>
<title>留言表</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="app" v-cloak style="width: 500px; margin: 0 auto;">
<div class="message">
<v-input v-model="username"></v-input>
<v-textarea v-model="message" ref="message"></v-textarea>
<button @click="handleSend">发布</button>
</div>
<list :list="list" @reply="handleReply"></list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="input.js"></script>
<script src="list.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js代码:
var app = new Vue({
el: '#app',
data: {
username: '',
message: '',
list: []
},
methods: {
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();
}
}
});
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(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);
});
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);
}
}
});
style.css代码:
[v-cloak]{
display: none;
}
*{
padding: 0;
margin: 0;
}
.message{
width: 450px;
text-align: right;
}
.message div{
margin-bottom: 12px;
}
.message span{
display: inline-block;
width: 100px;
vertical-align: top;
}
.message input, .message textarea{
width: 300px;
height: 32px;
padding: 0 6px;
color: #657180;
border: 1px solid #d7dde4;
cursor: text;
outline: none;
}
.message input:focus, .message textarea:focus{
border: 1px solid #3399ff;
}
.message textarea{
height: 60px;
padding: 4px 6px;
}
.message button{
display: inline-block;
padding: 6px 15px;
border: 1px solid #39f;
border-radius: 4px;
color: #fff;
background-color: #39f;
cursor: pointer;
outline: none;
}
.list{
margin-top: 50px;
}
.list-item{
padding: 10px;
border-bottom: 1px solid #e3e8ee;
overflow: hidden;
}
.list-item span{
display: block;
width: 60px;
float: left;
color: #39f;
}
.list-msg{
display: block;
margin-left: 60px;
text-align: justify;
}
.list-msg a{
color: #9ea7b4;
cursor: pointer;
float: right;
}
.list-msg a:hover{
color: #39f;
}
.list-nothing{
text-align: center;
color: #9ea7b4;
padding: 20px
}
最后,附上一张效果图: