电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

在Vue中使用HOC模式的實現

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

前言

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

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

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

HOC

HOC要解決的問題

并不是說哪種技術新穎,就得使用哪一種。得看這種技術能夠解決哪些痛點。

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

最初React也是使用Mixin的,但是后面發現Mixin在React中并不是一種好的模式,它有以下的缺點:

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

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

HOC是什么

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

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

來看看HOC在React的用法:

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

withComponent就是一個高階組件,它有以下特點:

HOC是一個純函數,且不應該修改原組件 HOC不關心傳遞的props是什么,并且WrappedComponent不關心數據來源 HOC接收到的props應該透傳給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函數的功能是在每次掛載完成后都打印一句:已經掛載完成。

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

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

然后導入到ChildComponent中

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

對于這個組件,我們在父組件中是這樣調用的

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

大家有沒有發現,當我們導入一個Vue組件時,其實是導入一個對象。

export default {}

至于說組件是函數,其實是經過處理之后的結果。所以Vue中的高階組件也可以是:接收一個純對象,返回一個純對象。

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

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經掛載完成’) }, 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是指已經被聲明的props屬性,this.$attrs是指沒被聲明的props屬性。這一定要兩個一起透傳,缺少哪一個,props都不完整。

為了通用性,這里使用了render函數來構建,這是因為template只有在完整版的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訪問到被插槽分發的內容。每個具名插槽都有其相應的property,例如v-slot:foo中的內容將會在this.$slots.foo中被找到。而default property包括了所有沒有被包含在具名插槽中的節點,或v-slot:default的內容。

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

先將this.$slots轉化為數組,因為渲染函數的第三個參數是子節點,是一個數組

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經掛載完成’) }, 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) } }}

總算是有模有樣了,但這還沒結束,你會發現使不使用具名插槽都一樣,最后都是按默認插槽來處理的。

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

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就是父組件要渲染的實例。 然后把renderContext和$options._renderChildren作為參數傳進resolveSlots()函數中。

接下里看看resolveSlots()函數,在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}

重點來看里面的一段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)}

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

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

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

這是因為由于HOC的介入,在原本的父組件與子組件之間插入了一個組件--也就是HOC,這導致了子組件中訪問的this.$vode已經不是原本的父組件的vnode了,而是HOC中的vnode,所以這時的this.$vnode.context引用的是高階組件,但是我們卻將slot透傳了,slot中的VNode的context引用的還是原來的父組件實例,所以就導致不成立。

從而都被處理為默認插槽。

解決方法也很簡單,只需手動的將slot中的vnode的context指向為HOC實例即可。注意當前實例 _self 屬性訪問當前實例本身,而不是直接使用 this,因為 this 是一個代理對象。

export default function withComponent (WrapperComponent) { return { mounted () { console.log(’已經掛載完成’) }, 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(’已經掛載完成’) }, 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) } }}

這樣就行了。

結尾

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

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

