Vue高级组件之函数式组件的使用场景与源码分析

目录
  • 介绍
  • 使用场景
  • 源码分析
  • 总结

介绍

Vue提供了一种可以让组件变为无状态、无实例的函数化组件。从原理上说,一般子组件都会经过实例化的过程,而单纯的函数组件并没有这个过程,它可以简单理解为一个中间层,只处理数据,不创建实例,也是由于这个行为,它的渲染开销会低很多。实际的应用场景是,当我们需要在多个组件中选择一个来代为渲染,或者在将children,props,data等数据传递给子组件前进行数据处理时,我们都可以用函数式组件来完成,它本质上也是对组件的一个外部包装。

使用场景

定义两个组件对象,test1,test2

var test1 = {
props: ['msg'],
render: function (createElement, context) {
  return createElement('h1', this.msg)
}
}
var test2 = {
props: ['msg'],
render: function (createElement, context) {
  return createElement('h2', this.msg)
}
}

定义一个函数式组件,它会根据计算结果选择其中一个组件进行选项

Vue.component('test3', {
// 函数式组件的标志 functional设置为true
functional: true,
props: ['msg'],
render: function (createElement, context) {
  var get = function() {
    return test1
  }
  return createElement(get(), context)
}
})

函数式组件的使用

<test3 :msg="msg" id="test">
</test3>
new Vue({
el: '#app',
data: {
  msg: 'test'
}
})

最终渲染的结果为:

<h2>test</h2>

源码分析

函数式组件会在组件的对象定义中,将functional属性设置为true,这个属性是区别普通组件和函数式组件的关键。同样的在遇到子组件占位符时,会进入createComponent进行子组件Vnode的创建。由于functional属性的存在,代码会进入函数式组件的分支中,并返回createFunctionalComponent调用的结果。 注意,执行完createFunctionalComponent后,后续创建子Vnode的逻辑不会执行,这也是之后在创建真实节点过程中不会有子Vnode去实例化子组件的原因。(无实例)

function createComponent(){
  ···
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }
}

createFunctionalComponent方法会对传入的数据进行检测和合并,实例化FunctionalRenderContext,最终调用函数式组件自定义的render方法执行渲染过程。

function createFunctionalComponent(
  Ctor, // 函数式组件构造器
  propsData, // 传入组件的props
  data, // 占位符组件传入的attr属性
  context, // vue实例
  children// 子节点
){
  // 数据检测合并
  var options = Ctor.options;
  var props = {};
  var propOptions = options.props;
  if (isDef(propOptions)) {
    for (var key in propOptions) {
      props[key] = validateProp(key, propOptions, propsData || emptyObject);
    }
  } else {
    // 合并attrs
    if (isDef(data.attrs)) { mergeProps(props, data.attrs); }
    // 合并props
    if (isDef(data.props)) { mergeProps(props, data.props); }
  }
  var renderContext = new FunctionalRenderContext(data,props,children,contextVm,Ctor);
  // 调用函数式组件中自定的render函数
  var vnode = options.render.call(null, renderContext._c, renderContext)
}

而FunctionalRenderContext这个类最终的目的是定义一个和真实组件渲染不同的render方法。

function FunctionalRenderContext() {
  // 省略其他逻辑
  this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); };
}

执行render函数的过程,又会递归调用createElement的方法,这时的组件已经是真实的组件,开始执行正常的组件挂载流程。

问题:为什么函数式组件需要定义一个不同的createElement方法?- 函数式组件createElement和以往唯一的不同是,最后一个参数的不同,之前章节有说到,createElement会根据最后一个参数决定是否对子Vnode进行拍平,一般情况下,children编译生成结果都是Vnode类型,只有函数式组件比较特殊,它可以返回一个数组,这时候拍平就是有必要的。我们看下面的例子:

Vue.component('test', {
  functional: true,
  render: function (createElement, context) {
    return context.slots().default
  }
}) 

<test>
     <p>slot1</p>
     <p>slot</p>
</test>

此时函数式组件test的render函数返回的是两个slot的Vnode,它是以数组的形式存在的,这就是需要拍平的场景。

简单总结一下函数式组件,从源码中可以看出,函数式组件并不会像普通组件那样有实例化组件的过程,因此包括组件的生命周期,组件的数据管理这些过程都没有,它只会原封不动的接收传递给组件的数据做处理,并渲染需要的内容。因此作为纯粹的函数可以也大大降低渲染的开销。

总结

