淺談組件增強

RoseannMcKe 7年前發布 | 12K 次閱讀 前端 Vue.js開發

淺談組件增強

11月的上海,褪去了一絲溫暖,夾帶著絲絲寒意。獨自一人走在街上,望著路邊的男男女女,不禁讓我想起了那個前任的她。因為有她,我才學會了做飯,學會照顧人,學會怎么謙讓,不過在生命的進程中,她不過是一個過客罷了,她的出現和離開,對我都沒有本質的改變。一陣寒風吹過,我再次抬頭,是啊,剛才的故事蠻好的,是時候來知乎分享剛編的故事了,顯然,程序員并不會有前任。

細細想一想,其實生活中存在很多起修飾作用的東西,萬物本通,在編程界,也有許多的起修飾作用的東東,而且基于這個概念還發展出了許許多多的名稱。

裝飾函數

來首先看第一個裝飾函數,函數可以接受參數并且返回值。如果接受的值和返回的一樣呢?如果我們有一個構造函數:

function Person(name) {
  this.name =  name
}

Person.prototype.greet = function() {
  console.log('hello:' + this.name)
}

現在由于 Person 函數是被封裝的,不可以改變其源代碼,而又想在先增加一個輸出時間的方法,再調用原輸出。就可以簡單采用如下方式:

const cloudic = new Person('cloudic')
cloudic.greet() // => hello:cloudic

// 函數直接包裝
function sayDate(f, ...args) {
 console.log('show date')
 typeOf f === 'funciton' && f(...args)
}
cloudic.dateGreet = sayDate(cloudic.greet)
cloudic.dateGreet() // => show date hello:cloudic

// 采用原型繼承包裝
function DateDecorator(man) {
  const newMan = Object.create(man)
  newMan.greet = function() {
     console.log('show date')
     man.greet()
  }
  return newMan
}
const newCloudic = DateDecorator(cloudic)
newCloudic.greet() // => show date hello:cloudic

這樣子類的更改并不會影響其他子類,當然第一種看起來更加簡單點,如果希望所做的改變能同步影響到子類,可以覆蓋原型對象上同名的方法,當然也可以用 decorator 語法糖。

為什么想動態增加個功能這樣繁瑣呢,JS 是動態語言,在運行的時候,可以方便的修改對象的屬性,但是由于函數存在作用域的限制,想要動態修改函數內部的變量很難實現。所以才繞著彎子,采用包裝的方式來實現。換個角度想下,如果一個對象或者函數內部提供了一個方法,該方法可以動態的修改函數內部的變量,那我們只需要調用該方法即可,這樣的模式暫且叫中間件模式吧,比如 redux 的中間件。

高階組件

說回組件層面,如果把上面說的應用到組件上,那么就是傳入一個組件返回另外一個組件。在 React 中,我們比較常用的叫 HOC 模式,常見的實現方式如下:

function HOC(WrappedComponent) {
  return class Wrap extends React.Component {
    render() {
      return <WrappedComponent {...this.props}/>
    }
  }
}

在使用的時候,只需要傳入需要包裝的組件,調用函數后會返回一個包裝過的組件。這樣的好處是通過避免直接調用原組件,采用類似代理的方式來間接調用,從而可以方便后期替換原組件,可以做適配器,同時相比于 mixin 的實現方式,高階組件更容易被調試,由于 mixin 是混入模式,導致在組件之間共享的代碼很隱晦,如果方法比較多的話,還可能會覆蓋現有的組件,所以 React 官方廢棄了 mixin 的模式,推崇高階組件來代替組件之間共享代碼。

抽象組件

在 Vue 中,如果想對一個組件或者 DOM 進行相關功能的增強,我們可以用下面幾種方式:

其一我們可以想到是采用指令的形式:

Vue.directive('name', {
  inserted: function (el) {
    // do something...
  }
})

指令可以方便的增加功能和復用,但是指令不能使用回調函數,不能傳 props,沒有事件,導致使用起來的靈活度不高。指令更多的是做 DOM 層面的工作,封裝一些方法來修飾元素或操作元素屬性。

其二我們可以用函數式組件:

Vue.component('name', {
  functional: true,
  render: function (createElement, context) {
    // ...do something
  },
  // Props 可選
  props: {
    // ...
  }
})

函數式組件不會被實例化,也就是沒有 data 和 this 上下文,也不能使用事件回調。可以簡單的認為就是一個函數而已。不過函數式組件的優點也在于此,并不會有額外的性能開銷,從而可以提高程序的性能。

其三就是采用抽象組件:

Vue.component('name', {
  abstract: true,
  render: function (createElement) {
    return this.$slots.default[0]
  }
})

抽象組件在 Vue 官網并沒有文檔介紹,因為這是一個內部組件定義的功能,不是很穩定,隨時可能會更改,并且不會通知到你。Vue 內置的組件比如 keep-alive, transition, component, slot 都是抽象組件,抽象組件沒有自己的 DOM 元素,只是簡單增加功能然后返回子元素。和純函數組件相比,它有自己的生命周期,會被實例化,內部有this,是一個真正的組件,所以可以用 emit 發放事件。

總結

在組件增強的各個方法中,每個都有優劣,至于到底選擇那個,還是需要使用者根據業務場景來決定。

 

 

來自:https://zhuanlan.zhihu.com/p/30818429

 

 本文由用戶 RoseannMcKe 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!