iT邦幫忙

6

[筆記][Vue.js]打開Vue.js世界的大門(11)-壓軸登場的組件基礎和Props使用方式篇

Hello!大家好!前幾天突然看到鐵人賽在十月開始,實在來的太快,讓小弟我感到非常錯愕XD,去年因為害怕所以沒有參加,但那時候也立志了今年要參加的決心!所以之後會好好準備那三十篇或以上的文章,Vue.js的更新可能就不會那麼頻繁,這一次真的真的真的不是偷懶的藉口XD,鐵人賽開幕時,再請各位大大多多指教了!


好的,題外話再分隔線以上結束,那來談談所謂的組件吧!組件在Vue中是個很重要的應用,所以我會分成幾個篇幅來說明,避免一篇文章中的資訊量太大讓人看了就想放棄XD。

1.什麼是組件?

組件類似於創建一個HTML的標籤,而該標籤的內容是在Vuecomponent屬性中設定的,在設定的同時我們他一個名稱,該名稱能夠在所有透過new Vue建立的實體中使用,聽起來很抽象嗎?讓我們來看看以下例子:
HTML

<div id="ex1">
    <!--直接下在component中設定的名稱為標籤-->
    <input-name></input-name>
</div>

JavaScript

//透過Vue的component創建一個組件,第一個值為該組件的名稱,第二個值為組件內容的物件
Vue.component('inputName',{
    data:()=>{
        return {
            inputType:'姓名',
            place:'Name',
        }
    },
    template:`<div>
                  <span>請輸入{{inputType}}:</span>
                  <input :placeholder="place" />
              </div>`
})

//創建一個Vue的實體
let ex1 = new Vue({
    el:'#ex1',
})

https://ithelp.ithome.com.tw/upload/images/20180902/201069356pemH4l7ab.jpg
值得注意的有幾個地方:

第一個是我在Vue.component('inputName',{...})設定的組件名稱和我在HTML標籤中的名稱不同,那是因為DOM的標籤只支援短橫線命名法,但是Vue.js並不會阻止你在創建組建的時候一定要用短橫線命名法,所以即使用駝峰命名法創建新組件,在HTML中還是要把他轉成短橫線命名法才行。

第二個是Vue.component和其他Vue物件一樣,都擁有datamethodscomputedwatch,但是他並不是對某個DOM建立實體,所以他不會有el。此外,data的值必須要是方法的回傳值;而回傳的是一個物件,這樣會讓每次創建一個組件實體的時候,都會重新new一個新的data,讓每次使用的相同組件都擁有自己獨立的data,這個部份可以參考下方的官方例子:
HTML

<div id="demoCounter">
    <!--一個組件可多次使用-->
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

JavaScript

Vue.component('button-counter', {
  data:function () {
    return {
      count: 0
    }
  },
  template:`<div>
                <button @click="count++">請點我</button>
                <span>目前點了{{count}}下</span>
            </div>`
})

let counter = new Vue({
    el:'#demoCounter' 
})

https://ithelp.ithome.com.tw/upload/images/20180902/20106935ARx7QtmUl3.jpg
上圖中重複使用了三次同一個組件,但是他們的count卻不是共用,而是獨立的。

第三個是上方的例子都是在全域中使用Vue.component('inputName',{...})建立組件,在全域中建立組件的話,不管是在頁面中任一個被new Vue過的DOM中都可以使用,但是組件並不是只能在全域中建立而已,他也可以針對某一個Vue物件建立,而用這種方式的話,該組件就只能夠在該DOM使用而已,可以看一下下方的例子:
HTML

<!--在被new Vue的DOM使用該組件-->
<div id="ex1">
    <input-name></input-name>
</div>

<!--inputName組件並不在ex2的new Vue物件中-->
<div id="ex2">
    <input-name></input-name>
</div>

JavaScript

//建立一個組件的物件
let componentsInputName = {
    data:()=>{
        return {
            inputType:'姓名',
            place:'name',
        }
    },
    template:`<div>
                  <span>請輸入{{inputType}}:</span>
                  <input :placeholder="place" />
             </div>`
}

let ex1 = new Vue({
    el:'#ex1',
    /*在new Vue中也可以使用components屬性,
      key為組件名稱,值為組件內容*/
    components:{
        inputName:componentsInputName
    }
})

let ex2 = new Vue({
    el:'#ex2',
})

https://ithelp.ithome.com.tw/upload/images/20180902/20106935dJnGguiFYk.jpg
在某個Vue物件中創建組件時,就只有對應該物件的DOM中可以使用組件而已,所以上方HTML中第二個div``ex2內雖然也有input-name,但設定的組件並不會出現,因為他只能在id="ex1"DOM中使用。

再來提的是如果要在組件內使用另一個組件該怎麼做,其實很簡單,只需要在該組件的物件中增加一個components屬性,並設定該子組件的資料即可,讓我們看看以下的例子:
HTML

<div id="ex1">
    <red-div></red-div>
</div>

JavaScript

//建立一個組件的物件,一個藍色的div
let componentsBlueDiv = {
    data:()=>{
        return {
           blueStyle:{
                height:'50px',
                width:'100px',
                backgroundColor:'blue',
            },
        }
    },
    template:`<div :style="blueStyle"></div>`
}

//建立一個組件的物件,是紅色的div
let componentsRedDiv = {
    /*在該組件中用components,
    指定一個組件名稱blueDiv為上面建立的componentsBlueDiv*/
    components:{
        blueDiv:componentsBlueDiv,
    },
    data:()=>{
        return {
            redStyle:{
                height:'100px',
                width:'200px',
                backgroundColor:'red',
            },
        }
    },
    //在template中直接使用上述設定的組件
    template:`<div :style="redStyle"><blue-div></blue-div></div>`,
}

