組件是vue的特性,它是Vue最強(qiáng)大的功能之一。在Vue中,組件可以擴(kuò)展HTML元素,封裝可重用的代碼;在較高層面上,組件是自定義元素,Vue的編譯器為它添加特殊功能;在有些情況下,組件也可以是原生HTML元素的形式,以is特性擴(kuò)展。
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
Vue.js 是一個(gè)優(yōu)秀的前端界面開(kāi)發(fā) JavaScript 庫(kù),它之所以非?;?,是因?yàn)橛斜姸嗤怀龅奶攸c(diǎn),其中主要的特點(diǎn)有以下幾個(gè)。
1) 輕量級(jí)的框架
Vue.js 能夠自動(dòng)追蹤依賴(lài)的模板表達(dá)式和計(jì)算屬性,提供 MVVM 數(shù)據(jù)綁定和一個(gè)可組合的組件系統(tǒng),具有簡(jiǎn)單、靈活的 API,使讀者更加容易理解,能夠更快上手。
2) 雙向數(shù)據(jù)綁定
聲明式渲染是數(shù)據(jù)雙向綁定的主要體現(xiàn),同樣也是 Vue.js 的核心,它允許采用簡(jiǎn)潔的模板語(yǔ)法將數(shù)據(jù)聲明式渲染整合進(jìn) DOM。
3) 指令
Vue.js 與頁(yè)面進(jìn)行交互,主要就是通過(guò)內(nèi)置指令來(lái)完成的,指令的作用是當(dāng)其表達(dá)式的值改變時(shí)相應(yīng)地將某些行為應(yīng)用到 DOM 上。
4) 組件化
組件(Component)是 Vue.js 最強(qiáng)大的功能之一。組件可以擴(kuò)展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 is 特性擴(kuò)展。
在 Vue 中,父子組件通過(guò) props 傳遞通信,從父向子單向傳遞。子組件與父組件通信,通過(guò)觸發(fā)事件通知父組件改變數(shù)據(jù)。這樣就形成了一個(gè)基本的父子通信模式。
在開(kāi)發(fā)中組件和 HTML、JavaScript 等有非常緊密的關(guān)系時(shí),可以根據(jù)實(shí)際的需要自定義組件,使開(kāi)發(fā)變得更加便利,可大量減少代碼編寫(xiě)量。
組件還支持熱重載(hotreload)。當(dāng)我們做了修改時(shí),不會(huì)刷新頁(yè)面,只是對(duì)組件本身進(jìn)行立刻重載,不會(huì)影響整個(gè)應(yīng)用當(dāng)前的狀態(tài)。CSS 也支持熱重載。
組件的優(yōu)勢(shì):
-
降低整個(gè)系統(tǒng)的耦合度,在保持接口不變的情況下,我們可以替換不同的組件快速完成需求,例如輸入框,可以替換為日歷、時(shí)間、范圍等組件作具體的實(shí)現(xiàn)
-
調(diào)試方便,由于整個(gè)系統(tǒng)是通過(guò)組件組合起來(lái)的,在出現(xiàn)問(wèn)題的時(shí)候,可以用排除法直接移除組件,或者根據(jù)報(bào)錯(cuò)的組件快速定位問(wèn)題,之所以能夠快速定位,是因?yàn)槊總€(gè)組件之間低耦合,職責(zé)單一,所以邏輯會(huì)比分析整個(gè)系統(tǒng)要簡(jiǎn)單
-
提高可維護(hù)性,由于每個(gè)組件的職責(zé)單一,并且組件在系統(tǒng)中是被復(fù)用的,所以對(duì)代碼進(jìn)行優(yōu)化可獲得系統(tǒng)的整體升級(jí)
5) 客戶端路由
Vue-router 是 Vue.js 官方的路由插件,與 Vue.js 深度集成,用于構(gòu)建單頁(yè)面應(yīng)用。Vue 單頁(yè)面應(yīng)用是基于路由和組件的,路由用于設(shè)定訪問(wèn)路徑,并將路徑和組件映射起來(lái),傳統(tǒng)的頁(yè)面是通過(guò)超鏈接實(shí)現(xiàn)頁(yè)面的切換和跳轉(zhuǎn)的。
6) 狀態(tài)管理
狀態(tài)管理實(shí)際就是一個(gè)單向的數(shù)據(jù)流,State 驅(qū)動(dòng) View 的渲染,而用戶對(duì) View 進(jìn)行操作產(chǎn)生 Action,使 State 產(chǎn)生變化,從而使 View 重新渲染,形成一個(gè)單獨(dú)的組件。
初識(shí)VUE組件應(yīng)用
實(shí)例化多個(gè)vue對(duì)象
用new創(chuàng)建多個(gè)vue對(duì)象并命名,可以通過(guò)變量相互訪問(wèn)
例子:對(duì)象2修改對(duì)象1的name變量
<!-- 第一個(gè)根元素 --> <div id="vue-app-one">這里是:{{name}}</div> <!-- 第二個(gè)根元素 --> <div id="vue-app-two"> <p>這里是:{{name}}</p><br> <button @click="changeName">change-one-name</button> <!-- 點(diǎn)擊后修改vue-app-one的name值--> </div>
// 第一個(gè)vue對(duì)象 var one = new Vue({ el:"#vue-app-one", data:{ "name":"ccy1" } }) // 第二個(gè)vue對(duì)象 var two = new Vue({ el:"#vue-app-two", data:{ "name":"ccy2" }, methods:{ // 修改vue-app-one的name為'ccy333' changeName:function(){ one.name = 'ccy333' } } })
效果:點(diǎn)擊后修改”ccy1“為”ccy333“
全局組件
定義與使用
- 定義全局組件,需給組件一個(gè)名字,調(diào)用時(shí),將組件名當(dāng)作標(biāo)簽名使用;相當(dāng)于自定義標(biāo)簽,該標(biāo)簽下可以包含很多子html標(biāo)簽;
- 這些子html標(biāo)簽定義在組件的template屬性中,每次調(diào)用該組件,都渲染template里的標(biāo)簽
- template里必須只有一個(gè)根元素
- 在組件中,data是函數(shù),將數(shù)據(jù)return回去
- 依然可以用this來(lái)調(diào)用data中定義的數(shù)據(jù)
例子:
定義組件:
① 定義一個(gè)組件,命名為my-component
② 其中包含數(shù)據(jù):name和方法:changeName
③ 渲染出的html效果有一個(gè)p標(biāo)簽,包含一個(gè)按鈕,點(diǎn)擊按鈕時(shí),修改name
④ 命名規(guī)范:camelCase (駝峰命名法) 與kebab-case (短橫線分隔命名)
- 當(dāng)寫(xiě)成標(biāo)簽時(shí),遇到有大寫(xiě)字母的命名,需要改成小寫(xiě)并用橫桿鏈接前后兩個(gè)部分,如定義組件時(shí)命名為myComponent,寫(xiě)成標(biāo)簽時(shí)應(yīng)寫(xiě)成<my-component>;
- 組件定義時(shí)也可以用橫桿法命名;
- 如果定義時(shí)用myComponent,標(biāo)簽用<my-component>是OK的,系統(tǒng)自動(dòng)識(shí)別
// 自定義的全局組件my-component // template中只有一個(gè)根元素p標(biāo)簽,里面包含一個(gè)button按鈕 Vue.component('my-component',{ template:`<p> 我的名字是:{{name}} <button @click='changeName()'>btn</button> </p>`, data(){ return { name:'ccy' } }, methods:{ changeName:function(){ this.name = '安之' } } }) // vue對(duì)象1 new Vue({ el:"#vue-app-one", }) // vue對(duì)象2 new Vue({ el:"#vue-app-two", })
使用組件:
① 在vue對(duì)象對(duì)應(yīng)的根元素(el指定標(biāo)簽)下使用
② 由于定義的是全局組件,所以可以在任意的vue對(duì)象下使用
③ 組件可復(fù)用,在一個(gè)vue對(duì)象下可以使用多次,且組件間互相獨(dú)立
<div id="vue-app-one"> <my-component></my-component> <my-component></my-component> </div> <div id="vue-app-two"> <my-component></my-component> </div>
效果:
data是一個(gè)函數(shù)
在vue對(duì)象中,data屬性值是一個(gè)對(duì)象,比如這樣的:
但是在全局組件中,同一份data可能被多個(gè)vue對(duì)象使用,每個(gè)對(duì)象不單獨(dú)維護(hù)一份data時(shí),如果某一個(gè)vue對(duì)象修改了data中的一個(gè)變量,其他vue對(duì)象獲取data時(shí)就會(huì)被影響;
如果用上面的例子做案例,若組件中的data是對(duì)象(引用),其他地方均不改變,兩個(gè)vue對(duì)象便共享同一個(gè)name變量;當(dāng)我通過(guò)其中一個(gè)vue對(duì)象改變name數(shù)據(jù)時(shí)(即點(diǎn)擊任一個(gè)btn按鈕),另一個(gè)對(duì)象獲得的name也發(fā)生了改變(其他按鈕處的’ccy’也都被改成了’安之’)
因此,為保證數(shù)據(jù)的獨(dú)立性,即每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝,data為每個(gè)實(shí)例都return一份新創(chuàng)建的數(shù)據(jù),不同的vue對(duì)象獲取的data均互不影響
在vscode中不允許組件中的data是對(duì)象,會(huì)報(bào)錯(cuò):
[Vue warn]: The “data” option should be a function that returns a per-instance value in component definitions.
局部組件
- 局部組件注冊(cè)在某個(gè)vue對(duì)象中,
- 只有注冊(cè)過(guò)該局部組件的vue對(duì)象才能使用這個(gè)局部組件
例子:
局部組件定義:
// template僅一個(gè)根元素:ul var msgComponent = { // 數(shù)據(jù)是自身提供的 (hobbies) template:`<ul><li v-for='hobby in hobbies' v-bind:key='hobby.id'>{{hobby}}</li></ul>`, data(){ return { hobbies:['看劇','看動(dòng)漫','吃好吃的'] } } }
注冊(cè)局部組件:
// 僅由注冊(cè)過(guò)該局部組件的vue對(duì)象才能使用,此處為div#vue-app-one // 注意命名規(guī)范,components中對(duì)象的key將會(huì)被作為標(biāo)簽名,多個(gè)單詞拼接的命名需使用橫桿法 // 可以寫(xiě)成msg-component,此處直接簡(jiǎn)化了命名為msg, new Vue({ el:"#vue-app-one", components:{ "msg": msgComponent } })
html文件中使用<msg></msg>
:
<div id="vue-app-one"> <p>這里是vue-app-one</p> <mycomponent></mycomponent> <mycomponent></mycomponent> <p>我的愛(ài)好:</p> <msg></msg> <!--使用局部組件--> </div>
效果: 紅框圈出的部分就是局部組件渲染出來(lái)的
父向子傳值/傳引用:prop
靜態(tài)傳值
創(chuàng)建子組件:
var titleComponent = { props:["title"], template:`<p>{{title}}</p>` // 所需要的數(shù)據(jù)title由父組件提供 }
在父組件的components屬性中注冊(cè)子組件:
new Vue({ el:"#vue-app-one", components:{ "msg": msgComponent, "titleComponent":titleComponent }, })
在父組件上使用子組件:
<!-- div#vue-app-one為父組件 --> <div id="vue-app-one"> <p>這里是vue-app-one</p> <mycomponent></mycomponent> <mycomponent></mycomponent> <!--使用子組件title-component,并傳值"我的愛(ài)好:"給子組件--> <title-component title="我的愛(ài)好:"></title-component> <msg></msg> </div>
效果:紅框標(biāo)記處就是父向子傳值并展示
動(dòng)態(tài)傳值:v-bind
定義子組件:
var titleComponent = { props:["title"], template:`<p>{{title}}</p>` }
在父組件的components屬性中注冊(cè)子組件:
new Vue({ el:"#vue-app-one", components:{ "msg": msgComponent, "titleComponent":titleComponent }, data(){ return { title:"my hobbies are ", } } })
使用子組件,通過(guò)綁定父組件data中的變量title來(lái)實(shí)現(xiàn)動(dòng)態(tài)傳值:
<!-- div#vue-app-one為父組件 --> <div id="vue-app-one"> <p>這里是vue-app-one</p> <mycomponent></mycomponent> <mycomponent></mycomponent> <!-- 動(dòng)態(tài)綁定title --> <title-component v-bind:title="title"></title-component> <msg></msg> </div>
效果:紅框處就是動(dòng)態(tài)綁定獲取數(shù)據(jù)的展示
傳遞數(shù)組等復(fù)雜數(shù)據(jù)時(shí),也可以使用v-bind來(lái)動(dòng)態(tài)傳值,如:
需要向子級(jí)傳遞hobbies數(shù)組,在vue實(shí)例對(duì)象(父)中創(chuàng)建數(shù)據(jù)hobbies:
new Vue({ el:"#vue-app-one", components:{ "msg": msgComponent, "titleComponent":titleComponent }, data:{ title:"my hobbies are ", hobbies:['看劇','看動(dòng)漫','吃好吃的'], //需要向子組件傳遞的數(shù)據(jù) } })
定義子組件:
var msgComponent = { template:` <p>{{hobby}}</p> `, props:["hobby"], data(){ return { } } }
使用子組件:
<!-- div#vue-app-one為父組件 --> <div id="vue-app-one"> <p>這里是vue-app-one</p> <mycomponent name="ccy"></mycomponent> <mycomponent name="ccy"></mycomponent> <title-component v-bind:title="title"></title-component> <!-- 動(dòng)態(tài)傳值:hobbies --> <msg v-for="hobby in hobbies" v-bind:hobby="hobby" v-bind:key="hobby.id"></msg> </div>
效果:
跳回“一點(diǎn)想法”處
子向父:事件傳值$emit
子組件不能通過(guò)prop向父組件傳遞數(shù)據(jù),需要使用事件向父組件拋出一個(gè)值,告知父組件我需要實(shí)現(xiàn)一個(gè)功能,由父組件處理這個(gè)事件
例子:點(diǎn)擊按鈕,改變名稱(chēng)chinesename
(由于data變量名不支持chinese-name形式,花括號(hào)里不支持chineseName形式,所以這里我都用了小寫(xiě),此處記錄一下,日后學(xué)到了新知再來(lái)填坑)
先在父組件的data中定義chinesename的初始值:
new Vue({ el:"#vue-app-one", data:{ chinesename:"anzhi" // chinesename初始值 } })
創(chuàng)建子組件,并注冊(cè)事件change-name(就像click事件一樣,需要讓系統(tǒng)能夠辨認(rèn)這是一個(gè)事件并監(jiān)聽(tīng),當(dāng)事件被觸發(fā)時(shí),執(zhí)行某項(xiàng)約定好的操作):
Vue.component('blog-post', { props: ['chinesename'], template: ` <div class="blog-post"> <h4>{{ chinesename }}</h4> <button v-on:click='$emit("change-name","ruosu")'> 修改名字 </button> </div> ` // blog-post組件包含一個(gè)h4,顯示chinesename,和一個(gè)按鈕 // 點(diǎn)擊這個(gè)按鈕,觸發(fā)change-name事件,將"ruosu"作為參數(shù)傳遞給指定的處理函數(shù)onChangeName })
在父組件中使用子組件,定義change-name的處理函數(shù)為onChangeName:
<div id="vue-app-one"> <p>這里是vue-app-one</p> <!-- v-bind:通過(guò)prop給子組件傳遞chinesename的初始值 --> <!-- v-on:子組件通過(guò)$emit給父組件傳遞新的chinesename的值 --> <div id="blog-posts-events-demo"> <blog-post v-bind:chinesename='chinesename' v-on:change-name = "onChangeName" ></blog-post> </div> </div>
在父組件處定義事件處理函數(shù)onChangeName:
new Vue({ el:"#vue-app-one", data:{ chinesename:"anzhi" }, methods:{ onChangeName:function(value){ // 將chinesename換成傳遞過(guò)來(lái)的數(shù)據(jù) this.chinesename=value } } })
效果:
一點(diǎn)想法
關(guān)于父子組件的區(qū)分,在此寫(xiě)一點(diǎn)總結(jié),還是日后學(xué)了新知識(shí)再來(lái)填坑
官網(wǎng)中沒(méi)有很明確指明兩者的定義和區(qū)別,在網(wǎng)上搜了一圈,覺(jué)得比較多人認(rèn)可并且好理解的是:
- el指定的根元素為父組件(使用之處為父組件)
- vue實(shí)例對(duì)象也可看做組件
在前面這些父子傳值的例子中,我們可以看到,對(duì)于局部組件,我們會(huì)在某個(gè)html根元素中注冊(cè)并使用,所以此時(shí)el指定的根元素在html文件中是這個(gè)局部組件的父組件,局部組件在html使用時(shí)便是這個(gè)父組件的一份子,承擔(dān)數(shù)據(jù)傳輸?shù)呢?zé)任
跳轉(zhuǎn)到父向子動(dòng)態(tài)傳值案例
再用繞口令說(shuō)一波,即:title-component組件定義處與使用處,兩者身份是不一樣的,在定義處,它是局部組件,也是子組件,需注冊(cè)才能使用;在使用處,它是根元素的包含一部分,根元素為父組件,而“它”,承擔(dān)著父組件與子組件數(shù)據(jù)溝通的重任
這個(gè)總結(jié)在全局組件情況下也適用,使用該全局組件的根元素是父組件,如上面的子向父?jìng)髦档陌咐?,p#vue-app-one是父組件,<blog-post></blog-post>作為父子組件溝通的橋梁,全局組件blog-post為子組件
圖示:
如果是子組件又嵌套了子組件,被嵌套的組件是子子組件,以此類(lèi)推
【相關(guān)視頻教程推薦:vue視頻教程、web前端入門(mén)】