日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

在Vue中使用HOC模式的實(shí)現(xiàn)

瀏覽:196日期:2022-12-03 17:23:32

前言

HOC是React常用的一種模式,但HOC只能是在React才能玩嗎?先來看看React官方文檔是怎么介紹HOC的:

高階組件(HOC)是React中用于復(fù)用組件邏輯的一種高級技巧。HOC自身不是ReactAPI的一部分,它是一種基于React的組合特性而形成的設(shè)計(jì)模式。

HOC它是一個模式,是一種思想,并不是只能在React中才能用。所以結(jié)合Vue的特性,一樣能在Vue中玩HOC。

HOC

HOC要解決的問題

并不是說哪種技術(shù)新穎,就得使用哪一種。得看這種技術(shù)能夠解決哪些痛點(diǎn)。

HOC主要解決的是可復(fù)用性的問題。在Vue中,這種問題一般是用Mixin解決的。Mixin是一種通過擴(kuò)展收集功能的方式,它本質(zhì)上是將一個對象的屬性拷貝到另一個對象上去。

最初React也是使用Mixin的,但是后面發(fā)現(xiàn)Mixin在React中并不是一種好的模式,它有以下的缺點(diǎn):

mixin與組件之間容易導(dǎo)致命名沖突 mixin是侵入式的,改變了原組件,復(fù)雜性大大提高。

所以React就慢慢的脫離了mixin,從而推薦使用HOC。并不是mixin不優(yōu)秀,只是mixin不適合React。

HOC是什么

HOC全稱:high-order component--也就是高階組件。具體而言,高階組件是參數(shù)為組件,返回值為新組件的函數(shù)。

而在React和Vue中組件就是函數(shù),所以的高階組件其實(shí)就是高階函數(shù),也就是返回一個函數(shù)的函數(shù)。

來看看HOC在React的用法:

function withComponent(WrappedComponent) { return class extends Component { componentDidMount () { console.log(’已經(jīng)掛載完成’) } render() { return <WrappedComponent {...props} />; } }}

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

HOC是一個純函數(shù),且不應(yīng)該修改原組件 HOC不關(guān)心傳遞的props是什么,并且WrappedComponent不關(guān)心數(shù)據(jù)來源 HOC接收到的props應(yīng)該透傳給WrapperComponent

在Vue中使用HOC

怎么樣才能將Vue上使用HOC的模式呢?

我們一般書寫的Vue組件是這樣的:

<template> <div> <p>{{title}}</p> <button @click='changeTitle'></button> </div></template><script>export default { name: ’ChildComponent’, props: [’title’], methods: { changeTitle () { this.$emit(’changeTitle’); } }}</script>

而withComponet函數(shù)的功能是在每次掛載完成后都打印一句:已經(jīng)掛載完成。

既然HOC是替代mixin的,所以我們先用mixin書寫一遍:

export default { mounted () { console.log(’已經(jīng)掛載完成’) }}

然后導(dǎo)入到ChildComponent中

import withComponent from ’./withComponent’;export default { ... mixins: [’withComponet’],}

對于這個組件,我們在父組件中是這樣調(diào)用的

<child-component :title=’title’ @changeTitle=’changeTitle’></child-component><script>import ChildComponent from ’./childComponent.vue’;export default { ... components: {ChildComponent}}</script>

大家有沒有發(fā)現(xiàn),當(dāng)我們導(dǎo)入一個Vue組件時,其實(shí)是導(dǎo)入一個對象。

export default {}

至于說組件是函數(shù),其實(shí)是經(jīng)過處理之后的結(jié)果。所以Vue中的高階組件也可以是:接收一個純對象,返回一個純對象。

所以改為HOC模式,是這樣的:

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經(jīng)掛載完成’) }, props: WrappedComponent.props, render (h) { return h(WrapperComponent, {on: this.$listeners,attrs: this.$attrs,props: this.$props }) } }}

注意{on: this.$listeners,attr: this.$attrs, props: this.props}這一句就是透傳props的原理,等價于React中的<WrappedComponent {...props} />;

