溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Vue高階組件怎么用

發(fā)布時(shí)間:2021-08-10 10:30:53 來(lái)源:億速云 閱讀:195 作者:小新 欄目:web開發(fā)

小編給大家分享一下Vue高階組件怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

高階組件( HOC )是 React 生態(tài)系統(tǒng)的常用詞匯, React 中代碼復(fù)用的主要方式就是使用高階組件,并且這也是官方推薦的做法。而 Vue 中復(fù)用代碼的主要方式是使用 mixins ,并且在 Vue 中很少提到高階組件的概念,這是因?yàn)樵?Vue 中實(shí)現(xiàn)高階組件并不像 React 中那樣簡(jiǎn)單,原因在于 ReactVue 的設(shè)計(jì)思想不同,但并不是說(shuō)在 Vue 中就不能使用高階組件,只不過(guò)在 Vue 中使用高階組件所帶來(lái)的收益相對(duì)于 mixins 并沒(méi)有質(zhì)的變化。

從 React 說(shuō)起

起初 React 也是使用 mixins 來(lái)完成代碼復(fù)用的,比如為了避免組件不必要的重復(fù)渲染我們可以在組件中混入 PureRenderMixin

const PureRenderMixin = require('react-addons-pure-render-mixin')
const MyComponent = React.createClass({
 mixins: [PureRenderMixin]
})

后來(lái) React 拋棄了這種方式,進(jìn)而使用 shallowCompare

const shallowCompare = require('react-addons-shallow-compare')
const Button = React.createClass({
 shouldComponentUpdate: function(nextProps, nextState) {
 return shallowCompare(this, nextProps, nextState);
 }
})

這需要你自己在組件中實(shí)現(xiàn) shouldComponentUpdate 方法,只不過(guò)這個(gè)方法具體的工作由 shallowCompare 幫你完成,即淺比較。

再后來(lái) React 為了避免開發(fā)者在組件中總是要寫這樣一段同樣的代碼,進(jìn)而推薦使用 React.PureComponent ,總之 React 在一步步的脫離 mixins ,他們認(rèn)為 mixinsReact 生態(tài)系統(tǒng)中并不是一種好的模式(注意:并沒(méi)有說(shuō) mixins 不好,僅僅針對(duì) React 生態(tài)系統(tǒng)),觀點(diǎn)如下:

1、 mixins 帶來(lái)了隱式依賴

2、 mixinsmixins 之間, mixins 與組件之間容易導(dǎo)致命名沖突

3、由于 mixins 是侵入式的,它改變了原組件,所以修改 mixins 等于修改原組件,隨著需求的增長(zhǎng) mixins 將變得復(fù)雜,導(dǎo)致滾雪球的復(fù)雜性。

具體大家可以查看這篇文章 Mixins Considered Harmful 。不過(guò) HOC 也并不是銀彈,它自然帶來(lái)了它的問(wèn)題,其觀點(diǎn)是: 使用普通組件配合 render prop 可以做任何 HOC 能做的事情 。

本篇文章不會(huì)過(guò)多討論 mixinsHOC 誰(shuí)好誰(shuí)壞,就像技術(shù)本身就沒(méi)有好壞之分,只有適合不適合。

ok ,我們回到高階組件,所謂高階組件其實(shí)就是高階函數(shù)啦, ReactVue 都證明了一件事兒: 一個(gè)函數(shù)就是一個(gè)組件 。所以組件是函數(shù)這個(gè)命題成立了,那高階組件很自然的就是高階函數(shù),即一個(gè)返回函數(shù)的函數(shù),我們知道在 React 中寫高階組件就是在寫高階函數(shù),很簡(jiǎn)單,那是不是在 Vue 中實(shí)現(xiàn)高階組件也同樣簡(jiǎn)單呢?其實(shí) Vue 稍微復(fù)雜,甚至需要你對(duì) Vue 足夠了解,接下來(lái)就讓我們一塊在 Vue 中實(shí)現(xiàn)高階組件,在文章的后面會(huì)分析為什么同樣都是 函數(shù)就是組件 的思想, Vue 卻不能像 React 那樣輕松的實(shí)現(xiàn)高階組件。

