<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
11
{{name}}
<p>11</p>
</div>
<script>
let el = document.querySelector("#root")
class Vnode {
constructor(tag, data, value, type) {
this.tag = tag
this.data = data
this.value = value
this.type = type
this.children = []
}
appendChild(vnode) {
this.children.push(vnode)
}
}
class Vue {
constructor(options) {
this._data = options.data
this._el = document.querySelector(options.el)
this._vnode = null
this._ast = null
this.mount()
}
mount() {
this.render = this.createRenderFn()
this.mountComponent()
}
//柯里化-用来缓存 AST抽象语法树的
//返回render函数,调用render函数的话会生成vnode
createRenderFn() {
this._ast = this.getVnode(this._el)
//将抽象语法树 + data 结合生成 vnode
return (data) => {
let _tmp = this.combine(this._ast, data)
return _tmp
}
}
mountComponent() {
const mount = () => {
this.update(this.render(this._data))
}
mount.call(this)
}
//更新
update(vnode) {
this.updated(this.vnodeToReal(vnode))
}
updated(realDOM) {
this._el.parentNode.replaceChild(realDOM, this._el)
}
//data和 vnode结合
combine(vnode) {
if (vnode.type == 1) {
for (let i = 0; i < vnode.children.length; i++) {
this.combine(vnode.children[i])
}
} else if (vnode.type == 3) {
vnode.value = vnode.value.replace(/\{\{(.+?)\}\}/g, (_, $1) => {
return this.getValueByPath($1.trim(), this._data)
})
}
return vnode
}
//格式化 data 中的 数据路径
getValueByPath(str, data) {
let arr = str.replace(/\[/g, '.').replace(/\]/, '').split('.')
return arr.reduce((total, item, index) => {
return total[item]
}, data)
}
//真实DOM转虚拟DOM
getVnode(template) {
let type = template.nodeType
let vnode = null
if (type == 1) {
let tag = template.nodeName
let attrs = template.attributes
let attrsObj = {}
for (let i = 0; i < attrs.length; i++) {
attrsObj[attrs[i].nodeName] = attrs[i].nodeValue
}
vnode = new Vnode(tag, attrs, undefined, type)
let childNodesList = template.childNodes
for (let i = 0; i < childNodesList.length; i++) {
vnode.appendChild(this.getVnode(childNodesList[i]))
}
} else if (type == 3) {
let value = template.nodeValue
vnode = new Vnode(undefined, undefined, value, type)
}
return vnode
}
//虚拟DOM转真实DOM
vnodeToReal(vnode) {
let el
if (vnode.type == 1) {
el = document.createElement(vnode.tag)
// for (let key in vnode.data || {}) {
// el.setAttribute(key, vnode.data[key])
// }
for (let i = 0; i < vnode.children.length; i++) {
el.appendChild(this.vnodeToReal(vnode.children[i]))
}
} else if (vnode.type == 3) {
el = document.createTextNode(vnode.value)
}
return el
}
}
new Vue({
el: '#root',
data: {
name: 'Ren'
}
})
</script>
</body>
</html>
vue源码模拟02-生成虚拟dom、渲染数据、虚拟dom转真实dom
最新推荐文章于 2024-04-29 15:10:57 发布