vue學習-2-組件及組件通訊

組件

好處:

  1. 可複用 可以提高開發效率
  2. 方便後期的維護和修改
  3. 可以減少渲染

全局組件 && 局部組件

全局組件:
註冊全局組件 ,可以用橫槓也可用駝峯,如myButton / my-button,但使用時,只支持橫槓

Vue.component('my-button', {
    template: '<div>{{msg}}</div>',  //template設置組件中的內容
    data() {   //組件中的data是個函數,它返回對象,與實例中的data不同,那個值是一個對象
        return {
            msg: 'aaa'
        }
    }
})

使用全局組件:

<div id="app">
    <!-- 使用全局組件 ,DOM中只支持-的寫法my-button -->
    <my-button></my-button>
</div>

局部組件:是在實例內部寫的。

let vm = new Vue({
    // template: '<div>1111</div>', //Vue實例中也有template,若在實例中發現直接渲染,沒有就會渲染掛載的Vue實例
    el: '#app',
    data: {},
    // 局部組件:在實例中 一般不這麼寫,寫太多會不好維護
    components: {
        'my-button': {
            template: '<div>{{msg}}</div>', //設置組件中的內容
            data() {  //組件的data是個函數,返回一個對象
                return {
                    msg: '局部組件bbb'
                }
            }
        }
    }

})

通常這麼寫:

<!-- //組件的三步驟:1. 定義組件;2.註冊組件;3.使用組件  -->
// 1.定義組件
let myButton = {
    template: '<div>{{msg}}</div>', //設置組件中的內容
    data() {  //組件的data是個函數,返回一個對象
        return {
            msg: '局部組件bbb'
        }
    }
}
let vm = new Vue({
    el: '#app',
    data: {},
    // 局部組件:在實例中 
    components: {
        //2.註冊組件,可以簡寫myButton,ES6寫法
        myButton: myButton 
    }

})
// dom中相應位置使用組件:
<div id="app">
    <!-- 使用全局組件 ,在dom中只支持橫槓寫法 my-button -->
    <!-- 3.使用組件 -->
    <my-button></my-button>
</div>

若在組件的template中,有很多div,以及有很多的層級,有點繁瑣且不美觀,可以使用下面這個寫法:

<body>
<div id="app">
    <!-- 3.使用組件 -->
    <my-button></my-button>
</div>
<template id="button">
    <div>{{msg}}</div>
</template>
</body>

把組件中的內容,在dom中定義一個id名爲button的template,然後在寫組件時,這麼寫:

let myButton = {
    // template: '<div>{{msg}}</div>', //設置組件中的內容
    template: '#button', //設置組件中的內容,如果有很多的代碼,寫到這裏太繁瑣,可以在dom中寫一個template,命名一個id,將組件中的內容都放到template中,在這使用時直接寫上id
    data() {  //組件的data是個函數,返回一個對象
        return {
            msg: '局部組件bbb'
        }
    }
}

若有多個組件,也是如此寫:先定義,再註冊,最後使用

// 定義組件:
    let hello = {
        template: '#hello'
    }
// dom中的:
    <template id="hello">
        <div>hello</div>
    </template>

// 註冊組件:
     components: {
        myButton, //2.註冊組件,myButton: myButton可以簡寫myButton,ES6寫法
        hello
    }
// 使用組件:
    <div id="app">
        <!-- 3.使用組件 -->
        <my-button></my-button>
        <hello></hello>
    </div>

此時myButton與hello是兄弟組件的關係,若想變成父子,也是可以的,註冊的時候放到myButton組件components中,使用的時候也在myButton的dom中使用

// 1.定義組件
 let hello = { //在調用前要定義,因let定義的變量不會提升 
        template: '#hello'
    }
// 2.註冊組件
    let myButton = {
        template: '#button',
        data() { 
            return {
                msg: '局部組件bbb'
            }
        },
        //每個組件中都有components,裏面放的是它的子組件
        components: {
            hello  //使用組件的時候要放到父組件中,hello是mybutton的一個子組件
        }
    }
// 3.使用組件
// <!-- 所有的模塊中元素要包裹在一個標籤中div/span等,這是hello組件在myButton的template中的使用 -->
    <template id="button">
        <div>
            {{msg}}
            <hello></hello>
        </div>
    </template>

組件通訊

支持.vue後綴文件(快速原型開發)