也正因如此所以我們有必要在實(shí)現(xiàn) Vue 高階組件之前充分了解 React 中的高階組件,看下面的 React 代碼:

function WithConsole (WrappedComponent) {
 return class extends React.Component {
 componentDidMount () {
  console.log('with console: componentDidMount')
 }
 render () {
  return <WrappedComponent {...this.props}/>
 }
 }
}

WithConsole 就是一個(gè)高階組件,它有以下幾個(gè)特點(diǎn):

1、高階組件( HOC )應(yīng)該是無(wú)副作用的純函數(shù),且不應(yīng)該修改原組件

可以看到 WithConsole 就是一個(gè)純函數(shù),它接收一個(gè)組件作為參數(shù)并返回了一個(gè)新的組件,在新組件的 render 函數(shù)中僅僅渲染了被包裝的組件( WrappedComponent ),并沒(méi)有侵入式的修改它。

2、高階組件( HOC )不關(guān)心你傳遞的數(shù)據(jù)( props )是什么,并且被包裝組件( WrappedComponent )不關(guān)心數(shù)據(jù)來(lái)源

這是保證高階組件與被包裝組件能夠完美配合的根本

3、高階組件( HOC )接收到的 props 應(yīng)該透?jìng)鹘o被包裝組件( WrappedComponent )

高階組件完全可以添加、刪除、修改 props ,但是除此之外,要將其余 props 透?jìng)鳎駝t在層級(jí)較深的嵌套關(guān)系中( 這是高階組件的常見(jiàn)問(wèn)題 )將造成 props 阻塞。

以上是 React 中高階組件的基本約定,除此之外還要注意其他問(wèn)題,如:高階組件( HOC )不應(yīng)該在 render 函數(shù)中創(chuàng)建;高階組件( HOC )也需要復(fù)制組件中的靜態(tài)方法;高階組件( HOC )中的 ref 引用的是最外層的容器組件而不是被包裝組件( WrappedComponent ) 等等。

Vue 中的高階組件

了解了這些,接下來(lái)我們就可以開始著手實(shí)現(xiàn) Vue 高階組件了,為了讓大家有一個(gè)直觀的感受,我仍然會(huì)使用 ReactVue 進(jìn)行對(duì)比的講解。首先是一個(gè)基本的 Vue 組件,我們常稱其為被包裝組件( WrappedComponent ),假設(shè)我們的組件叫做 BaseComponent

base-component.vue

<template>
 <div>
 <span @click="handleClick">props: {{test}}</span>
 </div>
</template>

<script>
export default {
 name: 'BaseComponent',
 props: {
 test: Number
 },
 methods: {
 handleClick () {
  this.$emit('customize-click')
 }
 }
}
</script>

我們觀察一個(gè) Vue 組件主要觀察三點(diǎn): props 、 event 以及 slots 。對(duì)于 BaseComponent 組件而言,它接收一個(gè)數(shù)字類型的 propstest ,并發(fā)射一個(gè)自定義事件,事件的名稱是: customize-click ,沒(méi)有 slots 。我們會(huì)這樣使用該組件:

<base-component @customize-click="handleCustClick" :test="100" />

現(xiàn)在我們需要 base-component 組件每次掛載完成的時(shí)候都打印一句話: I have already mounted ,同時(shí)這也許是很多組件的需求,所以按照 mixins 的方式,我們可以這樣做,首先定義個(gè) mixins

export default {
 name: 'BaseComponent',
 props: {
 test: Number
 },
 mixins: [ consoleMixin ]
 methods: {
 handleClick () {
  this.$emit('customize-click')
 }
 }
}

然后在 BaseComponent 組件中將 consoleMixin 混入:

export default {
 name: 'BaseComponent',
 props: {
 test: Number
 },
 mixins: [ consoleMixin ]
 methods: {
 handleClick () {
  this.$emit('customize-click')
 }
 }
}

