什麼是元件
元件(Component)是 Vue.js 最強大的功能之一。(好比電腦中的每一個元件(鍵盤,滑鼠,CPU),它是一個具有獨立的邏輯和功能或界面,同時又能根據規定的接口規則進行互相融合,變成一個完整的應用)
頁面就是由一個個類似這樣的部分組成的,比如:導航、清單、彈窗、下拉菜單等,頁面隻不過是這些元件的容器,元件自由結合形成功能完整的界面,當不需要某個元件,或者想要替換某個元件時,可以随時進行替換和删除,而不影響整個應用的運作
前端元件化的核心思想就是将巨大複雜的東西拆分成粒度合理的小東西

使用元件的好處
提高開發效率,友善重複使用,簡化調試步驟,提升整個項目的可維護性,便于協同開發
前端元件化的必要性
多年以前前端開發者們就一直嘗試元件化的道路上不斷探索,從一開始YUI、ExtJs到現在的Angular 、React、Vue、Bootstrap等,前端的元件化道路從來沒有停止過
元件的特性
高内聚性,元件功能必須是完整的,如我要實作下拉菜單功能,那在下拉菜單這個元件中,就把下拉菜單所需要的所有功能全部實作
低耦合性,通俗點說,代碼獨立不會和項目中的其他代碼發生沖突,實際工程中,我們經常會涉及到團隊協作,傳統按照業務線去編寫代碼的方式,就很容易互相沖突,是以運用元件化方式就可以大大避免這種沖突的存在
每個元件都有自己清晰的職責,完整的功能,較低的耦合便于單元測試和重複利用
vue中的元件
Vue中元件是一個自定義标簽,vue.js的編譯器為它添加特殊功能,也可以擴充原生的html元素,封裝成可重複使用的代碼
vue元件的基本組成(單檔案元件):Js,css,html 存在一個檔案中,是一個單檔案元件,下面vue模闆檔案裡的Hello.vue就是一個單檔案元件
<template>
<div class="hello">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: 'hello',
data () {
return {
msg: 'Hello Vue'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>
元件的引入方式一
首先定義兩個元件:Hello.vue和HelloWorld.vue
<template>
<div class="hello">
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: 'hello',
data () {
return {
msg: 'Hello Vue'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>
<template>
<div class="helloWorld">
{{ helloWorld }}
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
}
}
</script>
<style scoped></style>
然後再父元件中引入這兩個子元件,引入的時候需要注意标簽名的寫法
<template>
<div id="app">
{{ message }}
<hello></hello>
<!-- 如果元件名是這樣的形式:AaaBbb,那麼在标簽這裡應該這樣寫:aaa-bbb的格式 -->
<hello-world></hello-world>
</div>
</template>
<script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome'
}
}
}
</script>
<style>
</style>
元件引入方式二(動态元件)
這種引入方式的好處就是可以動态的引入元件,就是将元件作為一個變量,下面示例中如果comToRender這個變量的值發生改變那麼引入的元件就發生改變
<keep-alive></keep-alive>标簽,這個标簽的作用會将子元件緩存起來,比如兩個元件之間互相切換,當a元件,切換到b元件的時候,a元件會被緩存起來,切換回a元件的時候,b元件緩存起來
下面來做一個示例,點選切換按鈕後,切換引入的元件
<template>
<div id="app">
<button v-on:click="change">切換</button>
<!-- 引入元件還有下面這種方式:這表示div标簽就是元件Hello -->
<keep-alive><div v-bind:is="comToRender"></div></keep-alive>
</div>
</template>
<script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome',
comToRender: 'hello'
}
},
methods: {
change () {
if (this.comToRender === 'hello') {
this.comToRender = 'hello-world'
} else {
this.comToRender = 'hello'
}
}
}
}
</script>
<style>
</style>
測試,點選切換的時候即可實作兩個元件之間切換
元件之間進行通信
元件間通信基本原則——不要在子元件中直接修改父元件的狀态資料,資料在哪, 更新資料的行為(函數)就應該定義在哪
父子元件之間是需要進行通信的,比如select清單,父元件需要告訴子元件有哪些選項,子元件也需要向父元件傳遞,比如子元件選擇了哪個選項,需要告訴父元件
prop 是父元件用來傳遞資料的一個自定義屬性。父元件的資料需要通過 props 把資料傳給子元件,子元件需要顯式地用 props 選項聲明 "prop":
父子元件通信示意圖
元件之間的通信示例一父元件向子元件傳遞資料:prop 是單向綁定的:當父元件的屬性變化時,将傳導給子元件,但是不會反過來
父元件中傳遞資料的方法有幾種
直接将一個固定的值傳遞給子元件,下面是将一個字元串為5的一個值傳給了hello-world這個子元件
<hello-world number-size="5"></hello-world>
如果是動态引入的元件,寫法也一樣,他會将這個值傳遞給每個子元件(引入那個子元件就傳給那個子元件)
<keep-alive><div v-bind:is="comToRender" number-size="5"></div></keep-alive>
使用動态綁定的方式将父元件的某個變量值傳遞給子元件,比如下面将父元件中的number這個變量傳遞給子元件使用v-bind動态綁定的方式
<hello-world v-bind:number-size="number"></hello-world>
<keep-alive><div v-bind:is="comToRender" v-bind:number-size="number"></div></keep-alive>
在傳遞資料的時候也能規定傳遞的資料類型,下面表示傳遞的是一個Number類型的資料,子元件接收的時候也要規定有Number類型,否則報錯
<keep-alive><div v-bind:is="comToRender" v-bind:number-size.number="number"></div></keep-alive>
子元件接收父元件傳遞過來的資料
需要知道的是,子元件接收到資料之後就相當于data裡面有了這個變量,可以在任意地方進行使用這個變量
首先需要将父元件傳遞過來的資料進行注冊,使用props進行注冊,props接收兩種類型的值,一種是數組,一種是對象,對象可以做的事情比較多
首先來看接收數組的形式
props: ['number-size']
<template>
<div class="helloWorld">
{{ helloWorld }}
<p>{{ numberSize }}</p>
</div>
</template>
<script type="text/ecmascript-6">
export default {
props: ['number-size'],
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
}
}
</script>
<style scoped></style>
測試結果是可以将numberSize列印出來的
如果使用對象的方式去接收資料,可以規定接收的資料類型,比如下面是Number,如果子元件規定了隻接收Number,那麼父元件隻能傳遞Number類型的資料給子元件,否則會報錯
props: {
'number-size': Number
}
當然可以接收多種資料類型,下面表示可以接受Number和String類型的資料,父元件就要傳遞這兩種資料類型的資料,否則就報錯了
props: {
'number-size': [Number, String]
}
元件之間的通信—子元件向外傳遞事件(自定義事件)
父元件是使用 props 傳遞資料給子元件,但如果子元件要把資料傳遞回去,就需要使用自定義事件!
我們可以使用 v-on 綁定自定義事件, 每個 Vue 執行個體都實作了事件接口(Events interface),即:
使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件
另外,父元件可以在使用子元件的地方直接用 v-on 來監聽子元件觸發的事件。
首先子元件定義一個事件,這個事件向父元件送出子元件的資料
<template>
<div class="helloWorld">
<p>{{ helloWorld }}</p>
<button v-on:click="emitMyEvent">送出</button>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'HelloWorld',
data () {
return {
helloWorld: 'Hello World'
}
},
methods: {
emitMyEvent () {
this.$emit('my-event', this.helloWorld)
}
}
}
</script>
<style scoped></style>
父元件中接收子元件傳遞過來的資料
<template>
<div id="app">
<button v-on:click="change">切換</button>
<keep-alive><div v-bind:is="comToRender" v-on:my-event="getMyEvent"></div></keep-alive>
</div>
</template>
<script>
import Hello from './components/Hello'
import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld,
Hello
},
name: 'app',
data () {
return {
message: 'Welcome',
comToRender: 'hello',
number: 3
}
},
methods: {
change () {
if (this.comToRender === 'hello') {
this.comToRender = 'hello-world'
} else {
this.comToRender = 'hello'
}
},
getMyEvent (param) {
console.log(param)
}
}
}
</script>
<style>
</style>
使用 Slot 分發内容
使用 Slot 分發内容也是一種元件之間通信的方式,此方式用于父元件向子元件傳遞`标簽資料`
在父元件中的的模闆标簽内可以插入元素,子元件中使用slot接收父元件插入的元素,這個功能的使用場景,比如這個子元件是一個footer或者header,這樣就可以很友善的在父元件中向子元件插入元素
<template>
<div>
<slot name="xxx">不确定的标簽結構 1</slot>
<div>元件确定的标簽結構</div>
<slot name="yyy">不确定的标簽結構 2</slot>
</div> </template>
<script type="text/ecmascript-6">
export default {
name: 'child'
}
</script>
<style scoped>
</style>
<template>
<child>
<div slot="xxx">xxx 對應的标簽結構</div>
<div slot="yyy"><h1>yyyy 對應的标簽結構</h1></div>
</child>
</template>
<script>
import Child from './child'
export default {
name: 'prent',
components: {
Child
}
}
</script>
通過slot的屬性name來辨別父元件的元素插入到哪個slot,當父元件中沒有對應的slot元素,那麼就是顯示子元件中的slot中的内容
當父元件中沒有插入元素的時候,就會顯示子元件中的slot裡面的内容, 如下父元件中solt為yyy的沒有寫上,那麼就顯示子元件中的内容
<template>
<child>
<div style="background-color: red" @click="sss" slot="xxx">xxx 對應的标簽結構</div>
</child>
</template>
<script>
import Child from './child'
export default {
name: 'prent',
components: {
Child
},
methods: {
sss () {
alert('sss')
}
}
}
</script>
<style scoped>
</style>
可以看到父元件對應的solt中的事件和樣式都是有效的
元件之間的通信:消息訂閱與釋出(PubSubJS 庫)
PubSub是一種設計模式,中文叫釋出訂閱模式,簡單來說就是消息釋出者不直接向訂閱者釋出消息,而是釋出到中介,而中介根據不同主題對消息進行過濾,并通知對該主題感興趣的訂閱者。該模式在前端現在很火的元件化開發十分常用,因為該模式松耦合,易于擴充的優點正式元件化開發所需要的。
此方式可實作任意關系元件間通信(資料),在發送資料的元件中定義釋出消息方法,在接收資料的元件中定義訂閱方法
兄弟元件之間的通信
<template>
<div id="header">
<div class="container">
<div class="row center">
<h1>Search Github Users</h1>
<input type="text" v-model="searchName">
<input type="button" value="Search" class="btn btn-primary" placeholder="請輸入github使用者名" @click="search">
</div>
</div>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Header',
data () {
return {
searchName: ''
}
},
methods: {
search () {
// 釋出消息
const searchName = this.searchName.trim()
if (searchName) {
PubSub.publish('search', searchName)
}
}
}
}
</script>
<style>
#header{
height: 200px;
}
.container .center{
text-align: center;
}
</style>
<template>
<div id="main">
{{searchName}}
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Main',
data () {
return {
searchName: ''
}
},
mounted () {
// 訂閱消息(一般定義在mounted周期函數中,當訂閱了該消息的元件釋出了消息,這邊就能接收到)
PubSub.subscribe('search', (msg, searchName) => { // 這裡使用箭頭函數,那麼函數體内的this就是外部的this(vue執行個體對象),如果不是箭頭函數那麼this就不是vue執行個體了
this.searchName = searchName
})
}
}
</script>
<template>
<div id="app">
<search-header></search-header>
<serach></serach>
</div>
</template>
<script>
import Header from './view/header'
import Serach from './view/serach'
export default {
name: 'app',
components: {
'search-header': Header, // 需要注意的是當自定義的元件名跟原生的h5标簽有沖突時,需要自定義一個别的标簽名
Serach
},
data () {
return {}
}
}
</script>
父子元件之間的通信
<template>
<div id="app">
<search-header></search-header>
<p>{{searchName}}</p>
</div>
</template>
<script>
import Header from './view/header'
import PubSub from 'pubsub-js'
export default {
name: 'app',
components: {
'search-header': Header // 需要注意的是當自定義的元件名跟原生的h5标簽有沖突時,需要自定義一個别的标簽名
},
data () {
return {
searchName: ''
}
},
mounted () {
// 訂閱消息(一般定義在mounted周期函數中,當訂閱了該消息的元件釋出了消息,這邊就能接收到)
PubSub.subscribe('search', (msg, searchName) => { // 這裡使用箭頭函數,那麼函數體内的this就是外部的this(vue執行個體對象),如果不是箭頭函數那麼this就不是vue執行個體了
this.searchName = searchName
})
}
}
</script>
<template>
<div id="header">
<div class="container">
<div class="row center">
<h1>Search Github Users</h1>
<input type="text" v-model="searchName">
<input type="button" value="Search" class="btn btn-primary" placeholder="請輸入github使用者名" @click="search">
</div>
</div>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
export default {
name: 'Header',
data () {
return {
searchName: ''
}
},
methods: {
search () {
// 釋出消息
const searchName = this.searchName.trim()
if (searchName) {
PubSub.publish('search', searchName)
}
}
}
}
</script>
<style>
#header{
height: 200px;
}
.container .center{
text-align: center;
}
</style>
其他關系的元件之間通信也是一樣的寫法,釋出消息的地方發送消息,接收消息的地方訂閱消息(寫在mounted周期函數内)
轉載于:https://www.cnblogs.com/LO-ME/p/7419170.html