3 個概念,入門 Vue 元件開發
“元件”是 Vue 中比較基礎的概念,但我發現,許多同學對 Vue 元件的概念和由來並不是清楚。因此,我希望通過這個專題,帶大家換個角度來分析,最終讓大家更清楚元件開發。
首先,我們先不談元件,我想問大家一個問題:
我們平常用任何程式語言寫方法(method)的時候,當一個方法裡的邏輯過多時,我們會怎麼辦?當多個方法裡有很多相似的邏輯時,我們該怎麼辦?
答案很明瞭:拆分成一個獨立的方法。
如果拆分後還是有類似問題呢?那就繼續拆分。
類比一下,HTML 也一樣。當我們寫了一大堆的 HTML 後,發現有不少類似或重複的地方,我們也可以按照拆分的方法,拆分 HTML。除了拆分 HTML 之外,我們還可以拆分針對這段 HTML 書寫的邏輯,甚至是樣式。拆分後的 HTML,邏輯,樣式組合在一起,我們就稱之為元件。
這麼講似乎有點抽象,我們來舉個例子吧。假如我們要做一個簡單的 TodoList 專案,程式碼如下:
複製程式碼
<ul> <li> <input type="checkbox"> <span> 學習 Vue 屬性 </span> <button> 刪除 </button> </li> <li> <input type="checkbox"> <span> 學習 Vue 事件 </span> <button> 刪除 </button> </li> <li> <input type="checkbox"> <span> 學習 Vue 插槽 </span> <button> 刪除 </button> </li> </ul> </div>
可以看到,我們 li 標籤內的內容越來越多,似乎可以把它獨立出來,在 Vue 中,通過以下程式碼就可以將這部分程式碼獨立出來.
通過 Vue.component 定義 (註冊) 一個元件,起名為 todo-item, 元件的 HTML 寫在 template 欄位上:
複製程式碼
Vue.component('todo-item', { template: `<li> <input type="checkbox"> <span> 學習 Vue 屬性 </span> <button> 刪除 </button> </li>` })
然後,你可以通過下面這樣的方式來使用這個元件:
複製程式碼
<ul> <todo-item></todo-item> <todo-item></todo-item> <todo-item></todo-item> </ul> </div>
當然前提條件是你要先 new Vue 一個例項,並添加掛載點
複製程式碼
new Vue({ el:'#app'// 提供一個掛載點,這樣我們就可以在裡面使用 todo-item 了 })
這樣一來清爽了許多,可是這樣就變成三個“學習 Vue 屬性” 事項了,我們還缺“學習 Vue 事件”和“學習 Vue 插槽” ,怎麼辦呢,這就要用到 Vue 的屬性了。
屬性
我們接著改改,呼叫方法時,可以傳遞不同的引數,方法也可以接收引數,執行不同的邏輯,載入元件時同樣也可以傳遞不同的引數 (屬性),元件也可以接收引數 (屬性) 來顯示不同的內容:
複製程式碼
Vue.component('todo-item', { props: ['item'], // 宣告能接收的參 (屬) 數 (性) // {{item}} 使用傳遞過來的 item template: `<li> <input type="checkbox"> <span>{{item}}</span> <button> 刪除 </button> </li>` }) <div id="app"> <ul> <todo-item item=" 學習 Vue 屬性 "></todo-item> <todo-item item=" 學習 Vue 事件 "></todo-item> <todo-item item=" 學習 Vue 插槽 "></todo-item> </ul> </div>
我們再精簡一下:
複製程式碼
<ul> <todo-item v-for="item in list":item="item"></todo-item> </ul> </div> new Vue({ el:'#app', data() { return{ list: ['學習 Vue 屬性','學習 Vue 事件','學習 Vue 插槽'] } } })
這樣就可以了,是不是很簡單?
事件
我現在已經學完了 Vue 屬性,想要從 todolist 裡面把它刪除掉,這好像並不太容易。這時,我需要給 button 繫結一個事件,當然 Vue 提供給我們了一個簡單的方式進行繫結事件,用@xxx 就可以進行事件綁定了 (這裡的 xxx 指的任一字串,根據你的實際需要來命名就行)
複製程式碼
Vue.component('todo-item', { props: ['item'], template: `<li> <input type="checkbox"> <span>{{item}}</span> <button @click="handleClick"> 刪除 </button> </li>`, methods: { handleClick() { } } })
然後我們需要把點選事件告訴我們的上層 (父元件),Vue 同樣給我們提供了一個 API:this.$emit(‘xxx’, …),我們既然是做刪除操作,那就是叫 delete 好了,我們還可以傳遞更多的引數,如 this.item:
複製程式碼
handleClick() { this.$emit('delete', this.item) }
上層元件還缺少了一個用來接收 delete 的地方,我們可以通過 @delete 的方式來繫結一個用來接收 delete 事件的方法:
<todo-item v-for=“item in list” :item=“item”@delete =“handleDelete”>
最後只需要在 methods 欄位上定義一個 handleDelete 方法,改變 list 陣列就完成了我們的刪除操作:
複製程式碼
new Vue({ el:'#app', data() { return{ list: ['學習 Vue 屬性','學習 Vue 事件','學習 Vue 插槽'] } }, methods: { handleDelete(item) { const index = this.list.findIndex(text=>text === item); this.list.splice(index,1); } } })
以上就是 Vue 中事件的用法。
插槽
現在我想讓這個 TodoList 中的“學習 Vue XXX”前加個圖示 Icon,怎麼辦呢?還好 Vue 早就幫我想到了,我們不能再通過屬性傳遞這些帶有標籤的內容,而是通過一種名叫“插槽”的東西進行傳遞:
複製程式碼
<todo-item v-for="item in list":item="item"@delete="handleDelete"> <span> 我是 Icon</span> </todo-item>
當然我們也不能再用雙括號來解析,我們需要使用
複製程式碼
template: `<li> <input type="checkbox"> <span>{{item}}</span> <slot></slot> <button @click="handleClick"> 刪除 </button> </li>`,
這種我們稱之為預設插槽。
現在我想更進一步,新增兩個圖示,一個在文字前面,一個在文字後面,沒問題:
複製程式碼
<todo-item v-for="item in list":item="item"@delete="handleDelete"> <span slot="prefixIcon"> 我是字首 Icon</span> <span slot="suffixIcon"> 我是字尾 Icon</span> </todo-item>
同樣 template 需要更改:
複製程式碼
template: `<li> <input type="checkbox"> <slot name="prefixIcon"></slot> <span>{{item}}</span> <slot name="suffixIcon"></slot> <button @click="handleClick"> 刪除 </button> </li>`,
這便是我們的具名插槽。
如果想讓功能更加豐富的話,比如我想根據我的 input checkbox 的是否選中來改變圖示的顏色,該怎麼做?
第一步,記錄我們 input 的選中狀態,我們使用 Vue 的 v-model 進行 input 的雙向繫結:
複製程式碼
Vue.component('todo-item', { props: ['item'], data() { return{ checked: false, // 預設不選中 } }, template: `<li> <input type="checkbox"v-model="checked"> <slot name="prefixIcon"></slot> <span>{{item}}</span> <slot name="suffixIcon"></slot> <button @click="handleClick"> 刪除 </button> </li>`, methods: { handleClick() { this.$emit('delete', this.item) } } })
狀態有了,現在需要把這個狀態傳遞給上層:
複製程式碼
<slot name="prefixIcon"v-bind="{checked}"> 我是字首 Icon</slot>
接收到狀態後並根據狀態提供不同顏色的圖示:
複製程式碼
<span slot="prefixIcon"slot-scope="props":style="{color: props.checked ?'red':'blue'}"> 我是字首 Icon</span>
這就是我們的作用域插槽。
為了方便理解,插槽使用方式我們使用了 Vue 2.5 版本的語法進行講解,Vue 2.6 的語法可檢視視訊教程。
如果你已經閱讀到了這裡,那麼恭喜你,你已經可以進行簡單的 Vue 元件開發了。
如果還想學習更多的 Vue 實戰技巧,歡迎訂閱我的視訊課程《Vue 開發實戰》 。