這樣使用 BaseComponent 組件的時(shí)候,每次掛載完成之后都會(huì)打印一句 I have already mounted ,不過(guò)現(xiàn)在我們要使用高階組件的方式實(shí)現(xiàn)同樣的功能,回憶高階組件的定義: 接收一個(gè)組件作為參數(shù),返回一個(gè)新的組件 ,那么此時(shí)我們需要思考的是,在 Vue 中組件是什么?有的同學(xué)可能會(huì)有疑問(wèn),難道不是函數(shù)嗎?對(duì), Vue 中組件是函數(shù)沒(méi)有問(wèn)題,不過(guò)那是最終結(jié)果,比如我們?cè)趩挝募M件中的組件定義其實(shí)就是一個(gè)普通的選項(xiàng)對(duì)象,如下:

export default {
 name: 'BaseComponent',
 props: {...},
 mixins: [...]
 methods: {...}
}

這不就是一個(gè)純對(duì)象嗎?所以當(dāng)我們從單文件中導(dǎo)入一個(gè)組件的時(shí)候:

import BaseComponent from './base-component.vue'
console.log(BaseComponent)

思考一下,這里的 BaseComponent 是什么?它是函數(shù)嗎?不是,雖然單文件組件會(huì)被 vue-loader 處理,但處理后的結(jié)果,也就是我們這里的 BaseComponent 仍然還是一個(gè)普通的 JSON 對(duì)象,只不過(guò)當(dāng)你把這個(gè)對(duì)象注冊(cè)為組件( components 選項(xiàng))之后, Vue 最終會(huì)以該對(duì)象為參數(shù)創(chuàng)建一個(gè)構(gòu)造函數(shù),該構(gòu)造函數(shù)就是生產(chǎn)組件實(shí)例的構(gòu)造函數(shù),所以在 Vue 中組件確實(shí)是函數(shù),只不過(guò)那是最終結(jié)果罷了,在這之前我們完全可以說(shuō)在 Vue 中組件也可以是一個(gè)普通對(duì)象,就像單文件組件中所導(dǎo)出的對(duì)象一樣。

基于此,我們知道在 Vue 中一個(gè)組件可以以純對(duì)象的形式存在,所以 Vue 中的高階組件可以這樣定義: 接收一個(gè)純對(duì)象,并返回一個(gè)新的純對(duì)象 ,如下代碼:

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 template: '<wrapped v-on="$listeners" v-bind="$attrs"/>',
 components: {
  wrapped: WrappedComponent
 },
 mounted () {
  console.log('I have already mounted')
 }
 }
}

WithConsole 就是一個(gè)高階組件,它接收一個(gè)組件作為參數(shù): WrappedComponent ,并返回一個(gè)新的組件。在新的組件定義中,我們將 WrappedComponent 注冊(cè)為 wrapped 組件,并在 template 中將其渲染出來(lái),同時(shí)添加 mounted 鉤子,打印 I have already mounted 。

以上就完成了與 mixins 同樣的功能,不過(guò)這一次我們采用的是高階組件,所以是非侵入式的,我們沒(méi)有修改原組件( WrappedComponent ),而是在新組件中渲染了原組件,并且沒(méi)有對(duì)原組件做任何修改。并且這里大家要注意 $listeners$attrs

'<wrapped v-on="$listeners" v-bind="$attrs"/>'

這么做是必須的,這就等價(jià)于在 React 中透?jìng)?props

<WrappedComponent {...this.props}/>

否則在使用高階組件的時(shí)候,被包裝組件( WrappedComponent )接收不到 props事件 。

那這樣真的就完美解決問(wèn)題了嗎?不是的,首先 template 選項(xiàng)只有在完整版的 Vue 中可以使用,在運(yùn)行時(shí)版本中是不能使用的,所以最起碼我們應(yīng)該使用渲染函數(shù)( render )替代模板( template ),如下:

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 render (h) {
  return h(WrappedComponent, {
  on: this.$listeners,
  attrs: this.$attrs,
  })
 }
 }
}