this.$props是指已經(jīng)被聲明的props屬性,this.$attrs是指沒被聲明的props屬性。這一定要兩個一起透傳,缺少哪一個,props都不完整。

為了通用性,這里使用了render函數(shù)來構(gòu)建,這是因?yàn)閠emplate只有在完整版的Vue中才能使用。

這樣似乎還不錯,但是還有一個重要的問題,在Vue組件中是可以使用插槽的。

比如:

<template> <div> <p>{{title}}</p> <button @click='changeTitle'></button> <slot></slot> </div></template>

在父組件中

<child-component :title=’title’ @changeTitle=’changeTitle’>Hello, HOC</child-component>

可以用this.$solts訪問到被插槽分發(fā)的內(nèi)容。每個具名插槽都有其相應(yīng)的property,例如v-slot:foo中的內(nèi)容將會在this.$slots.foo中被找到。而default property包括了所有沒有被包含在具名插槽中的節(jié)點(diǎn),或v-slot:default的內(nèi)容。

所以在使用渲染函數(shù)書寫一個組件時,訪問this.$slots最有幫助的。

先將this.$slots轉(zhuǎn)化為數(shù)組,因?yàn)殇秩竞瘮?shù)的第三個參數(shù)是子節(jié)點(diǎn),是一個數(shù)組

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經(jīng)掛載完成’) }, props: WrappedComponent.props, render (h) { const keys = Object.keys(this.$slots); const slotList = keys.reduce((arr, key) => arr.concat(this.$slots[key]), []); return h(WrapperComponent, {on: this.$listeners,attrs: this.$attrs,props: this.$props }, slotList) } }}

總算是有模有樣了,但這還沒結(jié)束,你會發(fā)現(xiàn)使不使用具名插槽都一樣,最后都是按默認(rèn)插槽來處理的。

有點(diǎn)納悶,去看看Vue源碼中是怎么具名插槽的。在src/core/instance/render.js文件中找到了initRender函數(shù),在初始化render函數(shù)時

const options = vm.$optionsconst parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent treeconst renderContext = parentVnode && parentVnode.contextvm.$slots = resolveSlots(options._renderChildren, renderContext)

這一段代碼是Vue解析并處理slot的。將vm.$options._parentVnode賦值為vm.$vnode,也就是$vnode就是父組件的vnode。如果父組件存在,定義renderContext = vm.$vnode.context。renderContext就是父組件要渲染的實(shí)例。 然后把renderContext和$options._renderChildren作為參數(shù)傳進(jìn)resolveSlots()函數(shù)中。

接下里看看resolveSlots()函數(shù),在src/core/instance/render-helper/resolve-slots.js文件中

export function resolveSlots ( children: ?Array<VNode>, context: ?Component): { [key: string]: Array<VNode> } { if (!children || !children.length) { return {} } const slots = {} for (let i = 0, l = children.length; i < l; i++) { const child = children[i] const data = child.data // remove slot attribute if the node is resolved as a Vue slot node if (data && data.attrs && data.attrs.slot) { delete data.attrs.slot } // named slots should only be respected if the vnode was rendered in the // same context. if ((child.context === context || child.fnContext === context) && data && data.slot != null ) { const name = data.slot const slot = (slots[name] || (slots[name] = [])) if (child.tag === ’template’) { slot.push.apply(slot, child.children || []) } else { slot.push(child) } } else { (slots.default || (slots.default = [])).push(child) } } // ignore slots that contains only whitespace for (const name in slots) { if (slots[name].every(isWhitespace)) { delete slots[name] } } return slots}

重點(diǎn)來看里面的一段if語句

// named slots should only be respected if the vnode was rendered in the// same context.if ((child.context === context || child.fnContext === context) && data && data.slot != null) { const name = data.slot const slot = (slots[name] || (slots[name] = [])) if (child.tag === ’template’) { slot.push.apply(slot, child.children || []) } else { slot.push(child) }} else { (slots.default || (slots.default = [])).push(child)}

只有當(dāng)if ((child.context === context || child.fnContext === context) && data && data.slot != null ) 為真時,才處理為具名插槽,否則不管具名不具名,都當(dāng)成默認(rèn)插槽處理

else { (slots.default || (slots.default = [])).push(child)}

那為什么HOC上的if條件是不成立的呢?

這是因?yàn)橛捎贖OC的介入,在原本的父組件與子組件之間插入了一個組件--也就是HOC,這導(dǎo)致了子組件中訪問的this.$vode已經(jīng)不是原本的父組件的vnode了,而是HOC中的vnode,所以這時的this.$vnode.context引用的是高階組件,但是我們卻將slot透傳了,slot中的VNode的context引用的還是原來的父組件實(shí)例,所以就導(dǎo)致不成立。

從而都被處理為默認(rèn)插槽。

解決方法也很簡單,只需手動的將slot中的vnode的context指向?yàn)镠OC實(shí)例即可。注意當(dāng)前實(shí)例 _self 屬性訪問當(dāng)前實(shí)例本身,而不是直接使用 this,因?yàn)?this 是一個代理對象。

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經(jīng)掛載完成’) }, props: WrappedComponent.props, render (h) { const keys = Object.keys(this.$slots); const slotList = keys.reduce((arr, key) => arr.concat(this.$slots[key]), []).map(vnode => {vnode.context = this._selfreturn vnode }); return h(WrapperComponent, {on: this.$listeners,attrs: this.$attrs,props: this.$props }, slotList) } }}

