前言
认认真真学习vue源码,从0到1。。。
像Vue、React 这样的框架,对于其模板转换成虚拟DOM,再进行后续操作的用法应该耳熟能详,那么怎么实现一个DOM和虚拟DOM的相互转换呢? 这里我用的是递归的方式简单的做转化,与Vue、React底层实现的方式不同,只是为了更好的了解DOM和虚拟DOM。
首先要了解为什么需要虚拟DOM?
由于在我们日常开发中,经常需要修改DOM,频繁操作真实DOM会触发重绘和重排,严重影响页面性能,所以提出了虚拟DOM,作为真实DOM的抽象,以对象的形式存储DOM信息,缓存在内存中,一次性更新真实DOM,提高性能。
准备知识
1. DOM是什么?
详细的DOM原生API可以参考MDN:DOM
DOM是 JavaScript 操作网页的接口,全称为“文档对象模型”, DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。
节点(node)是DOM树的最小单位,文档的树形结构就是有各个不同列类型(Document、Element、Text)的节点组成的。
详细的NODE原生API参考MDN:NODE
在一个html文件中写一段简单的DOM结构
<div id="root" class="c1">
<p>你好</p>
</div>
<script>
const root = document.querySelector('#root')
console.dir(root)
console.dir(root.childNodes)
</script>
在控制台打印出DOM结构
2. NODE的属性
-
nodeName:node的名字,如果是element那名字是大写的,其他的名字前面写上#。
-
nodeType:node的类型,一般用数字表示,1表示element(也可以用Node.ELEMENT_NODE来表示),3表示text(Node.TEXT_NODE)。
如果是element,那么nodeName === tagName
如果是text,那么nodeName = #text, tagName = undefined
-
nodeValue:当前节点的值,对于text, comment节点来说, nodeValue返回该节点的文本内容,对于 attribute 节点来说, 返回该属性的属性值,而对于document和element节点来说,返回null
真实DOM => 虚拟Dom
// 创建VDom类
class VDom {
constructor(tag, data, value, type) {
this.tag = tag && tag.toLowerCase() // 节点名
this.data = data // 属性
this.value = value // 文本数据
this.type = type // 节点类型
this.children = []
}
appendChild(vnode) {
this.children.push(vnode)
}
}
function getVNode(node) {
let nodeType = node.nodeType
let _vnode = null
if (nodeType === 1) {
// 元素
let tag = node.nodeName
let attrs = node.attributes
let _attrObj = {}
for (let i = 0; i< attrs.length; i++) {
_attrObj[attrs[i].nodeName] = attrs[i].nodeValue
}
_vnode = new VDom(tag, _attrObj, undefined, nodeType)
let children = node.childNodes
for (let i = 0; i < children.length; i++) {
_vnode.appendChild(getVNode(children[i]))
}
} else if (nodeType === 3) {
// 文本
_vnode = new VDom(node.nodeName, undefined, node.nodeValue, nodeType)
}
return _vnode
}
const vroot = getVNode(document.querySelector('#root'))
console.log(vroot)
最终转换的VDom格式如下:
虚拟DOM => 真实DOM
这里有一个知识点,如果通过一个nodeName创建一个元素节点?
如何通过一段文本创建一个文本节点?
createElement: 通过指定名称创建一个元素
createTextNode:创建文本节点
function parseVNode(vnode) {
let type = vnode.type
let rdom = null
if (type === 1) {
rdom = document.createElement(vnode.tag)
// 元素
let attrs = vnode.data
for (let key in attrs) {
rdom.setAttribute(key, attrs[key])
}
let children = vnode.children
for (let i = 0; i < children.length; i++) {
rdom.appendChild(parseVNode(children[i]))
}
} else if (type === 3) {
// 文本
rdom = document.createTextNode(vnode.value)
}
return rdom
}
const realRoot = parseVNode(vroot)
console.log(realRoot)
document.body.appendChild(realRoot)
总结
本文主要是用递归的方式,用原生js实现真实DOM和虚拟DOM直接的转化,是很基础的知识点,现在很多人用框架久了不会使用原生方法了,做这些小练习对于了解原生js和阅读框架源码都有好处。
文章如有错误请大家及时指出,莫怪莫怪!
二话不多说,滚去继续学习啦~~~
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!