Vue組件傳值與通信集合

Vue的組件化給前端開發(fā)帶來極大的便利捆蜀,這種依賴數(shù)據(jù)來控制Dom的模式幔嫂,區(qū)別于以前的開發(fā)控制Dom的開發(fā)理念履恩,這也導(dǎo)致了一種情況,在Vue中是單向數(shù)據(jù)流的飒筑,意味著只能從父組件向子組件傳值绽昏,不允許子組件向父組件傳值全谤。

這樣會防止從子組件意外改變父級組件的狀態(tài)认然,從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。 ---vue教程

然而當(dāng)我們把組件拆分到足夠細(xì)的時(shí)候盈匾,子組件控制父組件的數(shù)據(jù)毕骡,或者兄弟組件之間的傳值就變得尤為突出挺峡,這里我將總結(jié)各式各樣的傳值,函數(shù)調(diào)用的方法尤仍。

父組件中的通信方法

閱讀完官方文檔后宰啦,我們一定會對props有強(qiáng)烈的印象,然而在父組件中可不止有這種通信的方式田炭。

序號 方法名 概要 推薦程度
1 props 通過對子組件的v-bind綁定或標(biāo)簽屬性值進(jìn)行傳值 強(qiáng)烈推薦
2 provide/inject 通過注入的方式教硫,以允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴辆布,不論組件層次有多深,并在起上下游關(guān)系成立的時(shí)間里始終生效景用。 謹(jǐn)慎使用
3 vm.$children 通過查詢當(dāng)前父級的子組件獲得子組件的數(shù)據(jù)與方法伞插,并不保證順序盾碗,也不是響應(yīng)式的 謹(jǐn)慎使用
4 vm.$slots 插槽<slot></slot>或者用vm.$slots獲取 推薦

props

  • 命名規(guī)范

HTML 中的特性名是大小寫不敏感的置尔,所以瀏覽器會把所有大寫字符解釋為小寫字符榜轿。這意味著當(dāng)你使用 DOM 中的模板時(shí)朵锣,camelCase (駝峰命名法) 的 prop 名需要使用其等價(jià)的 kebab-case (短橫線分隔命名) 命名:

<blog-post post-title="hello!"></blog-post>
Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
  • 傳遞靜態(tài)或動態(tài) Prop
    假如你想傳字符串給子組件只需要給便簽添加屬性就可以了
//這里的title的內(nèi)容就會傳給子組件
<blog-post title="My journey with Vue"></blog-post>

當(dāng)然那些非字符串的類型就直接依賴于v-bind進(jìn)行傳值(Number诚些、Boolean、Array...)

<!-- 動態(tài)賦予一個(gè)變量的值 -->
<blog-post v-bind:title="post.title"></blog-post>

<!-- 動態(tài)賦予一個(gè)復(fù)雜表達(dá)式的值 -->
<blog-post
  v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
  • 子組件接收

常見的就是聲明式接收:
props: [...] 數(shù)組形式

// 定義一個(gè)名為 button-counter 的新組件
Vue.component('button-counter', {
  props: ['title', 'count', 'isPublished', 'commentIds', 'author'],
  data: function () {
    return {
      counts: this.count //this指向即可獲取props值
    }
  },
  template: ''
})

但官方更推薦對象形式的接收砸烦,至少為每個(gè)prop指定類型

