認識組件
組件化開發:
? 將一個完成的頁面,劃分成一個個的組件,最終由一個個組件來完成整個頁面你的開發,這個過程就是組件化開發
優勢:復用!!
? 也就是說組件只需要寫一次,然后,用到組件的地方,只需要拿過去用就可以了
組成
一個組件由三部分組成:HTML+CSS+JS
兩種注冊組件的方式:
? 1 全局組件
? 2 局部組件
注冊全局組件:
? 第一個參數:表示組件名稱
? 第二個參數:是一個配置對象,這個配置對象類似于 Vue 構造函數中使用的配置對象
? 也就是說:在 Vue 構造函數中能夠使用的配置項,幾乎都可以用在組件中
template 作用:指定組件模板
? 注意:只能出現一個根節點
通過 data 指定組件中用到的數據
? 注意:組件中的數據data,是一個方法,通過方法的返回對象來作為組件數據
我的第一個全局組件
<!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>
</head>
<body>
<div id="app">
<!-- 使用組件: -->
<hello></hello> <hello></hello> <hello></hello> <hello></hello>
</div>
<script src="./vue.js"></script>
<script>
/*
兩種注冊組件的方式:
1 全局組件
2 局部組件
一個組件由三部分組成:HTML+CSS+JS
*/
// 注冊全局組件:
// 第一個參數:表示組件名稱
// 第二個參數:是一個配置對象,這個配置對象類似于 Vue 構造函數中使用的配置對象
// 也就是說:在 Vue 構造函數中能夠使用的配置項,幾乎都可以用在組件中
Vue.component('hello', {
// template 作用:指定組件模板
// 注意:只能出現一個根節點
template: `
<div>
<h1>這是我的第一個組件 - Hello 組件</h1>
<span @click="fn">{{ msg }}</span>
</div>
`,
// 通過 data 指定組件中用到的數據
// 注意:組件中的數據data,是一個方法,通過方法的返回對象來作為組件數據
data() {
return {
msg: 'Vue Component data'
}
},
// 鉤子函數
created() {
console.log('created 鉤子函數執行了')
},
// 方法
methods: {
fn() {
console.log('方法執行了')
}
}
})
const vm = new Vue({
el: '#app',
data: {}
})
</script>
</body>
</html>
注冊局部組件
? 通過配置項 components: {}
//html
<div id="app">
<item></item>
<wang></wang>
</div>
//js
Vue.component('wang',{
template:`
<p>我是王婷,<ting></ting></p>
`,
components:{
//組件名稱
ting:{
//組件模板
template:`
<span>我最美,{{wt}}</span>
`,
//組件使用的數據
data(){
return {
wt:'確認過眼神,這是真的'
}
}
}
}
});
const vm = new Vue({
el: '#app',
data: {
msg:''
},
components:{
item:{
template:`
<p>我是一個局部的組件:{{chang}}</p>
`,
data(){
return{
chang:'驗證通過'
}
}
}
}
})
兩中組件的區別
? 1.全局組件可以在任意的地方使用
? 2.局部組件只能在所屬組件的 模板 中使用
使用組件
組件使用中的細節點:
? 表單/ul/ol等中指定的標簽
產生的原因:在tbody中只能使用tr標簽,使用其他的標簽,瀏覽器解析的時候,會把其他標簽作為table外部標簽解析出去,結構就會發生分離,ul和ol也是如此
使用is解決問題,例如tr標簽
//html
<table>
<tbody>
<tr is=chang></chang>
</tbody>
</table>
//js
Vue.component('chang', {
template: `
<tr>
<td>常杰</td>
<td>李想</td>
<td>老丁</td>
</tr>
`
});
使用ref屬性獲取dom對象
//html
<div id="app">
<table>
<tbody>
<tr is=chang>
</chang>
</tbody>
</table>
</div>
//js
Vue.component('chang', {
template: `
<tr>
<td ref='hello' @click="handler">{{current}}</td>
</tr>
`,
data(){
return{
current:'常杰'
}
},
methods:{
handler(){
// alert('你觸發了點擊事件');
console.log(this.$refs.hello)
}
}
});
組件通訊:
? 組件是一個獨立且封閉的個體,也就是說:默認情況下,組件只能使用自身的數據,而不能使用外部的數據
? 但是,在組件化開發過程中,兩個組件通訊( 也就是兩個組件使用對方法的數據 ),是很常見的問題。
? 這種問題就需要通過 組件通訊 機制來解決
組件通訊的三種情況:
? 1 父組件 傳遞數據給 子組件
? 2 子組件 傳遞數據給 父組件
? 3 非父子組件(兄弟組件)
父 -->子通訊:
? 父組件: Vue實例
? 子組件: child組件
步驟:
? 1 在子組件標簽中添加要傳遞的屬性
<child msg="123"></child> msg就是要傳遞的屬性
<child :msg="parentMsg"></child> 表示傳遞動態數據給子組件
? 2 在子組件中通過 props 顯示指定要接受的屬性
? props: ['msg']
? 3 此時,就可以在子組件中直接使用 msg 這個數據了
<!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>
<style>
.parent {
background-color: hotpink;
padding: 50px;
}
.child {
background-color: #daa520;
height: 100px;
}
</style>
</head>
<body>
<div id="app" class="parent">
父組件:{{ parentMsg }}
<!-- 渲染子組件: -->
<!-- <child msg="123"></child> -->
<!-- <child msg="parentMsg"></child> -->
<!-- 表示將父組件中的數據 parentMsg 傳遞給子組件 -->
<child :msg="parentMsg"></child>
</div>
<script src="./vue.js"></script>
<script>
// 注冊子組件
Vue.component('child', {
template: `
<div class="child">
<p>這是子組件 - {{ msg }}</p>
</div>
`,
// 組件中通過 props 配置項,來顯示指定要接受的數據
props: ['msg']
})
const vm = new Vue({
el: '#app',
data: {
parentMsg: '這是父組件中的數據'
}
})
</script>
</body>
</html>
子 -->父通訊
·思路:父組件提供一個方法,讓子組件調用,子組件調用方法的時候將數據作為參數傳遞,這樣,父組件就拿到子組件中傳遞過來的數據了
? function parent(data) {
? console.log('子組件中傳遞過來的數據:', data)
? }
? 子組件調用方法: parent( '要傳遞給父組件的數據' )
實現子組件給父組件傳值的過程
? 1.首先給父組件注冊一個方法,同時在data設置一個變量準備接收子組件傳出的值
? 2.給子組件的標簽上綁定自定義事件,并把方法作為事件函數傳入
? 3.在子組件中設置模板。在模板中設置事件,觸發子組件的方法,在子組件的方法中,獲取到傳入的自定義事件,并且觸發這個事件,傳遞參數
? 4.子組件觸發這個方法,并把數據作為方法的參數傳出
? 5.在父組件中處理觸發事件的操作
//html
<div id="app">
我兒子給我發的信息:{{msg}}
<!-- 2.給子組件的標簽上綁定自定義事件,并把方法作為事件函數傳入 -->
<son @pmsg="fn"></son>
</div>
//js
Vue.component('son',{
// 3.在子組件中設置模板。在模板中設置事件,觸發子組件的方法,在子組件的方法中,獲取到傳入的自定義事件,并且觸發這個事件,傳遞參數
template:`
<p>我要給老爸發信息
<button @click="send">發送信息</button>
</p>
`,
// 4.子組件觸發這個方法,并把數據作為方法的參數傳出
methods:{
send(){
this.$emit('pmsg',"老爸,我資金有點....")
}
}
});
const vm = new Vue({
el: '#app',
data: {
msg:'沒啥用,就占個位'
},
// 1.首先給父組件注冊一個方法,同時在data設置一個變量準備接收子組件傳出的值
methods:{
// 5.在父組件中處理觸發事件的操作
fn(data){
this.msg = data
}
}
})
小案例:
計數器實現的步驟
- 1.首先創建一個子組件count
- 2.在組件中創建模板,在模板中綁定事件handler,插入數據num
- 3.在組件中創建模板中調用的方法handler
- 4.在模板中設置數據num
- 5.在組件標簽上注冊事件change,并綁定handlerChange方法(非常重要)
- 6.在組件標簽上添加屬性ref為one/two(非常重要)
- 7.在Vue實例中,添加方法handlerChange,并通過this.$refs.綁定的名稱獲取數據
- 8.獲取到子組件的數據進行操作
<!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>
</head>
<body>
<div id="app">
<!-- 5.在組件標簽上注冊事件change,并綁定handlerChange方法(非常重要) -->
<!-- 6.在組件標簽上添加屬性ref為one/two(非常重要) -->
<count ref="one" @change="handlerChange"></count>
<count ref="two" @change="handlerChange"></count>
<div>{{total}}</div>
</div>
<script src="./vue.js"></script>
<script>
// 1.首先創建一個子組件count
Vue.component('count',{
// 2.在組件中創建模板,在模板中綁定事件handler,插入數據num
template:`
<div @click="handler">{{num}}</div>
`,
// 4.在模板中設置數據num
data(){
return {
num: 0
}
},
// 3.在組件中創建模板中調用的方法handler
methods:{
handler(){
this.num++;
this.$emit("change")
}
}
});
const vm = new Vue({
el: '#app',
data: {
total: 0
},
// 7.在Vue實例中,添加方法handlerChange,并通過this.$refs.綁定的名稱獲取數據
methods:{
handlerChange(){
// console.log("handlerChange執行了")
// console.log(this.$refs.one.num)
// console.log(this.$refs.two.num)
// 8.獲取到子組件的數據進行操作
this.total = this.$refs.one.num + this.$refs.two.num
}
}
})
</script>
</body>
</html>
其他通訊(非父子)
思路:
1 創建一個事件總線(空的Vue實例 bus)
? 2 哪個組件要接收數據,就注冊事件
? bus.$on(事件名稱, () => {})
? 3 哪個組件要傳遞數據,就觸發事件
? bus.$emit(事件名稱, 要傳遞的數據)
注意: 注冊和觸發事件的 bus 是同一個,事件名稱也要相同
實際上,不管兩個組件是什么關系,都可以通過 bus 方式來實現通訊!
步驟:
? 1.首先創建一個 空的vue實例的對象 bus
? 2.然后創建兩個組件,一個臨時用于接收 (wang) ,一個臨時用于發送數據 (chang)
? 3.給兩個組件設置好模板內容
? 4. 組件chang設置方法,在方法中通過bus.$emit獲取事件,并設置觸發事件的參數
? 5.在組件wang的鉤子函數created中聲明創建事件listen,并設置獲取到參數后的處理
? 注意點:雖然用不到vm但是,必須聲明創建這個實例,只有指定了vue的邊界,代碼才能生效
//html
<div id="app">
<chang></chang>
<wang></wang>
</div>
//js
// 空Vue實例
// 事件總線
// 1.首先創建一個 空的vue實例的對象 即事件總線bus
let bus = new Vue();
// 2.然后創建兩個組件,一個臨時用于接收 (wang) ,一個臨時用于發送數據 (chang)
Vue.component('chang',{
// 3.給兩個組件設置好模板內容
template:`
<p>我是常杰,我想對王婷說:<button @click="fn">加密發送</button></p>
`,
// 4. 組件chang設置方法,在方法中通過bus.$emit獲取事件,并設置觸發事件的參數
methods:{
fn(){
bus.$emit('listen','那天我一直在等你')
}
}
});
Vue.component('wang',{
template:`
<p>我是王婷,常杰對我說:{{msg}}</p>
`,
data(){
return {
msg:'接收信息中,請等待...'
}
},
// 5.在組件wang的鉤子函數created中聲明創建事件listen,并設置獲取到參數后的處理
created(){
bus.$on('listen',data=>{
this.msg = data
})
}
});
//沒有用到數據,但是必須聲明
const vm = new Vue({
el:"#app",
data:{
}
});
細節補充
props的屬性
? props:是只讀的,在組件中使用的時候,只能讀取,不能修改props的值( 賦值
)如果修改這個值,會報錯!!!
? 如果props是一個引用類型的數據,不能直接賦值,但是,可以修改引用類型中某個屬性的值。
單向數據流:
? 是說組件之間數據流動方向是從 父組件流動到 子組件
? 父組件可以通過 props 將數據傳遞給子組件,并且,當父組件中的這個數據改變
后,這個改變后的數據會自動的再流動到子組件中。也就是說:子組件會自動接收到最
新的props數據,并且自動更新
? 總結:
? 1.props是只讀的屬性,只能讀取props的值,而不能直接修改props的值
? 如果是引用類型(對象、數組),可以直接修改對象中某個屬性的值,但是,如果要求很嚴謹,也不應該直接修改對象中某個屬性的值!!!
? 2.單項數據流: 兩個組件之間的數據流動方向,方向是: 父 -> 子
命名
HTML 標簽或屬性都是不區分大小寫的(大小寫不敏感的)
? 不管你寫的是大寫字母的標簽名或屬性名,還是小寫的,最終,都會被轉化為小寫字母
? 同樣的,在 Vue 中,給組件傳遞屬性的時候,如果屬性名中包含大寫字母,在解析的時候,也會被轉化為小寫字母,再作為屬性傳遞給組件
? 如何給props命名:
? 1 使用純小寫字母
? <hello :childmsg="parentMsg"></hello>
? props: ['childmsg']
? 2 傳遞屬性的時候,使用短橫線連接多個單詞,在子組件中通過 駝峰命名法 來接收這個數據(規范)
? 傳遞(短橫線連接) <hello :child-msg="parentMsg"></hello>
? 接收(駝峰命名法) props: ['childMsg']