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
- 子元件與父元件之間的傳值,個人感覺取決於什麼時候用,比如這裡的多個事件對應的多個傳值,也就是說,我們在傳值時務必弄清楚這個值所對應的事件是什麼