Vue.component('my-component', {
  props: {
    // 基礎(chǔ)的類型檢查 (`null` 和 `undefined` 會通過任何類型驗(yàn)證)
    propA: Number,
    // 多個(gè)可能的類型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認(rèn)值的數(shù)字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認(rèn)值的對象
    propE: {
      type: Object,
      // 對象或數(shù)組默認(rèn)值必須從一個(gè)工廠函數(shù)獲取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗(yàn)證函數(shù)
    propF: {
      validator: function (value) {
        // 這個(gè)值必須匹配下列字符串中的一個(gè)
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

type 可以是下列原生構(gòu)造函數(shù)中的一個(gè):

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

使用上我們除了this.prop外還可以使vm.$props(當(dāng)前組件接收到的 props 對象。Vue 實(shí)例代理了對其 props 對象屬性的訪問家破。)

  • 禁用特性繼承

如果你不希望組件的根元素繼承特性,這尤其適合配合實(shí)例的 $attrs 屬性使用,你可以在組件的選項(xiàng)中設(shè)置 :

Vue.component('my-component', {
  inheritAttrs: false,
  // ...
})

當(dāng)然還有非聲明式的接收 $attrs (2.4.0 新增):

包含了父作用域中不作為 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)喊积。當(dāng)一個(gè)組件沒有聲明任何 prop 時(shí)乾吻,這里會包含所有父作用域的綁定 (class 和 style 除外)拟蜻,并且可以通過 v-bind="$attrs" 傳入內(nèi)部組件——在創(chuàng)建高級別的組件時(shí)非常有用瞭郑。

<base-input label="姓名" class="username-input" 
  placeholder="Enter your username" data-date-picker="activated"
></base-input>
Vue.component("base-input", {
     inheritAttrs: false, //此處設(shè)置禁用繼承特性
     props: ["label"],
     template: `
        <label>
          {{label}}
          {{$attrs.placeholder}} //這里不用聲明props可以直接調(diào)用
          {{$attrs["data-date-picker"]}}
          <input v-bind="$attrs"/>
        </label>
        `,
      mounted: function() {
            console.log(this.$attrs);
        }
})

provide/inject

provide 和 inject 主要為高階插件/組件庫提供用例辜御。并不推薦直接用于應(yīng)用程序代碼中。

// 父級組件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子組件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

提示:provide 和 inject 綁定并不是可響應(yīng)的屈张。這是刻意為之的擒权。然而,如果你傳入了一個(gè)可監(jiān)聽的對象阁谆,那么其對象的屬性還是可響應(yīng)的碳抄。

更多的用法是用來注入方法的:

// 父級組件提供 'foo'
var Provider = {
  provide: {
    foo: this.fn
  },
  methods:{
    fn(){
      console.log('bar')
    }
  }
  // ...
}

// 子組件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    this.foo(); // => "bar"
  }
  // ...
}

vm.$children

通過Vue實(shí)例代理的$children獲取其子組件的值,為一個(gè)類數(shù)組场绿,你可以在控制臺中打印出來,里面有子組件中的一切屬性焰盗。


vm.$slots

插槽的使用更多在官方文檔中查看璧尸,這里顯示基礎(chǔ)的使用方法:

<navigation-link url="/profile">
  Your Profile
</navigation-link>

然后你在 <navigation-link> 的模板中可能會寫為:

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

除了用標(biāo)簽獲取傳遞的數(shù)據(jù),我更喜歡用$slots處理熬拒,比如在這個(gè)輸入框中爷光,你既要設(shè)定lable又要設(shè)定placeholder,然而它們兩其實(shí)是同一個(gè)數(shù)據(jù)澎粟,這時(shí)候我們可以這樣:


輸入框控件
<inputBox :readonly='readonly' v-model='item.contactName'>聯(lián)系人</inputBox>
<inputBox :readonly='readonly' v-model='item.mobile'>電話</inputBox>
var Component = {
        props: {
            readonly: {
                default: false
            },
            type: {
                default: 'text'
            },
            value: [String, Number],
            maxlength: {
                default: 20
            }
        },
        data() {
            return {
                inputValue: this.value
            }
        },
        watch: {
            value(newValue) {
                this.inputValue = newValue
            },
            inputValue(newValue) {
                this.$emit('input', newValue)
            }
        },
        template: `
        <div class='input'>
            <cube-input v-model.trim="inputValue" :placeholder="'請輸入' + this.$slots.default[0].text" :readonly="readonly" :type="type" :maxlength="maxlength">
                <template v-slot:prepend>
                    <slot></slot>
                </template>
            </cube-input>
        </div>
        `
    };

其他的為ui框架的組件不用在意蛀序,重要的是this.$slots.default[0].text這一段對$slots的應(yīng)用。


小結(jié)

通過上面的四種方法活烙,我們可以看出徐裸,它們都是可以完成值得傳遞或者獲取的,除了$slots不能傳遞方法函數(shù)外啸盏,其他三種均可完成重贺。


子組件中的通信方法

首先我們先要理解子組件傳遞的原理,下面這張圖大家在很多的地方都看過了,實(shí)際上就是父組件通過prop給子組件下發(fā)數(shù)據(jù)檬姥,子組件通過事件給父組件發(fā)送信息曾我,而使用的工具為:

父子組件通信過程

$on(evntName)監(jiān)聽事件;
$emit(eventName,optionalPayload)觸發(fā)事件健民;
接下來的方法都是對這個(gè)原理實(shí)現(xiàn)的變形

序號 方法名 概要 推薦程度
1 v-on/vm.$emit 通過綁定父級函數(shù)抒巢,用子級觸發(fā),父子組件雙向綁定 推薦
2 v-model/vm.$emit 官方提供的父子組件雙向綁定方法 強(qiáng)烈推薦
3 this.$parent 獲取直接上級的實(shí)例秉犹,調(diào)用父組件的函數(shù) 推薦

v-on / $emit

首先我們來實(shí)現(xiàn)一個(gè)單向傳遞的例子:

//父組件
<template>
   <child @childHandler="parentHandle"></child>
</template>

<script>
export default {
    data: {
        message: ''
    },
    methods: {
      parentHandle(send) {
         this.message = send;
      }
    }
}
</script>
//子組件
<template>
  <button @cilck="sendHandle"></button>
</template>
<script>
export default {
  date: {
    news:"from children"
  },
  methods: {
    sendHandle() {
      this.$emit('childHandler',this.news);
    }
  }
}
</script>

上面的過程我就不再描述一遍了蛉谜,你們自己看代碼理解理解,這樣我們就實(shí)現(xiàn)了由子組件傳遞數(shù)據(jù)到父組件崇堵。但在更復(fù)雜的場景時(shí)型诚,我們不僅需要修改父組件的值,還需要通過父組件的值影響到子組件的渲染鸳劳,也就是實(shí)現(xiàn)子父組件的數(shù)據(jù)雙向綁定狰贯。

更多的場景下我們希望其是自動觸發(fā)數(shù)據(jù)交互的,這時(shí)我們需要用到watch赏廓,現(xiàn)在我們對上面的代碼進(jìn)行修改一下涵紊。

//父組件
<template>
   <child @childHandler="parentHandle" :status="status" :name="name">
   <button @cilck="emptyHandle">清空</button>
</child>
</template>

<script>
export default {
    data: {
        status:'男',
        name:'Max.Law'
    },
    methods: {
       parentHandle(send) {
        this.child.status = send;
      },
      emptyHandle(){
        status = '';
      }
    }
}
</script>
//子組件
<template>
  <p><span>姓名:</span>{{name}}</p>
  <p><span>性別:</span>{{childStatus}}</p>
  <button @cilck="sendHandle('男')">男</button>
  <button @cilck="sendHandle('女')">女</button>
</template>
<script>
export default {
  props: {
     status: [String, Number],
     name: [String, Number]
  },
  date: {
    childStatus:this.status
  },
  methods: {
    sendHandle(data) {
      this.childStatus = data
  },
  watch: {
      status(newValue) {
         this.childStatus = newValue
       },
       childStatus(newValue) {
          this.$emit('childHandler', newValue)
       }
   },
}
</script>

這里實(shí)現(xiàn)的是在子組件的性別狀態(tài)繼承于父組件幔摸,子組件修改性別的狀態(tài)的同時(shí)改變父組件的數(shù)據(jù)以便父組件使用摸柄。

我們在使用vue的過程中很清楚,假如子組件直接寫{{status}}來繼承父組件的值既忆,在修改狀態(tài)的時(shí)候vue會提示不建議直接修改父組件的值來改變子組件(vue中一個(gè)重要邏輯驱负,當(dāng)前組件只處理當(dāng)前組件的數(shù)據(jù)),所以我們使用childStatus來接收父組件的值患雇。

那我們?nèi)绾巫龅阶咏M件的改變能影響父組件跃脊,父組件更新值時(shí)又能影響子組件呢?這時(shí)候重點(diǎn)都在這個(gè)watch上:

當(dāng)status改變時(shí)則改變childStatus的值庆亡,
當(dāng)childStatus改變時(shí)匾乓,用上面的方法與父組件通信捞稿,改變父組件的值

這樣我們就完成子父組件的數(shù)據(jù)雙向綁定又谋,整個(gè)過程為:

  1. 給子組件幫一個(gè)通信方法
  2. 綁定一個(gè)傳值對象
  3. 監(jiān)控?cái)?shù)據(jù)的變化

看到這里有沒有熟悉的感覺,是不是跟 v-modle 很像娱局,vue提供的雙向綁定的指令彰亥,而 v-modle 的本質(zhì)就是綁定了一個(gè)input的方法,和一個(gè)value值衰齐,這時(shí)候我們就能把上面的雙向數(shù)據(jù)綁定的方法簡化了(v-modle是在本組件內(nèi)實(shí)現(xiàn)雙向綁定任斋,并沒有做到子父組件雙向綁定)

v-model/vm.$emit

還記得我上面講$slot的代碼嗎?這里我們簡化一下:

//父組件
<template>
   <inputBox :readonly='readonly' v-model='brand'>設(shè)備品牌</inputBox>
</template>

<script>
export default {
    data: {
        brand:'默認(rèn)'
    }
}
</script>
//子組件
<template>
    <div>
        <h1><slot></slot></h1>
        <input v-model="inputValue"></input>
    </div>
</template>

<script>
export default {
   props: {
      value: [String, Number]
   },
   data() {
       inputValue: this.value
    },
    watch: {
        value(newValue) {
           this.inputValue = newValue
      },
      inputValue(newValue) {
           this.$emit('input', newValue)
      }
   }
}
</script>

這時(shí)候耻涛,我們不僅可以修改子組件中的輸入框值废酷,還是能同時(shí)改變父組件的值瘟檩,這在做上傳表單的時(shí)候尤為重要,保證我們父組件一直都是獲取用戶最新輸入的值澈蟆。(這個(gè)在我做微信公眾號一個(gè)表單項(xiàng)目時(shí)候突發(fā)奇想的實(shí)現(xiàn))

this.$parent

這個(gè)是vue提供的api墨辛,使用也很簡單,場景一般適合在父組件寫入多個(gè)子組件需要調(diào)用的公共方法趴俘,比provide/inject占用性能要低睹簇,也更明確作用域。

只需要在父組件注冊方法寥闪,在子組件這樣使用:
this.$parent.parentFn();
這里要注意層級太惠,也許是孫組件,或者從孫組件疲憋,可以這樣調(diào)用:
this.$parent.$parent.grandfatherFn();
只需要打印出當(dāng)前的this.$parent查看凿渊,確定層級即可。

好了缚柳,上面就是 《 Vue組件傳值與通信集合 》的全部內(nèi)容了嗽元,希望能夠?qū)δ阌兴鶐椭缬幸蓡枤g迎留言~


