Vue 組件

組件是可復(fù)用的 Vue 實例,所以與 new Vue 接收相同的選項,例如 data省容、computed、watch燎字、methods 以及生命周期鉤子等腥椒。僅有的例外是像 el 這樣根實例特有的選項阿宅。
一個組件的 data 選項必須是一個函數(shù),因此每個實例可以維護(hù)一份被返回對象的獨立的拷貝笼蛛。(類似閉包洒放。)否則同一組件的各個實例的data會共用。

data: function () {
  return {
    count: 0
  }
}

組件名

組件名可以接受kebab-casePascalCase,但在dom中使用時只能接受kebab-case(且至少包含一個連字符),例如 <my-component-name>滨砍。

組件注冊

全局注冊

全局注冊的組件可以用在其被注冊之后的任何 (通過 new Vue) 新創(chuàng)建的 Vue 根實例拉馋,也包括其組件樹中的所有子組件的模板中(包括自己引用自己)。

Vue.component('my-component-name', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})
局部注冊
  • 通過 components 對象進(jìn)行局部注冊惨好。其key就是自定義元素的名字,其屬性值就是這個組件的選項對象随闺。
  • 可注冊的位置包括new Vue()中日川,另一個component中,以及采用import時的 export default 內(nèi)矩乐。
components: {
  "my-component-name": {
    props: ['title'],
    template:'<h3>{{ title }}</h3>'
  }
}

根元素和屬性繼承

  • 每個組件必須有且只有一個根元素龄句。
  • 為組件賦予的classstyle等非prop屬性散罕,會自動合并到該組件實例的根元素上
    對于class分歇,style以外的非prop屬性可通過在組件的選項對象中配置inheritAttrs: false禁用自動合并到根元素,而實例的$attrs可以指定由哪個元素繼承組件上的屬性
<base-input
  v-model="username"
  required
  placeholder="Enter your username"
