前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从头创建您自己的vuei .js——第3部分(构建VDOM)

从头创建您自己的vuei .js——第3部分(构建VDOM)

作者头像
公众号---人生代码
发布2020-09-23 14:40:08
6440
发布2020-09-23 14:40:08
举报
文章被收录于专栏:人生代码人生代码

从头创建您自己的vuei .js——第3部分(构建VDOM)

如果你喜欢这篇文章,你可能也会喜欢我的推特。如果你很好奇,可以看看我的Twitter简介。?

这是“从头创建您自己的vuei .js”系列文章的第三部分,在这里我将教您如何创建响应式框架(比如vuei .js)的基础知识。要阅读这篇博客文章,我建议您阅读本系列的第一部分和第二部分。

这篇文章一开始可能很长,但可能不像它看起来那么专业。它描述了代码的每一步,这就是为什么它看起来很复杂的原因。但容忍我,所有这一切将在最后?完美的意义

Building the Virtual DOM

The skeleton

在本系列的第2部分中,我们了解了虚拟DOM如何工作的基础知识。从要点的最后一点复制VDOM框架。我们使用该代码进行跟踪。您还可以在那里找到VDOM引擎的完成版本。我还创建了一个Codepen,您可以在其中使用它。

Creating a virtual node

因此,要创建一个虚拟节点,我们需要标签、属性和子节点。我们的函数是这样的:

代码语言:javascript
复制
function h(tag, props, children){ ... }

(在Vue中,创建虚拟节点的函数命名为h,这就是我们在这里的调用方式。) 在这个函数中,我们需要一个以下结构的JavaScript对象。

代码语言:javascript
复制
{
    tag: 'div',
    props: {
        class: 'container'
    },
    children: ...
}

要实现这一点,我们需要在一个对象中包装标签、属性和子节点参数并返回:

代码语言:javascript
复制
function h(tag, props, children) {
    return {
        tag,
        props,
        children,
    }
}

这就是虚拟节点创建的全部内容。

Mount a virtual node to the DOM

我将虚拟节点挂载到DOM的意思是,将其附加到任何给定的容器。这个节点可以是原始容器(在我们的示例中是#app-div),也可以是另一个虚拟节点(例如,在

这将是一个递归函数,因为我们必须遍历所有节点的子节点并将其挂载到各自的容器中。

我们的挂载函数看起来像这样:

代码语言:javascript
复制
function mount(vnode, container) { ... }

我们需要创建一个DOM元素

代码语言:javascript
复制
const el = (vnode.el = document.createElement(vnode.tag))

2)我们需要将属性(道具)设置为DOM元素的属性:

我们通过迭代它们来做到这一点,像这样:

代码语言:javascript
复制
for (const key in vnode.props) {
    el.setAttribute(key, vnode.props[key])
}

3)我们需要装载元素中的子元素

记住,有两种类型的孩子:

一个简单的文本

虚拟节点数组

我们处理:

代码语言:javascript
复制
// Children is a string/text
if (typeof vnode.children === 'string') {
    el.textContent = vnode.children
}

// Chilren are virtual nodes
else {
    vnode.children.forEach(child => {
        mount(child, el) // Recursively mount the children
    })
}

在这段代码的第二部分中可以看到,使用相同的挂载函数挂载子线程。这将递归地继续,直到只剩下“文本节点”。然后递归停止。

作为挂载函数的最后一部分,我们需要将创建的DOM元素添加到相应的容器中:

代码语言:javascript
复制
container.appendChild(el)

Unmount a virtual node from the DOM

在卸载函数中,我们从实际DOM中的父节点中删除给定的虚拟节点。该函数仅将虚拟节点作为参数。

代码语言:javascript
复制
function unmount(vnode) {
    vnode.el.parentNode.removeChild(vnode.el)
}

Patch a virtual node

这意味着使用两个虚拟节点,比较它们,并找出它们之间的区别。

到目前为止,这是我们将为虚拟DOM编写的最广泛的函数,不过请耐心听我说。

1)分配我们将要使用的DOM元素

代码语言:javascript
复制
const el = (n2.el = n1.el)

2)检查节点是否属于不同的标签

如果节点具有不同的标记,我们可以假设内容完全不同,只需完全替换节点即可。我们通过挂载新节点并卸载旧节点来实现这一点。

代码语言:javascript
复制
if (n1.tag !== n2.tag) {
    // Replace node
    mount(n2, el.parentNode)
    unmount(n1)
} else {
    // Nodes have different tags
}

如果节点具有相同的标签;但是,它可以表示两种不同的意思:

新节点有字符串子节点

新节点有一组子节点

一个节点有字符串子节点的情况

在本例中,我们将继续使用“children”(实际上只是一个字符串)替换元素的textContent。

代码语言:javascript
复制
...
    // Nodes have different tags
    if (typeof n2.children === 'string') {
        el.textContent = n2.children
    }
...

4)如果节点有一组子节点

在这种情况下,我们必须检查孩子们之间的差异。有三种情况:

子结点的长度是一样的

旧节点比新节点有更多的子节点。在这种情况下,我们需要从DOM中删除“exceed”子节点

新节点比旧节点有更多的子节点。在本例中,我们需要向DOM添加额外的子元素。

所以首先,我们需要确定子节点的公共长度,或者换句话说,每个节点的子节点的最小值:

代码语言:javascript
复制
const c1 = n1.children
const c2 = n2.children
const commonLength = Math.min(c1.length, c2.length)

5) Patch common children

对于从点4)开始的每一种情况,我们需要修补节点共有的子节点:

代码语言:javascript
复制
for (let i = 0; i < commonLength; i++) {
    patch(c1[i], c2[i])
}

在长度相等的情况下,这已经是它了。没有什么可做的了。

6) Remove unneeded children from the DOM

如果新节点的子节点比旧节点少,则需要从DOM中删除这些子节点。我们已经为此编写了unmount函数,所以现在我们需要遍历额外的子节点并卸载它们:

代码语言:javascript
复制
if (c1.length > c2.length) {
    c1.slice(c2.length).forEach(child => {
        unmount(child)
    })
}

7) Add additional children to the DOM

如果新节点比旧节点有更多的子节点,我们需要将它们添加到DOM中。我们已经为此编写了mount函数。现在我们需要遍历额外的子节点并挂载它们:

代码语言:javascript
复制
else if (c2.length > c1.length) {
    c2.slice(c1.length).forEach(child => {
        mount(child, el)
    })
}

就是这样。我们发现了节点之间的每个差异,并相应地修正了DOM。这个解决方案没有实现的是属性的修补。这会使博客文章更长,而且会错过要点。

Rendering a virtual tree in the real DOM

我们的虚拟DOM引擎已经准备好了。为了演示它,我们可以创建一些节点并渲染它们。让我们假设我们想要以下HTML结构:

代码语言:javascript
复制
<div class="container">
    <h1>Hello World ?</h1>
    <p>Thanks for reading the marc.dev blog ?</p>
</div>

1) Create the virtual node with h

代码语言:javascript
复制
const node1 = h('div', { class: 'container' }, [
    h('div', null, 'X'),
    h('span', null, 'hello'),
    h('span', null, 'world'),
])

2) Mount the node to the DOM

我们想挂载新创建的DOM。在哪里?到文件最顶部的#app-div:

代码语言:javascript
复制
mount(node1, document.getElementById('app'))

结果应该是这样的:

3) Create a second virtual node

现在,我们可以创建第二个节点,并对其进行一些更改。让我们添加一些节点,这样结果将是这样的:

代码语言:javascript
复制
<div class="container">
    <h1>Hello Dev ?</h1>
    <p><span>Thanks for reading the </span><a href="https://marc.dev">marc.dev</a><span> blog</span></p>
    <img src="https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif" style="width: 350px; border-radius: 0.5rem;" />
</div>

下面是创建该节点的代码:

代码语言:javascript
复制
const node2 = h('div', { class: 'container' }, [
    h('h1', null, 'Hello Dev ?'),
    h('p', null, [
        h('span', null, 'Thanks for reading the '),
        h('a', { href: 'https://marc.dev' }, 'marc.dev'),
        h('span', null, ' blog'),
    ]),
    h(
        'img',
        {
            src: 'https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif',
            style: 'width: 350px; border-radius: 0.5rem;',
        },
        [],
    ),
])

如您所见,我们添加了一些节点,还更改了一个节点。

4) Render the second node

我们希望用第二个节点替换第一个节点,因此不使用mount。我们要做的是找出两者之间的区别,进行更改,然后渲染。所以我们修补它:

代码语言:javascript
复制
setTimeout(() => {
    patch(node1, node2)
}, 3000)

我在这里添加了一个超时,以便您可以看到代码DOM的变化。如果没有,就只能看到呈现的新VDOM。

Summary

就是这样!我们有一个非常基本的DOM引擎版本,它让我们:

创建虚拟节点

将虚拟节点挂载到DOM

从DOM中删除虚拟节点

找出两个虚拟节点之间的差异,并相应地更新DOM

你可以在我为你准备的Github要点中找到我们在这篇文章中做的代码。如果你只是想玩玩它,我还创建了一个Codepen,你可以这样做。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CryptoCode 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Building the Virtual DOM
    • The skeleton
    • Creating a virtual node
    • Mount a virtual node to the DOM
    • Unmount a virtual node from the DOM
    • Patch a virtual node
    • 1)分配我们将要使用的DOM元素
    • 2)检查节点是否属于不同的标签
    • 一个节点有字符串子节点的情况
    • 4)如果节点有一组子节点
    • 5) Patch common children
    • 6) Remove unneeded children from the DOM
    • 7) Add additional children to the DOM
    • Rendering a virtual tree in the real DOM
    • 1) Create the virtual node with h
    • 2) Mount the node to the DOM
    • 3) Create a second virtual node
    • 4) Render the second node
    • Summary
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档