Vue 案例:ToDoList
ToDoList
要求
- 目标:
- 显示一个输入框,一个按钮
- 在输入框中输入内容,点击按钮即在页面中显示出该内容,之后输入框恢复空白
- 若要完全理解这个小demo,需要一些前提条件:
vue
基础指令- 初步理解
JavaScript
中的作用域以及this
关键字 - Vue全局组件以及局部组件的用法
最普通的方式
<div id="app">
<input type="text" v-model="inputValue"/> <!-- v-model实现双向数据绑定 -->
<button type="button" @click="buttonClick">添加</button> <!-- @click触发点击事件 -->
<ul>
<li v-for="item in list">{{ item }}</li> <!-- v-for绑定数据来循环渲染 -->
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 引入Vue -->
<script type="text/javascript">
var app = new Vue({ //创建一个新的实例对象
el: '#app', //el:element 的简写,用来指定Vue应用程序接管的元素(包括所有的子元素)
data: { //data:data就是Vue实例中的数据
list: [], //对应到v-for指令的数据
inputValue: '' //对应到v-model的数据绑定
},
methods: { //方法
buttonClick: function(){ //对应到@click的方法
//将input框中的内容添加到list中,this指向的是这个app实例
this.list.push(this.inputValue);
//添加完之后清空input框
this.inputValue = '';
}
}
})
</script>
全局组件实现
<div id="app">
<input type="text" v-model="inputValue"/> <!-- v-model实现双向数据绑定 -->
<button type="button" @click="buttonClick">添加</button> <!-- @click触发点击事件 -->
<ul>
<!-- 使用组件 --> <!-- 注意命名规则,全局组件的TodoItem在这里要写成todo-item -->
<todo-item v-bind:content='item' v-for='item in list'></todo-item>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 引入Vue -->
<script type="text/javascript">
//用Vue中的component方法用来定义一个全局组件
Vue.component('TodoItem', { //组件名为TodoItem
props: ['content'], //来自父组件的传值,即要渲染的content值
template: '<li>{{content}}</li>', //HTML模板,在其中仍然用插值表达式来显示content值
})
var app = new Vue({ //创建一个新的实例对象
el: '#app', //el:element 的简写,用来指定Vue应用程序接管的元素(包括所有的子元素)
data: { //data:data就是Vue实例中的数据
list: [], //对应到v-for指令的数据
inputValue: '' //对应到v-model的数据绑定
},
methods: { //方法
buttonClick: function(){ //对应到@click的方法
//将input框中的内容添加到list中,this指向的是这个app实例
this.list.push(this.inputValue);
//添加完之后清空input框
this.inputValue = '';
}
}
})
</script>
- 其实,对比我们之前用最普通的方式,这里无非就是把之前的
<li v-for="item in list">{{ item }}</li>
换成了<todo-item v-bind:content='item' v-for='item in list'></todo-item>
,
然后定义了一个全局的组件TodoItem
- 那么,组件与普通方式之间,分别对应着什么呢?
- 我们用
Vue.component
方法定义了一个叫TodoItem
的全局组件,它的template
属性的值,就是用来替换<li>{{ item }}</li>
的 - 注册组件之后我们就可以使用啦,我们在
div
中直接用<todo-item></todo-item>
即可 - 另外,为了替换掉普通方法中的
v-for='item in list'
,
我们在组件中使用了v-bind:content='item'
以及v-for='item in list'
- 最后一步:组件传值,我们现在只有
v-bind:connent
和content
,还没传值呢,所以我们用props
属性来接受父组件的传值(也就是这里的input
框中的输入,另外,实际上这里的Vue实例最外层就是父组件)
- 我们用
- 来梳理一下整个过程:
- 在
input
框中输入内容,并点击添加
,触发buttonClick()
,data.list
添加input
框中的内容 v-for
用item
遍历了data.list
,并将值通过v-bind:content
传给<todo-item></todo-item>
标签- 最后用插值表达式将
content
打印出来
- 在
局部组件实现
<div id="app">
<input type="text" v-model="inputValue"/> <!-- v-model实现双向数据绑定 -->
<button type="button" @click="buttonClick">添加</button> <!-- @click触发点击事件 -->
<ul>
<!-- 注意命名,这里的名称以Vue实例中的components的属性名为准 -->
<todo v-bind:content='item' v-for='item in list'></todo> <!-- 使用组件 -->
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 引入Vue -->
<script type="text/javascript">
var TodoItem = { //定义局部组件的名称为TodoItem
props: ['content'], //接受父组件传值
template: '<li>{{ content }}</li>', //HTML模板
}
var app = new Vue({ //创建一个新的实例对象
el: '#app', //el:element 的简写,用来指定Vue应用程序接管的元素(包括所有的子元素)
components: { //注册组件
//定义时为TodoItem,现在在实例中另外取名为todo,使用时以后者为准
todo: TodoItem,
},
data: { //data:data就是Vue实例中的数据
list: [], //对应到v-for指令的数据
inputValue: '' //对应到v-model的数据绑定
},
methods: { //方法
buttonClick: function(){ //对应到@click的方法
//将input框中的内容添加到list中,this指向的是这个app实例
this.list.push(this.inputValue);
//添加完之后清空input框
this.inputValue = '';
}
}
})
</script>
- 其实这里的局部组件已经很容易掌握了,对比之前,全局组件是用
Vue.component
方法定义的,而局部组件直接将整个内容写成了一个 对象 - 然后在Vue实例中,通过
components
属性,将该组件作为属性值注册了该组件,最后直接使用组件
增加一个功能
要求
- 在之前的
ToDoList
基础上,要增加一项功能:- 点击一项内容,点击后即消失
- 在输入框中回车即可添加一项
- 知识前提:
- 组件传值
- JavaScript字符串方法
- Vue按键修饰符
组件实现
<div id="app">
<!-- 事件修饰符将回车事件keyup.enter指向buttonClick函数 -->
<input type="text" v-model="inputValue" @keyup.enter="buttonClick"/>
<button type="button" @click="buttonClick">添加</button>
<ul>
<todo-item v-bind:content = 'item'
v-for = '(item, index) in list'
v-bind:index = 'index'
v-on:delete = 'parentDelete'>
</todo-item>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script type="text/javascript">
Vue.component('TodoItem', {
props: ['content', 'index'], //接受来自父组件的传值
//点击元素,触发子组件的childDelete事件
template: '<li @click="childDelete">{{ content }}</li>',
methods: {
/*子组件向外触发一个名为delete的事件,这个事件将会被父组件监听,从而触发parentDelete
子组件向父组件传的值不仅有事件名称delete,还有一个index来表示该元素的下标*/
childDelete: function(){
this.$emit('delete', this.index); //this.index来自于props接受的值
}
}
})
var app = new Vue({
el: '#app',
data: {
list: [],
inputValue: ''
},
methods: {
buttonClick: function(){
this.list.push(this.inputValue);
this.inputValue = '';
},
//父组件监听到子组件的delete事件被触发时
parentDelete: function(index){ //接受子组件的传值,index来自于子组件的this.index
this.list.splice(index, 1); //js字符串方法删除指定下标的元素
}
}
})
</script>
- 很明显,我们不该从上往下读代码,这样是自己为难自己,我们应该从功能的角度考虑.
- 我们要实现的功能是:点击元素,让它消失,但是元素在子组件中,消失与否是受父组件的
list
与v-for
共同确定渲染的 - 所以,我们的大概思路应该是:
- 点击元素,让子组件将该元素的下标都传给父组件
- 然后父组件删除在
list
中该下标的元素,即可实现整个过程
- 所以,我们的关键操作是:
- 在定义局部组件时,给
template
属性的HTML模板添加childDelete
,点击时触发 - 通过
this.$emit
将this.index
传给父组件,但是我们需要为此绑定一个事件,以供父组件实时监听,我们命名为delete
- 父组件通过
v-on:delete
监听到时,触发parentDelete
来删除该元素 - 但是
parentDelete
肯定需要知道该元素下标,所以我们在最开始就要把下标传给子组件,子组件再返回给parentDelete
- 在定义局部组件时,给
- 子组件与父组件之间的传值,个人感觉取决于什么时候用,比如这里的多个事件对应的多个传值,也就是说,我们在传值时务必弄清楚这个值所对应的事件是什么
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: