api是什么简称_Vue核心思想是数据驱动和组件化,那么组件的产出是什么?收藏...

参考:http://hcysun.me/vue-design/zh/essence-of-comp.html#%E7%BB%84%E4%BB%B6%E7%9A%84%E4%BA%A7%E5%87%BA%E6%98%AF%E4%BB%80%E4%B9%88

2f7738087d820bd549a42ae79a4efef9.png

当我们使用 Vue 或 React 时,往往会将页面拆分为各种组件,通过拼装组件来形成页面和应用,就像搭积木一样。

那么,大家有没有思考过:"组件的产出是什么?"

组件的产出是什么

在 JQuery 盛行的年代,比起组件,“模板引擎”的概念要更加流行。

我们可以使用 lodash.template 函数来回忆一下当年是如何用模板开发一个页面的:

af0c58d1cc96a0bf216acd80fcbfd70f.png

模板引擎的概念是:字符串 + 数据 => html

lodash.template 函数虽然称不上是“引擎”,但足以说明问题。

我们将模板字符串传递给 template 函数,该函数返回一个编译器 compiler,只要把数据传入 compiler 函数,便能得到最终想要渲染的内容。

当数据发生变化时,我们需要使用新的数据重新编译模板:

const newHtml = compiler({ title: 'New Component' })

如果把上面的逻辑封装成一个函数,那么一个组件就诞生了:

bb14aeea299ab2245aba8d1d9ae5a608.png

我们可以这样使用它:

document.getElementById('app').innerHTML = MyComponent({ title: 'MyComponent' })

MyComponent 组件也许会带给你这样的感觉:一个组件就是一个函数,给我什么样的数据,我就渲染对应的 html 内容。

这个概念,与我们如今谈论的 Vue 或 React 并没有什么不同,这就是 组件的本质

组件的本质虽然没变,但组件的产出却改变了。在模板引擎的年代,组件的产出是 html 字符串

b4704c667f96e72f9a6bb8805ad7af2f.png

而如今的 Vue 或 React,它们的组件所产出的内容并不是 html 字符串,而是大家所熟知的 Virtual DOM

6f2c386f5296cc783c7fdc94ea519f17.png

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

06995b7fa77a504dd71b7b6ca4f25a81.png

Virtual DOM 终究要渲染真实 DOM,这个过程就可以理解为模板引擎年代的完全替换 html,只不过它采用的不是完全替换,我们通常把这个过程叫做 patch,同样可以借助 snabbdom 的 API 轻松地实现:

3f5e2d9e468622486deb835a86f02a5c.png

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

ffacf3db46111ae60dd0abb755b459fe.png

以上就是我们要达成的共识:组件的产出就是 Virtual DOM

为何组件要从直接产出 html 变成产出 Virtual DOM 呢?其原因是 Virtual DOM 带来了 分层设计,它对渲染过程的抽象,使得框架可以渲染到 web(浏览器) 以外的平台,以及能够实现 SSR 等。

至于 Virtual DOM 相比原生 DOM 操作的性能,这并非 Virtual DOM 的目标,确切地说,如果要比较二者的性能是要“控制变量”的,例如:页面的大小、数据变化量等。

组件的 VNode 如何表示,在后续行文时,将统一使用 VNode 来简称 Virtual DOM

VNode 是真实 DOM 的描述,比如我们可以用如下对象描述一个 div 标签:

fde2e4e404754e087edf545cc5d18aa4.png

想要把 elementVnode 渲染成真实 DOM,我们还需要一个渲染器(Renderer):

function render(vnode, container) {}

渲染器接收两个参数,分别是将要渲染的 vnode 和 元素挂载点(真实 DOM 被渲染的位置)。

为了渲染如上的 div 标签,我们可以这样调用 render 函数:

// 把 elementVnode 渲染到 id 为 app 的元素下render(elementVnode, document.getElementById('app'))

render 函数的实现也很简单:

e44eda382bafc2f21fd4cef80b5218e7.png

如上,在 render 函数内调用了 mountElement,它的作用是根据 VNode 创建真实 DOM 并将其添加到容器中。

这段代码对于标准 html 标签是可以正常工作的(不考虑 SVG),但并不适用于组件。为了能够渲染组件,我们需要思考:组件的 VNode 应该如何表示?

对于 html 标签的 VNode 来说,其 tag 属性的值就是标签的名字,但如果是组件的话,其 VNode中 tag 属性的值应该是什么呢?

很简单,我们可以将其指向组件自身。假设我们有如下组件:

b6965bff209c475b2b217255e85c70b2.png

如上,我们使用 class 定义了一个类,它是一个组件(有状态组件),我们可以使用如下 VNode 来描述它:

const componentVnode = { tag: MyComponent}

如上,直接将 tag 属性的值指向组件自身。但想要正确地渲染该组件,我们还需要修改 render:

7c44d35b8842ff362a2f5bd026d9a688.png

如上,通过 判断 vnode.tag 是否是字符串 来区分:一个 VNode 到底是 html 标签还是组件。如果是组件的话,调用 mountComponent 函数挂载组件,而非 mountElement,如下是 mountComponent 函数的实现:

b4a8beeb85604caf2378e8d77f24a1a3.png

道理很简单,由于 vnode.tag 指向组件类,所以我们创建一个组件实例,接着调用其 render 函数产出 VNode 并将其添加到实例属性 instance.$vnode,最后借用 mountElement 函数完成标签的挂载即可。

组件的种类

大家可能会注意到,在上文中,采用了两种不同的方式来描述组件。

第一种方式是使用一个普通的函数

function MyComponent(props) {}

第二种方式是使用一个

class MyComponent {}

实际上它们分别代表两类组件:函数式组件(Functional component) 和 有状态组件(Stateful component)。

它们的区别如下:

函数式组件:

  1. 是一个纯函数
  2. 没有自身状态,只接收外部数据
  3. 产出 VNode 的方式:单纯的函数调用

有状态组件:

  1. 是一个类,可实例化
  2. 可以有自身状态
  3. 产出 VNode 的方式:需要实例化,然后调用其 render 函数

欢迎关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值