Vue 組件(上篇)

什麼是組件

組件 (Component) 是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠是原生 HTML 元素的形式,以 is 特性擴展。javascript

使用組件

註冊

註冊一個全局組件,你可使用 Vue.component(tagName, options)html

<html>

<head>
    <meta charset="utf-8" />
    <script type="text/javascript" src='https://unpkg.com/vue'></script>
</head>
<body>
    <div id="example">
        <my-component></my-component>
    </div>
</body>
<script>
    // 註冊
    Vue.component('my-component', {
        template: '<div>A custom component!</div>'
    })
    // 建立根實例
    new Vue({
        el: '#example'
    })

</script>

</html>

運行結果:vue

<div id="example">
<div>A custom component!</div>
</div>

局部註冊

沒必要在全局註冊每一個組件。經過使用組件實例選項註冊,可使組件僅在另外一個實例/組件的做用域中可用:java

<div id="example-2">
    <child-component></child-component>
</div>

<script>
var Child = {
    template: '<div>A child component!</div>'
}
new Vue({
    el: '#example-2',
    components: {
        'child-component': Child
    }
})
</script>

運行結果:git

<div id="example-2">
<div>A child component!</div>
</div>

若是把 child-component 只能用example-2中,不能放到其餘的div中,若是把放到其餘的div中就會報錯。github

<div id="example">
   <my-component></my-component>
   <child-component></child-component>
</div>

報錯提示

DOM 模板解析說明

當使用 DOM 做爲模版時 (例如,將 el 選項掛載到一個已存在的元素上), 你會受到 HTML 的一些限制,由於 Vue 只有在瀏覽器解析和標準化 HTML 後才能獲取模版內容。尤爲像這些元素<ul>,<ol>,<table>,<select> 限制了能被它包裹的元素,而一些像 <option> 這樣的元素只能出如今某些其它元素內部。
在自定義組件中使用這些受限制的元素時會致使一些問題,例如:數組

<table>
  <my-row>...</my-row>
</table>

自定義組件 <my-row> 被認爲是無效的內容,所以在渲染的時候會致使錯誤。變通的方案是使用特殊的 is 屬性:瀏覽器

<table>
  <tr is="my-row"></tr>
</table>

應當注意,若是您使用來自如下來源之一的字符串模板,這些限制將不適用:微信

  • <script type="text/x-template">函數

  • JavaScript 內聯模版字符串

  • .vue 組件

所以,有必要的話請使用字符串模版。

data 必須是函數

<div id="example-3">
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
</div>

<script>
var data = { counter: 0 }
Vue.component('simple-counter', {
    template: '<button v-on:click="counter += 1">{{ counter }}</button>',
    // 技術上 data 的確是一個函數了,所以 Vue 不會警告,
    // 可是咱們返回給每一個組件的實例的卻引用了同一個data對象
    data: function () {
        return data
    }
})
new Vue({
    el: '#example-3'
})
</script>

因爲這三個組件共享了同一個 data,所以增長一個 counter 會影響全部組件!這不對。咱們能夠經過爲每一個組件返回全新的 data 對象來解決這個問題:

data: function () {
    //return data
    return {
        counter: 0
    }
}

如今每一個 counter 都有它本身內部的狀態了。

構成組件

在 Vue 中,父子組件的關係能夠總結爲 props down, events up。父組件經過 props 向下傳遞數據給子組件,子組件經過 events 給父組件發送消息。看看它們是怎麼工做的。

父子組件-w200

Prop

使用prop傳遞數據

要讓子組件使用父組件的數據,咱們須要經過子組件的 props 選項。

<div id="example-4">
    <child message="hello world!"></child>
</div>

<script>
Vue.component('child', {
    // 聲明 props
    props: ['message'],
    // 就像 data 同樣,prop 能夠用在模板內
    // 一樣也能夠在 vm 實例中像「this.message」這樣使用
    template: '<span>{{ message }}</span>'
})

new Vue({
    el: '#example-4'
})
</script>

運行結果:

<div id="example-4">
<span>hello world!</span>
</div>

camelCase vs. kebab-case

HTML 特性是不區分大小寫的。因此,當使用的不是字符串模版,camelCased (駝峯式) 命名的 prop 須要轉換爲相對應的 kebab-case (短橫線隔開式) 命名:

<div id="example-5">
    <child2 my-message="hello world message!"></child2>
</div>

<script>
    Vue.component('child2', {
        // 聲明 props
        props: ['myMessage'],
        // 就像 data 同樣,prop 能夠用在模板內
        // 一樣也能夠在 vm 實例中像「this.message」這樣使用
        template: '<span>{{ myMessage }}</span>'
    })

    new Vue({
        el: '#example-5'
    })

</script>

運行結果:

<div id="example-5">
<span>hello world message!</span>
</div>

若是把child2中的 my-message 改成 myMessage,

<child2 myMessage="hello world message!"></child2>

錯誤提示:

錯誤提示

動態 Prop

在模板中,要動態地綁定父組件的數據到子模板的 props,與綁定到任何普通的HTML特性相相似,就是用 v-bind。每當父組件的數據變化時,該變化也會傳導給子組件

<div id="example-6">
    <input v-model="parentMsg">
    <br>
    <child2 v-bind:my-message="parentMsg"></child2>
</div>

