四、Vue组件化开发学习笔记——父子组件通信,父级向子级传值(props),子级向父级传值(自定义事件),slot插槽
一、父子組件的通信
在上一篇博文中,我們提到了子組件是不能引用父組件或者Vue實例的數據的。
但是,在開發中,往往一些數據確實需要從上層傳遞到下層:
- 比如在一個頁面中,我們從服務器請求到了很多的數據。
- 其中一部分數據,并非是我們整個頁面的大組件來展示的,而是需要下面的子組件進行展示。
- 這個時候,并不會讓子組件再次發送一個網絡請求,而是直接讓大組件(父組件)將數據傳遞給小組件(子組件)。
如何進行父子組件間的通信呢?Vue官方提到
在下面的代碼中,我直接將Vue實例當做父組件,并且其中包含子組件來簡化代碼。
真實的開發中,Vue實例和子組件的通信和父組件和子組件的通信過程是一樣的。
二、父級向子級傳遞
2.1、props基本用法
在組件中,使用選項props來聲明需要從父級接收到的數據。
props的值有兩種方式:
我們先來看一個最簡單的props傳遞:
2.2、props數據驗證
在前面,我們的props選項是使用一個數組。
我們說過,除了數組之外,我們也可以使用對象,當需要對props進行類型等驗證時,就需要對象寫法了。
驗證都支持哪些數據類型呢?
當我們有自定義構造函數時,驗證也支持自定義的類型:
注意:v-bind綁定的屬性名稱不要使用駝峰寫法,vue不支持這種寫法,如下圖所示:
要想使用駝峰標識,應該把上圖中的cInfo改成c-info,如下圖所示:
三、子級向父級傳遞
props用于父組件向子組件傳遞數據,還有一種比較常見的是子組件傳遞數據或事件到父組件中。
我們應該如何處理呢?這個時候,我們需要使用自定義事件來完成。
什么時候需要自定義事件呢?
- 當子組件需要向父組件傳遞數據時,就要用到自定義事件了。
- 我們之前學習的v-on不僅僅可以用于監聽DOM事件,也可以用于組件間的自定義事件。
自定義事件的流程:
我們來看一個簡單的例子:
我們來看一個簡單的例子:
- 我們之前做過一個兩個按鈕+1和-1,點擊后修改counter。
- 我們整個操作的過程還是在子組件中完成,但是之后的展示交給父組件。
- 這樣,我們就需要將子組件中的counter,傳給父組件的某個屬性,比如total。
四、父子組件的訪問
4.1 父子組件的訪問方式: $children
有時候我們需要父組件直接訪問子組件,子組件直接訪問父組件,或者是子組件訪問根組件。
- 父組件訪問子組件:使用$children或$refs reference(引用)
- 子組件訪問父組件:使用$parent
我們先來看下$children的訪問:
- this.$children是一個數組類型,它包含所有子組件對象。
- 我們這里通過一個遍歷,取出所有子組件的message狀態。
$children的缺陷: - 通過$children訪問子組件時,是一個數組類型,訪問其中的子組件必須通過索引值。
- 但是當子組件過多,我們需要拿到其中一個時,往往不能確定它的索引值,甚至還可能會發生變化。
有時候,我們想明確獲取其中一個特定的組件,這個時候就可以使用$refs
$refs的使用:
$refs和ref指令通常是一起使用的。
- 首先,我們通過ref給某一個子組件綁定一個特定的ID。
- 其次,通過this.$refs.ID就可以訪問到該組件了。
4.2 父子組件的訪問方式: $parent
如果我們想在子組件中直接訪問父組件,可以通過$parent
注意事項:
- 盡管在Vue開發中,我們允許通過$parent來訪問父組件,但是在真實開發中盡量不要這樣做。
- 子組件應該盡量避免直接訪問父組件的數據,因為這樣耦合度太高了。
- 如果我們將子組件放在另外一個組件之內,很可能該父組件沒有對應的屬性,往往會引起問題。
- 另外,更不好做的是通過$parent直接修改父組件的狀態,那么父組件中的狀態將變得飄忽不定,很不利于我的調試和維護。
五、插槽slot
5.1為什么使用slot
slot翻譯為插槽: 在生活中很多地方都有插槽,電腦的USB插槽,插板當中的電源插槽。
插槽的目的是讓我們原來的設備具備更多的擴展性。
比如電腦的USB我們可以插入U盤、硬盤、手機、音響、鍵盤、鼠標等等。
組件的插槽:
- 組件的插槽也是為了讓我們封裝的組件更加具有擴展性。
- 讓使用者可以決定組件內部的一些內容到底展示什么。
栗子:移動網站中的導航欄。
- 移動開發中,幾乎每個頁面都有導航欄。
- 導航欄我們必然會封裝成一個插件,比如nav-bar組件。
- 一旦有了這個組件,我們就可以在多個頁面中復用了。
但是,每個頁面的導航是一樣的嗎?No,我以京東M站為例
5.2 如何封裝這類組件呢?slot
如何去封裝這類的組件呢?
- 它們也很多區別,但是也有很多共性。
- 如果,我們每一個單獨去封裝一個組件,顯然不合適:比如每個頁面都返回,這部分內容我們就要重復去封裝。
- 但是,如果我們封裝成一個,好像也不合理:有些左側是菜單,有些是返回,有些中間是搜索,有些是文字,等等。
如何封裝合適呢?抽取共性,保留不同。
- 最好的封裝方式就是將共性抽取到組件中,將不同暴露為插槽。
- 一旦我們預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什么內容。
- 是搜索框,還是文字,還是菜單。由調用者自己來決定。
這就是為什么我們要學習組件中的插槽slot的原因。
5.3 slot基本使用
了解了為什么用slot,我們再來談談如何使用slot?
- 在子組件中,使用特殊的元素就可以為子組件開啟一個插槽。
- 該插槽插入什么內容取決于父組件如何使用。
我們通過一個簡單的例子,來給子組件定義一個插槽:
- <slot>中的內容表示,如果沒有在該組件中插入任何其他內容,就默認顯示該內容
- 有了這個插槽后,父組件如何使用呢?
5.4 具名插槽slot
當子組件的功能復雜時,子組件的插槽可能并非是一個。
- 比如我們封裝一個導航欄的子組件,可能就需要三個插槽,分別代表左邊、中間、右邊。
- 那么,外面在給插槽插入內容時,如何區分插入的是哪一個呢?
- 這個時候,我們就需要給插槽起一個名字
如何使用具名插槽呢?
非常簡單,只要給slot元素一個name屬性即可
<slot name='myslot'></slot>
我們來給出一個案例:
這里我們先不對導航組件做非常復雜的封裝,先了解具名插槽的用法。
5.5 編譯作用域
在真正學習插槽之前,我們需要先理解一個概念:編譯作用域。
官方對于編譯的作用域解析比較簡單,我們自己來通過一個例子來理解這個概念:
我們來考慮下面的代碼是否最終是可以渲染出來的:
<my-cpn v-show="isShow"></my-cpn>中,我們使用了isShow屬性。
isShow屬性包含在組件中,也包含在Vue實例中。
答案:最終可以渲染出來,也就是使用的是Vue實例的屬性。
為什么呢?
官方給出了一條準則:父組件模板的所有東西都會在父級作用域內編譯;子組件模板的所有東西都會在子級作用域內編譯。
- 而我們在使用的時候,整個組件的使用過程是相當于在父組件中出現的。
那么他的作用域就是父組件,使用的屬性也是屬于父組件的屬性。 - 因此,isShow使用的是Vue實例中的屬性,而不是子組件的屬性。
5.6 作用域插槽
作用域插槽是slot一個比較難理解的點,而且官方文檔說的又有點不清晰。
這里,我們用一句話對其做一個總結,然后我們在后續的案例中來體會:
父組件替換插槽的標簽,但是內容由子組件來提供。
我們先提一個需求:
子組件中包括一組數據,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]
需要在多個界面進行展示:
- 某些界面是以水平方向一一展示的,
- 某些界面是以列表形式展示的,
- 某些界面直接展示一個數組
內容在子組件,希望父組件告訴我們如何展示,怎么辦呢?
利用slot作用域插槽就可以了
我們來看看子組件的定義:
在父組件使用我們的子組件時,從子組件中拿到數據:
- 我們通過<template slot-scope="slotProps">獲取到slotProps屬性
- 在通過slotProps.data就可以獲取到剛才我們傳入的data了
總結
以上是生活随笔為你收集整理的四、Vue组件化开发学习笔记——父子组件通信,父级向子级传值(props),子级向父级传值(自定义事件),slot插槽的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十四、PHP框架Laravel学习笔记—
- 下一篇: JS面向对象——class定义类、类的构