vue自定義組件總結

組件系統是Vue.js其中一個重要的概念,它提供了一種抽象,讓我們可以使用獨立可復用的小組件來構建大型應用,任意類型的應用界面都可以抽象為一個組件樹。

一、四個核心組成

1、props 2、自定義事件 event 3、插槽及作用域插槽 slot 4、組件方法 method。
任何組件均離不開以上4點,我們在開發過程中,以這4點入手,封裝我們想要的組件。

以element-ui table組件為例

Table Attributes 指的是 props
props data 顯示的數據

Table Events 指的是 自定義事件
自定義事件 selection-change 當選擇項發生變化時會觸發該事件

Table Slot 插槽
append 插入至表格最后一行之后的內容,如果需要對表格的內容進行無限滾動操作,可能需要用到這個 slot。若表格有合計行,該 slot 會位于合計行之上。

Table-column Scoped Slot 插槽及作用域插槽 slot
Table-column 是 el-table-column組件,有一個默認插槽 自定義列的內容,參數為 { row, column, $index }

我們通常這樣使用

<el-table-column
    fixed="right"
    label="操作"
    width="100">
      <template slot-scope="scope">
        <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
        <el-button type="text" size="small">編輯</el-button>
      </template>
</el-table-column>

slot-scope="scope" 父組件通過scope訪問子組件作用域。
Table Methods 指的是 組件方法
組件方法 clearSelection 用于多選表格,清空用戶的選擇 。 組件方法是通過 添加ref 索引,獲取組件實例后調用。this.$refs.組件ref標識.組件方法

以上是組件核心概念。

二、組件注冊

1、組件名
組件名應該始終是多個單詞的,根組件 App 除外

這樣做可以避免跟現有的以及未來的 HTML 元素相沖突,因為所有的 HTML 元素名稱都是單個單詞的。

單文件組件的文件名應該要么始終是單詞大寫開頭 (PascalCase),要么始終是橫線連接 (kebab-case)

混用文件命名方式有的時候會導致大小寫不敏感的文件系統的問題,這也是橫線連接命名同樣完全可取的原因

使用 kebab-case
當使用 kebab-case (短橫線分隔命名) 定義一個組件時,你也必須在引用這個自定義元素時使用 kebab-case,例如 <my-component-name>

Vue.component('my-component-name', { /* ... */ })

使用 PascalCase
當使用 PascalCase (駝峰式命名) 定義一個組件時,你在引用這個自定義元素時兩種命名法都可以使用。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。注意,盡管如此,直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的

Vue.component('MyComponentName', { /* ... */ })

2、全局注冊
以上方法都屬于全局注冊, 也就是說它們在注冊之后可以用在任何新創建的 Vue 根實例 (new Vue) 的模板中, 比如

HTML

<div id="app">
  <component-a></component-a>
  <component-b></component-b>
  <component-c></component-c>
</div>

JS

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
 
 
new Vue({ el: '#app' })

在所有子組件中也是如此,也就是說這三個組件在各自內部也都可以相互使用.

3、局部注冊

如果不需要全局注冊,或者是讓組件使用在其它組件內,可以用選項對象的 components 屬性實現局部注冊, 這里不做詳述。

三、父子組件通信

在vue組件通信中其中最常見通信方式就是父子組件之中的通信,而父子組件的設定方式在不同情況下又各有不同。最常見的就是父組件為控制組件子組件為視圖組件。父組件傳遞數據給子組件使用,遇到業務邏輯操作時子組件觸發父組件的自定義事件。無論哪種組織方式父子組件的通信方式都是大同小異

父組件到子組件通訊

父組件到子組件的通訊主要為:子組件接受使用父組件的數據,這里的數據包括屬性和方法(String, Number, Boolean, Object, Array, Function)。vue提倡單項數據流,因此在通常情況下都是父組件傳遞數據給子組件使用,子組件觸發父組件的事件,并傳遞給父組件所需要的參數

通過 props 傳遞數據 (推薦)

父子通訊中最常見的數據傳遞方式就是通過props傳遞數據,就好像方法的傳參一樣,父組件調用子組件并傳入數據,子組件接受到父組件傳遞的數據進行驗證使用

props 可以是數組或對象,用于接收來自父組件的數據。props 可以是簡單的數組,或者使用對象作為替代,對象允許配置高級選項,如類型檢測、自定義校驗和設置默認值

prop 的定義應該盡量詳細,至少需要指定其類型

<!-- 父組件 -->
<template>
    <div>
        <my-child :parentMessage="parentMessage"></my-child>
    </div>
</template>
 
