01 - Vue源码解读-数据驱动-dom生成
本片文章根据bilibili vue源码分析 视频练习及知识点记录
模仿vue原理 实现简单的DOM模板替换
1 | <div id="app"> |
1 | let data = { |
模板 要求一直在内存中 不会发生变化
数据 数据发生变化后,会引起 DOM 的变化
DOM 由模板和数据生成的
页面 由 DOM 产生
对 DOM 渲染方法进行封装
1 | <div id="app"> |
1 | /* |
对函数进行改造,使其能够转换DOM中多层嵌套的对象,例如other.behavior.name
1 | // 修改data对象里面的属性为多层嵌套 |
函数柯里化
目的是为了缓存一些内容,减少解析
- 柯里化: 一个函数原本有多个参数,传入一个参数,生成一个新函数,由新函数来接收剩余的参数,运行得到结果
- 偏函数: 一个函数原本有多个参数,传入一部分参数,生成一个新函数,由新函数来接收剩余的参数,运行得到结果
- 高阶函数: 一个函数,参数是一个函数,该函数对参数函数进行加工,得到加工后的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function createSplitAttribute(g) {
let paths = g.split('.')
return function splitAttribute(data) {
let res = data
// g.split('.').map(item => {
// res = res[item]
// })
//2.
let prop
while(prop = paths.shift()) {
// 当对一个变量赋值对象时,是对象的引用,变量中的某个值发生变化,对象中对应的值也会发生变化
// 但是给变量重新赋值对象,并不会造成原先对象的值发生变化,因为此时变量的引用已经发生变化,不在指向源对象
res = res[prop]
}
return res
}
}
// 调用
const split = createSplitAttribute(g.trim())
return split(data)
虚拟DOM
思路和深拷贝类似
将 真正的DOM 转化为 虚拟DOM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52// 虚拟DOM 减少了回流和重绘 提高了速度
/*
* <div /> => {tag: 'div'}
* <div title="1" class="c" /> => {tag: 'div',data: {title: '1', class: 'c'}}
* <div><div /></div> => {tag: 'div', children: [{tag: 'div'}]}
*/
class VNode {
/*
* tag: node.nodeValue
* data: node.attributes => obj => {node.attribute.nodeName: node.attribute.nodeValue}
*/
constructor(tag, data, value, type) {
this.tag = tag && tag.toLowerCase()
this.data = data
this.value = value
this.type = type
this.children = []
}
appendCild(vnode) {
this.children.push(vnode)
}
}
// node 为页面中真实的 DOM 节点
// 使用递归的方法提取出 DOM 节点中所有的子孙节点
// Vue源码中使用 栈 的方式
function getVnode(node) {
let nodeType = node.nodeType
let _vnode = null
// 判断nodeType类型
if(nodeType === 1) { // 元素节点
let nodeName = node.nodeName
// 获取 node 元素的 所有 attribute 属性, 是一个伪数组,需要转换成 对象
let attrs = node.attributes
let _attrs = Object.create(null)
for(let i = 0; i < attrs.length; i++) {
_attrs[attrs[i].nodeName] = attrs[i].nodeValue
}
_vnode = new VNode(nodeName, _attrs, undefined, nodeType)
// 然后处理元素节点的所有自节点
let childNodes = node.childNodes
for(let i = 0; i < childNodes.length; i++) {
_vnode.appendCild(getVnode(childNodes[i]))
}
} else if(nodeType === 3) { // 文本节点
// 文本节点没有 tag 属性 data属性
_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
}
return _vnode
}
let app = document.querySelector('#app')
let app1 = getVnode(app)
console.log(app1)将 虚拟DOM 转化为 真正的DOM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function parseVnode(vnode) {
const {tag, data, type, value, children} = vnode
let dom = null
if(type === 1) {
dom = document.createElement(tag)
for(let key in data) {
dom.setAttribute(key, data[key])
}
dom.nodeType = type
for(let i = 0; i < children.length; i++) {
// dom.append(parseVnode(children[i]))
dom.appendChild(parseVnode(children[i]))
}
} else if(type === 3) {
dom = document.createTextNode(tag)
dom.nodeValue = value
dom.nodeType = type
}
return dom
}
const dom = parseVnode(app1)
console.log(dom, 'dom')
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Comment
DisqusValine