目錄
vue元件三要素
- props引數
- slot定製插槽
- event自定義事件
基本用法
在使用 vue-cli 建立的專案中,元件的建立非常方便,只需要新建一個 .vue 檔案,然後在 template 中寫好 HTML 程式碼,一個簡單的元件就完成了 一個完整的元件,除了 template 以外,還有 script和 style
<template>
<div class="headComponent">
子元件{{{myData}}
</div>
</template>
<script>
export default {
props:['data','type'],
inheritAttrs: false,
data(){
return{
myData:'',
}
},
mounted(){
},
methods:{
}
}
</script>
<style scoped>
</style>
複製程式碼
然後在其他檔案的 js 裡面引入並註冊,就能直接使用這個元件了
import list from '../components/headComponent.vue'
複製程式碼
一、props:資料父元件傳入子元件
父對子傳參,就需要用到 props,通常的 props 是這樣的:
props:['data','type']
複製程式碼
但是通用元件的的應用場景比較複雜,對 props 傳遞的引數應該新增一些驗證規則,常用格式如下:
props: {
// 基礎型別檢測 (`null` 意思是任何型別都可以)
propA: Number,
// 多種型別
propB: [String, Number],
// 必傳且是字串
propC: {
type: String,
required: true
},
// 數字,有預設值
propD: {
type: Number,
default: 100
},
// 陣列/物件的預設值應當由一個工廠函式返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函式
propF: {
validator: function (value) {
return value > 10
}
}
}
複製程式碼
對於通過 props 傳入的引數,不建議對其進行操作,因為會同時修改父元件裡面的資料 // vue2.5已經針對 props 做出優化,這個問題已經不存在了 如果一定需要有這樣的操作,可以這麼寫:
let copyData = JSON.parse(JSON.stringify(this.data))
複製程式碼
為什麼不直接寫 let myData = this.data 呢? 因為直接賦值,對於物件和陣列而言只是淺拷貝,指向的是同一個記憶體地址,其中一個改變另一個也會改變。而通過 JSON顛倒轉換之後,實現了深拷貝,則可以互不影響。
二、子元件觸發父元件事件
在通用元件中,通常會需要有各種事件,比如核取方塊的 change 事件,或者元件中某個按鈕的 click 事件,有時子元件需要觸發一個事件,並傳遞給父元件
// 子元件方法:觸發父元件方法,並傳遞引數data到父元件
handleSubmit(data){
this.$emit('submitToParent', data)
}
複製程式碼
// 父元件呼叫子元件
<child-component @submitToParent="parentSubmit"></child-component>
... ...
// 父元件中被觸發的方法,接受到子元件傳來的引數
parentSubmit(data){
// 父元件的邏輯處理
}
複製程式碼
父元件中的邏輯要放在父元件處理,子元件基於父元件的資料做的邏輯放在子元件中處理; 這樣既降低了耦合性,也保證了各自的資料不被汙染。
三、記得留一個 slot
一個通用元件,往往不能夠完美的適應所有應用場景 所以在封裝元件的時候,只需要完成元件 80% 的功能,剩下的 20% 讓父元件通過 solt 解決
上面是一個通用元件,在某些場景中,右側的按鈕是 “處理” 和 “委託”。在另外的場景中,按鈕需要換成 “檢視” 或者 “刪除” 在封裝元件的時候,就不用寫按鈕,只需要在合適的位置留一個 slot,將按鈕的位置留出來,然後在父元件寫入按鈕
子元件
<div class="child-btn">
<!-- 具名插槽 -->
<slot name="button"></slot>
<!-- 匿名插槽(每個元件只能有一個) -->
<slot><slot>
</div>
父元件
<child>
<!-- 對應子元件中button的插槽 -->
<button slot="button">slot按鈕</button>
</child>
複製程式碼
開發通用元件的時候,只要不是獨立性很高的元件,建議都留一個 slot,即使還沒想好用來幹什麼。
再來個例子:
開發過程中,常常需要在子元件內新增新的內容,這時候可以在子元件內部留一個或者多個插口
然後在呼叫這個子元件的時候加入內容
新增的內容就會分發到對應的 slot 中
slot 中還可以作為一個作用域,在子元件中定義變數,然後在父元件中自定義渲染的方式 子元件:
父元件:這個示例中,首先在子元件中新增 slot,並在子元件中定義了陣列變數 navs 然後在父元件中以作用域 template 新增內容,其中 scope 是固有屬性,它的值對應一個臨時變數 props 而 props 將接收從父元件傳遞給子元件的引數 navs
關於slot的用法還可檢視:juejin.im/post/5cc856…
四、Vuex:巧用但不要濫用
子元件向子元件傳遞資料
Vue 沒有直接子對子傳參的方法,建議將需要傳遞資料的子元件,都合併為一個元件。如果一定需要子對子傳參,可以先從傳到父元件,再傳到子元件。 父子元件之間是通過 props 和 自定義事件來傳參,非父子元件通常會採用 Vuex 傳參 Vuex設計初衷是用來管理元件狀態,也可以用於複雜元件的引數傳遞
但是 Vuex 的雖然可以用來傳參,但並不推薦;因為 Vuex 類似於一個全域性變數,會一直佔用記憶體 在寫入資料龐大的 state 的時候,就會產生記憶體洩露(人是活的,不能因為事物有弊端就不使用,合理利用即可)。 Tips: 當頁面重新整理的時候,Vuex 會初始化,同時也會丟失編輯過的資料 如果重新整理頁面時需要保留資料,可以通過 url 或者 sessionstorage 儲存 id,然後 ajax 請求資料
五、元件的css:合理運用 scoped 控制樣式作用域
在編寫元件的時候,可以在 style 標籤中新增 scoped,讓標籤中的樣式只對當前元件生效 但是一味的使用 scoped,肯定會產生大量的重複程式碼 所以在開發的時候,應該避免在元件中寫樣式 當全域性樣式寫好之後,再針對每個元件,通過 scoped 屬性新增元件樣式
六、動態元件
Vue 還可以將多個子元件,都掛載在同一個位置,通過變數來切換元件,實現 tab 選單這樣的效果
這樣的功能可以通過路由 vue-router 實現,但路由更適合較大的元件,而且 url 會有相應的改變 Vue 自身保留的 元素,可以將元件動態繫結到 is 特性上,從而很方便的實現動態元件切換 上例中,當 tabView 的值改變,component 就會渲染對應的元件,和路由的效果十分類似,但是位址列並沒有發生改變,但這樣一來,每次切換元件都會重新渲染,無法保留元件上的資料。這時可以使用 ** keep-alive ** 將元件保留在記憶體中,避免重新渲染七、遞迴元件
當元件擁有 name 屬性的時候,就可以在它的模板內遞迴的呼叫自己,這在開發樹形元件的時候十分有效
上面是一個子元件,定義了 name 為 simple03,然後在模板中呼叫自身,結合 v-for 實現遞迴 為了防止出現死迴圈,在呼叫自身的時候,加入了 v-if 作為判定條件 父元件中呼叫的時候,需要通過 props 傳入一個 tree:最終渲染結果:
其他情況
子元件改變父元件的資料
在vue2.0之後的版本中,不允許子元件直接改變父元件的資料,在1.0的版本中可以這樣操作的,但是往往專案需求需要改變父元件的資料,那我們如何變通呢? 當我們把父元素的資料給子元件時,要傳一個非基礎型別,即傳遞物件or陣列,子元件通過訪問物件中的屬性運算元據,因為物件和陣列是傳引用,所以在子元件中修改的時候,父元件也會同步改變,如下:
// 父元件要props傳遞給子元件的資料
myData:{
info:'父元件資訊'
}
// 子元件
<template id="tpl">
<div>
<button @click="change">change</button>
<p>{{data.info}}</p>
</div>
</template>
... 省略部分無關程式碼 ...
props:['data'],
methods:{
change(){
this.data.info = 'change info'
}
}
複製程式碼
當子元件點選change按鈕改變資料的時候,父元件也會同步改變
如果不想影響父元件的值,我們也可以用文章開頭講的JSON顛倒轉換的方式,在mounted方法中進行深拷貝,在data中初始化資料時用另一個變數作為承載,這樣才承載變數改變,就不會影響父元件該變數的值了。
參考部落格: www.cnblogs.com/wisewrong/p… www.cnblogs.com/yesyes/p/66…