上面的代碼中,我們將模板改寫成了渲染函數(shù),看上去沒(méi)什么問(wèn)題,實(shí)則不然,上面的代碼中 WrappedComponent 組件依然收不到 props ,有的同學(xué)可能會(huì)問(wèn)了,我們不是已經(jīng)在 h 函數(shù)的第二個(gè)參數(shù)中將 attrs 傳遞過(guò)去了嗎,怎么還收不到?當(dāng)然收不到, attrs 指的是那些沒(méi)有被聲明為 props 的屬性,所以在渲染函數(shù)中還需要添加 props 參數(shù):

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 render (h) {
  return h(WrappedComponent, {
  on: this.$listeners,
  attrs: this.$attrs,
  props: this.$props
  })
 }
 }
}

那這樣是不是可以了呢?依然不行,因?yàn)?this.$props 始終是空對(duì)象,這是因?yàn)檫@里的 this.$props 指的是高階組件接收到的 props ,而高階組件沒(méi)有聲明任何 props ,所以 this.$props 自然是空對(duì)象啦,那怎么辦呢?很簡(jiǎn)單只需要將高階組件的 props 設(shè)置與被包裝組件的 props 相同即可了:

hoc.js

export default function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 props: WrappedComponent.props,
 render (h) {
  return h(WrappedComponent, {
  on: this.$listeners,
  attrs: this.$attrs,
  props: this.$props
  })
 }
 }
}

現(xiàn)在才是一個(gè)稍微完整可用的高階組件。大家注意用詞: 稍微 ,納尼?都修改成這樣了還不行嗎?當(dāng)然,上面的高階組件能完成以下工作:

1、透?jìng)?props

2、透?jìng)鳑](méi)有被聲明為 props 的屬性

3、透?jìng)魇录?/p>

大家不覺(jué)得缺少點(diǎn)兒什么嗎?我們前面說(shuō)過(guò),一個(gè) Vue 組件的三個(gè)重要因素: props 、 事件 以及 slots ,前兩個(gè)都搞定了,但 slots 還不行。我們修改 BaseComponent 組件為其添加一個(gè)具名插槽和默認(rèn)插槽,如下:

base-component.vue

<template>
 <div>
  <span @click="handleClick">props: {{test}}</span>
  <slot name="slot1"/> <!-- 具名插槽 -->
  <p>===========</p>
  <slot/> <!-- 默認(rèn)插槽 -->
 </div>
</template>

<script>
export default {
 ...
}
</script>

然后我們寫下如下測(cè)試代碼:

<template>
 <div>
  <base-component>
   <h3 slot="slot1">BaseComponent slot</h3>
   <p>default slot</p>
  </base-component>
  <enhanced-com>
   <h3 slot="slot1">EnhancedComponent slot</h3>
   <p>default slot</p>
  </enhanced-com>
 </div>
</template>

<script>
 import BaseComponent from './base-component.vue'
 import hoc from './hoc.js'

 const EnhancedCom = hoc(BaseComponent)

 export default {
  components: {
   BaseComponent,
   EnhancedCom
  }
 }
</script>

渲染結(jié)果如下:

Vue高階組件怎么用

上圖中藍(lán)色框是 BaseComponent 組件渲染的內(nèi)容,是正常的。紅色框是高階組件渲染的內(nèi)容,可以發(fā)現(xiàn)無(wú)論是具名插槽還是默認(rèn)插槽全部丟失。其原因很簡(jiǎn)單,就是因?yàn)槲覀冊(cè)诟唠A組件中沒(méi)有將分發(fā)的插槽內(nèi)容透?jìng)鹘o被包裝組件( WrappedComponent ),所以我們嘗試著修改高階組件:

hoc.js