let ex1 = new Vue({
    el:'#ex1',
    components:{
        //所以這時候的redDiv會有componentsBlueDiv和componentsRedDiv兩個div
        redDiv:componentsRedDiv,
    },
})

https://ithelp.ithome.com.tw/upload/images/20180902/20106935A56zKjIeV2.jpg
像上方程式那樣設定就能建立一個有父子關係的組件,但是他並不能像以下使用(小弟我一開始這樣寫,卡到一個天荒地老,拯救我的文章是這篇):

<div id="ex1">
    <red-div><blue-div></blue-div></red-div>
</div>

題外話是看到畫面突然發現有點像國旗XD

關於建立組件最後要提的一點是,當組件中不只有一個DOM時,必須包在一個div或任何父元素內,因為每個組件都只能擁有一個根元素,這部分在一開始也卡了很久/images/emoticon/emoticon16.gif

2.透過Props傳遞數據

好的,出現的東西越來越多,這又是做什麼用的呢?先讓我們來看看以下程式碼:
HTML

<div id="ex1">
    <input-name></input-name>
    <input-nickname></input-nickname>
</div>

JavaScript

Vue.component('inputName',{
    data:()=>{
        return {
            inputType:'姓名',
            place:'Name',
        }
    },
    template:`<div>
                  <span>請輸入{{inputType}}:</span>
                  <input :placeholder="place" />
              </div>`
})

Vue.component('inputNickname',{
    data:()=>{
        return {
            inputType:'暱稱',
            place:'Nickname',
        }
    },
    template:`<div>
                  <span>請輸入{{inputType}}:</span>
                  <input :placeholder="place" />
              </div>`
})

let ex1 = new Vue({
    el:'#ex1',
})

https://ithelp.ithome.com.tw/upload/images/20180902/201069356yH1lgamys.jpg
雖然只是兩個組件,但是他們明明就差不多卻因為某些地方不同,就要再另外新建立一個組件,看起來是不是很受不了?這時候我們就需要Props這個屬性來協助我們處理這個問題,先看一下加入Props後的程式碼:
HTML

<div id="ex1">
    <!--在使用組件時可依屬性傳遞參數的值-->
    <input-data title="姓名" place="Name"></input-data>
    <input-data title="暱稱" place="Nicename"></input-data>
</div>

JavaScript

Vue.component('input-data',{
    //使用props接收參數的值,陣列中的值是key對應屬性傳過來的資料,順序沒關係
    props:['title','place'],
    //可在組件中直接使用props內的key,會帶入組件使用時的屬性值
    template:`<div>
                  <span>請輸入{{title}}:</span>
                  <input :placeholder="place" />
              </div>`
})

let ex1 = new Vue({
    el:'#ex1',
})

https://ithelp.ithome.com.tw/upload/images/20180902/20106935xh3ppjL95K.jpg
最後得到的結果會和上方一樣,但是程式碼的部分明顯的差很多!個人私心認為這個屬性還滿酷的,主要是在組件的props屬性內先設定key值,之後在HTML上使用的時候依照陣列中的key傳入值。

但是Don't repeat yourself,重複的事情我們就不做了,所以既然長的差不多,就用迴圈吧!最後加上迴圈v-for的樣子:
HTML

<div id="ex1">
    <input-data v-for="type in types" :title="type.title" :place="type.place"></input-data>
</div>

JavaScript

Vue.component('input-data',{
    props:['title','place'],
    template:`<div>
                  <span>請輸入{{title}}:</span>
                  <input :placeholder="place" />
              </div>`
})

let ex1 = new Vue({
    el:'#ex1',
    data:{
        types:[
            {title:'姓名',place:'Name'},
            {title:'暱稱',place:'Nicename'}
        ]
    }
})

但是這麼做的話,當一個組件的key越來越多,使用組件的時候就必須設定更多的屬性和值,這個時候我們可以直接對某個key傳入整個物件,讓我們再重構一下程式:
HTML

<div id="ex1">
    <input-data v-for="type in types" :data="type"></input-data>
</div>

JavaScript

Vue.component('input-data',{
    //用data接收陣列內的每一個物件內容
    props:['data'],
    //傳進來的物件會跑進data中,所以data也會是物件
    template:`<div>
                  <span>請輸入{{data.title}}:</span>
                  <input :placeholder="data.place" />
              </div>`
})

let ex1 = new Vue({
    el:'#ex1',
    data:{
        types:[
            {title:'姓名',place:'Name'},
            {title:'暱稱',place:'Nicename'}
        ]
    }
})

這幾個例子中,使用props簡略了JavaScript的部分,接著用v-for和傳入data內的整個物件再一次簡略了HTML的部分,而實務上每個方式都應該會有適合使用的地方,所以學會運用還是最重要的!

下一篇會繼續說明有關組件的事件使用和v-model雙向綁定,希望可以把組件基礎的部分補完,之後會有組件的進階篇,那時候一定會更燒腦,不過文章中會盡量把每個講到的東西都舉例出來,讓大家不會和我一樣因為不熟悉而卡關XD,但是說不定還是會有漏掉的地方,只能請大大們留言指導我一下了,謝謝!


那分隔線下面的地方,也感謝各位大大觀看這篇文章,如果以上有理解錯誤或是解釋不清楚的地方,再麻煩留言告訴我,我會盡快修正文章內容的,謝謝大家/images/emoticon/emoticon41.gif


尚未有邦友留言

立即登入留言