返回顶部
首页 > 资讯 > 前端开发 > 其他 >vue高阶组件是什么
  • 884
分享到

vue高阶组件是什么

vue高阶组件 2023-05-14 21:05:01 884人浏览 独家记忆
摘要

本教程操作环境:windows7系统、vue3版,DELL G3电脑。高阶组件介绍Vue 高阶组件的认识,在React中组件是以复用代码实现的,而Vue中是以mixins 实现,并且官方文档中也缺少一些高阶组件的概念,因为在vue中实现高阶

vue高阶组件是什么

教程操作环境:windows7系统、vue3版,DELL G3电脑。

高阶组件介绍

Vue 高阶组件的认识,在React中组件是以复用代码实现的,而Vue中是以mixins 实现,并且官方文档中也缺少一些高阶组件的概念,因为在vue中实现高阶组很困难,并不像React简单,其实vue中mixins也同样和以代替,在读了一部分源码之后,对vue有了更深的认识

所谓高阶组件其实就是一个高阶函数, 即返回一个组件函数的函数,Vue中怎么实现呢? 注意 高阶组件有如下特点

高阶组件(HOC)应该是无副作用的纯函数,且不应该修改原组件,即原组件不能有变动
高阶组件(HOC)不关心你传递的数据(props)是什么,并且新生成组件不关心数据来源
高阶组件(HOC)接收到的 props 应该传递给被包装组件即直接将原组件prop传给包装组件
高阶组件完全可以添加、删除、修改 props

高阶组件举例

Base.vue

<template>
  <div>
    <p @click="Click">props: {{test}}</p>
  </div>
</template>
<script>
export default {
  name: 'Base',
  props: {
    test: Number
  },
  methods: {
    Click () {
      this.$emit('Base-click')
    }
  }
}
</script>

Vue 组件主要就是三点:props、event 以及 slots。对于 Base组件 组件而言,它接收一个数字类型的 props 即 test,并触发一个自定义事件,事件的名称是:Base-click,没有 slots。我们会这样使用该组件:

现在我们需要 base-component 组件每次挂载完成的时候都打印一句话:haha,同时这也许是很多组件的需求,所以按照 mixins 的方式,我们可以这样做,首先定义个 mixins

export default consoleMixin {
  mounted () {
    console.log('haha')
  }
}

然后在 Base 组件中将 consoleMixin 混入:

<template>
  <div>
    <p @click="Click">props: {{test}}</p>
  </div>
</template>
<script>
export default {
  name: 'Base',
  props: {
    test: Number
  },
  mixins: [ consoleMixin ],
  methods: {
    Click () {
      this.$emit('Base-click')
    }
  }
}
</script>

这样使用 Base 组件的时候,每次挂载完成之后都会打印一句 haha,不过现在我们要使用高阶组件的方式实现同样的功能,回忆高阶组件的定义:接收一个组件作为参数,返回一个新的组件,那么此时我们需要思考的是,在 Vue 中组件是什么?Vue 中组件是函数,不过那是最终结果,比如我们在单文件组件中的组件定义其实就是一个普通的选项对象,如下:

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

这难道不是一个纯对象嘛

import Base from './Base.vue'
console.log(Base)

这里的Base是什么呢 对就是一个JSON对象,而当以把他加入到一个组件的components,Vu最终会以该参数即option来构造实例的构造函数,所以Vue中组件就是个函数,但是在引入之前仍只是一个options对象,所以这样就很好明白了 Vue中组件开始只是一个对象,即高阶组件就是 一个函数接受一个纯对象,并且返回一个新纯对象

export default function Console (BaseComponent) {
  return {
    template: '<wrapped v-on="$listeners" v-bind="$attrs"/>',
    components: {
      wrapped: BaseComponent
    },
    mounted () {
      console.log('haha')
    }
  }
}