function WithConsole (WrappedComponent) {
 return {
  mounted () {
   console.log('I have already mounted')
  },
  props: WrappedComponent.props,
  render (h) {

   // 將 this.$slots 格式化為數(shù)組,因?yàn)?nbsp;h 函數(shù)第三個(gè)參數(shù)是子節(jié)點(diǎn),是一個(gè)數(shù)組
   const slots = Object.keys(this.$slots)
    .reduce((arr, key) => arr.concat(this.$slots[key]), [])

   return h(WrappedComponent, {
    on: this.$listeners,
    attrs: this.$attrs,
    props: this.$props
   }, slots) // 將 slots 作為 h 函數(shù)的第三個(gè)參數(shù)
  }
 }
}

好啦,大功告成刷新頁(yè)面,如下:

Vue高階組件怎么用

納尼:scream:?我們發(fā)現(xiàn),分發(fā)的內(nèi)容確實(shí)是渲染出來(lái)了,不過(guò)貌似順序不太對(duì)。。。。。。藍(lán)色框是正常的,在具名插槽與默認(rèn)插槽的中間是有分界線( =========== )的,而紅色框中所有的插槽全部渲染到了分界線( =========== )的下面,看上去貌似具名插槽也被作為默認(rèn)插槽處理了。這到底是怎么回事呢?

想弄清楚這個(gè)問(wèn)題,就回到了文章開始時(shí)我提到的一點(diǎn),即你需要對(duì) Vue 的實(shí)現(xiàn)原理有所了解才行,否則無(wú)解。接下來(lái)就從原理觸發(fā)講解如何解決這個(gè)問(wèn)題。這個(gè)問(wèn)題的根源在于: Vue 在處理具名插槽的時(shí)候會(huì)考慮作用域的因素 。不明白沒(méi)關(guān)系,我們一點(diǎn)點(diǎn)分析。

首先補(bǔ)充一個(gè)提示: Vue 會(huì)把模板( template )編譯成渲染函數(shù)( render ) ,比如如下模板:

<div>
 <h3 slot="slot1">BaseComponent slot</h3>
</div>

會(huì)被編譯成如下渲染函數(shù):

var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c("div", [ _c("h3", {  attrs: { slot: "slot1" },  slot: "slot1" }, [  _vm._v("BaseComponent slot") ]) ])}

想要查看一個(gè)組件的模板被編譯后的渲染函數(shù)很簡(jiǎn)單,只需要在訪問(wèn) this.$options.render 即可。觀察上面的渲染函數(shù)我們發(fā)現(xiàn)普通的 DOM 是通過(guò) _c 函數(shù)創(chuàng)建對(duì)應(yīng)的 VNode 的?,F(xiàn)在我們修改模板,模板中除了有普通 DOM 之外,還有組件,如下:

var render = function() {
 var _vm = this
 var _h = _vm.$createElement
 var _c = _vm._self._c || _h
 return _c("div", [
  _c("h3", {
   attrs: { slot: "slot1" },
   slot: "slot1"
  }, [
   _vm._v("BaseComponent slot")
  ])
 ])
}

那么生成的渲染函數(shù)( render )是這樣的:

<div>
 <base-component>
  <h3 slot="slot1">BaseComponent slot</h3>
  <p>default slot</p>
 </base-component>
</div>

我們發(fā)現(xiàn)無(wú)論是普通DOM還是組件,都是通過(guò) _c 函數(shù)創(chuàng)建其對(duì)應(yīng)的 VNode 的。其實(shí) _cVue 內(nèi)部就是 createElement 函數(shù)。 createElement 函數(shù)會(huì)自動(dòng)檢測(cè)第一個(gè)參數(shù)是不是普通DOM標(biāo)簽,如果不是普通DOM標(biāo)簽?zāi)敲?createElement 會(huì)將其視為組件,并且創(chuàng)建組件實(shí)例, 注意組件實(shí)例是這個(gè)時(shí)候才創(chuàng)建的 。但是創(chuàng)建組件實(shí)例的過(guò)程中就面臨一個(gè)問(wèn)題: 組件需要知道父級(jí)模板中是否傳遞了 slot 以及傳遞了多少,傳遞的是具名的還是不具名的等等 。那么子組件如何才能得知這些信息呢?很簡(jiǎn)單,假如組件的模板如下:

