【拆解Vue3】Vue是如何运行的?

488 阅读4分钟

我正在参与掘金创作者训练营第5期, 点击了解活动详情

用模板描述界面

Vue区别于其它框架的一个显著特征是,开发者可以使用Vue提供的模板语法来描述界面。利用这种类似HTML的模板语法,提供了一种符合直觉的开发体验。但模板终究不是HTML,它到底是如何作用的?在Vue这个大框架中又扮演什么角色呢?

回忆一下,在写模板的时候,我们会用到的东西。让我们再具体一点,用模板描述一个DOM元素需要什么:

  • 标签:这个元素是什么标签?
  • 属性:这个元素上有什么属性(id,class等)?
  • 事件:这个元素能触发什么事件(click,keydown等)?
  • 子元素:这个元素里有没有嵌套子元素(可能有多个)?

例如,我们给出以下模板:

<div id="demo" @click="handler">
    click
</div>

正如前文提到的,模板语法虽然像HTML,但不是HTML,因此,这样的模板是不能够直接被浏览器识别渲染的。不是HTML还要操作DOM,既然要操作DOM,JavaScript肯定能做。那接下来的问题就是,如何把模板变成JavaScript,并且还要能够保留模板中对元素的描述

用虚拟DOM描述界面

为了支撑模板语法,我们必须想个办法,能够在JavaScript中承接模板语法对元素的描述。想想模板在描述元素的时候都有什么?对元素的描述包括标签,属性,事件和子元素这四部分。把元素当作对象,尝试用JavaScript对象来描述元素。

const vnode = {
    tag: 'div',
    props: {
        id: 'demo',
        onClick: handler,
    },
    children: ['click'],
}

上面这段代码,使用了JavaScript对象来对元素进行描述,这种描述方式与使用模板描述元素在语义上完全相同。因此,我们可以认为,这两种描述方式都可以表示同一个DOM元素。这种用来描述元素JavaScript对象,就是虚拟DOM。我们可以模仿HTML解析的过程(参考理解DOM中HTML解析器的实现过程),将Vue模板代码编译为JavaScript对象。有了JavaScript对象,我们就能够进一步使用JavaScript操作DOM,来渲染我们描述的元素了。

在这里需要说明的是,如何你想跳过编译,Vue中同样支持手写渲染函数来描述DOM。

import { h } from 'vue'
export default {
    render() {
        return h('div', { id: 'demo', onClick: handler}, ['click'])
    }
}

Vue中提供了一个全局APIh,可以通过向该函数中传入属性的方式来返回一个JavaScript对象,即虚拟DOM。

未命名文件(19).png

渲染器做了什么?

如果说虚拟DOM是对元素的一种语义化描述,那么渲染器的任务就是把这种语义描述转变为可见的页面。虚拟DOM本质是一个JavaScript对象,自然可以很方便的取这个对象上的属性。我们还按照标签,属性,事件和子元素这四部分考虑渲染器如何渲染虚拟DOM。

  • 标签(tag):将vnode.tag作为标签名创建元素;
  • 属性(props):遍历vnode.props,若key不以on开头则为属性,添加属性到元素上;
  • 事件(props):遍历vnode.props,若key以on开头则为事件,将key值中的on去掉并转为小写字母(例如onClick => click),得到合法事件名,添加事件到元素上;
  • 子元素(children):递归渲染vnode.children

最后我们只需要给告诉浏览器,我们要将这个DOM元素挂载在哪个地方,我们就能渲染一个虚拟DOM显示到页面上了。

function renderer(vnode, container) {
  // 处理标签
  const el = document.createElement(vnode.tag)

  for(const key in vnode.props) {
    // 处理事件
    if(/^on/.test(key)) {/* 渲染事件 */}
    // 处理属性
    else {/* 渲染属性 */}
  }

  //处理children
  vnode.children.forEach(child => renderer(child, el))

  //挂载生成的元素
  container.appendChild(el)
}

当然,这个实现还较为简单,仅仅展示了本文示例的处理过程,真实的Vue中显然要考虑更复杂的组件渲染,响应式更新等问题。本文仅为了说明Vue的大致工作流程,前面提到的这些具体问题,会在后续更新中详细展开。

参考资料