如何實現Vue組件化

組件化是一門藝術,如何用好組件化優雅的實現頁面,是每一個前端開發的必修課,最近學了vue組件化的相關課程,本文就對經常使用的vue組件化技術的進行總結整理。前端

父子組件通訊

父組件向子組件通訊

屬性prop

//父組件
<HelloWorld msg="Welcome to Your Vue.js App"/>

//子組件
props: { msg: String }
複製代碼

refs引用

//父組件 
<HelloWorld ref="hw"/> 
this.$refs.hw.xx
複製代碼
  • 當父組件爲自定義組件時,refs得到的是自定義組件實例
  • 當父組件爲HTML元素時,refs得到的時DOM元素

子組件向父組件通訊

//子組件
this.$emit('add', good)

//父組件
<Cart @add="cartAdd($event)"></Cart>
複製代碼

兄弟組件:經過共同祖輩組件通訊

經過共同的祖輩組件搭橋,$parent$rootvue

//兄弟組件1
this.$parent.$on('foo', handle)

//兄弟組件2
this.$parent.$emit('foo')
複製代碼

祖先和後代之間

因爲嵌套層數過多,傳遞props不切實際,vue提供了provide / inject API完成該任務。vuex

provide / inject

  • provide:包含一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。
  • injecct:一個字符串數組,或一個對象,對象的 key 是本地的綁定名,value 是:在可用的注入內容中搜索用的 key (字符串或 Symbol),或一個對象,該對象的:from 屬性是在可用的注入內容中搜索用的 key (字符串或 Symbol)default 屬性是降級狀況下使用的 value。
//祖輩組件
<template>
  <div id="app">
    <router-view v-if="isRouterAlive" />
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      isRouterAlive: true
  },

// 父組件中返回要傳給下級的數據
  provide () {
    return {
      reload: this.reload
    }
  },
  methods: {
    reload () {
      this.isRouterAlive = false
      this.$nextTick(() => {
        this.isRouterAlive = true
      })
    }
  }
}
</script>
複製代碼
//孫組件
<template>
  <popup-assign
    :id="id"
    @success="successHandle"
  >
    <div class="confirm-d-tit"><span class="gray-small-btn">{{ name }}</span></div>
    <strong>將被分配給</strong>
    <a
      slot="reference"
      class="unite-btn"
    >
      指派
    </a>
  </popup-assign>
</template>
<script>
import PopupAssign from '../PopupAssign'
export default {
//引用vue reload方法
  inject: ['reload'],
  components: {
    PopupAssign
  },
methods: {
    async successHandle () {
      this.reload()
    }
  }
}
</script>
複製代碼

provide / inject 的優勢:數組

  • 祖先組件不須要知道哪些後代組件使用它提供的屬性
  • 後代組件不須要知道被注入的屬性來自哪裏

provide / inject 的缺點:bash

  • provide 和 inject 綁定並非可響應的。然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。
  • provide 和 inject只能實現從祖輩組件向孫組件傳值。

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

dispatch:後代給祖先傳值

// 定義一個dispatch方法,指定要派發事件名稱和數據 
function dispatch(eventName, data) { 
  let parent = this.$parent 
  // 只要還存在父元素就繼續往上查找 
  while (parent) { 
    // 父元素用$emit觸發
    parent.$emit(eventName,data) 
    // 遞歸查找父元素
    parent = parent.$parent 
  } 
}

// 使用,HelloWorld.vue 
<h1 @click="dispatch('hello', 'hello,world')">{{ msg }}</h1> 

// App.vue 
this.$on('hello', this.sayHello)
複製代碼

任意兩個組件之間:事件總線 或 vuex

事件總線

建立一個Bus類負責事件派發、監聽和回調管理async

// Bus:事件派發、監聽和回調管理
class Bus{ 
  constructor(){ 
    this.callbacks = {} 
  }
  $on(name, fn){ 
    this.callbacks[name] = this.callbacks[name] || [] 
    this.callbacks[name].push(fn) 
  }
  $emit(name, args){ 
    if(this.callbacks[name]){ 
      this.callbacks[name].forEach(cb => cb(args))
    } 
  }
}

