- 作者:老汪软件技巧
- 发表时间:2024-09-10 21:01
- 浏览量:
前言
Vue.js 在其运行过程中会将模板编译成虚拟 DOM (VNode),然后再将 VNode 渲染成实际的 DOM 节点。这个过程是由 Vue 内部的编译器和渲染系统完成的.
虽然Vue 3 的虚拟 DOM 编译过程对于开发者来说通常是透明的,但了解这些内部机制有助于更好地理解和优化应用程序。
如果你对 Vue 3 的内部实现感兴趣,可以查阅 Vue 3 的官方文档或阅读 Vue 3 的源码来深入了解这一过程。
Vue 3 中虚拟 DOM 的编译过程1. 模板编译
在 Vue 3 中,模板编译主要由两个阶段组成:解析和优化。
2. 生成渲染函数
一旦 AST 被创建并优化后,编译器会生成一个渲染函数,这个函数可以用来创建虚拟 DOM 节点(VNode)。渲染函数通常会利用 Vue 内置的 h 函数(createVNode 的别名)来创建 VNode。
3. 创建虚拟 DOM (VNode)
在 Vue 3 中,h 函数被用来创建 VNode。一个 VNode 是一个 JavaScript 对象,它包含了关于 DOM 节点的信息,如标签名、属性、子节点等。例如:
const vnode = h(
'div', // 标签名
{ id: 'app' }, // 属性对象
'Hello Vue 3!' // 子节点
);
4. 渲染到真实 DOM
当 VNode 被创建后,Vue 会使用高效的算法来比较新旧 VNode,并更新真实的 DOM。这个过程称为 patching。Vue 3 的 diff 算法旨在最小化 DOM 操作,从而提高性能。
今天来简单介绍一下如何将一份虚拟dom转成真实dom。
vdomToDom
虚拟dom结构已有,挂载到root节点上,请问如何实现render函数?
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="root">div>
<script>
const vnode = {
tag: 'div',
attrs: {
id: 'app',
class:'box'
},
children: [
{
tag: 'span',
children: [{
tag: 'a',
children: [],
}],
},
{
tag: 'span',
children: [{
tag: 'a',
children: [],
}]
}
]
}
render(vnode,document.getElementById('root'))
function render(vnode, container) {
}
script>
body>
html>
我们先来看一眼这份虚拟dom长什么样。
首先最外层有个id为app类名为box的div,里面有两个子节点span,第一个子节点中又有一个a,第二个子节点中也有一个a
那么vue中编译dom的原理是什么,我们来一份简易版看看。
首先我们就想有一个方法只要给一个虚拟dom就能生成dom,然后将其挂载到root上去,接下来就是如何实现createDom
function render(vnode, container) {
const newDom = createDom(vnode)
container.appendChild(newDom)
}
function createDom(vnode) {
const { tag, attrs, children } = vnode
const dom = document.createElement(tag)
if (typeof attrs === 'object' && attrs !== null) {
updateProps(dom, {}, attrs) // 为dom添加属性
}
if (children.length > 0) {
reconcileChildren(children, dom) // 为dom添加子容器
}
return dom
}
然后思考,如何为子容器添加属性以及如何为容器添加子容器?
function updateProps(dom, oldProps = {}, newProps = {}) {
for (const key in newProps) {
if (key === 'style') {
let styleObj = newProps[key]
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr]
}
} else { // id / class
dom[key] = newProps[key]
}
}
}
function reconcileChildren(children, dom) {
for (let child of children) {
render(child, dom)
}
}
完整代码:
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="root">div>
<script>
const vnode = {
tag: 'div',
attrs: {
id: 'app',
className: 'box'
},
children: [
{
tag: 'span',
children: [{
tag: 'a',
children: [],
}],
},
{
tag: 'span',
children: [{
tag: 'a',
children: [],
}]
}
]
}
render(vnode, document.getElementById('root'))
function render(vnode, container) {
const newDom = createDom(vnode)
container.appendChild(newDom)
}
function createDom(vnode) {
const { tag, attrs, children } = vnode
const dom = document.createElement(tag)
if (typeof attrs === 'object' && attrs !== null) {
updateProps(dom, {}, attrs) // 为dom添加属性
}
if (children.length > 0) {
reconcileChildren(children, dom) // 为dom添加子容器
}
return dom
}
function updateProps(dom, oldProps = {}, newProps = {}) {
for (const key in newProps) {
if (key === 'style') {
let styleObj = newProps[key]
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr]
}
} else { // id / class
dom[key] = newProps[key]
}
}
}
function reconcileChildren(children, dom) {
for (let child of children) {
render(child, dom)
}
}
script>
body>
html>
效果