这里 Console就是一个高阶组件,它接受一个参数 BaseComponent即传入的组件,返回一个新组件,将BaseComponent作为新组件的子组件并且在mounted里设置钩子函数 打印haha,我们可以完成mixins同样做到的事,我们并没有修改子组件Base,这里的 $listeners $attrs 其实是在透传props 和事件 那这样真的就完美解决问题了吗?不是的,首先 template 选项只有在完整版的 Vue 中可以使用,在运行时版本中是不能使用的,所以最起码我们应该使用渲染函数(render)替代模板(template)

Console.js

export default function Console (BaseComponent) {
  return {
    mounted () {
      console.log('haha')
    },
    render (h) {
      return h(BaseComponent, {
        on: this.$listeners,
        attrs: this.$attrs,
      })
    }
  }
}

我们将模板改写成了渲染函数,看上去没什么问题,实际还是有问题,上面的代码中 BaseComponent 组件依然收不到 props,为什么呢,我们不是已经在 h 函数的第二个参数中将 attrs 传递过去了吗,怎么还收不到?当然收不到,attrs 指的是那些没有被声明为 props 的属性,所以在渲染函数中还需要添加 props 参数:

export default function Console (BaseComponent) {
  return {
    mounted () {
      console.log('haha')
    },
    render (h) {
      return h(BaseComponent, {
        on: this.$listeners,
        attrs: this.$attrs,
        props: this.$props
      })
    }
  }
}

那这样呢 其实还是不行 props始终是空对象,这里的props是高阶组件的对象,但是高阶组件并没有声明props所以如此故要再声明一个props

export default function Console (BaseComponent) {
  return {
    mounted () {
      console.log('haha')
    },
    props: BaseComponent.props,
    render (h) {
      return h(BaseComponent, {
        on: this.$listeners,
        attrs: this.$attrs,
        props: this.$props
      })
    }
  }
}

ok 一个差不多的高阶组件就完成了 但是能还每完 我们只实现了 透传props,透传事件,emmmm就剩下slot了 我们修改 Base 组件为其添加一个具名插槽和默认插槽 Base.vue

<template>
  <div>
    <span @click="handleClick">props: {{test}}</span>
    <slot name="slot1"/> <!-- 具名插槽 --></slot>
    <p>===========</p>
    <slot><slot/> <!-- 默认插槽 -->
  </div>
</template>
 
<script>
export default {
  ...
}
</script>

<template>
  <div>
    <Base>
      <h2 slot="slot1">BaseComponent slot</h2>
      <p>default slot</p>
    </Base>
    <wrapBase>
      <h2 slot="slot1">EnhancedComponent slot</h2>
      <p>default slot</p>
    </wrapBase>
  </div>
</template>
 
<script>
  import Base from './Base.vue'
  import hoc from './Console.js'
 
  const wrapBase = Console(Base)
 
  export default {
    components: {
      Base,
      wrapBase
    }
  }
</script>

这里的执行结果就是 wrapBase里的slot都没有了 所以就要改一下高阶组建了

function Console (BaseComponent) {
  return {
    mounted () {
      console.log('haha')
    },
    props: BaseComponent.props,
    render (h) {
 
      // 将 this.$slots 格式化为数组,因为 h 函数第三个参数是子节点,是一个数组
      const slots = Object.keys(this.$slots)
        .reduce((arr, key) => arr.concat(this.$slots[key]), [])
 
      return h(BaseComponent, {
        on: this.$listeners,
        attrs: this.$attrs,
        props: this.$props
      }, slots) // 将 slots 作为 h 函数的第三个参数
    }
  }
}

这时 slot内容确实渲染出来了 但是顺序不太对 高阶组件的全部渲染到了末尾。。 其实 Vue在处理具名插槽会考虑作用域的因素 首先 Vue 会把模板(template)编译成渲染函数(render),比如如下模板:

<div>
  <p slot="slot1">Base slot</p>
</div>

会被编译成如下渲染函数:

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

观察上面的渲染函数我们发现普通的 DOM 是通过 _c 函数创建对应的 Vnode 的。现在我们修改模板,模板中除了有普通 DOM 之外,还有组件,如下:

<div>
  <Base>
    <p slot="slot1">Base slot</p>
    <p>default slot</p>
  </Base>
</div>

其render函数

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