<script>
    import MyChild from '@components/common/MyChild'
 
    export default {
        components: {
            MyChild
        },
        data() {
            return {
                parentMessage: "我是來自父組件的消息"
            }
        }
    }
</script>
 
<!-- 子組件 -->
<template>
    <div>
        <span>{{ parentMessage }}</span>
    </div>
</template>
 
<script>
    export default {
        props: {
            parentMessage: {
                type: String,
                default: '默認顯示的信息'
                // require: true // 必填
            }
        }
    }
</script>
子組件到父組件通訊

子組件到父組件的通訊主要為父組件如何接受子組件之中的數據。這里的數據包括屬性和方法(String, Number, Boolean, Object, Array, Function)

通過 $emit 傳遞父組件數據 (推薦)

與父組件到子組件通訊中的$on配套使用,可以向父組件中觸發的方法傳遞參數供父組件使用

<!-- 父組件 -->
<template>
    <div>
        <my-child @childEvent="parentMethod"></my-child>
    </div>
</template>
 
<script>
    import MyChild from '@components/common/MyChild'
 
    export default {
        components: {
            MyChild
        },
        data() {
            return {
                parentMessage: '我是來自父組件的消息'
            }
        },
        methods: {
            parentMethod({ name, age }) {
                console.log(this.parentMessage, name, age)
            }
        }
    }
</script>
 
 
<!-- 子組件 -->
<template>
    <div>
        <h3>子組件</h3>
    </div>
</template>
 
<script>
    export default {
        mounted() {
            this.$emit('childEvent', { name: 'zhangsan', age:  10 })
        }
    }
</script>
不推薦的通信方式

this.parent this.children
this.$refs

四、兄弟組件通信

1、vuex
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,它是響應式的,狀態發生變化,組件會更新。

vuex實現兄弟組件通信非常簡單,組件A引用vuex數據,組件B通過方法改變vuex數據,vuex狀態是響應式的,數據放生變化,組件A會更新。

2、eventBus又稱為事件總線
在vue中可以使用eventBus來作為溝通橋梁, 就像是所有組件共用相同的事件中心,可以向該中心注冊發送事件或接收事件, 所有組件都可以通知其他組件。

初始化

首先需要創建一個事件總線并將其導出, 以便其他模塊可以使用或者監聽它。

我們在src/components/目錄下新建文件bus.js。

import Vue from 'vue'
export default new Vue()

發送事件

假設你有兩個兄弟組件: ComA和ComB,ComA發送消息給ComB。

ComA這樣

<template>
    <div>
        <button @click="sendMsg">給組件B發送消息</button> 
    </div>
</template>
 
<script>
import bus from './bus'
 
export default {
    name: 'comA',
    data () {
        return {
             
        }
    },
     
    methods: {
        sendMsg() {
            bus.$emit('fromA', {
                phone: 13800138000
            })
        }
    }
}
</script>

很顯然,ComA中使用bus.$emit(事件名,數據);向事件中心注冊發送事件。

接收事件

ComB接受ComA發送過來的消息。

<template>
    <div>
        <p>{{fromA}}</p>
    </div>
</template>
 
<script>
import bus from './bus'
 
export default {
    name: 'comB',
    data () {
        return {
            fromA: '',
        }
    },
    mounted() {
        bus.$on('fromA', param => {
            this.fromA = param.phone;
        })
    }
}
</script>

于是,當ComA發送了一個手機號碼phone給ComB時,comB就會接收并顯示。

父組件

在父組件中調用ComA和ComB兩個兄弟組件。

<template>
  <div>
    <comA></comA>
    <comB></comB>
  </div>
</template>
 
<script>
import ComA from './ComA.vue'
import ComB from './ComB.vue'
 
export default {
    components: {
       ComA,
       ComB
    },
}
</script>

五、多層級組件通訊

1、vuex
2、eventBus
3、provide / inject

provide 和 inject 主要在開發高階插件/組件庫時使用。并不推薦用于普通應用程序代碼中。

這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在其上下游關系成立的時間里始終生效。

// 父級組件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}
 
// 子組件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

六、作用域插槽

有時讓插槽內容能夠訪問子組件中才有的數據是很有用的。例如,設想一個帶有如下模板的 <current-user> 組件:

<span>
  <slot>{{ user.lastName }}</slot>
</span>

我們可能想換掉備用內容,用名而非姓來顯示。如下:

<current-user>
  {{ user.firstName }}
</current-user>

然而上述代碼不會正常工作,因為只有 <current-user> 組件可以訪問到 user 而我們提供的內容是在父級渲染的。

為了讓 user 在父級的插槽內容中可用,我們可以將 user 作為 <slot> 元素的一個 attribute 綁定上去:

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