到此这篇关于Vue高级组件之函数式组件的使用场景与源码分析的文章就介绍到这了,更多相关Vue高级组件之函数式组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue函数式组件的应用实例详解

    一.函数式组件和普通组件的区别 渲染快 没有实例,意味着没有(this) 没有生命周期(没有响应式数据) 二.组件函数的使用 1.以局部组件为例,将组件标记为functional=ture; 因为函数式没有实例,因此组件需要的一切都是通过context参数传递,它是一个包括如下字段的对象: props:提供所有 prop 的对象children: VNode 子节点的数组slots: 一个函数,返回了包含所有插槽的对象scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象.也

  • 浅谈Vue 函数式组件的使用技巧

    什么是函数式组件 没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法,它只是一个接受一些 prop 的函数.简单来说是 一个无状态和无实例的组件 基本写法: Vue.component('my-component', { functional: true, // Props 是可选的 props: { // ... }, // 为了弥补缺少的实例 // 提供第二个参数作为上下文 render: function(createElement, context) { // ... }

  • Vue函数式组件-你值得拥有

    函数式组件特点: 没有管理任何状态 没有监听任何传递给它的状态 没有生命周期方法 它只是接收一些prop的函 我们将这样的组件标记为functional: 无状态 == 无响应式数据 无实例 == 无this上下文 函数式组件的优点: 渲染开销低,因为函数式组件只是函数: 函数式组件基本写法: { functional: true, // Props 是可选的 props: { // ... }, // 为了弥补缺少的实例 // 提供第二个参数作为上下文 render: function (cr

  • Vue高级组件之函数式组件的使用场景与源码分析

    目录 介绍 使用场景 源码分析 总结 介绍 Vue提供了一种可以让组件变为无状态.无实例的函数化组件.从原理上说,一般子组件都会经过实例化的过程,而单纯的函数组件并没有这个过程,它可以简单理解为一个中间层,只处理数据,不创建实例,也是由于这个行为,它的渲染开销会低很多.实际的应用场景是,当我们需要在多个组件中选择一个来代为渲染,或者在将children,props,data等数据传递给子组件前进行数据处理时,我们都可以用函数式组件来完成,它本质上也是对组件的一个外部包装. 使用场景 定义两个组件

  • Vue3源码分析组件挂载初始化props与slots

    目录 前情提要 初始化组件 (1).setupComponent (2).initProps (3).initSlots 额外内容 总结 前情提要 上文我们分析了挂载组件主要调用了三个函数: createComponentInstance(创建组件实例).setupComponent(初始化组件).setupRenderEffect(更新副作用).并且上一节中我们已经详细讲解了组件实例上的所有属性,还包括emit.provide等的实现.本文我们将继续介绍组件挂载流程中的初始化组件. 本文主要内

  • Vue 中 template 有且只能一个 root的原因解析(源码分析)

    引言 今年, 疫情 并没有影响到各种面经的正常出现,可谓是络绎不绝(学不动...).然后,在前段时间也看到一个这样的关于 Vue 的问题, 为什么每个组件 template 中有且只能一个 root? 可能,大家在平常开发中,用的较多就是 template 写 html 的形式.当然,不排除用 JSX 和 render() 函数的.但是,究其本质,它们最终都会转化成 render() 函数.然后,再由 render() 函数转为 Vritual DOM (以下统称 VNode ).而 rende

  • Vue.js源码分析之自定义指令详解

    前言 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上时,该元素在初次渲染.插入到父节点.更新.解绑时可以执行一些特定的操作(钩子函数() 自定义指令有两种注册方式,一种是全局注册,使用Vue.directive(指令名,配置参数)注册,注册之后所有的Vue实例都可以使用,另一种是局部注册,在创建Vue实例时通过directives属性创建局部指

  • Vue编译器AST抽象语法树源码分析

    目录 引言 baseCompile主要核心代码 如何写一个程序来识别 Token parse 函数解析模板字符串 引言 接上篇  Vue编译器源码分析compile 解析 baseCompile主要核心代码 // `createCompilerCreator` allows creating compilers that use alternative // parser/optimizer/codegen, e.g the SSR optimizing compiler. // Here we

  • vue 虚拟dom的patch源码分析

    本文介绍了vue 虚拟dom的patch源码分析,分享给大家,具体如下: 源码目录:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 let newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0]

  • Vue 源码分析之 Observer实现过程

    导语: 本文是对 Vue 官方文档深入响应式原理(https://cn.vuejs.org/v2/guide/reactivity.html)的理解,并通过源码还原实现过程. 响应式原理可分为两步,依赖收集的过程与触发-重新渲染的过程.依赖收集的过程,有三个很重要的类,分别是 Watcher.Dep.Observer.本文主要解读 Observer . 这篇文章讲解上篇文章没有覆盖到的 Observer 部分的内容,还是先看官网这张图: Observer 最主要的作用就是实现了上图中touch

  • Vue源码分析之虚拟DOM详解

    为什么需要虚拟dom? 虚拟DOM就是为了解决浏览器性能问题而被设计出来的.例如,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量.简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag).属性(attrs)和子元素对象( children)三个属性. ----- 元素节点: 元素节点更贴近于我们

  • Vue编译器optimize源码分析

    目录 引言 optimize 源码之旅 markStatic$1源码 isStatic源码 复杂点的 回归到markStatic$1 markStaticRoots 源码 引言 接上文 parseHTML 函数源码解析 chars.end.comment钩子函数 上一章节我们讲到通过解析将template转成AST(抽象语法树),接下来继续对模型树优化,进行静态标注.那么问题来了,什么是静态标注?为什么要静态标注. 在源码的注释中我们找到了下面这段话: /** * Goal of the opt

  • Vue编译器源码分析compileToFunctions作用详解

    目录 引言 Vue.prototype.$mount函数体 源码出处 options.delimiters & options.comments compileToFunctions函数逐行分析 createFunction 函数源码 引言 Vue编译器源码分析 接上篇文章我们来分析:compileToFunctions的作用. 经过前面的讲解,我们已经知道了 compileToFunctions 的真正来源你可能会问为什么要弄的这么复杂?为了搞清楚这个问题,我们还需要继续接触完整的代码. 下面

随机推荐