我们发现无论是普通DOM还是组件,都是通过 _c 函数创建其对应的 VNode 的 其实 _c 在 Vue 内部就是 createElement 函数。createElement 函数会自动检测第一个参数是不是普通DOM标签如果不是普通DOM标签那么 createElement 会将其视为组件,并且创建组件实例,注意组件实例是这个时候才创建的 但是创建组件实例的过程中就面临一个问题:组件需要知道父级模板中是否传递了 slot 以及传递了多少,传递的是具名的还是不具名的等等。那么子组件如何才能得知这些信息呢?很简单,假如组件的模板如下

<div>
  <Base>
    <p slot="slot1">Base slot</p>
    <p>default slot</p>
  </Base>
</div>

父组件的模板最终会生成父组件对应的 VNode,所以以上模板对应的 VNode 全部由父组件所有,那么在创建子组件实例的时候能否通过获取父组件的 VNode 进而拿到 slot 的内容呢?即通过父组件将下面这段模板对应的 VNode 拿到

<Base>
    <p slot="slot1">Base slot</p>
    <p>default slot</p>
  </Base>

如果能够通过父级拿到这段模板对应的 VNode,那么子组件就知道要渲染哪些 slot 了,其实 Vue 内部就是这么干的,实际上你可以通过访问子组件的 this.$vnode 来获取这段模板对应的 VNode

this.$vnode 并没有写进 Vue 的官方文档

子组件拿到了需要渲染的 slot 之后进入到了关键的一步,这一步就是导致高阶组件中透传 slot 给 Base组件 却无法正确渲染的原因 children的VNode中的context引用父组件实例 其本身的context也会引用本身实例 其实是一个东西

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

而 Vue 内部做了一件很重要的事儿,即上面那个表达式必须成立,才能够正确处理具名 slot,否则即使 slot 具名也不会被考虑,而是被作为默认插槽。这就是高阶组件中不能正确渲染 slot 的原因

即 高阶组件中 本来时父组件和子组件之间插入了一个组件(高阶组件),而子组件的 this.$vnode其实是高阶组件的实例,但是我们将slot透传给子组件,slot里 VNode 的context实际引用的还是父组件 所以

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

最终导致具名插槽被作为默认插槽,从而渲染不正确。

决办法也很简单,只需要手动设置一下 slot 中 VNode 的 context 值为高阶组件实例即可

function Console (Base) {
  return {
    mounted () {
      console.log('haha')
    },
    props: Base.props,
    render (h) {
      const slots = Object.keys(this.$slots)
        .reduce((arr, key) => arr.concat(this.$slots[key]), [])
        // 手动更正 context
        .map(vnode => {
          vnode.context = this._self //绑定到高阶组件上
          return vnode
        })
 
      return h(WrappedComponent, {
        on: this.$listeners,
        props: this.$props,
        attrs: this.$attrs
      }, slots)
    }
  }
}

说明白就是强制把slot的归属权给高阶组件 而不是 父组件 通过当前实例 _self 属性访问当实例本身,而不是直接使用 this,因为 this 是一个代理对象

【相关推荐:vuejs视频教程、WEB前端开发

以上就是vue高阶组件是什么的详细内容,更多请关注编程网其它相关文章!

--结束END--

本文标题: vue高阶组件是什么