綁定在 <slot> 元素上的 attribute 被稱為插槽 prop。現在在父級作用域中,我們可以使用帶值的 v-slot 來定義我們提供的插槽 prop 的名字:

<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

在這個例子中,我們選擇將包含所有插槽 prop 的對象命名為 slotProps,但你也可以使用任意你喜歡的名字。

七、組件的方法

組件方法,就是我們寫組件時,在methods選項里邊定義的一些方法,他通常是對數據的CURD。
element-ui我們常用的有

this.refs[formName].resetFields(); form表單重置 this.refs.[treeName].getCheckedKeys() tree 返回目前被選中的節點的 key 所組成的數組

八、vue 語法糖

1、v-model
v-model可以實現數據雙向的綁定,自動為組件添加了props 名為 value 和 自定義事件 名為 input。

<input type="text" v-model="name">

實際上,上面的代碼是下面代碼的語法糖。

<input  v-bind:value="name"  v-on:input="name=$event.target.value"/>

自定義組件的 v-model

一個組件上的 v-model 默認會利用名為 value 的 prop 和名為 input 的事件,但是像單選框、復選框等類型的輸入控件可能會將 value attribute 用于不同的目的。model 選項可以用來避免這樣的沖突:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

現在在這個組件上使用 v-model 的時候:

<base-checkbox v-model="lovingVue"></base-checkbox>

這里的 lovingVue 的值將會傳入這個名為 checked 的 prop。同時當 <base-checkbox> 觸發一個 change 事件并附帶一個新的值的時候,這個 lovingVue 的 property 將會被更新。
2、.sync 修飾符
在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”。不幸的是,真正的雙向綁定會帶來維護上的問題,因為子組件可以變更父組件,且在父組件和子組件都沒有明顯的變更來源。

這也是為什么我們推薦以 update:myPropName 的模式觸發事件取而代之。舉個例子,在一個包含 title prop 的假設的組件中,我們可以用以下方法表達對其賦新值的意圖:

this.$emit('update:title', newTitle)

然后父組件可以監聽那個事件并根據需要更新一個本地的數據 property。例如:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

為了方便起見,我們為這種模式提供一個縮寫,即 .sync 修飾符:

<text-document v-bind:title.sync="doc.title"></text-document>

九、組件選項推薦順序

<script>
  export default {
    el: '#app', // 只在由 new 創建的實例中遵守。
     
    // 全局感知
    name: 'name', // 組件name
    parent: VueInstance, // 指定父實例
 
    // 組件類型
    functional: false, // 沒有data 實例 沒有上下文
 
    // 模板修改器
    delimiters: ['${', '}'], // 分隔符變成了 ES6 模板字符串的風格
    comments: false, // 當設為 true 時,將會保留且渲染模板中的 HTML 注釋。默認行為是舍棄它們。
 
    // 模板依賴
    components: {}, // 子組件
    directives: {}, // 自定義指令
    filters: {}, // 自定義過濾器
 
    // 組合
    extends: CompA, // 擴展另一個組件 和 mixins 類似
    mixins: [tableEvents], // 混入選項對象, 混入實例對象可以像正常的實例對象一樣
 
    // 接口
    inheritAttrs: true,
    model: { // 自定義組件在使用 v-model 時定制 prop 和 event
      prop: 'checked',
      event: 'change'
    },
    propsData: { // 只用于 new 創建的實例中。   創建實例時傳遞 props。
 
    },
 
    // 本地狀態
    data: () => ({ // 本地狀態
 
    }),
    computed: { // 計算屬性
 
    },
 
    // 事件 生命周期鉤子
    watch: {
 
    },
 
    // 生命周期鉤子
    beforeCreate() {
 
    },
    created() {
 
    },
    beforeMount() {
 
    },
    mounted() {
 
    },
    beforeUpdate() {
 
    },
    updated() {
 
    },
    activated() {
 
    },
    deactivated() {
 
    },
    beforeDestroy() {
 
    },
    destroyed() {
 
    },
 
    // 非響應式的屬性 (不依賴響應系統的實例屬性)
    methods() {
 
    },
 
    // 渲染 (組件輸出的聲明式描述)
    template: '<div>demo</div>', // 渲染模板
    render: function (createElement) {
      return createElement(
        'h' + this.level,   // 標簽名稱
        this.$slots.default // 子節點數組
      )
    },
    renderError (h, err) { // 只在開發者環境下工作。
      return h('pre', { style: { color: 'red' }}, err.stack)
    }
     
  }
</script>

十、組件樣式推薦使用CSS 作用域

<style scoped>
    
</style>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容