Vue 組件的通訊

組件定義

// 全局定義
Vue.component('my-lists', {
    // options
})
複製代碼

定義後,能夠在template模版中使用<my-list></mylist>,此時會將組件中定義的內容顯示出來。html

<template>
    <div>
        <my-list></mylist>
    </div>
</template>
複製代碼

經過例子來看一下vue

<body>
    <div id='app'>
        <my-list></my-list>
    </div>
    
</body>
<script>
    Vue.component('my-list', {
        template: `
            <ul>
                <li>上海</li>
                <li>北京</li>
                <li>南京</li>
            </ul>
        `
    })
    new Vue({
        el: '#app'
    })

</script>
複製代碼

頁面會顯示出咱們定義的my-list的組件內容,``這個符號是ES6中的模版字符串,想詳細瞭解的話,能夠看MDN上關於模版字符串的說明vuex

若是不須要全局定義的話,那能夠在Vue實例中局部定義vue-cli

var myList = {
    template: `
        <ul>
            <li>上海</li>
            <li>北京</li>
            <li>南京</li>
        </ul>
    `
}
new Vue({
    el: '#app', // 掛載
    components: {
        'my-list': myList
    }
})
複製代碼

組件內的data

上面是簡單的組件,若是咱們想本身經過變量,來控制列表的展現,這個時候,咱們組件內部定義data。咱們來從新寫my-list組件:npm

var citys = ['上海', '北京', '廣州']
Vue.component('my-list', {
    template: `
        <ul>
            <li v-for='(item, index) in citys' :key='index'>{{item}}</li>
        </ul>
    `,
    data () {
        return {
            citys: citys
        }
    }
})
new Vue({
    el: '#app'
})
複製代碼

 有一點要注意,data必須是一個函數,將值經過函數返回。若是是一個變量:數組

var citys = ['上海', '北京', '廣州']
Vue.component('my-list', {
    template: `
        <ul>
            <li v-for='(item, index) in citys' :key='index'>{{item}}</li>
        </ul>
    `,
    data: {
        citys: citys
    }
})

複製代碼

會致使一個問題,多個相同組件同時使用時,它們的data是共享的,會出現相互影響的狀況。相似於淺拷貝的這種狀況bash

var a = {name: 'a'}
var b = a
var c = a

console.log(b.name, c.name) // a a

console.log(b.name, c.name) // a a

b.name = 'b'
console.log(b.name) // b
console.log(c.name) // b

// b是copy a的引用,因此b改變了的同時會影響a
// 組件內的data同理,因此將data的值以函數返回,以建立不一樣的對象,而不是共享一個

var a = function () {
    return {
        name: 'a'
    }
}

var b = a() // b是新對象
var c = a() // c也是新對象

b.name = 1
console.log(c.name) // a

複製代碼

父子組件的通訊

  1. 父組件經過傳遞Props給子組件
  2. 子組件獲取到父組件傳遞的值,執行一些列操做後,經過 emit 觸發事件,告訴父組件發生了什麼
  3. 父組件能夠見經過v-on/@監聽這個事件

以上述的my-list爲例子,實現當點擊城市名稱時,會在下方顯示點擊的城市:app

<body>
    <div id='app'>
        <my-list 
            :citys='citys'
            @select='getCity'></my-list>

        <p>{{selCity}}</p>
    </div>
    
</body>
<script>
Vue.component('my-list', {
    template: `
        <ul>
            <li 
                v-for='(item, index) in citys' 
                :key='index'
                @click='select(item)'>{{item}}</li>
        </ul>
    `,
    props: ['citys'],
    methods: {
        select (city) {
            this.$emit('select', city)
        }
    }
})
new Vue({
    el: '#app',
    data () {
        return {
            citys: ['上海', '北京', '廣州'],
            selCity: ''
        }
    },
    methods: {
        getCity (city) {
            this.selCity = city
        }
    }
})
</script>
複製代碼
  • :citys 動態綁定props的用戶,:v-bind的縮寫;若是傳遞的是一個靜態的值,則能夠直接寫成citys="['上海','北京']"
  • @select 父組件監聽子組件觸發的事件this.$emit('select', city); @v-on的縮寫;若是子組件觸發了select,會響應父組件的'getCity'函數

咱們須要注意的是,父子組件通訊是單向的,即父組件傳遞給子組件的props發生變化時,子組件也會接收到這種變化,可是若是子組件改變props的值時,不會反饋給父組件,防止修改了父組件的狀態,致使整個的數據流難以理解,若是修改了,Vue也會發出警告。 props對於子組件來講應該是隻讀的。有兩種狀況下,咱們會想改變props:異步

  • props傳遞進來後,咱們須要對props進行處理,而後輸出到頁面上,這種狀況下,能夠經過computed來計算處理後的變量
