Vue 案例:ToDoList

Ozzie發表於2019-11-24

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:connentcontent,還沒傳值呢,所以我們用props屬性來接受父元件的傳值(也就是這裡的input框中的輸入,另外,實際上這裡的Vue例項最外層就是父元件)
  • 來梳理一下整個過程:
    • input框中輸入內容,並點選新增,觸發buttonClick()data.list新增input框中的內容
    • v-foritem遍歷了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>
  • 很明顯,我們不該從上往下讀程式碼,這樣是自己為難自己,我們應該從功能的角度考慮.
  • 我們要實現的功能是:點選元素,讓它消失,但是元素在子元件中,消失與否是受父元件的listv-for共同確定渲染的
  • 所以,我們的大概思路應該是:
    • 點選元素,讓子元件將該元素的下標都傳給父元件
    • 然後父元件刪除在list中該下標的元素,即可實現整個過程
  • 所以,我們的關鍵操作是:
    • 在定義區域性元件時,給template屬性的HTML模板新增childDelete,點選時觸發
    • 通過this.$emitthis.index傳給父元件,但是我們需要為此繫結一個事件,以供父元件實時監聽,我們命名為delete
    • 父元件通過v-on:delete監聽到時,觸發parentDelete來刪除該元素
    • 但是parentDelete肯定需要知道該元素下標,所以我們在最開始就要把下標傳給子元件,子元件再返回給parentDelete
  • 子元件與父元件之間的傳值,個人感覺取決於什麼時候用,比如這裡的多個事件對應的多個傳值,也就是說,我們在傳值時務必弄清楚這個值所對應的事件是什麼

相關文章