標簽: Vue
相關文章:
主站蜘蛛池模板: 发电机价格|发电机组价格|柴油发电机价格|柴油发电机组价格网 | 哈尔滨京科脑康神经内科医院-哈尔滨治疗头痛医院-哈尔滨治疗癫痫康复医院 | 电脑刺绣_绣花厂家_绣花章仔_织唛厂家-[源欣刺绣]潮牌刺绣打版定制绣花加工厂家 | 氮化镓芯片-碳化硅二极管 - 华燊泰半导体 | 数控专用机床,专用机床,自动线,组合机床,动力头,自动化加工生产线,江苏海鑫机床有限公司 | 杭州画室_十大画室_白墙画室_杭州美术培训_国美附中培训_附中考前培训_升学率高的画室_美术中考集训美术高考集训基地 | MTK核心板|MTK开发板|MTK模块|4G核心板|4G模块|5G核心板|5G模块|安卓核心板|安卓模块|高通核心板-深圳市新移科技有限公司 | 模型公司_模型制作_沙盘模型报价-中国模型网 | 台湾阳明固态继电器-奥托尼克斯光电传感器-接近开关-温控器-光纤传感器-编码器一级代理商江苏用之宜电气 | 细石混凝土泵_厂家_价格-烟台九达机械有限公司 | 瓶盖扭矩仪(扭力值检测)-百科 | 北京森语科技有限公司-模型制作专家-展览展示-沙盘模型设计制作-多媒体模型软硬件开发-三维地理信息交互沙盘 | 路斯特伺服驱动器维修,伦茨伺服驱动器维修|万骏自动化百科 | 合肥升降机-合肥升降货梯-安徽升降平台「厂家直销」-安徽鼎升自动化科技有限公司 | 金属抛光机-磁悬浮抛光机-磁力研磨机-磁力清洗机 - 苏州冠古科技 | CE认证_产品欧盟ROHS-REACH检测机构-商通检测 | 水冷式工业冷水机组_风冷式工业冷水机_水冷螺杆冷冻机组-深圳市普威机械设备有限公司 | 甲级防雷检测仪-乙级防雷检测仪厂家-上海胜绪电气有限公司 | 成都办公室装修-办公室设计-写字楼装修设计-厂房装修-四川和信建筑装饰工程有限公司 | 电加热导热油炉-空气加热器-导热油加热器-翅片电加热管-科安达机械 | 合肥弱电工程_安徽安防工程_智能化工程公司-合肥雷润 | 插针变压器-家用电器变压器-工业空调变压器-CD型电抗器-余姚市中驰电器有限公司 | 碳化硅,氮化硅,冰晶石,绢云母,氟化铝,白刚玉,棕刚玉,石墨,铝粉,铁粉,金属硅粉,金属铝粉,氧化铝粉,硅微粉,蓝晶石,红柱石,莫来石,粉煤灰,三聚磷酸钠,六偏磷酸钠,硫酸镁-皓泉新材料 | 礼仪庆典公司,礼仪策划公司,庆典公司,演出公司,演艺公司,年会酒会,生日寿宴,动工仪式,开工仪式,奠基典礼,商务会议,竣工落成,乔迁揭牌,签约启动-东莞市开门红文化传媒有限公司 | PU树脂_水性聚氨酯树脂_聚氨酯固化剂_聚氨酯树脂厂家_宝景化工 | 课件导航网_ppt课件_课件模板_课件下载_最新课件资源分享发布平台 | 同步带轮_同步带_同步轮_iHF合发齿轮厂家-深圳市合发齿轮机械有限公司 | 广东教师资格网-广东教师资格证考试网| EFM 022静电场测试仪-套帽式风量计-静电平板监测器-上海民仪电子有限公司 | 垃圾清运公司_环卫保洁公司_市政道路保洁公司-华富环境 | ◆大型吹塑加工|吹塑加工|吹塑代加工|吹塑加工厂|吹塑设备|滚塑加工|滚塑代加工-莱力奇塑业有限公司 | 塑钢件_塑钢门窗配件_塑钢配件厂家-文安县启泰金属制品有限公司 深圳南财多媒体有限公司介绍 | 扬尘监测_扬尘监测系统_带证扬尘监测设备 - 郑州港迪科技有限公司 | 企业彩铃制作_移动、联通、电信集团彩铃上传开通_彩铃定制_商务彩铃管理平台-集团彩铃网 | Copeland/谷轮压缩机,谷轮半封闭压缩机,谷轮涡旋压缩机,型号规格,技术参数,尺寸图片,价格经销商 CTP磁天平|小电容测量仪|阴阳极极化_双液系沸点测定仪|dsj电渗实验装置-南京桑力电子设备厂 | 细砂提取机,隔膜板框泥浆污泥压滤机,螺旋洗砂机设备,轮式洗砂机械,机制砂,圆锥颚式反击式破碎机,振动筛,滚筒筛,喂料机- 上海重睿环保设备有限公司 | 北京开业庆典策划-年会活动策划公司-舞龙舞狮团大鼓表演-北京盛乾龙狮鼓乐礼仪庆典策划公司 | 安徽净化工程设计_无尘净化车间工程_合肥净化实验室_安徽创世环境科技有限公司 | 北京京云律师事务所| PCB接线端子_栅板式端子_线路板连接器_端子排生产厂家-置恒电气 喷码机,激光喷码打码机,鸡蛋打码机,手持打码机,自动喷码机,一物一码防伪溯源-恒欣瑞达有限公司 假肢-假肢价格-假肢厂家-河南假肢-郑州市力康假肢矫形器有限公司 | 洁净棚-洁净工作棚-无菌室-净化工程公司_北京卫护科技有限公司 |