var render = function() {
 var _vm = this
 var _h = _vm.$createElement
 var _c = _vm._self._c || _h
 return _c(
  "div",
  [
   _c("base-component", [
    _c("h3", { attrs: { slot: "slot1" }, slot: "slot1" }, [
     _vm._v("BaseComponent slot")
    ]),
    _vm._v(" "),
    _c("p", [_vm._v("default slot")])
   ])
  ],
  1
 )
}

父組件的模板最終會(huì)生成父組件對(duì)應(yīng)的 VNode ,所以以上模板對(duì)應(yīng)的 VNode 全部由父組件所有,那么在創(chuàng)建子組件實(shí)例的時(shí)候能否通過(guò)獲取父組件的 VNode 進(jìn)而拿到 slot 的內(nèi)容呢?即通過(guò)父組件將下面這段模板對(duì)應(yīng)的 VNode 拿到:

<div>
 <base-component>
  <h3 slot="slot1">BaseComponent slot</h3>
  <p>default slot</p>
 </base-component>
</div>

如果能夠通過(guò)父級(jí)拿到這段模板對(duì)應(yīng)的 VNode ,那么子組件就知道要渲染哪些 slot 了,其實(shí) Vue 內(nèi)部就是這么干的,實(shí)際上你可以通過(guò)訪問(wèn)子組件的 this.$vnode 來(lái)獲取這段模板對(duì)應(yīng)的 VNode

Vue高階組件怎么用

其中 this.$vnode 并沒(méi)有寫進(jìn) Vue 的官方文檔。子組件拿到了需要渲染的 slot 之后進(jìn)入到了關(guān)鍵的一步,這一步就是導(dǎo)致高階組件中透?jìng)?slotBaseComponent 卻無(wú)法正確渲染的原因,看下圖:

Vue高階組件怎么用

這張圖與上一張圖相同,在子組件中打印 this.$vnode ,標(biāo)注中的 context 引用著 VNode 被創(chuàng)建時(shí)所在的組件實(shí)例,由于 this.$vnode 中引用的 VNode 對(duì)象是在父組件中被創(chuàng)建的,所以 this.$vnode 中的 context 引用著父實(shí)例。理論上圖中標(biāo)注的兩個(gè) context 應(yīng)該是相等的:

復(fù)制代碼 代碼如下:


console.log(this.$vnode.context === this.$vnode.componentOptions.children[0].context) // true

Vue 內(nèi)部做了一件很重要的事兒,即上面那個(gè)表達(dá)式必須成立,才能夠正確處理具名 slot ,否則即使 slot 具名也不會(huì)被考慮,而是被作為默認(rèn)插槽。這就是高階組件中不能正確渲染 slot 的原因。

那么為什么高階組件中上面的表達(dá)式就不成立了呢?那是因?yàn)橛捎诟唠A組件的引入,在原本的父組件與子組件之間插入了一個(gè)組件( 也就是高階組件 ),這導(dǎo)致在子組件中訪問(wèn)的 this.$vnode 已經(jīng)不是原來(lái)的父組件中的 VNode 片段了,而是高階組件的 VNode 片段,所以此時(shí) this.$vnode.context 引用的是高階組件,但是我們卻將 slot 透?jìng)鳎?slot 中的 VNodecontext 引用的還是原來(lái)的父組件實(shí)例,所以這就造成了以下表達(dá)式為假:

復(fù)制代碼 代碼如下:


console.log(this.$vnode.context === this.$vnode.componentOptions.children[0].context) // false

最終導(dǎo)致具名插槽被作為默認(rèn)插槽,從而渲染不正確。

而解決辦法也很簡(jiǎn)單,只需要手動(dòng)設(shè)置一下 slotVNodecontext 值為高階組件實(shí)例即可,修改高階組件如下:

hoc.js

