1. 組件化跟模塊化的區(qū)別
- 組件的出現(xiàn)就是為了拆分 vue 實(shí)例的龐大內(nèi)容的,我們可以以不同的組件來劃分不同的功能模塊
- 之后我們需要什么樣的功能就直接搬運(yùn)過來什么樣的組件就可以了
- 模塊化是從程序邏輯代碼結(jié)構(gòu)來劃分的(比如我們會(huì)將所有 api 封裝起來裸扶,單獨(dú)放到一個(gè)模塊中這就是按照邏輯來劃分的),后端的模塊化是為了代碼的分層開發(fā),保證每個(gè)功能模塊的職能單一
- 組件化是按照 ui 視圖界面來劃分的(比如我們將表單作為一個(gè)組件來使用镶柱,這個(gè)就是按照視圖來劃分),前端的組件化是為了代碼的重用
2. Vue 中定義組件的三種方式
(1) 使用 Vue.extend 來創(chuàng)建全局組件
- 這種方式是通過對(duì) Vue 實(shí)例的繼承來完成的,因此創(chuàng)建的組件將會(huì)是全局組件聘芜,也就是所有 Vue 實(shí)例都可以訪問的組件
- 在定義的時(shí)候我們將會(huì)用一個(gè)變量來接受這個(gè)新的實(shí)例
- 其定義參數(shù)為一個(gè)對(duì)象,其中 template 屬性指向了我們定義的 html 結(jié)構(gòu)
- 之后再使用 Vue.component('組件名稱',{創(chuàng)建出來的模板對(duì)象缝龄,也就是我們用來接受新的組件實(shí)例的那個(gè)變量的名字})
- 這樣一操作我們就可以將組件注冊(cè)到我們的組件堆里面了
- 在 vm 實(shí)例里面用駝峰形式定義的東西在 html 頁(yè)面中使用的化必須要轉(zhuǎn)成中間用杠鏈接的形式汰现,如:'myNewComp'->'my-new-comp'
- 我們也可以把 Vue.extend 作為 Vue.component 的第二個(gè)參數(shù)直接進(jìn)行注冊(cè),這樣可以省一個(gè)步驟
html
<div id="app">
<div>{{message}}</div>
<!-- 直接使用組件的名稱即可 -->
<my-comp-one></my-comp-one>
</div>
javascript
// 創(chuàng)建一個(gè)Vue.extend實(shí)例
let bigFont = Vue.extend({
template: "<h1>This text is big font text"
});
// 將創(chuàng)建的實(shí)例注冊(cè)到組件堆上面
Vue.component("myCompOne", bigFont);
let vm = new Vue({
el: "#app",
data: {
message: "yerts"
}
});
(2) 直接使用 Vue.component 中的字面量形式
- 在第一種定義方式中我們將 Vue.extend 作為第二個(gè)參數(shù)傳入到了 Vue.component 中二拐,其實(shí)我們可以做的更絕
- 也就是將 Vue.extend 直接作為一個(gè)對(duì)象字面量的形式服鹅,直接在里面定義 template 屬性,然后將其傳入到 Vue.component 的第二個(gè)參數(shù)
- 記住,所有的組件必須只能有一個(gè)根節(jié)點(diǎn)丘侠,不然都會(huì)報(bào)錯(cuò)
html
<!-- 直接使用組件的名稱即可 -->
<my-comp-one></my-comp-one>
<my-comp-tow></my-comp-tow>
javascript
//直接將定義過程作為字面量的形式傳入注冊(cè)函數(shù)的方式創(chuàng)建組件
Vue.component("myCompTow", {
template: "<h2 style='color: red;'>This is my second component.</h2>"
});
let vm = new Vue({
el: "#app",
data: {
message: "yerts"
}
});
(3) 通過讓 template 指向 html 文件中的 template 的方式來構(gòu)建組件
- 前兩種方式在定義一個(gè)組建的時(shí)候都定義在了一個(gè) js 文件中姑廉,但是這樣就帶來了不會(huì)有智能提示的功能
- 因此第三種定義組建的方式就是解決這一問題的
- 我們可以在 html 頁(yè)面中使用 template 標(biāo)簽作為模板的容器,并設(shè)置其 id 屬性作為我們的選擇器仗哨,之后的使用過程中我們只需要在獲取 template 的地方使用
template:"#選擇器"
這種方式來讓其指向我們自己定義的模板即可,這種方式定義的時(shí)候也需要我們的Vue.component('組件名稱',{template:'#id'})
來注冊(cè)全局組件
html
<div id="app">
<div>{{message}}</div>
<!-- 直接使用組件的名稱即可 -->
<my-comp-one></my-comp-one>
<my-comp-tow></my-comp-tow>
<my-comp-three></my-comp-three>
</div>
<!-- 定義組件 -->
<template id="myTempOne">
<div>
<div>This is a template build by template tag.</div>
<div style="color: cornflowerblue;">easy to use !</div>
</div>
</template>
javascript
// 通過在html頁(yè)面定義好組件的結(jié)構(gòu)铅辞,在js文件里面注冊(cè)其行為的方式構(gòu)建組件
Vue.component("myCompThree", {
template: "#myTempOne"
});
let vm = new Vue({
el: "#app",
data: {
message: "yerts"
}
});
- 這種情況下我們的 template 標(biāo)簽不用包括在 app 標(biāo)簽里面了
3. Vue 中定義私有組件
- 我們可以使用 Vue 實(shí)例中的 components 屬性來定義 Vue 實(shí)例的私有組件
- 其中的 template 屬性什么的依然可以使用上面提到的三種方式厌漂,只是不用再使用 Vue.extend 來定義了,因?yàn)?templates 已經(jīng)在 Vue 實(shí)例內(nèi)部了
- 私有組件的 data 屬性是用來存放組件身上的私有數(shù)據(jù)的斟珊,但是與 Vue 實(shí)例的不同之處在于組件中的 data 是函數(shù)苇倡,并且會(huì)返回私有組件的數(shù)據(jù)
- 其返回的數(shù)據(jù)是對(duì)象形式返回的,因此我們?cè)?return 的時(shí)候需要 return 一個(gè)對(duì)象才能正常使用囤踩,定義完之后我們就可以像 Vue 實(shí)例一樣引用組件自身定義的數(shù)據(jù)
JavaScript
let vm = new Vue({
el: "#app",
data: {
message: "yerts"
},
components: {
// 自定義私有組件的方式來創(chuàng)建并注冊(cè)一個(gè)組件
myCompFour: {
template: "#myTempTow",
data() {
return {
message: "this is message from myCompFour"
};
}
}
}
});
html
<div id="app">
<div>{{message}}</div>
<!-- 直接使用組件的名稱即可 -->
<my-comp-one></my-comp-one>
<my-comp-tow></my-comp-tow>
<my-comp-three></my-comp-three>
<my-comp-four></my-comp-four>
</div>
<template id="myTempTow">
<div>
<div>This is a template build by template tag and it's a privte comp</div>
<div style="color:coral;">{{message}}</div>
</div>
</template>
- 為什么組件身上的 data 必須要是函數(shù)
- 這樣設(shè)計(jì)的好處在于每次定義的組件都可以有自己的數(shù)據(jù)旨椒,而不是共用一個(gè) data,而且每個(gè)組件身上的 data 發(fā)生變化也不會(huì)影響到其他組件身上的 data 數(shù)據(jù)
- 這樣的話每當(dāng)我們使用一次組件的時(shí)候都會(huì)返回一個(gè) data 數(shù)據(jù)對(duì)象堵漱,因此不會(huì)造成所有組件共同使用同一個(gè)數(shù)據(jù)的尷尬情景
- 我們可以在我們組件身上使用一些指令來實(shí)現(xiàn)我們的一些想法综慎,比如我們可以通過 v-if 來判斷要展示哪個(gè)組件,其實(shí)現(xiàn)方法是在要展示的組件身上加上 v-if勤庐,v-else 指令示惊,用其他變量去控制他們的顯隱屬性來達(dá)到我們的目的
- 這樣就可以達(dá)到組件切換的目的,其實(shí)我們也可以使用 Vue 本身提供的 component 標(biāo)簽來實(shí)現(xiàn)組件的切換
- 下面將給出兩種組件切換的方式愉镰,分別是使用 v-if 或者 component 標(biāo)簽來實(shí)現(xiàn)的
v-if 來實(shí)現(xiàn)切換
<input type="button" value="進(jìn)行切換" @click="flag=!flag" />
<my-comp-one v-if="flag"></my-comp-one>
<my-comp-tow v-else="flag"></my-comp-tow>
- 我們也可以使用 component 元素來實(shí)現(xiàn)我們的切換效果米罚,其中如果我們想要哪個(gè)組件出現(xiàn)在 component 這個(gè)元素的位置的話
- 我們就將 component 元素的 is 屬性綁定為我們想要出現(xiàn)的組件名稱就可以了
- 所以說 component 元素更像是一種占位符,專門用來給組件占位置的
component 組件來實(shí)現(xiàn)切換
<a href="" @click.prevent="showThree">展示三號(hào)組件</a>
<a href="" @click.prevent="showFour">展示四號(hào)組件</a>
<component :is="componentId"></component>
let vm = new Vue({
el: "#app",
data: {
message: "yerts",
flag: true,
componentId: "myCompFour"
},
methods: {
showFour: function() {
this.componentId = "myCompFour";
},
showThree: function() {
this.componentId = "myCompThree";
}
},
components: {
// 自定義私有組件的方式來創(chuàng)建并注冊(cè)一個(gè)組件
myCompFour: {
template: "#myTempTow",
data() {
return {
message: "this is message from myCompFour"
};
}
}
}
});
實(shí)現(xiàn)組件之間的切換動(dòng)畫
- 我們想實(shí)現(xiàn)切換動(dòng)畫只需要在 component 標(biāo)簽的外面包上 transition 標(biāo)簽即可丈探,不過要設(shè)置一些屬性
- 比如要設(shè)置 v-enter,v-leave-to,v-enter-active,v-leave-active 這些樣式過度類需要設(shè)置
- 但是我們發(fā)現(xiàn)兩個(gè)組件在切換的時(shí)候會(huì)有占位的情況录择,也就是第二個(gè)組件若想要進(jìn)來則必須要等第一個(gè)組件先出去這樣
- 這個(gè)問題我們可以直接設(shè)置 transition 標(biāo)簽的 mode 屬性,這個(gè)屬性可以幫我們?cè)O(shè)置動(dòng)畫在運(yùn)行的時(shí)候兩個(gè)組件之間怎么完成交接
- 比較常用的有 mode="out-in"也就是先出去,后進(jìn)來糊肠,這樣就保證了不會(huì)有占位等待的煩惱了
4. 父組件向子組件傳值(通過屬性綁定)
- 實(shí)際上我們也可以將 Vue 的實(shí)例理解成一個(gè)組件辨宠,因?yàn)樗凶约旱?data,自己的 methods货裹,自己的其他相關(guān)屬性嗤形,它里面的 template 是 el,這樣一理解的話我們就會(huì)發(fā)現(xiàn) Vue 實(shí)例實(shí)際上是一個(gè)大的組件弧圆,并且其他在他身上定義的組件為其子組件
- 在默認(rèn)情況下子組件不能直接去訪問父組件的 data 屬性赋兵,同樣父組件也不能直接去訪問子組件的 data 屬性
- 因此我們需要一些方法來打通父子組件之間的通信問題
- 我們可以這樣理解,在組件的使用的時(shí)候我們有一個(gè) app 組件實(shí)例搔预,在默認(rèn)情況下我們是不能直接向它內(nèi)部的組件傳遞值的霹期,因此我們就想到了一種曲線救國(guó)的方法,那就是我通過屬性綁定的方式拯田,把父組件的數(shù)據(jù)綁定到子組件內(nèi)部的變量中历造, 因此我只要去找被綁定的數(shù)據(jù)就可以訪問到我父組件的數(shù)據(jù),也就是 v-bind
:
去綁定我的數(shù)據(jù)到子組件的變量中
- 我們就可以在子組件身上使用屬性綁定機(jī)制綁定一個(gè)自定義屬性
- 比如我們?cè)谧咏M件身上定義一個(gè)自定義屬性船庇,我們將其命名為 parentMessage
- 之后我們把父組件身上的一個(gè)數(shù)據(jù)通過屬性綁定的方式傳到 parentMessage 身上吭产,也就是:
:parentMessage="dataFromParent"
,也就是我們需要在父組件身上的 dataFromParent 這個(gè)變量身上綁定我們要傳遞的數(shù)據(jù)
- 我們想要在子組件身上直接使用這個(gè)數(shù)據(jù)的話實(shí)際上是不可能用上的鸭轮,因?yàn)槲覀兊臄?shù)據(jù)還沒有在子組件身上定義
- 所以我們就要在子組件身上定義一個(gè)變量去接收父組件傳過來的數(shù)據(jù)臣淤,這樣我們就完成了一次傳遞
- Vue 規(guī)定父組件傳過來的數(shù)據(jù)接收變量必須要放在一個(gè)組件內(nèi)部叫做 props 的一個(gè)數(shù)組身上,所以我們需要在子組件內(nèi)部定義這個(gè) props 屬性窃爷,也就是把我們?cè)诮M件調(diào)用的地方定義的變量作為一個(gè)元素放進(jìn) props 屬性內(nèi)部就可以了
- 因此我們可以將 data 屬性跟 props 屬性區(qū)別開來了邑蒋,data 屬性中的數(shù)據(jù)是組件私有的數(shù)據(jù),這些數(shù)據(jù)一般在初始化的時(shí)候需要定義按厘,而 props 中的數(shù)據(jù)一般是由父組件傳遞進(jìn)來的医吊,可能在不同的地方組件的數(shù)據(jù)會(huì)是不同的內(nèi)容
- 比如我們子組件 Ajax 過來的數(shù)據(jù)就可以放到 data 身上,而且每個(gè)組件調(diào)用的地方都有自己的 data刻剥,因此不用擔(dān)心數(shù)據(jù)的混淆問題
- data 上面的數(shù)據(jù)是可讀可寫的遮咖,但 props 里面的數(shù)據(jù)是只讀不可寫的
html
<div id="app">
<!-- 在調(diào)用的時(shí)候需要去傳值 -->
<my-comp-four :data-from-parent="messageToChild"></my-comp-four>
</div>
<!-- 定義組件 -->
<template id="myTempFour">
<div>
<div>This is a template build by template tag and it's a privte comp</div>
<div style="color:coral;">
<!-- 使用父組件傳進(jìn)來的數(shù)據(jù) -->
{{dataFromParent.message}}-----{{dataFromParent.number}}
</div>
</div>
</template>
JavaScript
// 通過在html頁(yè)面定義好組件的結(jié)構(gòu)滩字,在js文件里面注冊(cè)其行為的方式構(gòu)建組件
Vue.component("myCompThree", {
template: "<h1>this is comp three.</h1>"
});
let vm = new Vue({
el: "#app",
data: {
message: "yerts",
flag: true,
componentId: "myCompFour",
messageToChild: {
message: "this message is from parent.",
number: 0
}
},
methods: {
showFour: function() {
this.componentId = "myCompFour";
},
showThree: function() {
this.componentId = "myCompThree";
}
},
components: {
// 自定義私有組件的方式來創(chuàng)建并注冊(cè)一個(gè)組件
myCompFour: {
template: "#myTempFour",
data() {
return {
message: "this is message from myCompFour"
};
},
//需要在props里面定義被傳進(jìn)來的數(shù)據(jù)
props: ["dataFromParent"]
}
}
});
- 這里記住一個(gè)問題我們沒有解決造虏,如果使用 component 這個(gè)標(biāo)簽來切換的話,我們?cè)趺慈?shí)現(xiàn)父組件向子組件傳值麦箍,后期我們將會(huì)有相應(yīng)的解答
5. 子組件向父組件傳值(通過事件調(diào)用)
- 如果父組件想要給子組件傳遞方法的話需要使用事件綁定機(jī)制漓藕,也就是 v-on:或者@來實(shí)現(xiàn),只需要將把 v-bind 換成 v-on 就可以了挟裂,其他內(nèi)容都一樣享钞,當(dāng)我們?cè)谟|發(fā)一個(gè)從父組件身上傳過來的函數(shù)的時(shí)候會(huì)先觸發(fā)子組件的方法,之后再去通過子組件觸發(fā)父組件的方法,這就相當(dāng)于子組件觸發(fā)了父組件的方法栗竖,也就是傳遞了一個(gè)方法
- 但是在傳遞方法的時(shí)候不用在 props 選項(xiàng)里面寫自己用于接收的方法名暑脆,不過要在組件內(nèi)部創(chuàng)建一個(gè)方法,用來執(zhí)行父組件傳過來的方法狐肢,執(zhí)行的方式就是在這個(gè)方法內(nèi)部寫一個(gè)
this.$emit('我們自己定義的用于接收方法的方法名')
其含義就是執(zhí)行的意思添吗,也就是說我們?cè)谝粋€(gè)方法內(nèi)部說明執(zhí)行一個(gè)方法就可以了,之后再通過事件觸發(fā)這個(gè)方法就可以做到我們調(diào)用里面定義執(zhí)行的方法了
- 下面將給一個(gè)例子
html
<div id="app">
<!-- 這里我們用屬性綁定機(jī)制把數(shù)據(jù)傳到datafromparent上面了份名,我們用事件綁定機(jī)制把方法綁定到childactive身上了 -->
<my-comp-four
:data-from-parent="messageToChild"
@child-active="parentMethod"
></my-comp-four>
</div>
<!-- 定義組件 -->
<template id="myTempFour">
<div>
<!-- 當(dāng)我們觸發(fā)了這個(gè)函數(shù)之后碟联,這個(gè)函數(shù)內(nèi)部的this.$emit方法將會(huì)執(zhí)行父組件傳過來的方法 -->
<div @click="myClick">
This is a template build by template tag and it's a privte comp
</div>
<div style="color:coral;">
{{dataFromParent.message}}-----{{dataFromParent.number}}
</div>
</div>
</template>
JavaScript
let vm = new Vue({
el: "#app",
data: {
message: "yerts",
flag: true,
componentId: "myCompFour",
messageToChild: {
message: "this message is from parent.",
number: 0
}
},
methods: {
showFour: function() {
this.componentId = "myCompFour";
},
showThree: function() {
this.componentId = "myCompThree";
},
// 用于傳遞給子組件的方法
parentMethod: function() {
console.log("this is parent method.");
}
},
components: {
// 自定義私有組件的方式來創(chuàng)建并注冊(cè)一個(gè)組件
myCompFour: {
template: "#myTempFour",
data() {
return {
message: "this is message from myCompFour"
};
},
props: ["dataFromParent"],
methods: {
//用于執(zhí)行接收到父組件的內(nèi)容的方法的方法
myClick: function() {
//emit代表觸發(fā)的意思,其參數(shù)直接輸入我們自己定義的接收父組件傳遞的方法就可以了
this.$emit("child-active");
}
}
}
}
});
- 既然我們可以調(diào)用父組件的方法了僵腺,那么也就是說我們也可以在調(diào)用這個(gè)方法的同時(shí)也傳入我們參數(shù)鲤孵,這樣一來我們就可以把子組件的數(shù)據(jù)作為方法的參數(shù)傳給父組件,就完成了子組件向父組件傳遞數(shù)據(jù)的功能辰如,雖然兩種方式都是父組件在向子組件綁定內(nèi)容普监,但是在第二種情形下子組件就可以通過這個(gè)綁定的內(nèi)容來傳遞自己的參數(shù)
- 下面我們就來實(shí)踐一下 ,同時(shí)在父組件內(nèi)部定義幾個(gè)數(shù)據(jù)用于接收子組件傳過來的數(shù)據(jù)
- 在使用 this.$emit 的時(shí)候第一個(gè)參數(shù)輸入函數(shù)名琉兜,之后的參數(shù)就作為輸入父組件需要的實(shí)參的位置了
html
<div id="app">
<!-- 我們使用childactive來接收了父組件傳過來的方法鹰椒,之后我們?cè)趀mit的時(shí)候可以加上參數(shù)去執(zhí)行這個(gè)方法,這樣子組件的數(shù)據(jù)就可以傳遞到父組件身上了 -->
<my-comp-four
:data-from-parent="messageToChild"
@child-active="parentMethod"
></my-comp-four>
</div>
<!-- 定義組件 -->
<template id="myTempFour">
<div>
<div @click="myClick">
This is a template build by template tag and it's a privte comp
</div>
<div style="color:coral;">
{{dataFromParent.message}}-----{{dataFromParent.number}}
</div>
</div>
</template>
JavaScript
let vm = new Vue({
el: "#app",
data: {
message: "yerts",
flag: true,
componentId: "myCompFour",
messageToChild: {
message: "this message is from parent.",
number: 0
},
// 子組件傳過來的數(shù)據(jù)就可以綁定在這個(gè)對(duì)象身上了
childData: {}
},
methods: {
showFour: function() {
this.componentId = "myCompFour";
},
showThree: function() {
this.componentId = "myCompThree";
},
parentMethod: function(childData) {
//子組件傳遞進(jìn)來的數(shù)據(jù)
this.childData = childData;
console.log("this is parent method.");
//把子組件傳遞的數(shù)據(jù)綁定到這個(gè)對(duì)象身上
console.log(this.childData);
}
},
components: {
// 自定義私有組件的方式來創(chuàng)建并注冊(cè)一個(gè)組件
myCompFour: {
template: "#myTempFour",
data() {
return {
message: "this is message from myCompFour"
};
},
props: ["dataFromParent"],
methods: {
myClick: function() {
//emit代表觸發(fā)的意思呕童,其參數(shù)直接輸入我們自己定義的接收父組件傳遞的方法就可以了
//第一個(gè)參數(shù)為方法名漆际,之后的內(nèi)容都可以作為傳遞的參數(shù)
this.$emit("child-active", this.message);
}
}
}
}
});
- 一般需要用戶去查看的數(shù)據(jù)最好放在本地存儲(chǔ)也就是 localStorage 中,因?yàn)檫@樣操作將會(huì)對(duì)用戶體驗(yàn)帶來很大的提升
- 有些需要上傳完數(shù)據(jù)就要更新的功能可以添加到父組件內(nèi)部夺饲,之后用子組件去調(diào)用即可
6. 用ref獲取DOM元素身上的東西跟子組件的引用
- 在我們的業(yè)務(wù)開發(fā)中有些時(shí)候會(huì)有需要獲取當(dāng)前元素的dom內(nèi)容的情況奸汇,比如我們用getElementById('xx')來獲取dom之后再訪問其innerHTML內(nèi)容的情況,這種情況下Vue給我們提供了this.$refs的選項(xiàng)往声,這就類似于getElementById('xx')擂找,之后的內(nèi)容直接在其身上獲取就可以了,
- 我們?cè)趯?shí)現(xiàn)這個(gè)功能的時(shí)候需要在被操作的元素身上加上ref="xxx"這個(gè)屬性浩销,這樣才能通過this.$refs.xxx去獲取它身上的內(nèi)容
- xxx.innerText就是表示這個(gè)元素身上在標(biāo)簽里面包裹的內(nèi)容
- 這樣我們就操作了dom身上的內(nèi)容
- 類似的我們也可以在組件身上使用這一功能贯涎,我們也可以為組件綁定一個(gè)ref屬性,并且通過ref屬性來獲取組件內(nèi)部的東西
- 通過這種方式我們可以直接獲取組件身上的數(shù)據(jù)慢洋,方法等一系列內(nèi)容
<div id="app">
<h1 ref="refH1">這是一個(gè)大大的標(biāo)簽</h1>
<my-comp-four ref="compFour" :data-from-parent="messageToChild" @child-active="parentMethod"></my-comp-four>
</div>
<!-- 定義組件 -->
<template id="myTempFour">
<div>
<div @click="myClick">This is a template build by template tag and it's a privte comp</div>
<div style="color:coral;">{{dataFromParent.message}}-----{{dataFromParent.number}}</div>
</div>
</template>
methods: {
showFour: function () {
this.componentId = 'myCompFour'
},
showThree: function () {
this.componentId = 'myCompThree'
},
parentMethod: function (childData) {
this.childData = childData
console.log('this is parent method.')
console.log(this.childData)
console.log(`${this.$refs.compFour.message}-----refMessage`)
console.log(`${this.$refs.refH1.innerText}-----refMessage`)
}
},