參考資料:
Vue.js: 元件 Components 簡介 - 註冊與使用
Vue.官方文件
Summer 夏天Vue.js: Slot
元件(components)簡介:
什麼是元件?
每個 Vue.js 的應用程式都是從Vue建構式 (vue constructor) 建立根實體 (root vue instance) 開始,再一個個將元件 (Components) 搭建上去而來的,透過元件的方式能讓開發者
將程式碼封裝而且更好重複利用
。
元件特性?
一、元件資料都是獨立的
1.透過props向內部組件傳遞數據
2.透過emit event觸發事件將資料往外送
二、data必須是一個函數 額外練習:官網範例連結
1 | Vue.component("button-counter", { |
Q:為何子元件中data必須是函式?
A:因為 JavaScript 切分變數有效範圍的最⼩單位為 function
,為了避免子元件資料互相污染,Vue強置規定子元件data必須是函式
Component(全域註冊)
下面將練習題目拆分成不同部分講解
寫法:Vue.component(‘自定義名稱’, {Function | Object})
逗點後方可以使用function或object
範例如下(請先忽略props,後面會介紹到)
1 | Vue.component('row-component', { |
使用X-template建立元件
此時,因為我們使用到X-template建立元件,因此我們必須額外新一個script
,並指定一個 id來來使⽤
1 | <script type="text/x-template" id="rowComponentTemplate"> |
說明:這邊script
是另外撰寫喔!
補充:放在codepen上時,X-template
會將上面一段script
寫在html中
接著,是HTML結構部分
1 | <row-component v-for="(item, key) in data" :person="item" :key="key"></row-component> |
示意圖如下
- 該如何改善?(使用is動態載入template)正常版
1
2
3<tr is="row-component" v-for="(item, key) in data" :person="item" :key="key"></tr>
//使用is動態載入template - 最後,前面提到的props功能為何?
將v-for中的data資料傳進template元件中。示意圖如下
區域註冊介紹(範例如下)
寫法:var ComponentA = { /* … */ }
官網介紹
說明:ComponentA,為自定義名稱
1 | var child = { |
提醒:components
,有s
基礎建立元件方式(官網教學內容)
1 | // 提醒:Vue.component必須寫在new Vue前方,原因是元件必須先定義才能被使用。參閱下方討論連結 |
基礎建立元件跟使用X-template差異僅在於
template
放置位置
Vue.component new Vue 擺放順序
結論
1.由於 HTML 不分⼤⼩寫的特性,使用⾃定義的標籤時限定全⼩
寫
(可加入破折號 - ) 的標籤名稱
2.元件註冊分為全域
與區域
3.區域元件
離開指定實體後就不能取用
4.全域元件
則可以提供多組實體使用
Q:子元件中的data是否可以使用箭頭函式?
A:不行,因為這會影響到this作用域。會造成無法取得data資料。其實像是computed、methods、watch也都不行使用箭頭函式
But,僅有Filter可以使用arrow function,因為filter無法取得實體資料!
Q:下面寫法是全域註冊還是區域註冊?
1 | var CustomBlock = Vue.component('custom-block', { |
A:是全域註冊喔,別搞混了!
使用Props由外到內傳遞資料
說明:
我們之所以需要使用props的目的,在於Vue元件中所有元件都是獨立的,因此資料不能互相取用,這也意味著你不能 (也不應該)在子元件的模組直接引⽤⽗元件的資料。而需要透過props將資料從外部進行傳遞
我們先來看一個簡單範例:
codepen連結
提醒:使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名
簡單來說:JS上用小駝峰parentMSg
,則html上要用短橫線分隔 如parent-msg
畫面如下:
靜態傳入與動態傳入差異
六角課程範例
動態傳入:傳入的是實體內內容
靜態傳入:傳入的是純字串
小結論:動態傳入跟靜態傳入寫法差異僅在於是否有
:
只要沒有:
即使透過number傳入也會是string喔!同學討論連結
Props驗證與預設值
我們知道Props是將外部資料傳入元件內,但你怎麼知道傳入的東西是否符合需求或安全呢?這時就需要很常使用到驗證
首現先介紹型別:
1 | String |
提醒:型別字首要大寫 如:
type: Number
範例:
這邊僅列出幾個例子
方便我日後快速回覆記憶,完整內容請看codepen
1 | props: { |
提醒:props本來就應該用物件包裝起來,因為要對傳進來物件進行驗證
這邊特別注意:
1.驗證object時,default必須為一個function 如下
1 | propE: { |
2.這兩種寫法相同喔!都是對型別不做任何驗證
1 | props:['parentMsg'] |
Props使用注意事項
單向數據流
什麼意思呢?Props是將資料由外部往內傳遞,而在老師範例中可以清楚看到如果我們從內部修改外部傳進來內容
,便會造成錯誤!
後記:仔細想想也蠻有道理,假設我們可以從子層更動父層實體內容,如此一來父層實體內容便會無法管理(或是說資料會不知道被誰更動過!)
這時候該如何解決呢?
很簡單,只要新宣告一個參數來接受外部修改資料就好~寫法如下
或是在元件中使用computedget
、set
這兩種寫法目的:都是讓資料獨立存在於子層而非父層
1 | Vue.component('photo', { |
當V-model遇到props
範例如下:
透過上方圖片講解,重點在於第四點,在component底下的data資料是無法改變上層父元素內容!也就是下方圖片中更改4號
是無法更改1、2、3的資料
codepen示範
尚未宣告變數(就是AJAX資料時間差)
老師這邊提到的目的是,因為在new vue中我們使用AJAX傳遞資料,而AJAX特性就是非同步,簡單來說就是網頁載入時,AJAX資料可能還未載入,因此網頁上就空空的或是圖片破版
。該如何解決呢?
1 | 老師這邊提供一個解法就是v-if,當v-if中內容為truthy時,再將內容載入 |
父子元件之間溝通方法
總共區分為監聽事件、觸發事件兩種:
監聽事件:使用V-on 或$on
(後者主要會用在event bus)
觸發事件:透過$emit
元件父與子
子元件可以透過this.$parent
取得父層實體內容 範例如下
1 | Vue.component('my-component',{ |
這邊的parent,指的是id=“app”
這個區塊
父组件則是透過 this.$children
這個成員 (陣列), 來存取他的⼦組件。但是要注意this.$children
的順序會受到 v-if 的影響,建議先以ref
給子組件設定別名以確保不受組件的順序影響。 範例如下:
1 | newVue({ |
使用時機
1.debug除錯
2.實務上建議不要這樣直接複寫父層內容(因為子層無法脫離父層內容)
觸發事件:透過emit 向外傳遞事件
上面當V-model遇到props 範例中,我們發現子層資料無法更動父層資料。如果要更動上層資料就會使用到$emit,將資料由內傳到外。
我們先來看個簡單例子:
示意圖如下:
codepen練習檔案
說明:只要更動4號欄位中內容,其餘1、2、3資料也會同步更新喔!
範例2:
課程範例連結
我們的目的如下:點擊內部元件後,改變外部資料數值
以下摘錄轉寫重點
1 | <button-counter @increment="incrementTotal"> |
1 | Vue.component('buttonCounter',{ |
.sync修飾符傳遞
可以幫助我們達到資料雙向綁定 官網介紹
補充:雖然我們看似寫了.sync後就達到雙向綁定,實際上我們在子元件中還是透過$emit向外層傳遞更改後的數值
提醒:為何update不能更改為其他JS原生事件:click?、input?
A:因為vue並未定義JS原生事件。所以必須要使用vue本身設定update
優點:
Vue實體不需要在使用methods接受子層傳遞出來資料!
小結論:
這就是props in , emit out的由來
Event Bus
用途:將原本元件之間資料傳遞時的樹狀結構更改為網狀結構!
寫法:
Step1. 新增一個Vue實體
var bus = new Vue()
Step2. 透過向bus發送事件,與訂閱事件來完成元件與元件的溝通
接著,我們直接來看範例:
備註:訂閱事件必須撰寫在created()階段
缺點:
1.當事件名稱重複時,會同時觸發不同子元件
2.因為事件是自己手動訂閱,所以銷毀時候,在beforeDestroy必須手動刪除
綜合練習:todoList
更新Component 資料方法整理
1.Event Bus
2.Props in Emit Out
3.$children 、$parent
4.Vuex
is動態切換元件
用途:最常使用在頁籤切換!
我們可以透過
1.<component>
加上:is
屬性來來決定目前元件是誰
2.或是<div>
加上:is
寫法:
1 | <component:is="currentView"></component> |
說明:上面介紹兩種寫法其實沒有差異,只是列出比較常使用方法而已。
注意:使用 is 要注意的只有像<li>
、<tr>
、 <option>
這類有特別限制上層 DOM 元素必須要是哪幾種的,像 <li>
的外層就只能是<ol>
或 <ul>
。官網連結
範例: codepen六角課堂範例
而當我們透過 <keep-alive> … </keep-alive>
來為元件保留內部狀態 codepen
延伸閱讀:[[Vue Instance/生命週期介紹]]
Slot 元件插槽
在介紹slot之前,我們必須先瞭解一個觀念:編譯作用域
我們先來看個範例
1 | <div id="app"> |
猜猜看,child
元件中是否會印出父層的msg:I'mparent
內容?
答案是不會的!原因是目前child所處的位置是父層作用域
,當然不會印出子層內容~ codepen連結
小結論:
1.父層作用域在父層編譯
2.子層作用域在子層編譯
若要突破作用域編譯問題,就要使用到slot
Slot簡介
參考資料:Summer 夏天Vue.js: Slot
突破作用域,將父層內容傳遞進來!通常會應用在如果內容不一定要放入component中時,可以透過slot傳遞一整個html結構進來。
Slot 是一種用於內容分配(Content Distribution / Transclusion)的元件,適用於複雜或巢狀元件的實作上,可以想像成是空間預留的方法,在迭代過程中再把內容塞進去。
卡斯伯老師在課堂上將slot分為三種:
1.沒有slot的狀態
1 | <no-slot-component> |
說明:即使寫p段落文字在子元件中,在html上依舊不會顯示。因為元件中的內容都會被模板替換掉。這就是上方提到作用域編譯的問題~
2.單組slot
特點:在模板中添加
1 | <single-slot-component> |
1 | <script type="text/x-template" id="singleSlotComponent"> |
畫面上,就可以將<p>使用這段取代原本的 Slot。</p>
呈現在畫面上!如下
3.多組slot(具名插槽)
簡單來說,將內容放入指定位置
在html加入slot
在JS中插入 name
1 | <named-slot-component> |
1 | <div class="card-header"> |
補充:html結構中若不想顯示標籤如a、header,可以使用,這樣就不會顯示標籤摟~