而且scopeSlot與slot的處理方式是不同的,所以將scopeSlot一起透傳

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經(jīng)掛載完成’) }, props: WrappedComponent.props, render (h) { const keys = Object.keys(this.$slots); const slotList = keys.reduce((arr, key) => arr.concat(this.$slots[key]), []).map(vnode => {vnode.context = this._selfreturn vnode }); return h(WrapperComponent, {on: this.$listeners,attrs: this.$attrs,props: this.$props,scopedSlots: this.$scopedSlots }, slotList) } }}

這樣就行了。

結(jié)尾

更多文章請移步樓主github,如果喜歡請點(diǎn)一下star,對作者也是一種鼓勵。

到此這篇關(guān)于在Vue中使用HOC模式的文章就介紹到這了,更多相關(guān)Vue使用HOC模式內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲精一区二区三区| 精品国产美女a久久9999| 日本不良网站在线观看| 国产一区二区三区四区五区| 久久免费精品| 亚洲黄色免费看| 好看的亚洲午夜视频在线| 婷婷成人在线| 精品视频免费| 日韩中文影院| 黄色日韩精品| 老司机久久99久久精品播放免费| 日韩视频二区| 亚洲图片久久| 亚洲在线一区| 日韩精品亚洲aⅴ在线影院| 亚洲精品激情| 欧美片第1页综合| 欧美aa在线视频| 成人av三级| 日韩网站在线| 国产欧美一区二区精品久久久 | 久久一二三区| 亚洲欧洲国产精品一区| 国产精品欧美一区二区三区不卡 | 水蜜桃精品av一区二区| av高清不卡| 香蕉国产精品| 午夜精品福利影院| 另类小说一区二区三区| 日本国产精品| 亚洲精品美女91| 精品中文在线| 91久久视频| 国产精品成人一区二区网站软件| 水蜜桃久久夜色精品一区| 亚洲国内欧美| 91成人精品在线| 国产一二在线播放| 亚洲一级网站| 少妇精品久久久一区二区 | 久久精品97| 日韩一区二区三区免费播放| 亚洲一区二区三区无吗| 精品视频在线观看网站| 激情综合自拍| 国产精品片aa在线观看| 激情视频一区二区三区| 日韩中出av| 色婷婷精品视频| 日韩欧美久久| 国产精品精品| 欧美在线资源| 国产精品久久久久久久久久久久久久久| 嫩草伊人久久精品少妇av杨幂| 久久国产电影| 国产精品网在线观看| 免费精品国产的网站免费观看| 日韩高清不卡在线| 久久久久午夜电影| 日韩av三区| 欧美精品一区二区三区精品| 麻豆国产欧美一区二区三区 | 国产欧美一区二区精品久久久| 亚洲不卡系列| 中文字幕一区二区三区在线视频| 精品美女久久| 中文字幕一区二区av| 日韩精品dvd| 欧美一级一区| 欧美日韩国产免费观看视频| 日韩av一级片| 99在线观看免费视频精品观看| 麻豆精品久久久| 亚洲欧美日韩国产| 日韩欧美在线中字| 欧美亚洲免费| 亚洲免费网址| 久久久久久久久久久9不雅视频| 欧美私人啪啪vps| 久久超碰99| 人人精品人人爱| 中文字幕在线看片| 国产精品欧美大片| 亚洲精品无播放器在线播放| 亚洲福利精品| 日本免费一区二区三区四区| 国产精品一区二区三区四区在线观看| 一区二区自拍| 久久蜜桃精品| 国产不卡一区| 久久wwww| 国产伦理一区| 色狠狠一区二区三区| 中文精品在线| 精品一区二区三区免费看| 日本vs亚洲vs韩国一区三区二区| 宅男噜噜噜66国产日韩在线观看| 成人精品天堂一区二区三区| 精品视频在线观看网站| 国产免费播放一区二区| 深夜福利亚洲| 男人的天堂久久精品| 亚洲一本视频| 久久中文字幕av| 国产精品二区不卡| 精品中国亚洲| 精品国产中文字幕第一页| 国产日韩免费| 国产欧美一区二区精品久久久| 午夜一级久久| 五月天久久777| 欧美女激情福利| 91精品蜜臀一区二区三区在线| 中国字幕a在线看韩国电影| 国产一区二区三区成人欧美日韩在线观看| 国产亚洲一卡2卡3卡4卡新区| 日韩1区2区3区| 日本国产欧美| 日韩一区中文| 伊人久久亚洲| 亚洲欧洲美洲国产香蕉| 快she精品国产999| 蜜桃91丨九色丨蝌蚪91桃色| 亚洲网址在线观看| 一区免费在线| 蜜臀av在线播放一区二区三区| 蜜桃久久精品一区二区| 亚洲精品第一| 国产日本亚洲| 欧美成人aaa| 精品国产一区二区三区噜噜噜| 精品国产鲁一鲁****| 精品国产三区在线| 精品久久美女| 欧美亚洲日本精品| 国内激情久久| 韩日一区二区三区| 日本 国产 欧美色综合| 日韩二区在线观看| 日本91福利区| 97se亚洲| 91欧美日韩| 久久久久亚洲| 蜜臀av在线播放一区二区三区| 日本国产亚洲| 精品高清久久| 久久久久久久久久久9不雅视频| 午夜久久黄色| 国产一级久久| 伊人久久一区| 久久久91麻豆精品国产一区| 九九九精品视频| 久久精品一区二区三区中文字幕| 中文字幕在线看片| 久久免费大视频| 亚洲精选成人| 国产日韩欧美一区二区三区在线观看| 麻豆国产精品777777在线| 91精品韩国| 激情六月综合| 日韩av中文字幕一区| 国产一区二区三区久久| 欧美日韩国产在线一区| 日本不卡中文字幕| 激情国产在线| 美女国产一区| 国产精品久久乐| 亚洲精品.com| 亚洲最新av| 欧美激情99| 国产精品99一区二区| 日本午夜精品| 香蕉成人av| 日韩高清在线观看一区二区| 风间由美中文字幕在线看视频国产欧美| 99久久久久国产精品| 日韩国产欧美在线视频| 91亚洲成人| 亚洲精品美女91| 久久亚洲人体| re久久精品视频| 国产精品一区二区av日韩在线| 久久久久美女| 国产精品1luya在线播放| 亚洲综合电影| 亚洲精品精选| 欧产日产国产精品视频| 日韩国产在线一| 久久一区二区三区喷水| 国产美女亚洲精品7777| 午夜欧美视频| 欧美国产日韩电影| 亚洲欧美激情诱惑| 国产传媒在线观看| 日韩在线黄色| 国产高清久久| 精品在线网站观看| 人人爽香蕉精品| 欧美亚洲激情|