npm install -g @vue/cli
npm install -g @vue/cli-service-global
開發環境:vue serve App.vue
生產環境:vue build App.vue

  1. 父組件傳遞參數給子組件,使用屬性的方式:m=“money”,而子組件使用props:[‘m’]來獲取。
    子組件使用觀察者模式,使用$emit將值傳給父組件
    父組件:
<template>
  <div>
    Parent
    <!-- m="500"是靜態的字符串,若是要傳動態的使用:m -->
    <!-- 父組件監聽子組件的事件,並給事件綁定函數 -->
    <!-- <Son :m="money" @change="fn"></Son> -->
    <!-- 父子傳遞數據,語法糖的寫法 -->
    <!-- <Son :m.sync="money"></Son>-->
    <!-- v-model的原理是把money綁定在屬性value中 綁定的事件名@input -->
    <!-- <Son :value="money" @input="(data)=>money=data"></Son> -->
    <Son v-model="money"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
    //   money: 'ABC',
      money: 500,
    };
  },
  components: {
    Son
  },
//    父組件中的綁定事件執行 
  methods: {
    fn(data) {
      // 當父組件監聽到子組件的點擊事件執行了,綁定方法就執行,並且會拿到子組件對應傳過來的值
      this.money = data;
    }
  }
};
</script>

子組件:

<template>
  <div>
    Son
    <!-- 獲取傳過來的值用這個 -->
    <!-- {{m}}   -->
    <!-- 這是改變後的值 -->
    {{changeM}} 
    <button @click="giveData">給父組件數據</button>
     <!--這是v-model對應的值value  -->
    {{value}}
  </div>
</template>
<script>
export default {
// props: ["m"], //獲取從父組件拿到的值使用,props
  // computed: {
  //   //若想對傳過來的值修改,例如變成小寫的,推薦使用計算屬性
  //   changeM() {
  //     return this.m.trim().toLowerCase();
  //   }
  // },
  
  // 驗證傳過來的值,下面會詳解
  props: {
    m: {
      type: [String, Number] //值的類型
      // default: 100 //默認值 , 它與required不能同時使用,用一個就可以
      // required: true //必傳
      // validator: value => {
      //   //自定義驗證規則
      //   return value > 400 && value < 1000;
      // }
    },
    arr: {
      type: Array, //若是數組或對象,默認值是函數
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函數中的直接寫{}是作用域,所以需要加()
    },
    value: {
      //這是v-model對應的value
      type: Number
    }
  },
  computed: {   //若想對傳過來的值修改,推薦使用計算屬性
    changeM() {
      return this.m.trim().toLowerCase();
    }
  },
//   <!-- 子組件中的方法,給父組件發數據 -->
  methods: {
    giveData() {
      // 子組件執行父組件監聽的事件,子組件使用$emit將數據發送給父組件
    //   this.$emit("change", 999); //change是監聽的事件,999是參數
    // this.$emit("update:m", 999);  //若在父組件使用:m.sync='money',在這要使用update帶屬性名錶示事件
    this.$emit("input", 999); //使用v=model傳的值,事件是input事件
    }
  }
};
</script>

props驗證

父組件傳的值爲:

data() {
    return {
      money: 400,
    };
  },//控制檯會報錯,自定義驗證規則失敗,因不滿足條件

子組件:

<template>
  <div>
    Son
    <!-- 獲取傳過來的值用這個 -->
    {{m}}  
  </div>
</template>
<script>
export default {
// 驗證傳過來的值
props:{
    m:{
        type:[String,Number], //值的類型
        // default:100, //默認值 , 它與required不能同時使用,用一個就可以   
        required:true, //必傳
        validator:(value)=>{ //自定義驗證規則
            return value>400 &&  value<1000
        }
    }
},
};
</script>

若傳的值是一個對象或數組(空數組,或空對象),可以設置默認值,需要注意:
數組的話,默認值是一個函數。

props:{
    arr: {
      type: Array, //若是數組或對象,默認值是函數
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函數中的直接寫{}是作用域,所以需要加()
    }
}
  1. $attrs $listeners:批量把屬性和方法往下傳
    attrs(vbind="attrs(通過 v-bind="attrs" 再往下一層傳遞,批量把屬性往下傳);
    listeners(使vonvon="listeners(使用v-on向下傳遞 v-on="listeners.click()"可以在GrandSon組件中執行Parent父組件的事件,批量把方法往下傳)。
    父組件:
    <template>
  <div>
    Parent
    <!-- m="500"是靜態的字符串,若是要傳動態的使用:m -->
    <!-- 父組件監聽子組件的事件,並給事件綁定函數 -->
    <!-- <Son :m="money" @change="fn"></Son> -->
    <!-- 父子傳遞數據,語法糖的寫法 -->
    <!-- <Son :m.sync="money"></Son> -->
    <!-- v-model的原理是把money綁定在屬性value中 綁定的事件名@input -->
    <!-- <Son :value="money" @input="(data)=>money=data"></Son> -->
    <!-- <Son v-model="money"></Son> -->
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
      // money: "ABC" //傳遞的數據是大寫
      money: 400 //傳遞的數據是大寫
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert(1);
    },
    fn(data) {
      // 當父組件監聽到子組件的點擊事件執行了,綁定方法就執行,並且會拿到子組件對應傳過來的值
      this.money = data;
    }
  }
};
</script>

子組件:

<template>
  <div>
    Son
    <!--
    獲取傳過來的值用這個   
    -->
    <!-- {{m}} -->
    {{arr}}
    {{obj}}
    <!--這是v-model對應的值value  -->
    {{value}}
    <!-- 這是修改過的值 -->
    <!-- {{changeM}} -->

    <!-- 直接使用這個$attrs可以獲取父組件傳過來的屬性{ "name": {"a":1}, "age": 2 } -->
    {{$attrs}}
    {{$attrs.name}}
    <!-- { "a": 1 }-->
    <!-- 執行父組件的事件使用$listeners,裏面的事件click() -->
    <!-- {{$listeners.click()}} -->

    <button @click="giveData">給父組件數據</button>
    <!-- 把從父組件拿到的屬性$attrs.name傳給GrandSon組件 -->
    <GrandSon v-bind="$attrs" :name1="$attrs.name" v-on="$listeners"></GrandSon>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  // props: ["m"], //獲取從父組件拿到的值使用,props
  // computed: {
  //   //若想對傳過來的值修改,例如變成小寫的,推薦使用計算屬性
  //   changeM() {
  //     return this.m.trim().toLowerCase();
  //   }
  // },
  props: {
    // 驗證傳過來的值
    m: {
      type: [String, Number] //值的類型
      // default: 100 //默認值 , 它與required不能同時使用,用一個就可以
      // required: true //必傳
      // validator: value => {
      //   //自定義驗證規則
      //   return value > 400 && value < 1000;
      // }
    },
    arr: {
      type: Array, //若是數組或對象,默認值是函數
      default: () => [1]
    },
    obj: {
      type: Object,
      default: () => ({}) //ES6中,函數中的直接寫{}是作用域,所以需要加()
    },
    value: {
      //這是v-model對應的value
      type: Number
    }
  },
  methods: {
    giveData() {
      // 子組件執行父組件監聽的事件,子組件使用$emit將數據發送給父組件
      // this.$emit("change", 999); //change是監聽的事件,999是參數
      // this.$emit("update:m", 999); //若在父組件使用:m.sync='money',在這要使用update帶屬性名錶示事件
      this.$emit("input", 999); //使用v=model傳的值,事件時input
    }
  },
  components: {
    GrandSon
  }
};
</script>

GrandSon組件:

<template>
  <div>
    GrandSon
    <!-- 這是從Son傳來的來自Parent的數據,還有Parent的click事件,可以直接調用Parent組件中的事件 -->
    {{$attrs}}{{name1}}
    {{$listeners.click()}}
  </div>
</template>
<script>
export default {
  props: ["name1"]
};
</script>
  1. provide inject 因爲是全局的,只要定義,在任意地方都可以使用,但沒法找到是在哪裏定義的變量
    使用provide定義全局變量,想在哪裏使用就使用inject注入即可
    Parent組件:
<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  provide() {
    return {
      parentMsg: "Parent中使用provide定義的全局數據"
    };
  },
  data() {
    return {
      money: 400 //傳遞的數據是大寫
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert(1);
    },
    fn(data) {
      // 當父組件監聽到子組件的點擊事件執行了,綁定方法就執行,並且會拿到子組件對應傳過來的值
      this.money = data;
    }
  }
};
</script>

GrandSon組件,使用inject注入Parent中定義的全局屬性parentMsg:

<template>
  <div>
    GrandSon
    <!-- 這是調用從全局注入的parentMsg -->
    {{parentMsg}}
  </div>
</template>
<script>
export default {
  // 注入全局定義的parentMsg屬性(定義的位置在Parent組件)
  inject: ["parentMsg"],
  props: ["name1"]
};
</script>
  1. $parent $children ref
    $parent 獲取父組件 $children 獲取子組件 ref 實例如下:
    父組件:
<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
  </div>
</template>
<script>
import Son from "./Son";
export default {
  data() {
    return {
      money: 400 //傳遞的數據是大寫
    };
  },
  components: {
    Son
  },
  methods: {
    fn1() {
      alert("Parent組件中的方法111");
    }
  }
};
</script>

Son子組件:

<template>
  <div>
    Son
    <GrandSon ref="grandSon"></GrandSon>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  props: {
    //這是v-model對應的value
    value: {
      type: Number
    },
    // 驗證傳過來的值
    m: {
      type: [String, Number] //值的類型
      // default: 100 //默認值 , 它與required不能同時使用,用一個就可以
      // required: true //必傳
      // validator: value => {
      //   //自定義驗證規則
      //   return value > 400 && value < 1000;
      // }
    }
  }
  components: {
    GrandSon
  },
  //DOM加載完成
  mounted() {
    // 調用父組件的方法:獲取父組件,調用fn1的方法
    // this.$parent.fn1();
    // 調用子組件的方法:子組件是個數組,取第一個子組件的fn2方法
    // this.$children[0].fn2();
    // 調用子組件的方法:通過$refs.grandSon可以拿到GrandSon組件,同樣可以調用fn2方法
    this.$refs.grandSon.fn2();
  }
};
</script>

GrandSon組件:

<template>
  <div>
    GrandSon
  </div>
</template>
<script>
export default {
  methods: {
    fn2() {
      alert("GrandSon222中的方法");
    }
  }
};
</script>
  1. eventBus 兄弟之間傳遞數據 $on emitVue.prototype.emit Vue.prototype.bus = new Vue; //busthis.bus是公共的。 this.bus.on(test,function(data)console.log(data));//this.on('test',function(data){console.log(data)}); //一個兄弟組件寫這個,監聽事件,改變值。 this.bus,emit(test,1000);//App.vueemit('test',1000); //另一個兄弟組件發送數據。 示例如下: App.vue入口文件中定義bus:
<template>
  <div>
    <!--3.使用組件  -->
    <Parent></Parent>
  </div>
</template>
<style scoped>
</style>
<script>
// 1.引入組件
import Parent from "./components/Parent";
import Vue from "vue";
export default {
  data() {
    return {};
  },
  components: {
    Parent //2.註冊組件
  }
};
Vue.prototype.$bus = new Vue(); //$bus是公共的
</script>

父組件:

<template>
  <div>
    Parent
    <Son :name="{a:1}" :age="2" @click="fn1"></Son>
    <Brother></Brother>
  </div>
</template>
<script>
import Son from "./Son";
import Brother from "./brother";
export default {
  data() {
    return {
      // money: "ABC" //傳遞的數據是大寫
      money: 400 //傳遞的數據是大寫
    };
  },
  components: {
    Son,
    Brother
  }
};
</script>

Son組件:

<template>
  <div>
    Son
    <button @click="change">改變兄弟</button>
  </div>
</template>
<script>
import GrandSon from "./GrandSon";
export default {
  methods: {
    change() {
      this.$bus.$emit("test", 1000);
      console.log(this.$bus);
    }
  }
};
</script>

Brother兄弟組件:

<template>
  <div>我是Son組件的兄弟組件{{m}}</div>
</template>
<script>
export default {
  mounted() {
    this.$bus.$on("test", data => { //使用箭頭函數,this指向現在的Vue示例,因要改當前Brother組件的m的值。若不用箭頭函數,this指向App.vue中定義的存儲值的vue實例
    //   console.log(data, this);
      this.m = data;
    });
  },
  data() {
    return {
      m: 222
    };
  }
};
</script>

執行:
vue serve App.vue
點擊修改兄弟按鈕,就可以看到運行結果了。

這裏沒有package.json文件,因是用的快速原型開發,下載完後(全局安裝)就可以通過vue serve App.vue(入口文件名)使用。

學習代碼已上傳git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章