Vue 系列(一):初探 Vue3
惯例:一般都从 Hello World
讲起
<template>
<div @click="helloWorld">
Hello World
</div>
</template>
<script lang="ts" setup>
const helloWorld = () => {
alert('Hello World');
};
</script>
上面是一个我们非常熟悉并且十分简单的 Vue
例子。该段代码在 Vue
框架中执行之后,会渲染出 Hello World
文本,点击该文本会出现一个写有 Hello World
的弹窗。本文将要探讨的问题就是:Vue 在背后用了什么魔法?
虚拟DOM
谈虚拟 DOM 之前,我们先看看真实的 DOM:
<div onclick="helloWorld()">
Hello World
</div>
这段代码的功能与前面的 Vue
代码一样,不同之处在于,她可以直接在浏览器中运行,而 Vue
代码要想实现同样的效果就需要把上面的代码进行转化,转化为可以直接运行的真实 DOM。那么 Vue
要如何进行转化呢?
Vue
首先会将模板(<template>..</template>
)编译为 JavaScript
对象,以上面 Vue
代码为例,通过编译器的一顿操作,将会转换为类似如下的代码:
{
tag: 'div',
props: { onClick: helloWorld },
children: 'Hello World'
}
说明
其实上面的 JavaScript
对象只是编译器处理过程中的一个中间代码,在后面的文章当中我们会详细介绍编译器,这里暂时先简单地理解~~
可以看到实际上就是利用 JavaScript
对象来描述了上面的 UI
代码,而这种通过使用 JavaScript
对象来描述 UI
的方式,就是所谓的 虚拟 DOM。
在编写 Vue
代码的时候,我们不仅仅只是在一个模板中写一下 UI
代码,我们肯定会引入各种各样的 组件,如果我们将上面的 Vue
代码封装成一个名为 HelloWorld
的组件,上述代码将变成:
<template>
<div>
<hello-world />
</div>
</template>
<script lang="ts" setup>
import HelloWorld from 'HelloWorld.vue';
</script>
那我们该如何用虚拟DOM描述组件呢?其实,组件就是一组DOM元素的封装,理解这句话之后我们就可以用虚拟DOM这样去描述组件了:
const HelloWorld = {
tag: 'div',
props: { onClick: helloWorld },
children: 'Hello World'
}
const vnode = {
tag: HelloWorld
}
可以看到,我们将组件封装成了一个 JavaScript
对象,然后在虚拟DOM中引用这个对象即可,当然,组件也可以是一个函数:
const HelloWorld = () => ({
tag: 'div',
props: { onClick: helloWorld },
children: 'Hello World'
})
const vnode = {
tag: HelloWorld
}
渲染器
前面我们讲了 Vue
会通过编译器将模板编译为虚拟DOM,那模板是怎么渲染成真实DOM的呢,这就需要了解 Vue
的渲染器了:
Vue
是通过渲染器将虚拟DOM渲染为真实DOM的。先不要想的太复杂,要想实现渲染器,实际上就是实现怎么将虚拟DOM转换为真实DOM,以上面的虚拟DOM为例,我们可以定义如下方法:
const mountElement = (vnode, container) => {
// 创建标签为tag的DOM元素
const el = document.createElement(vnode.tag);
// 将属性、事件添加到元素中,这里只实现了事件
for (const key in vnode.props) {
// 以 on 开头,说明是事件
if (/^on/.test(key)) {
el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key]);
}
}
// 处理 children
if (typeof vnode.children === 'string') {
// 文本直接添加
el.appendChild(document.createTextNode(vnode.children))
} else if (Array.isArray(vnode.children)) {
// 递归渲染子节点
vnode.children.foreach(child => renderer(child, el))
}
container.appendChild(el);
}
const renderer = (vnode, root) => {
// tag 类型为 string 说明是普通的标签
if (typeof vnode.tag === 'string') {
mountElement(vnode, root);
// 为 function 说明是组件,这里也可以为 object
} else if (typeof vnode.tag === 'function') {
const componentVnode = vnode.tag()
// 递归渲染
renderer(componentVnode, root);
}
}
上面我们就简单实现了一个将虚拟DOM转换为真实DOM的方法,这就是渲染器,当然 Vue
的渲染器要比这个复杂的多!
通过上面的介绍,我们其实可以看到 Vue
的工作流程实际上是这样的:
最后
本文只是对 Vue
的简单一瞥,主要是对 Vue
的整个设计思路有一个大致的了解,后续的文章将会对每一个模块进行详细地介绍。