Vue3.0源码逐行解析(二) 组件的本质

在上篇文章已经介绍了 Vue2.x 与 Vue3 的从创建应用实例到挂载的过程,本章节会介绍组件渲染流程。

组件的本质

当我们使用 Vue 或 React 时,往往会将页面拆分为各种组件,通过拼装组件来形成页面和应用,就像搭积木一样。那大家是否思考过:"组件的产出是什么?"

拿 Vue 来说 , 一个组件最核心的东西是 render 函数,剩余的其他内容,如 data compouted、props 等都是为 render 函数提供数据来源服务的。render 函数产出了 Virtual DOM借助 snabbdom 的 API 我们可以很容易地用代码描述这个过程:

import { h } from 'snabbdom'

// h 函数用来创建 VNode,组件的产出是 VNode
const MyComponent = props => {
  return h('h1', props.title)
}

Virtual DOM 要渲染成真实的 DOM trre 我们通常把这个过程叫做 patch,同样可以借助 snabbdom 的 API 复刻下这个过程:

import { h, init } from 'snabbdom'
// init 方法用来创建 patch 函数
const patch = init([])

const MyComponent = props => {
  return h('h1', props.title)
}

// 组件的产出是 VNode
const prevVnode = MyComponent({ title: 'prev' })
// 将 VNode 渲染成真实 DOM
patch(document.getElementById('app'), prevVnode)

当数据变更时,组件会产出新的VNode,我们只需再次调用patch函数即可:

// 数据变更,产出新的 VNode
const nextVnode = MyComponent({ title: 'next' })
// 通过对比新旧 VNode,高效地渲染真实 DOM
patch(prevVnode, nextVnode)

以上就是我们要达成的共识:组件的产出就是 Virtual DOM。为何组件要产出 Virtual DOM 呢?其原因是 Virtual DOM 带来了 分层设计,它对渲染过程的抽象,使得框架可以渲染到 web(浏览器) 以外的平台(SSR Weex 小程序)。

组件的 VNode 如何表示

Vue 通过建立一个虚拟 DOM来追踪自己要如何改变真实 DOM。

return createElement('h1', this.blogTitle)

createElement到底会返回什么呢?其实不是一个实际的DOM 元素。它更准确的名字可能是createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为"VNode"。"虚拟 DOM"是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。

TIP 虚拟DOM: 渲染函数 & JSX — Vue.js

创建VNode

通过官方解释可以知道 vnode 本质上是用来描述 DOM 的 JavaScript 对象,它在 Vue.js 中可以描述不同类型的节点,比如普通元素节点、组件节点等。

什么是普通元素节点呢?举个例子,在 HTML 中我们使用 <button> 标签来写一个按钮:

<button class="fist">click counter</button>

我们可以用 vnode 这样表示<button>标签:

const vnode = {
  type: 'button',
  props: { 
    'class': 'fist',
  },
  children: ['click counter']
}

其中 type 属性表示 DOM 的标签类型,props 属性表示 DOM 的一些附加信息,比如 style 、class 等,children 属性表示 DOM 的子节点,如果元素只有一个子节点且是文本的情况,可以用字符串表示 。

什么是组件节点呢?其实, vnode 除了可以像上面那样用于描述一个真实的 DOM,也可以用来描述组件。

<blog-post title='Why Vue is so fun'></blog-post>

我们可以用 vnode 这样表示 <blog-post> 组件标签:

const blogPost= {
  // 在这里定义组件对象
}
const vnode = {
  type: blogPost,
  props: { 
    msg: 'Why Vue is so fun'
  }
}

组件 vnode 其实是对抽象事物的描述,这是因为我们并不会在页面上真正渲染一个 <blog-post> 标签,而是渲染组件内部定义的 HTML 标签。 除此之外 vnode 类型,还有纯文本 vnode、注释 vnode 等等,在编译的过程会把元素的文本、注释作为子节点转化成vnode对象统一管理。

在Vue3 针对 vnode 的 type,做了更详尽的分类,包括 Suspense、Teleport 等,且把 vnode 的类型信息做了编码,以便在后面的 patch 阶段,可以根据不同的类型执行相应的处理逻辑:

const shapeFlag = isString(type)
  ? 1 /* ELEMENT */
  : isSuspense(type)
    ? 128 /* SUSPENSE */
    : isTeleport(type)
      ? 64 /* TELEPORT */
      : isObject(type)
        ? 4 /* STATEFUL_COMPONENT */
        : isFunction(type)
          ? 2 /* FUNCTIONAL_COMPONENT */
          : 0

我们已经了解了 VN

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值