computed: {
        newCitys () {
            this.citys.filter((ele) => {
                return ele !== '上海'
            }) 
        }
    }
複製代碼
  • 做爲一個初始值,後續須要對其進行修改,計算等;這種狀況下,能夠將props賦值給自身的屬性
data () {
    return {
        initCitys: this.citys
    }
}

複製代碼

若是咱們想修改props的值,並將變化反饋給父組件,除了$emit觸發事件 + 父組件監聽事件這種方式外,還有一種較爲簡單的方式,經過sync操做符編輯器

// 父組件
<child-component :name.sync='name'></child>

// 子組件想要可以修改name,能夠這麼作
this.$emit('update:name', 'newVal')

複製代碼

上述咱們是直接傳遞props,咱們能夠在子組件中經過定義props格式來對props進行簡單的驗證,如下一段驗證代碼來自官網,容許偷個懶= _=||

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
      }
    }
  }
})
複製代碼

多個同級組件間的通訊

在複雜項目中,可能會出現多個同級的組件須要通訊,而僅僅是簡單的父子組件通訊,這個時候怎麼去作呢?

經過新建一個Vue實例,做爲事件總線

var emitEvent = new Vue()

// 在不一樣的組件中,經過觸發事件,監聽事件來實現通訊
emitEvent.$emit('event-name', val)
emitEvent.$on('event-name', funciton (val) {

})

複製代碼

統一狀態管理,能夠在項目中使用Vuex

Vuex 官網

Vuex 是一個專門爲Vue.js開發狀態管理軟件。 Vuex中有幾個核心的概念

  • State 存儲狀態,相似於data屬性

  • Getter 從state中派生出一些屬性,相似於computed

  • Mutation 更改state的屬性,state屬性是沒法直接經過賦值進行更改的,須要經過Mutation定義的函數來進行賦值,提交的是state

  • Actions Mutation必須是同步的函數,因此在Actions中處理異步的操做,而且提交的是Mutation

  • Module 當項目很大的時候,須要根據不一樣的功能分割成不一樣的模塊,每次須要的時候,只須要引用須要的模塊便可

 好比一個項目中使用Vuex,首先安裝Vuex npm install vuex --save,經過腳手架vue-cli,通常默認自帶Vuex

Vuex.store的目錄結構能夠是這樣的

store
  - index.js  // 統一模塊的index
  - modules
      - a.js // 模塊 a
      - b.js // 模塊 b

複製代碼

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

import a from './modules/a'
import b from './modules/b'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    a,
    b
  }
})

複製代碼

store/modules/a.js

export default {
  namespaced: true, // 必要的
  state: {
    data: null
  },
  mutations: {
    setData(state, data) {
        state.data = data
    }
  },
  actions: {
      getData (context, cb) {
        // 假設執行的從服務端獲取的數據
        http.post(url, {name: 'b'}, function (res) {
            // 提交mutation操做
            context.commit('setData', res)

            // 若是有回調函數的話
            typeof cb === 'funciton' && cb(res)
        })
      }
      
  
  }
}
複製代碼

store/modules.b.js 相似於a,在此就不舉例子了。

在組件中如何是使用呢?

首先導入Vuex提供的方法

// components/a.vue

// 首先,肯定須要哪些功能
// 若是隻須要state中的數據
import { mapState } from 'vuex'

// 若是隻須要state中的數據 和 mutations操做
import { mapState, mapMutations } from 'vuex'

// 若是還對Actions有需求
import { mapState, mapMutations, mapActions } from 'vuex'
// 須要什麼導入什麼便可

// 小tips: 能夠先寫 from 'vuex',再寫{}中的,編輯器會自動不全
複製代碼

而後導入咱們在Store中定義的屬性和方法

# 使用 a 模塊,而且把a模塊中的data賦值給aData
computed: {
    ...mapState('a', {
        aData: state => state.data
    })
    ...mapGetters('a', {
        xxx: state => state.xxx
    })
}

# 導入方法
methods: {
    ...mapMutations('a', [
      'setData'
    ])

    ...mapActions('a', [
      'getData'
    ])
}
複製代碼

使用屬性和方法

// 屬性和方法經過this調用
this.aData

// 從新賦值
this.setData('aaa')

// 傳入回調函數
this.getData(function (res) {
    console.log(res)
})
複製代碼

上面的代碼中咱們使用... Javascript的擴展操做符和 aData: state => state.data的箭頭函數,若是對這兩項有不瞭解的,能夠查閱連接

擴展操做符

箭頭函數

感謝,若是有什麼寫的很差的地方,請聯繫做者,比心~

相關文章
相關標籤/搜索