該篇收錄于文集:前端技術(shù)棧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喂击,一起剝皮案震驚了整個(gè)濱河市剂癌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翰绊,老刑警劉巖佩谷,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異监嗜,居然都是意外死亡谐檀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門裁奇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桐猬,“玉大人,你說我怎么就攤上這事刽肠±7荆” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵音五,是天一觀的道長惫撰。 經(jīng)常有香客問我,道長躺涝,這世上最難降的妖魔是什么厨钻? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上夯膀,老公的妹妹穿的比我還像新娘诗充。我一直安慰自己,他們只是感情好诱建,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布其障。 她就那樣靜靜地躺著,像睡著了一般涂佃。 火紅的嫁衣襯著肌膚如雪励翼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天辜荠,我揣著相機(jī)與錄音汽抚,去河邊找鬼。 笑死伯病,一個(gè)胖子當(dāng)著我的面吹牛造烁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播午笛,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惭蟋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了药磺?” 一聲冷哼從身側(cè)響起告组,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎癌佩,沒想到半個(gè)月后木缝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡围辙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年我碟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姚建。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矫俺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掸冤,到底是詐尸還是另有隱情厘托,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布贩虾,位于F島的核電站催烘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缎罢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望策精。 院中可真熱鬧舰始,春花似錦、人聲如沸咽袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽询刹。三九已至谜嫉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凹联,已是汗流浹背沐兰。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔽挠,地道東北人住闯。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像澳淑,于是被迫代替她去往敵國和親比原。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 您將在本文當(dāng)中了解到,往網(wǎng)頁中添加數(shù)據(jù),從傳統(tǒng)的dom操作過渡到數(shù)據(jù)層操作,實(shí)現(xiàn)同一個(gè)目標(biāo),兩種不同的方式....
    itclanCoder閱讀 25,803評論 1 12
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容杠巡,還有我對于 Vue 1.0 印象不深的內(nèi)容量窘。關(guān)于...
    云之外閱讀 5,050評論 0 29
  • 組件(Component)是Vue.js最核心的功能,也是整個(gè)架構(gòu)設(shè)計(jì)最精彩的地方氢拥,當(dāng)然也是最難掌握的绑改。...
    六個(gè)周閱讀 5,614評論 0 32
  • 此文基于官方文檔,里面部分例子有改動兄一,加上了一些自己的理解 什么是組件厘线? 組件(Component)是 Vue.j...
    陸志均閱讀 3,828評論 5 14
  • 什么是組件? 組件 (Component) 是 Vue.js 最強(qiáng)大的功能之一出革。組件可以擴(kuò)展 HTML 元素造壮,封裝...
    youins閱讀 9,480評論 0 13