上期回顧
- 條件判斷(v-if、v-show)的基本使用、開發(fā)時(shí)選擇
- 條件渲染案例(input添加key的區(qū)別)
- 循環(huán)遍歷(v-for)
- 表單綁定(v-model)
- lazy修飾符
- number修飾符
- trim修飾符
- 拓展:數(shù)組的響應(yīng)式方法
什么是組件化:
獨(dú)立的炕吸,可復(fù)用的萧福,整體化的一個(gè)功能模塊
為什么要組件化:
1.實(shí)現(xiàn)功能模塊的復(fù)用
2.開發(fā)單頁面復(fù)雜應(yīng)用
3.高效執(zhí)行
注冊(cè)組件
三大步驟
1.創(chuàng)建組件構(gòu)造器( Vue.extend() )
2.注冊(cè)組件( Vue.component() )
3.使用組件
- 代碼演練
<div id="app">
<!-- 第三步:使用組件 -->
<my-cpn></my-cpn>
<!-- 錯(cuò)誤的使用方式 -->
<myCpn></myCpn>
</div>
<script src="../js/vue.js"></script>
<script>
//第一步:創(chuàng)建組件構(gòu)造器
let cpn = Vue.extend({
template: `
<div>
<h2>我是組件</h2>
</div>
`
})
//第二步:注冊(cè)組件
Vue.component("myCpn", cpn)
const app = new Vue({
el: "#app",
data: {
}
})
</script>
注意:
第三步不能使用小駝峰命名饼问,大寫字母變?yōu)樾懽帜钢烧眨懊婕?"-"
Vue.extend({template})
Vue.extend():返回一個(gè)組件構(gòu)造器
template:代表自定義組件的html模板
Vue.vomponent("組件命名"矫付,組件構(gòu)造器)
Vue.vomponent():注冊(cè)組件
全局組件和局部組件
- 代碼演示
<div id="app1">
<!-- 全局 -->
<global-cpn></global-cpn>
<!-- 局部 -->
<part-cpn></part-cpn>
</div>
<div id="app2">
<!-- 全局 -->
<global-cpn></global-cpn>
<!-- 局部 -->
<part-cpn></part-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
//1.創(chuàng)建全局組件構(gòu)造器
let cpn1 = Vue.extend({
template: `
<div>
<h2>我是全局組件</h2>
</div>
`
})
//1.創(chuàng)建局部組件構(gòu)造器
let cpn2 = Vue.extend({
template: `
<div>
<h2>我是局部組件</h2>
</div>
`
})
//2.注冊(cè)全局組件
Vue.component("globalCpn", cpn1)
const app1 = new Vue({
el: "#app1",
//第二步:注冊(cè)局部組件
components: {
"partCpn": cpn2
}
})
const app2 = new Vue({
el: "#app2"
})
</script>
父組件和子組件
- 代碼演示
<div id="app">
<!-- 使用父組件 -->
<father-cpn></father-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
//1.創(chuàng)建子組件構(gòu)造器
let cpn2 = Vue.extend({
template: `
<div>
<h2>我是子組件</h2>
</div>
`
})
//1.創(chuàng)建父組件構(gòu)造器
let cpn1 = Vue.extend({
template: `
<div>
<h2>我是父組件</h2>
<!-- 使用子組件 -->
<son-cpn></son-cpn>
</div>
`,
//在父組件中掛載子組件
components: {
"sonCpn": cpn2
}
})
const app1 = new Vue({
el: "#app",
//在全局組件中掛載父組件
components: {
"fatherCpn": cpn1
}
})
</script>
組件的簡寫方式
- 代碼演示
<div id="app">
<!-- 使用全局組件 -->
<global-cpn></global-cpn>
<!-- 使用局部組件 -->
<part-cpn></part-cpn>
</div>
<script src="./js/vue.js"></script>
<script>
// 創(chuàng)建全局組件
Vue.component("globalCpn", {
template: `
<div>
<h2>我是全局組件</h2>
</div>
`
})
const app1 = new Vue({
el: "#app",
// 創(chuàng)建局部組件
components: {
"partCpn": {
template: `
<div>
<h2>我是局部組件</h2>
</div>
`
}
}
})
</script>
省去了Vue.extend()的步驟凯沪,直接用一個(gè)對(duì)象代替
模板的兩種分離寫法
1.使用<script>標(biāo)簽
- 代碼演示
<div id="app">
<my-cpn></my-cpn>
</div>
<!-- 使用<script>標(biāo)簽,注意加上type屬性和id屬性 -->
<script type="text/x-template" id="myCpn">
<div>
<h2>我是組件</h2>
</div>
</script>
<script src="./js/vue.js"></script>
<script>
const app1 = new Vue({
el: "#app",
// 創(chuàng)建局部組件
components: {
"myCpn": {
template: "#myCpn"
}
}
})
</script>
2.使用<template>標(biāo)簽(推薦)
- 代碼演示
<div id="app">
<my-cpn></my-cpn>
</div>
<!-- 使用<template>標(biāo)簽买优,注意加上id屬性 -->
<template id="myCpn">
<div>
<h2>我是組件</h2>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
const app1 = new Vue({
el: "#app",
// 創(chuàng)建局部組件
components: {
"myCpn": {
template: "#myCpn"
}
}
})
</script>
注意:
使用兩種標(biāo)簽妨马,主要是將template中的html模板抽離出去,所以杀赢,兩種標(biāo)簽一定要加上id屬性烘跺,用于template的綁定
組件數(shù)據(jù)的處理
- 代碼演示
<div id="app">
{{message1}} - {{message2}}
<my-cpn></my-cpn>
</div>
<template id="myCpn">
<div>
<h2>{{message1}}</h2>
<h2>{{message2}}</h2>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
cpn = {
"myCpn": {
template: "#myCpn",
// 數(shù)據(jù)存放在組件內(nèi)部,組件可以訪問葵陵,全局不能
data() {
return {
message2: "組件內(nèi)的數(shù)據(jù)"
}
},
}
}
const app1 = new Vue({
el: "#app",
components: cpn,
// 數(shù)據(jù)存放在實(shí)例的data內(nèi)液荸,全局可以訪問在組件中無法訪問
data() {
return {
message1: "實(shí)例內(nèi)的數(shù)據(jù)"
}
},
})
</script>
注意:
組件中data必須是函數(shù):這樣可以保證組件每次使用的數(shù)據(jù)不會(huì)被別的組件更改,保證各組件間的數(shù)據(jù)互不干擾脱篙,data不是函數(shù)時(shí)娇钱,會(huì)報(bào)錯(cuò)!0砝А文搂!
組件間的數(shù)據(jù)傳遞(父傳子)
props基本用法
- 代碼演示
<div id="app">
<!-- 1.通過v-bind綁定數(shù)據(jù)message -->
<my-cpn :info="message"></my-cpn>
</div>
<template id="myCpn">
<div>
<!-- 3.展示組件內(nèi)props的數(shù)據(jù) info -->
<h2>{{info}}</h2>
</div>
</template>
<script src="./js/vue.js"></script>
<script>
cpn = {
"myCpn": {
template: "#myCpn",
// 2.接收組件標(biāo)簽綁定的數(shù)據(jù)
props: ["info"]
}
}
const app1 = new Vue({
el: "#app",
components: cpn,
data: {
message: "hello Vue"
}
})
</script>
注意數(shù)據(jù)傳遞過程:
1.通過v-bind綁定數(shù)據(jù)messag
2.接收組件標(biāo)簽綁定的數(shù)據(jù)
3.展示組件內(nèi)props的數(shù)據(jù) info
props數(shù)據(jù)驗(yàn)證
- 代碼演示
<div id="app">
<my-cpn :info="message" :addNum="add()" :num="num3"></my-cpn>
</div>
<template id="myCpn">
<div>
<h2>{{info}}</h2>
<h2>{{addNum}}</h2>
<h2>{{num}}</h2>
<h2>{{str}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "hello Vue",
num1: 3,
num2: 5,
num3: 10,
str: "test"
},
methods: {
add() {
console.log(this.num1 + this.num2)
}
},
components: {
"my-cpn": {
template: "#myCpn",
props: {
// key:類型
info: String,
addNum: Function,
num: {
type: Number,
// 設(shè)為必填值
required: true,
},
str: {
type: String,
// 設(shè)置默認(rèn)值
default: "我是默認(rèn)值"
}
}
}
}
})
</script>
- props支持驗(yàn)證類型
String
Number
Array
Boolean
Function
Object
Date
Symbol
組件間的數(shù)據(jù)傳遞(子傳父)
- 代碼演示
<div id="app">
<h2>{{num}}</h2>
<!-- 第四步: 將子組件傳出的函數(shù)轉(zhuǎn)為自定義函數(shù),并綁定自定義事件的實(shí)現(xiàn)函數(shù)-->
<son-cpn :amount="num" @add="change" @cut="change"></son-cpn>
</div>
<template id="myCpn">
<div>
<!-- 第一步: 綁定事件 -->
<button @click="add">加10</button>
<button @click="cut">減10</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
num: 1
},
methods: {
// 第五步: 實(shí)現(xiàn)自定義事件的函數(shù)
// count 是第三步傳入的參數(shù)
change(count) {
this.num = count
}
},
components: {
"sonCpn": {
template: "#myCpn",
props: {
amount: {
type: Number,
required: true,
}
},
data() {
return {
count: this.amount
}
},
methods: {
// 第二步: 事件函數(shù)的實(shí)現(xiàn)
add() {
this.count += 10
// this.amount += 10 避免直接改變屬性,因?yàn)楫?dāng)父組件重新呈現(xiàn)時(shí)秤朗,該值將被覆蓋煤蹭。
// 第三步: 使用 emit("函數(shù)名",參數(shù)) 通知父組件,并傳遞參數(shù)
this.$emit("add", this.count)
},
cut() {
this.count -= 10
// this.amount += 10 避免直接改變屬性,因?yàn)楫?dāng)父組件重新呈現(xiàn)時(shí)硝皂,該值將被覆蓋常挚。
// 第三步: 使用 emit("函數(shù)名",參數(shù)) 通知父組件,并傳遞參數(shù)
this.$emit("cut", this.count)
}
}
}
}
})
數(shù)據(jù)傳遞(子傳父)過程
1.在子組件中稽物,通過$emit("事件名"奄毡,參數(shù))來觸發(fā)事件。
2.在父組件中贝或,通過v-on來監(jiān)聽子組件事件吼过。
父組件直接訪問子組件的數(shù)據(jù)($children、$refs)
- 代碼演示
<div id="app">
<f-cpn></f-cpn>
</div>
<!-- 子組件模板 -->
<template id="zCpn">
<div>
<h2>我是子組件</h2>
<h2>{{info}}</h2>
<h2>調(diào)用方法:{{mult}}</h2>
</div>
</template>
<!-- 父組件模板 -->
<template id="fCpn">
<div>
<h2>我是父組件</h2>
<!-- 通過ref給某一個(gè)子組件綁定一個(gè)特定的ID -->
<z-cpn ref="child"></z-cpn>
<button @click="access$children">通過$children訪問子組件</button>
<button @click="access$refs">通過$refs訪問子組件</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//子組件實(shí)例
let zCpn = {
template: "#zCpn",
data() {
return {
info: "我是子組件數(shù)據(jù)",
num: 2
}
},
computed: {
mult() {
return this.num * 3
}
}
}
const app = new Vue({
el: "#app",
data: {
},
components: {
// 注冊(cè)父組件
"f-cpn": {
template: "#fCpn",
components: {
// 注冊(cè)子組件
"z-cpn": zCpn
},
methods: {
access$children() {
// 通過$children訪問子組件
console.log(this.$children)
console.log(this.$children[0].info)
console.log(this.$children[0].mult)
// console.log(this.$children[0].mult()) ?錯(cuò)誤寫法咪奖,mult是計(jì)算屬性盗忱,不是函數(shù)
},
access$refs(){
// 通過$refs訪問子組件
console.log(this.$refs)
console.log(this.$refs.child)
console.log(this.$refs.child.info)
console.log(this.$refs.child.mult)
// console.log(this.$refs.child.mult()) ?錯(cuò)誤寫法,mult是計(jì)算屬性羊赵,不是函數(shù)
}
}
}
}
})
</script>
總結(jié):
- $children需要通過子組件的下標(biāo)($children[index])趟佃,才能訪問該子組件,易出錯(cuò)慷垮,
- $refs是通過ref綁定的ID訪問子組件($refs.ID)揖闸,更加精確到位,推薦使用$refs
子組件直接訪問父組件的數(shù)據(jù)($parent)
- 代碼演示
<div id="app">
<f-cpn></f-cpn>
</div>
<!-- 子組件模板 -->
<template id="zCpn">
<div>
<h2>我是子組件</h2>
<button @click="getFInfo">點(diǎn)擊獲取父組件的信息</button>
</div>
</template>
<!-- 父組件模板 -->
<template id="fCpn">
<div>
<h2>我是父組件</h2>
<z-cpn></z-cpn>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//子組件實(shí)例
let zCpn = {
template: "#zCpn",
methods: {
getFInfo() {
console.log(this.$parent)
console.log(this.$parent.info)
this.$parent.show()
}
}
}
const app = new Vue({
el: "#app",
data: {
},
components: {
// 注冊(cè)父組件
"f-cpn": {
template: "#fCpn",
components: {
// 注冊(cè)子組件
"z-cpn": zCpn
},
data() {
return {
info: "我是父組件信息"
}
},
methods: {
show(){
console.log("通過$parent訪問父組件的方法")
}
}
}
}
})
</script>
注意:
在開發(fā)中盡量避免子組件通過$parent直接訪問父組件的數(shù)據(jù)料身,因?yàn)檫@樣耦合度太高了汤纸。而且這樣會(huì)使父組件中的狀態(tài)將變得飄忽不定,很不利于我的調(diào)試和維護(hù)芹血。
插槽(slot)的使用
插值基本用法
- 代碼演示
<div id="app">
<div>==========單個(gè)slot使用==========</div>
<my-cpn1></my-cpn1>
<my-cpn1>
<!-- 填入內(nèi)容替換插槽 -->
<h2>hello vue</h2>
</my-cpn1>
<div>==========具名solt使用==========</div>
<!-- 如果my-cpn2內(nèi)部沒有內(nèi)容,具名插槽不會(huì)顯示,不具名插槽會(huì)顯示,如my-cpn1 -->
<my-cpn2></my-cpn2>
<!-- 如果my-cpn2內(nèi)部所有的標(biāo)簽都綁定了具名插槽,則不具名的插槽不會(huì)顯示 -->
<my-cpn2>
<div name="content1"><h2>替換文字1</h2></div>
<div name="content2"><h2>替換文字2</h2></div>
<div name="content3"><h2>替換文字3</h2></div>
</my-cpn2>
</div>
<template id="mySlot1">
<div>
<!-- 設(shè)置一個(gè)插槽 -->
<slot>默認(rèn)內(nèi)容</slot>
</div>
</template>
<template id="mySlot2">
<div>
<!-- 設(shè)置一個(gè)插槽 -->
<slot><h2>默認(rèn)內(nèi)容3</h2></slot>
<!-- 設(shè)置三個(gè)具名插槽 -->
<slot slot="content1"><h2>默認(rèn)內(nèi)容1</h2></slot>
<slot slot="content2"><h2>默認(rèn)內(nèi)容2</h2></slot>
<slot slot="content3"><h2>默認(rèn)內(nèi)容3</h2></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: {
myCpn1: {
template: "#mySlot1"
},
myCpn2: {
template: "#mySlot2"
}
}
})
</script>
slot數(shù)據(jù)傳遞
- 代碼演示
<div id="app">
<my-cpn>
<!-- 第二步: 接收數(shù)據(jù) -->
<template slot-scope="props">
<ul>
<!-- 第三步: 使用數(shù)據(jù) -->
<li v-for="(value,index) in props.info">{{index}} - {{value}}</li>
</ul>
</template>
</my-cpn>
</div>
<template id="mySlot">
<div>
<!-- 第一步: 綁定數(shù)據(jù) -->
<slot :info="course"></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#mySlot",
data() {
return {
course: {
1001: "Python入門",
1002: "數(shù)據(jù)結(jié)構(gòu)與算法",
1003: "離散數(shù)學(xué)",
1004: "概率論",
1005: "形式與政策",
}
}
}
}
const app = new Vue({
el: "#app",
components: {
myCpn: cpn
}
})
</script>
注意:
編譯作用域(補(bǔ)充)
- 代碼演示
<div id="app">
<my-cpn v-if="isShow">
</my-cpn>
</div>
<template id="mySlot">
<div>
<slot>默認(rèn)數(shù)據(jù)</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: "#mySlot",
data() {
return {
isShow: false
}
}
}
const app = new Vue({
el: "#app",
components: {
myCpn: cpn
},
data: {
isShow: true
}
})
</script>
注意:任意組件模板的所有東西都會(huì)在該模板作用域內(nèi)編譯贮泞,即變量會(huì)從該實(shí)例中尋找