></base-input>
...
Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    </label>
  `
})

數(shù)據(jù)傳遞

通過 Prop 向子組件傳遞數(shù)據(jù)
  • prop名在 JavaScript 中可以是 camelCase 的(但依然推薦使用kebab-case)欧漱,但在 HTML 中總是 kebab-case 的职抡。
  • 在組件實例中能夠訪問props的值,就像訪問 data 中的值一樣误甚。prop會讀取組件中的自定義屬性缚甩。
  • prop可以以字符串?dāng)?shù)組形式表示,也可以以對象形式窑邦,指定各個prop值的類型擅威,并提供必選,默認(rèn)值冈钦,校驗等配置郊丛。
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
//或
props: {
  title: String,
  likes: [Number,String],//多個可能的類型
  isPublished: {
    type: Boolean,
    required: true,//必填
    default: 100//設(shè)置默認(rèn)值  
  },
  counts:{
      validator: function (value) {//自定義驗證函數(shù),可在開發(fā)環(huán)境拋出log警告
        // 這個值必須匹配下列字符串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
  },
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}
  • 傳入布爾值
<!-- 包含該 prop 沒有值的情況在內(nèi)瞧筛,都意味著 `true`厉熟。-->
<blog-post is-published></blog-post>

<!-- 使用 `v-bind` 告訴 Vue 這是一個布爾值表達(dá)式而非字符串。 -->
<blog-post v-bind:is-published="false"></blog-post>
  • 傳入一個對象的所有屬性
    可以使用不帶參數(shù)的 v-bind
post: {
  id: 1,
  title: 'My Journey with Vue'
}
...
<blog-post v-bind="post"></blog-post>
//等價于
<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>
  • 單項數(shù)據(jù)流
    父級 prop 的更新會向下流動到子組件中驾窟,但是反過來則不行庆猫。
    每次父級組件發(fā)生更新時,子組件中所有的 prop 都將會刷新為最新的值绅络。因此不應(yīng)該在一個子組件內(nèi)部改變 prop月培。
    當(dāng)需要在子組件中改變prop時嘁字,可采取:
    1. 定義一個本地的 data 屬性并將這個 prop 用作其初始值
    2. 使用一個計算屬性來關(guān)聯(lián)這個 prop 的值
    • 注意杉畜,在子組件中改變prop引用的對象或數(shù)組本身將會影響到父組件的狀態(tài)纪蜒。

事件驅(qū)動

  • 事件名應(yīng)始終使用kebab-case。(不像prop中vue做了優(yōu)化此叠,會將js中的camelCase在html中轉(zhuǎn)為kebab-case)
  • 父級組件 v-on 監(jiān)聽子組件實例纯续,子組件通過調(diào)用$emit 來觸發(fā)一個事件,也可以在子組件中通過$on監(jiān)聽事件
<blog-post v-on:enlarge-text="postFontSize += $event"></blog-post>

components: {
  "my-component-name": {
    template: '
        <button v-on:click="$emit('enlarge-text', 0.1)">
            Enlarge text
        </button>',
    mounted() {
      this.$on('enlarge-text', target => {
        console.log("enlarge-text", target)
      })
   }
}

通過EventBus實現(xiàn)組件間通訊

公共事件總線EventBus的實質(zhì)就是創(chuàng)建一個vue實例灭袁,通過一個空的vue實例作為橋梁實現(xiàn)vue組件間的通信猬错。它是實現(xiàn)非父子組件通信的一種解決方案。
通過單獨建一個vue實例茸歧,并在需要通訊的A倦炒、B組件中同時導(dǎo)入

import Vue from 'Vue'
export default new Vue
import EventBus from '這里是你引入bus.js的路徑'

在A組件中調(diào)用EventBus.$emit,在B組件中監(jiān)聽EventBus.$on软瞎,即實現(xiàn)通訊
注意逢唤,在B組件的beforeDestroy或者destroyed中應(yīng)調(diào)用EventBus.$off清除監(jiān)聽,否則重復(fù)加載B時會導(dǎo)致創(chuàng)建多個監(jiān)聽

將原生事件綁定到組件

如根元素直接為發(fā)生事件的元素涤浇,需使用.native修飾符鳖藕。否則此處事件(focus)會被認(rèn)為是對子組件emit的監(jiān)聽,而非瀏覽器原生事件只锭。

<base-input v-on:focus.native="onFocus"></base-input>

否則可利用$listeners屬性

雙向綁定

通過props和$emit完成父子組件雙向綁定

v-model

v-model是雙向綁定的一個特例著恩,利用名為 value 的 prop 和名為 input 的事件(或在子組件model配置中修改),即可簡寫為v-model

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>
//或
<custom-input v-model="searchText"></custom-input>
...
Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})
  • 像單選框蜻展、復(fù)選框等類型的輸入控件可能會將 value 特性用于不同的目的页滚。model 選項可以用來避免這樣的沖突:
<base-checkbox v-model="lovingVue"></base-checkbox>
...
Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})
.sync修飾符

.sync修飾符是雙向綁定的一個特例,利用名為 XXX 的 prop 和名為 update:XXX 的事件铺呵,即可簡寫為.sync

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>
...
this.$emit('update:title', newTitle)
<text-document v-bind:title.sync="doc.title"></text-document>

插槽

組件元素間的html內(nèi)容,可以通過<slot></slot>插入渲染后的組件實例裹驰。否則會被全部廢棄。

<alert-box>
  Something bad happened.
</alert-box>
...
Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})
后備內(nèi)容

寫在<slot>標(biāo)簽內(nèi)片挂,若父級組件中沒有傳入插槽內(nèi)容幻林,則顯示該后備內(nèi)容。(雖然寫在子組件內(nèi)音念,但作用域同父級組件)

具名插槽 和 作用域插槽 (2.6.0)
  • 具名插槽
    通過 <slot> 元素的name屬性沪饺,定義額外的插槽。一個不帶 name<slot> 出口會帶有隱含的名字“default”闷愤。在向具名插槽提供內(nèi)容的時候整葡,我們可以在一個 <template> 元素上使用 v-slot 指令,并以 v-slot 的參數(shù)的形式提供其名稱讥脐。
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
</div>
...
<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>
  <p>A paragraph for the main content.</p>
</base-layout>
  • 作用域插槽

父級模板里的所有內(nèi)容都是在父級作用域中編譯的遭居;子模板里的所有內(nèi)容都是在子作用域中編譯的啼器。

因此通常插槽可訪問父級作用域而非子級作用域。如果需要訪問子組件實例中的屬性俱萍,可以在 <slot> 元素上添加特性端壳,并為v-slot賦值進(jìn)行關(guān)聯(lián)

<span>
  <slot :user="user">
    {{ user.lastName }}
  </slot>
</span>
...
//此處的slotProps等同于插槽的props
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>
  • v-onv-bind 一樣,v-slot 也可縮寫為 #枪蘑。例如 v-slot:header 可以被重寫為 #header
獨占插槽的縮寫

當(dāng)僅含有默認(rèn)插槽時,v-slot可以不寫在<template>中而直接寫在組件上损谦,甚至可以省略default

<current-user v-slot:default="slotProps"></current-user>
<current-user v-slot="slotProps"></current-user>
動態(tài)插槽

同動態(tài)指令參數(shù),使用 [XXX]

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

動態(tài)組件 和 異步組件

動態(tài)組件

通過 <component> 元素和 is 特性來實現(xiàn):

<!-- 組件會在 `currentTabComponent` 改變時改變 -->
<component v-bind:is="currentTabComponent"></component>

在解析dom模板時岳颇,有些 HTML 元素照捡,諸如 <ul>、<ol>话侧、<table> 和 <select>麻敌,對于哪些元素可以出現(xiàn)在其內(nèi)部是有嚴(yán)格限制的。而有些元素掂摔,諸如 <li>、<tr> 和 <option>赢赊,只能出現(xiàn)在其它某些特定的元素內(nèi)部乙漓。此時可以通過is防止渲染出錯。

<table>
  <tr is="blog-post-row"></tr>
</table>
在動態(tài)組件上使用 keep-alive

組件動態(tài)切換時释移,可以繼承上次離開時的狀態(tài)

<!-- 失活的組件將會被緩存叭披!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
異步組件

可以使用工廠函數(shù)(Promise)按需加載模塊 查看文檔

單文件組件

文件擴(kuò)展名為 .vue 的 單文件組件,解決了多個component重名玩讳,字符串模板的語法高亮涩蜘,組件作用域的CSS,模塊化構(gòu)建等問題熏纯,且方便使用各種預(yù)處理器同诫。

組件間通信方式總結(jié)

  1. provide / inject
    允許一個祖先組件向其所有子孫后代注入一個依賴。
    可以視為祖先組件和后代組件間的“l(fā)ong range props”樟澜,除了:
  • 父組件不需要知道哪些子組件使用它 provide 的 property
  • 子組件不需要知道 inject 的 property 來自哪里

provide 和 inject 綁定沒有做響應(yīng)式處理误窖。但如果傳入了一個對象,其屬性還是可響應(yīng)的秩贰。

// 祖先組件
app.component('todo-list', {
  data() {
    return {
      a:{name:100}
    }
  },
  provide: {
    a:this.a,//無法如此操作,應(yīng)改為如下的provide函數(shù)形式
    b: 200
  },
  provide(){
    return {
      a:this.a霹俺,
      todoLength: Vue.computed(() => this.todos.length)
    }
  }
}

// 子組件
app.component('todo-list-statistics', {
  inject: ['a'],
  created() {
    console.log(`Injected property: ${this.a.name}`)
  }
})
  1. Vue.observable
    讓一個對象可響應(yīng)。Vue 內(nèi)部會用它來處理 data 函數(shù)返回的對象毒费。
    返回的對象可以直接用于渲染函數(shù)和計算屬性內(nèi)丙唧,并且會在發(fā)生改變時觸發(fā)相應(yīng)的更新。也可以作為最小化的跨組件狀態(tài)存儲器觅玻,用于簡單的場景:
const state = Vue.observable({ count: 0 })
const Demo = {
  render(h) {
    return h('button', {
      on: { click: () => { state.count++ }}
    }, `count is: ${state.count}`)
  }
}
  1. $attrs
    包含了父作用域中未被props涵蓋的特性想际,通常用于在創(chuàng)建高級別組件時再通過 v-bind="$attrs" 傳入更內(nèi)部的組件培漏。
  2. $listeners
    包含了父作用域中的 (非 .native 修飾器的) v-on 事件監(jiān)聽器。它可以通過 v-on="$listeners" 傳入內(nèi)部組件——在創(chuàng)建更高層次的組件時非常有用沼琉。
  3. props
  4. $emit
  5. eventbus
  6. vuex
  7. $parent / $children / ref

父子組件鉤子順序

  • 加載渲染過程
    父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 子組件更新過程
    父beforeUpdate->子beforeUpdate->子updated->父updated
  • 父組件更新過程
    父beforeUpdate->父updated
  • 銷毀過程
    父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末北苟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子打瘪,更是在濱河造成了極大的恐慌友鼻,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闺骚,死亡現(xiàn)場離奇詭異彩扔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)僻爽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門虫碉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胸梆,你說我怎么就攤上這事敦捧。” “怎么了碰镜?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵兢卵,是天一觀的道長。 經(jīng)常有香客問我绪颖,道長秽荤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任柠横,我火速辦了婚禮窃款,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牍氛。我一直安慰自己晨继,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布搬俊。 她就那樣靜靜地躺著踱稍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悠抹。 梳的紋絲不亂的頭發(fā)上珠月,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音楔敌,去河邊找鬼啤挎。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庆聘。 我是一名探鬼主播胜臊,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伙判!你這毒婦竟也來了象对?” 一聲冷哼從身側(cè)響起深胳,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤苗分,失蹤者是張志新(化名)和其女友劉穎躬拢,沒想到半個月后鱼的,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡神僵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年囤躁,在試婚紗的時候發(fā)現(xiàn)自己被綠了德崭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片常潮。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡弟胀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喊式,到底是詐尸還是另有隱情孵户,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布岔留,位于F島的核電站夏哭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贸诚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一厕吉、第九天 我趴在偏房一處隱蔽的房頂上張望酱固。 院中可真熱鬧,春花似錦头朱、人聲如沸运悲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽班眯。三九已至,卻和暖如春烁巫,著一層夾襖步出監(jiān)牢的瞬間署隘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工亚隙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留磁餐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓阿弃,卻偏偏與公主長得像诊霹,于是被迫代替她去往敵國和親羞延。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355