Vue組件通訊(傳值)

先介紹一下什麼是組件把:
建立組件的兩種方式:html

  • 全局組件
// 組件就是vue的一個拓展實例
 let component=Vue.extend({
     data(){
       return{
         //與vue實例中的data不一樣,子組件的data必須是一個方法,必須有返回值,且返回值是對象。
         //這樣作可使組件中的數據獨立。
         //須要共享數據時可把return的對象聲明全局變量
       }
     }
     template:'<div></div>'// 模板  這個組件的html元素
    // })
    //註冊組件
 Vue.component('hehe',component)

(簡寫)vue

Vue.component('組件名',{
        template:''// 模板  
    })
  • 局部組件
new Vue({
        el:'#app',
        data:{
            test:11
        },
        components:{
            one:{
                template:'<h1>這裏是局部組件 </h1>'
            }
        }

    })

須要注意的是子組件的命名沒法識別駝峯命名法,當組件作爲標籤使用的時候須要用「-」和小寫字母替換該大寫字母。vuex

<div>
   <one-data></one-data>
</div>
new Vue({
        el:'#app',
        data:{
            test:11
        },
        components:{
            oneData:{
                template:'<h1>這裏是局部組件 </h1>'
            }
        }

    })

一、父子屬性傳值
子組件不能直接使用父級data的內容,也不能直接修改父組件傳遞的值,但能夠經過設置props屬性來得到自身屬性值。
原理:npm

  • 經過子組件的props拋出一個組件標籤屬性
  • 父組件經過該標籤屬性將參數傳遞給子組件
  • 子組件內部經過該標籤屬性獲取值

代碼實現:(實現過程當中踩過的坑)
1.template標籤內必須有且只有一個根元素
2.子組件標籤內要經過v-bind的方式綁定父級的data的值
3.子組件經過props定義的自身屬性值(test)獲取父組件的data中的內容(父組件:{{name}} ;子組件:{{test}})app

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div>
       {{name}}
       <son :test='name'></son>
   </div> 
   <!--實現功能較複雜時,會單獨寫個template標籤,經過id與子組件中的template相關聯,不然字符串拼接會很麻煩-->
   <template>
       <div>{{test}}</div>
   </template>
</body>
<script>
    new Vue({
        el:'#myApp',
        data:{
            name:'加油鴨!'
        },
        components:{
            son:{
                template:'#tp',//相似於 el
                props:['test']
            }
        }
    })
</script>
</html>
  • 再用全局組件寫一個父子屬性傳值,實現點擊父組件按鈕,子組件div顯示和隱藏

(全局組件無固定父子關係,該組件標籤放在哪一個標籤內就是哪一個標籤的子組件)異步

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>父向子傳值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div>
   <!--c1就是myApp的子組件-->
       <c1></c1>
   </div> 
   <template>
       <div>
           <h4>我是c1,是父級</h4>
           <button @click="change">點我c2變</button>
           {{state}}
           <hr>
           <!--寫在c1的template內,就是c1的子組件-->
           <c2 :changed='state'></c2>
       </div>
   </template>
   <template>
       <div>
           我是c2,是子級
           <div v-show='changed'><h1>我是div</h1></div>
        </div>
   </template>
</body>
<script>
    Vue.component("c1",{
        template:'#tp1',
        data(){
            return {
                state:true
            }
        },
        methods:{
            change(){
                this.state=!this.state
            }
        }
    })
    Vue.component("c2",{
        template:'#tp2',
        props:['changed']
    })
    let vm=new Vue({
        el:'#myApp'
    })
</script>
</html>

二、子父事件傳值
子組件不能直接修改父組件傳遞的值,可間接經過事件修改函數

原理ui

  • 子組件建立自定義事件
  • 自定義事件的處理函數是父組件的函數
  • 子組件經過$emit觸發屬於本身的自定義事件

代碼實現:(點擊子組件按鈕,父組件div顯示和隱藏)
代碼中有一些思路註釋:this

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>子向父傳值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div>
       <c1></c1>
   </div> 
   <template>
       <div>
           <h4>我是c1,是父級</h4>
           <div v-show='state'><h1>我是div</h1></div>           
           <hr>
           <!--c2自定義了一個changed事件處理父組件的函數change-->
           <c2 @changed='change'></c2>
       </div>
   </template>
   <template>
       <div>
           我是c2,是子級
           <!--子組件點擊按鈕,父組件顯示隱藏,固然有點擊事件啦,
           因此給button加點擊事件觸發一個函數,發現c2沒有設置methods,
           因此去c2的實例中加一個methods並設置函數sclick,
           再經過sclick函數把其自定義事件changed拋出,
           changed裏面就是c1的處理函數-->
           <button @click="sclick">點我c1變</button>
        </div>
   </template>
</body>
<script>
    Vue.component("c1",{
        template:'#tp1',
        data(){
            return {
                state:true
            }
        },
        methods:{
            change(){
                this.state=!this.state
            }       
        }
       
    })
    Vue.component("c2",{
        template:'#tp2',
        methods:{
            sclick(){
                this.$emit('changed')
            }
        }
    })
    let vm=new Vue({
        el:'#myApp'
    })
</script>
</html>

三、非父子傳值spa

原理

  • 建立公共汽車實例
  • 實例上註冊事件,事件的處理函數就是要改變數據的處理函數
  • 觸發事件

(1)公有的父元素做爲橋接-----結合父子屬性傳值和子父事件傳值來實現(適用於親兄弟)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>兄弟傳值</title>
    <script src="base/vue.js"></script>
</head>
<body>
   <div>
       我是父級
       {{state}}
       <hr>
       <!--c1和c2都是div的子組件-->
       <!--子父事件傳值-->
       <c1 @emit='change'></c1>
       <hr>
       <!--父子屬性傳值-->
       <c2 :test='state'></c2>
   </div> 
   <template>
       <div>
           我是c1,是c2的兄弟
           <!--間接經過父級改變了兄弟級的值-->
           <button @click='emit'>點我個人兄弟和父級都會發生改變</button>
        </div>
   </template>
   <template>
       <div>我是c2,是c1的兄弟
           <p>{{test}}</p>
       </div>
   </template>
</body>
<script>
    Vue.component('c1',{
        template:'#tp1',
        methods:{
            emit(){
                this.$emit('emit')
            }
        }
    })
    Vue.component('c2',{
        template:'#tp2',
        props:["test"]
    })
    new Vue({
        el:'#myApp',
        data:{
            state:false
        },
       methods:{
           change(){
               this.state=!this.state
           }
       }
    })
</script>
</html>

(2)公共汽車傳值(聲明一個公共實例做爲過濾)

修改誰的值就去觸發誰的方法
在這裏插入圖片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>兄弟之間經過公共汽車傳值</title>
    <script src="base/vue.js"></script>
</head>
<body>
    <div>
        <father></father>
    </div>
    <template>
        <div>我是父組件
            <hr>
            <son1></son1>
            <hr>
            <son2></son2>
        </div>
        
    </template>
    <template>
        <div>{{name}}
        <!--點擊事件觸發一下sclick函數,至關於把拋出的change函數觸發了-->
            <button @click='sclick'>點我</button>
        </div>
    </template>
    <template>
        <div>{{name}}</div>
    </template>
</body>
<script>
    let Bus=new Vue();
    new Vue({
        el:'#myApp',
        components:{
            father:{
                template:'#father',
                components:{
                    son1:{
                        template:'#son1',
                        data(){
                            return{
                                name:'我是子組件1'
                            }
                        },
                        methods:{
                            sclick(){
                            //觸發test事件,再傳一個參數給change函數中的val
                            //name值就從「我是子組件2」經過點擊事件變成了"加油鴨!!!!"
                                Bus.$emit('test',"加油鴨!!!!")
                            }
                        }
                    },
                    son2:{
                        template:'#son2',
                        data(){
                            return{
                                name:'我是子組件2'
                            }
                        },
                        methods:{
                            change(val){
                                this.name=val
                            }
                        },
                        mounted(){
                        //只要mounted執行了,說明組件已經準備就緒
                        //註冊一個函數,函數名叫test,處理函數就是methods中的change函數
                        //簡單來講,只要想辦法觸發test事件就至關於執行了change函數
                            Bus.$on('test',this.change)
                        }
                    }
                }
            }
        }
    })
</script>
</html>

(3)vuex(集中式的數據狀態管理器)
簡單來講,就是用來管理全局變量的,無論哪裏進行修改,頁面都會隨之變化。好比咱們手機端的選擇所在區域,假設你選了北京,跳到其餘頁面仍是顯示的北京就能夠用vuex來實現。
解決的問題:
幫助咱們管理共享狀態。
vuex的相關配置步驟:

  1. npm install vuex --save-dev
  2. 引入:`import Vuex from ‘vuex’
  3. 使用:Vue.use(Vuex)
  4. 設置vuex
var store=new Vuex.Store({
       state:{
       },
        mutations:{
        },
        actions:{
        },
        getters:{
        }
 })
  1. 將數據放入到實例中
new Vue({
  el: '#app',
  router,
  store,
  components: { App},
  template: '<App/>',
})

vuex中的模塊:

  1. state:存儲狀態,可經過this.$store.state獲取咱們在vuex中聲明的全局變量的值
import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    }
})
export default store;
//通常咱們會新建一個store文件夾,
//在該文件夾的js文件中經過暴露出store,
//再經過在main.js中引入該文件的方式來使用vuex

在組件中咱們只須要經過{{this.$store.state.name}}就能夠渲染name的值
寫一個組件:

<template>
    <div>
        {{msg}}
        {{this.$store.state.name}}
        <hr>
    </div>
</template>

<script>
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

在這裏插入圖片描述
有木有以爲{{this.$store.state.name}}太長了,可不能夠直接寫{{name}}:

<template>
    <div>
        {{msg}}
        {{name}}
        <hr>
    </div>
</template>

<script>
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    computed:{
        name(){
            return this.$store.state.name
        }
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

有小夥伴會說了,這樣也不簡單呀,那這樣呢?

<template>
    <div>
        {{msg}}
        {{name}}
        <hr>
    </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    //可不能夠不用拓展操做符?能夠呀
    //computed:mapState(['name'])
    //但是這樣你若是還有其餘的方法要在computed中操做就無法操做了
    computed:{
        ...mapState(['name'])
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

若是遇到須要更名的狀況,可經過對象的形式:

{{_name}} //改後的名字進行頁面渲染
...mapState({_name:"name"})

後面的方法基本思路同樣,接下來就都用簡寫形式啦!
2. mutations:修改狀態

import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    },
     mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
})
export default store;

咱們來作一個小效果,點擊按鈕改變狀態值從1變爲888

<template>
    <div>
         <div @click='CHANGE_NUM(888)'>change</div>
        {{msg}}
        {{_name}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapMutations} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
          //非簡寫形式,點擊事件的調用方法換爲getName
         //getName(){
         //    this.$store.commit('CHANGE_NUM',888)
         //     }
        ...mapMutations(['CHANGE_NUM']),
    },
    computed:{
        ...mapState({_name:'name'})
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

在這裏插入圖片描述
在這裏插入圖片描述
3. actions:專門用來作異步操做
經過計時器模擬一個異步操做,並經過commit觸發mutations

import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1,
    },
     mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
     actions:{
       //邏輯處理及異步請求
       getNumApi({commit},params){
           setTimeout(()=>{
               commit('CHANGE_NUM',params)
           },2000)
       }
    }
})
export default store;

再來實現一個點擊按鈕改變值的效果:

<template>
    <div>
         <div @click='getNumApi("xixi")'>change</div>
        {{msg}}
        {{_name}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapActions} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
        //非簡寫的寫法:(經過dispatch觸發相應的action)
         //emitActions(params){
        //     this.$store.dispatch('getNumApi',params)
        // }
        ...mapActions(['getNumApi'])
    },
    computed:{
        ...mapState({_name:'name'})
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

2秒後name值改變了
在這裏插入圖片描述
4. getters:相似於計算屬性computed,裏面進行一些計算操做

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let store=new Vuex.Store({
    state:{
        name:1
    },
    mutations:{
        CHANGE_NUM(state,name){
            state.name=name
        }
    },
    getters:{
        double(state){
            return state.name*2
        }
    },
    actions:{
       //邏輯處理及異步請求
       getNumApi({commit},params){
           setTimeout(()=>{
               commit('CHANGE_NUM',params)
           },2000)
       }
    }
})
export default store;

再來作點擊事件:

<template>
    <div>
         <div @click='getNumApi("xixi")'>change</div>
        {{msg}}
        {{_name}}
        {{double}}
        <hr>
    </div>
</template>

<script>
import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'
export default {
    name:'List',
    data(){
        return {
            msg:'list',
        }
    },
    methods:{
        ...mapActions(['getNumApi'])
    },
    computed:{
        ...mapState({_name:'name'}),
         ...mapGetters(['double'])
    }
}
</script>
<style>
#list div{
    width: 50px;
    height: 50px;
    background: red;
}
</style>

因爲字符串不能進行乘法操做,因此打印結果是NaN
在這裏插入圖片描述
5. modules:模塊
在實際開發中,咱們會把以上四個模塊,分別寫在四個js文件中,在經過引入、暴露的形式寫在一個index.js文件下。

import actions from './actions'
import state from './state'
import mutations from './mutations'
import getters from './getters'

export default {
    state,
    mutations,
    getters,
    actions
}

再把這5個文件放在store文件夾下的shopcar文件夾下(名字隨意),在再store文件夾下,新建一個index.js文件,並經過modules的形式展示:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import shopcar from './shopcar'
let store=new Vuex.Store({
    modules:{
        shopcar:shopcar 
    }
})
export default store

在這裏插入圖片描述

來源:https://blog.csdn.net/weixin_43747906/article/details/84981199

相關文章
相關標籤/搜索