2024年最新动手写一个简易的 Virtual DOM,加强阅读源码的能力(2),面试资料不拿回来安全吗

打开全栈工匠技能包-1小时轻松掌握SSR

两小时精通jq+bs插件开发

生产环境下如歌部署Node.js

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

网易内部VUE自定义插件库NPM集成

谁说前端不用懂安全,XSS跨站脚本的危害

webpack的loader到底是什么样的?两小时带你写一个自己loader

function generateList(list) {

// VDOM 生成过程,待下补上

}

patch(oldUL, generateList([“Banana”, “Apple”, “Orange”]));

不要介意patch函数,它的作用是就将更改的部分附加到DOM中。以后再改变DOM时:

patch(oldUL, generateList([“Banana”, “Apple”, “Mango”]));

patch函数发现只有第三个li发生了变化,,而不是所有三个元素都发生了变化,所以只会操作第三个 li 元素。

构建 VDOM!

我们需要做4件事:

  • 创建一个虚拟节点(vnode)

  • 挂载 VDOM

  • 卸载 VDOM

  • Patch (比较两个vnode,找出差异,然后挂载)

创建 vnode

function createVNode(tag, props = {}, children = []) {

return { tag, props, children}

}

在Vue(和许多其他地方)中,此函数称为h,hyperscript 的缩写。

挂载 VDOM

通过挂载,将vnode附加到任何容器,如#app或任何其他应该挂载它的地方。

这个函数将递归遍历所有节点的子节点,并将它们挂载到各自的容器中。

注意,下面的所有代码都放在挂载函数中。

function mount(vnode, container) { … }

创建DOM元素

const element = (vnode.element = document.createElement(vnode.tag))

你可能会想这个vnode.element是什么。 它只是一个内部设置的属性,我们可以根据它知道哪个元素是vnode的父元素。

props 对象设置所有属性。我们可以对它们进行循环

Object.entries(vnode.props || {}).forEach([key, value] => {

element.setAttribute(key, value)

})

挂载子元素,有两种情况需要处理:

  • children 只是文本

  • children 是 vnode 数组

if (typeof vnode.children === ‘string’) {

element.textContent = vnode.children

} else {

vnode.children.forEach(child => {

mount(child, element) // 递归挂载子节点

})

}

最后,我们必须将内容添加到DOM中:

container.appendChild(element)

最终的结果:

function mount(vnode, container) {

const element = (vnode.element = document.createElement(vnode.tag))

Object.entries(vnode.props || {}).forEach([key, value] => {

element.setAttribute(key, value)

})

if (typeof vnode.children === ‘string’) {

element.textContent = vnode.children

} else {

vnode.children.forEach(child => {

mount(child, element) // Recursively mount the children

})

}

container.appendChild(element)

}

卸载 vnode

卸载就像从DOM中删除一个元素一样简单:

function unmount(vnode) {

vnode.element.parentNode.removeChild(vnode.element)

}

patch vnode.

这是我们必须编写的(相对而言)最复杂的函数。要做的事情就是找出两个vnode之间的区别,只对更改部分进行 patch。

function patch(VNode1, VNode2) {

// 指定父级元素

const element = (VNode2.element = VNode1.element);

// 现在我们要检查两个vnode之间的区别

// 如果节点具有不同的标记,则说明整个内容已经更改。

if (VNode1.tag !== VNode2.tag) {

// 只需卸载旧节点并挂载新节点

mount(VNode2, element.parentNode)

unmount(Vnode1)

} else {

// 节点具有相同的标签

// 所以我们要检查两个部分

// - Props

// - Children

// 这里不打算检查 Props,因为它会增加代码的复杂性,我们先来看怎么检查 Children 就行啦

// 检查 Children

// 如果新节点的 children 是字符串

if (typeof VNode2.children == “string”) {

// 如果两个孩子完全不同

if (VNode2.children !== VNode1.children) {

element.textContent = VNode2.children;

}

} else {

// 如果新节点的 children 是一个数组

// - children 的长度是一样的

// - 旧节点比新节点有更多的子节点

// - 新节点比旧节点有更多的子节点

// 检查长度

const children1 = VNode1.children;

const children2 = VNode2.children;

const commonLen = Math.min(children1.length, children2.length)

// 递归地调用所有公共子节点的patch

for (let i = 0; i < commonLen; i++) {

patch(children1[i], children2[i])

}

// 如果新节点的children 比旧节点的少

if (children1.length > children2.length) {

children1.slice(children2.length).forEach(child => {

unmount(child)

})

}

// 如果新节点的children 比旧节点的多

if (children2.length > children1.length) {

children2.slice(children1.length).forEach(child => {

mount(child, element)

})

}

}

}

}

这是vdom实现的一个基本版本,方便我们快速掌握这个概念。当然还有一些事情要做,包括检查 props 和一些性能方面的改进。

现在让我们渲染一个vdom!

回到generateList例子。对于我们的vdom实现,我们可以这样做

function generateList(list) {

let children = list.map(child => createVNode(“li”, null, child));

return createVNode(“ul”, { class: ‘fruits-ul’ }, children)

最后

推荐一些系统学习的途径和方法。

路线图

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

HTML 和 CSS:

html5知识

css基础知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值