<script>
Vue.component('child2', {
    // 聲明 props
    props: ['myMessage'],
    // 就像 data 同樣,prop 能夠用在模板內
    // 一樣也能夠在 vm 實例中像「this.message」這樣使用
    template: '<span>{{ myMessage }}</span>'
})


new Vue({
    el: '#example-6',
    data:{
        parentMsg:'Message from parent',
    }
})
</script>

運行結果:

運行結果

字面量語法 vs 動態語法

<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>

由於它是一個字面 prop,它的值是字符串 "1" 而不是 number。若是想傳遞一個實際的 number,須要使用 v-bind,從而讓它的值被看成 JavaScript 表達式計算:

<div id="example-7">
    <comp v-bind:some-prop="1"></comp>
</div>

<script>
Vue.component('comp', {
    // 聲明 props
    props: ['someProp'],
    template: "<span>{{ someProp +'----'+ typeof someProp }}</span>"
})
new Vue({
    el: '#example-7'
})
</script>

單向數據流

prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,可是不會反過來。這是爲了防止子組件無心修改了父組件的狀態——這會讓應用的數據流難以理解。

Prop 驗證

咱們能夠爲組件的 props 指定驗證規格。若是傳入的數據不符合規格,Vue 會發出警告。當組件給其餘人使用時,這頗有用。
要指定驗證規格,須要用對象的形式,而不能用字符串數組:

<div id="example-8">
    <example prop-a="11231" prop-c='asda'></example>
</div>

<script>
Vue.component('example', {
    props: {
        // 基礎類型檢測 (`null` 意思是任何類型均可以)
        propA: Number,
        // 多種類型
        propB: [String, Number],
        // 必傳且是字符串
        propC: {
            type: String,
            required: true
        },
        // 數字,有默認值
        propD: {
            type: Number,
            default: 100
        },
        // 數組/對象的默認值應當由一個工廠函數返回
        propE: {
            type: Object,
            default: function () {
                return { message: 'hello' }
            }
        },
        // 自定義驗證函數
        propF: {
            validator: function (value) {
                return value > 10
            }
        }
    },
    template: "<span> {{'propA: '+ propA +' propC:'+propC}} </span>"
})

new Vue({
    el: '#example-8'
})
</script>

錯誤提示:

錯誤提示

propA 類型是number,而咱們傳遞給它的是個字符串,須要以下修改

<example v-bind:prop-a="11231" prop-c='asda'></example>

// :prop-a 是 v-bind:prop-a的縮寫
<example :prop-a="11231" prop-c='asda'></example>

若是把prop-c去掉,格式以下

<example :prop-a="11231" </example>

錯誤提示:

錯誤提示

這是因爲propC required: true 是必傳的。

type 能夠是下面原生構造器:

  • String

  • Number

  • Boolean

  • Function

  • Object

  • Array

  • Symbol

type 也能夠是一個自定義構造器函數,使用 instanceof 檢測。
當 prop 驗證失敗,Vue 會在拋出警告 (若是使用的是開發版本)。注意 props 會在組件實例建立以前進行校驗,因此在 default 或 validator 函數裏,諸如 data、computed 或 methods 等實例屬性還沒法使用。

非 Prop 屬性

所謂非 prop 屬性,就是它能夠直接傳入組件,而不須要定義相應的 prop。
明確給組件定義 prop 是傳參的推薦方式,但組件的做者並不總能預見到組件被使用的場景。因此,組件能夠接收任意傳入的屬性,這些屬性都會被添加到組件的根元素上。
例如,第三方組件 bs-date-input,當它要和一個 Bootstrap 插件互操做時,須要在這個第三方組件的 input 上添加 data-3d-date-picker 屬性,這時能夠把屬性直接添加到組件上 (不須要事先定義 prop):

<bs-date-input data-3d-date-picker="true"></bs-date-input>

添加屬性 data-3d-date-picker="true" 以後,它會被自動添加到 bs-date-input 的根元素上

替換/覆蓋現有的特性

<div id="example-9">
    <bs-date-input data-3d-date-picker="true" class="date-picker-theme-dark"></bs-date-input>
</div>

<script>
Vue.component('bs-date-input',{
    template: '<input type="date" class="form-control">'
})

new Vue({
    el: '#example-9'
})
</script>

運行結果:

<div id="example-9">
<input type="date" class="form-control date-picker-theme-dark" data-3d-date-picker="true">
</div>

對於多數特性來講,傳遞給組件的值會覆蓋組件自己設定的值。即例如傳遞 type="large" 將會覆蓋 type="date" 且有可能破壞該組件!索性咱們對待 class 和 style 特性會更聰明一些,這兩個特性的值都會作合併 (merge) 操做,讓最終生成的值爲:form-control date-picker-theme-dark。

源碼: 源碼下載

Vue組建(下篇)

社羣品牌:從零到壹全棧部落
定位:尋找共好,共同窗習,持續輸出全棧技術社羣
業界榮譽:IT界的邏輯思惟
文化:輸出是最好的學習方式
官方公衆號:全棧部落
社羣發起人:春哥(從零到壹創始人,交流微信:liyc1215)
技術交流社區:全棧部落BBS
全棧部落完整系列教程:全棧部落完整電子書學習筆記

關注全棧部落官方公衆號,每晚十點接收系列原創技術推送
相關文章
相關標籤/搜索