function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 props: WrappedComponent.props,
 render (h) {
  const slots = Object.keys(this.$slots)
  .reduce((arr, key) => arr.concat(this.$slots[key]), [])
  // 手動(dòng)更正 context
  .map(vnode => {
   vnode.context = this._self
   return vnode
  })

  return h(WrappedComponent, {
  on: this.$listeners,
  props: this.$props,
  attrs: this.$attrs
  }, slots)
 }
 }
}

現(xiàn)在,都能夠正常渲染啦,如下圖:

Vue高階組件怎么用

這里的關(guān)鍵點(diǎn)除了你需要了解 Vue 處理 slot 的方式之外,你還要知道通過(guò)當(dāng)前實(shí)例 _self 屬性訪問(wèn)當(dāng)實(shí)例本身,而不是直接使用 this ,因?yàn)?this 是一個(gè)代理對(duì)象。

現(xiàn)在貌似看上去沒(méi)什么問(wèn)題了,不過(guò)我們還忘記了一件事兒,即 scopedSlots ,不過(guò) scopedSlotsslot 的實(shí)現(xiàn)機(jī)制不一樣,本質(zhì)上 scopedSlots 就是一個(gè)接收數(shù)據(jù)作為參數(shù)并渲染 VNode 的函數(shù),所以不存在 context 的概念,所以直接透?jìng)骷纯桑?/p>

hoc.js

function WithConsole (WrappedComponent) {
 return {
 mounted () {
  console.log('I have already mounted')
 },
 props: WrappedComponent.props,
 render (h) {
  const slots = Object.keys(this.$slots)
  .reduce((arr, key) => arr.concat(this.$slots[key]), [])
  .map(vnode => {
   vnode.context = this._self
   return vnode
  })

  return h(WrappedComponent, {
  on: this.$listeners,
  props: this.$props,
  // 透?jìng)?nbsp;scopedSlots
  scopedSlots: this.$scopedSlots,
  attrs: this.$attrs
  }, slots)
 }
 }
}

到現(xiàn)在為止,一個(gè)高階組件應(yīng)該具備的基本功能算是實(shí)現(xiàn)了,但這僅僅是個(gè)開始,要實(shí)現(xiàn)一個(gè)完整健壯的 Vue 高階組件,還要考慮很多內(nèi)容,比如:

函數(shù)式組件中要使用 render 函數(shù)的第二個(gè)參數(shù)代替 this 。

以上我們只討論了以純對(duì)象形式存在的 Vue 組件,然而除了純對(duì)象外還可以函數(shù)。

創(chuàng)建 render 函數(shù)的很多步驟都可以進(jìn)行封裝。

處理更多高階函數(shù)組件本身的選項(xiàng)( 而不僅僅是上面例子中的一個(gè)簡(jiǎn)單的生命周期鉤子 )

我覺(jué)得需要放上兩個(gè)關(guān)于高階組件的參考鏈接,供參考交流:

Discussion: Best way to create a HOC

https://github.com/jackmellis/vue-hoc

為什么在 Vue 中實(shí)現(xiàn)高階組件比較難

前面說(shuō)過(guò)要分析一下為什么在 Vue 中實(shí)現(xiàn)高階組件比較復(fù)雜而 React 比較簡(jiǎn)單。這主要是二者的設(shè)計(jì)思想和設(shè)計(jì)目標(biāo)不同,在 React 中寫組件就是在寫函數(shù),函數(shù)擁有的功能組件都有。而 Vue 更像是高度封裝的函數(shù),在更高的層面 Vue 能夠讓你輕松的完成一些事情,但與高度的封裝相對(duì)的就是損失一定的靈活,你需要按照一定規(guī)則才能使系統(tǒng)更好的運(yùn)行。

有句話說(shuō)的好:

會(huì)了不難,難了不會(huì)

復(fù)雜還是簡(jiǎn)單都是相對(duì)而言的,最后希望大家玩的轉(zhuǎn) Vue 也欣賞的了 React 。放上兩張我比較認(rèn)同的圖片供各位看官討論:

Vue高階組件怎么用

Vue高階組件怎么用

以上是“Vue高階組件怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI