組件
好處:
- 可複用 可以提高開發效率
- 方便後期的維護和修改
- 可以減少渲染
全局組件 && 局部組件
全局組件:
註冊全局組件 ,可以用橫槓也可用駝峯,如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
- 父組件傳遞參數給子組件,使用屬性的方式: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中,函數中的直接寫{}是作用域,所以需要加()
}
}
- $attrs $listeners:批量把屬性和方法往下傳
attrs" 再往下一層傳遞,批量把屬性往下傳);
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>
- 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>
- $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>
- eventBus 兄弟之間傳遞數據 $on bus = new Vue; //bus.bus,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