// main.js 
Vue.prototype.$bus = new Bus() // child1 
this.$bus.$on('foo', handle) // child2 
this.$bus.$emit('foo')
複製代碼

vuex

建立惟一的全局數據管理者store,經過它管理數據並通知組件狀態變動ide

插槽

插槽語法是Vue實現的內容分發API,用於複合組件開發,該技術在通用組件庫開發中有大量應用。函數

Vue 2.6.0以後採用全新v-slot語法取代以前的slot、slot-scope組件化

匿名插槽

// comp1 
<div>
  <slot></slot>
</div> 
// parent 
<comp>hello</comp>
複製代碼

具名插槽

// comp2 
<div>
  <slot></slot>
  <slot name="content"></slot>
</div> 

// parent 
<Comp2> 
  <!-- 默認插槽用default作參數 -->
  <template v-slot:default>具名插槽</template>
  <!-- 具名插槽用插槽名作參數 -->
  <template v-slot:content>內容...</template>
</Comp2>
複製代碼

做用域插槽

// comp3
<div>
  <slot :foo="foo"></slot> 
</div> 

// parent
<Comp3>
  <!-- 把v-slot的值指定爲做用域上下文對象 -->
  <template v-slot:default="ctx">
    來自子組件數據:{{ctx.foo}}
  </template>
</Comp3>
複製代碼

做用於插槽能夠實現組件和業務的剝離,適合應用於至少包含三級以上的組件層級,是一種優秀的組件化方案。

案例分析

有一個購物網站,網站裏有諸如 「猜你喜歡」,「每日特價」等商品列表,咱們能夠將這個頁面進行組件化拆分。

  • ColumnList:存放各種列表的組件。
  • CommodityList:展現各種商品信息的列表組件。
  • Commodity:商品信息組件。

當咱們想要實現點擊具體商品跳轉到相應的詳情頁面,並但願該點擊能在ColumnList組件中實現時,傳統作法,會將在Commodity的點擊事件$emit層層上拋至組件ColumnList,並在ColumnList中進行監聽。但這種方法,使得子組件與業務緊耦合,利用做用域插槽就能夠優雅的解決這個問題。

  • 在ColumnList實現點擊事件
  • 利用template的v-slot屬性賦值上下文對象
  • 在Commodity組件上綁定點擊事件

組件化技巧

屬性綁定

//父組件
<my-input type="text" autocomplete placeholder="Please input something"></my-input>

//子組件
<div>
  <input v-bind="$attrs" />
</div>

export default {
  inheritAttrs: false
}

//渲染結果
<div>
  <input type="text" autocomplete placeholder="Please input something" />
</div>
複製代碼
  • inheritAttrs:默認狀況下父做用域的不被認做 props 的特性綁定 (attribute bindings) 將會「回退」且做爲普通的 HTML 特性應用在子組件的根元素上。經過設置 inheritAttrs 到 false,這些默認行爲將會被去掉。
  • $attrs:包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件——在建立高級別的組件時很是有用。

組件實例建立函數

針對例如彈窗等短暫出現的組件,能夠經過一個實例建立函數進行調用。 優勢:

  • 當組件出現時掛載,消失時卸載
  • 不須要進行註冊
  • 避免內存泄漏
import Vue from 'vue';

export default function create(component, props) {
  const vm = new Vue({
    render(h) {
      return h(component, {props});
    }
  }).$mount();

  //vm.$el爲Vue實例使用的根DOM元素
  document.body.appendChild(vm.$el);

  //vm.$children爲當前實例的直接子組件
  const comp = vm.$children[0];
  comp.remove = function() {
    document.body.removeChild(vm.$el);
    vm.$destroy();
  }
  return comp;
}
複製代碼
相關文章
相關標籤/搜索