本文链接: https://lsjlt.com/news/205979.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue高阶组件是什么
    本教程操作环境:windows7系统、vue3版,DELL G3电脑。高阶组件介绍vue 高阶组件的认识,在React中组件是以复用代码实现的,而Vue中是以mixins 实现,并且官方文档中也缺少一些高阶组件的概念,因为在vue中实现高阶...
    99+
    2023-05-14
    vue高阶组件
  • Vue高阶组件怎么用
    小编给大家分享一下Vue高阶组件怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!高阶组件( HOC )是 React 生态系...
    99+
    2024-04-02
  • react高阶组件指的是什么
    这篇文章主要介绍了react高阶组件指的是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇react高阶组件指的是什么文章都会有所收获,下面我们一起来看看吧。 ...
    99+
    2024-04-02
  • vue高阶组件有哪些
    今天小编给大家分享的是vue高阶组件有哪些,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。在vue中,高阶组件其实就是一个高阶函数, 即返回一个组件函数的函数。高阶组件的特点:1、是无...
    99+
    2023-07-04
  • vue如何使用高阶组件
    这篇文章主要为大家展示了“vue如何使用高阶组件”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue如何使用高阶组件”这篇文章吧。高阶组件1 一般情况//&nb...
    99+
    2024-04-02
  • Vue组件生命周期的三个阶段是什么
    这篇文章主要介绍“Vue组件生命周期的三个阶段是什么”,在日常操作中,相信很多人在Vue组件生命周期的三个阶段是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue组件生命周期的三个阶段是什么”的疑惑有所...
    99+
    2023-07-04
  • 什么是Vue 进阶
    这期内容当中小编将会给大家带来有关什么是Vue 进阶,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。现在我们有一个需求,变量 firstName = "hello...
    99+
    2024-04-02
  • 什么是Python高阶函数
    这篇文章主要介绍“什么是Python高阶函数”,在日常操作中,相信很多人在什么是Python高阶函数问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是Python高阶函数”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-15
  • 什么是python 高阶函数
    本篇文章为大家展示了什么是python 高阶函数,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。体验高阶函数在Python中,abs()函数可以完成对数字求绝对值计算。abs(-10) #&...
    99+
    2023-06-06
  • Python高阶函数是什么
    Python高阶函数是指可以接受函数作为参数或者返回一个函数的函数。在Python中,函数可以作为一种数据类型传递给其他函数,这样的...
    99+
    2024-03-08
    python
  • vue中什么是递归组件
    递归所指的是程序自己调用自身,而vue中的递归组件就是组件自身调用自身。实现方法如下:准备一个父组件存放递归数据,再创建一个子组件作为递归调用的组件,从而实现递归。父组件。<template>  <di...
    99+
    2024-04-02
  • Vue组件的概念是什么
    本篇内容介绍了“Vue组件的概念是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在多次使用到相同的函数和相同的HTML代码时,可以考虑抽...
    99+
    2023-06-20
  • vue的四大组件是什么
    这篇“vue的四大组件是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue的四大组件是什么”文章吧。vue有4大组件:...
    99+
    2023-07-04
  • ReactHOC高阶组件深入讲解
    目录1. 概念2. 属性代理2.1 代理props2.2 条件渲染2.3 添加状态3. 反向继承3.1 拦截渲染3.2 劫持生命周期3.3 操作state3.4 修改react树3....
    99+
    2022-11-13
    React HOC React 高阶组件
  • js中什么是高阶函数
    这篇文章主要介绍了js中什么是高阶函数,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。什么是高阶函数?高阶函数只是将函数作为参数或返回值的函数...
    99+
    2024-04-02
  • Vue中的局部组件是什么
    这篇文章给大家介绍Vue中的局部组件是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。在Vue中我们可以自己定义(注册)局部组件定义组件名的方式:var ComponentA = {&n...
    99+
    2023-06-21
  • vue缓存组件是什么意思
    本教程操作环境:windows7系统、vue3版,DELL G3电脑。在vue中,缓存组件是“keep-alive”,是一个抽象组件。 缓存组件“keep-alive”keep-alive是Vue的内置组件,包裹动态组件时,会将不活动的组件...
    99+
    2023-05-14
    缓存组件 Vue
  • python中高阶函数的判定条件是什么
    这篇文章将为大家详细讲解有关python中高阶函数的判定条件是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和...
    99+
    2023-06-14
  • Vue组件是什么及怎么应用
    本篇内容主要讲解“Vue组件是什么及怎么应用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue组件是什么及怎么应用”吧!什么是组件用面向对象的思维去理解Vue...
    99+
    2024-04-02
  • vue组件有什么
    这篇文章给大家分享的是有关vue组件有什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。问答1、vue 组件有什么?为什么要加上 vue,因为不同的 MVC 框架,东西不一样,不...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作