javascript --- > [虚拟DOM] 初始化&& 实现

说明

  • 本篇主要说明为什么要使用虚拟DOM技术,以及如何实现简单的虚拟dom
  • 您将会学到:
    1.原生JS对DOM的操作
    2.虚拟DOM的相关概念
    3.DIFF算法的基础概念

为什么提出 -> DOM操作慢

  • 我们使用createElement属性来创建一个最常见的div,看看一个最常见的DOM有多少个属性
<script>
	const div = document.createElement('div');
	let str = '';
	for(let key in div){
		str += key + ' ';
	}
	console.log(str);
</script>

在这里插入图片描述

  • 可以看出,每个DOM其实是由很多内置的属性.因此,当DOM元素的操作过多的时候,其性能可想而知.
  • 这就迫使我们去想一个办法去减少DOM操作

为什么提出 -> 对比Ajax技术的出现

  • 早期的网页交互,是整个页面进行更新的.
  • 但是大多数时候,用户对页面的操作,只是一小部分,这就导致了大多数更新是多余的.
  • 于是产生了Ajax技术(网页的部分更新)
  • 可以模仿Ajax技术,去部分渲染DOM

为什么提出 -> DOM树的概念

  • 你可以会反驳,减少DOM的操作,不一定非要用到虚拟DOM,而可以直接对DOM进行操作.
  • 这就得先理解DOM树.
  • 先看一个浏览器得请求过程:
    1.用户输入网址后,浏览器像服务器发送HTTP请求获得HTML页面
    2.得到页面后,HTML解释器、词法分析器、语法分析器就会把HTML从字节流解释成DOM树的结构(过程比较复杂,也许会开一篇新文章具体说明)
    3.得到DOM树后,WebKit会分批次的将结果词语返回给渲染线程进行渲染
  • 上面对DOM的产生和渲染说的比较细了,这样说的主要原因是: 说明没有一个类或者方法,可以得到内存中待渲染的DOM树(有可能要,但是我不知道QAQ).
  • 下面开始逐步实现虚拟DOM

createElement

  • 我们想实现以下结构
    在这里插入图片描述
  • 语法如下:
let vertualDom = createElement('ul', { class: 'list'}, [
	createElement('ul', { class: 'list'}, ['a']),
	createElement('ul', { class: 'list'}, ['b']),
	createElement('ul', { class: 'list'}, ['c'])
])
  • 我们想通过createElement之后,变为对象,结构如下:
    在这里插入图片描述
  • 很显然,可以在创建虚拟节点时,返回一个VNode类,其中包含3个属性(type、props、children)
class VNode {
	constructor(type, props, children) {
		this.type = type;
		this.props = props;
		this.children = children;
	}
}

const createElement = (type, props, children) {
	return new VNode(type, props, children);
}
  • 上面之后,就可以返回一个虚拟DOM对象了.
  • 下面需要一个render方法,根据 虚拟DOM对象 来生成真实的DOM,并渲染.

render

  • render方法接收一个虚拟dom对象,根据对象创建真实的DOM
  • 1.首先我们根据传入的对象,创建ul
const render = (vnode) {
	let el = document.createElement(vnode.type);
	return el;
}
  • 打印一下:
let vertualDom = createElement('ul', { class: 'list' }, [
    createElement('ul', { class: 'list' }, ['a']),
    createElement('ul', { class: 'list' }, ['b']),
    createElement('ul', { class: 'list' }, ['c']),
])

let el = render(vertualDom);

console.log(el);

在这里插入图片描述

  • 2.有了DOM之后,我们给dom设置属性.由于属性可能比较多,
  • 因此我们使用for ... in 拿到键和值
  • 使用setAttribute来设置属性
const render = (vnode)  => {
	let el = document.createElement(vnode.type);
	let props = vnode.type;
	for(let key in props) {
		el.setAttribute(key, props[key]);
	}
}

在这里插入图片描述

  • 检测一下,改写vertualDom
let vertualDom = createElement('ul', { class: 'list', style:'border:1px solid black' }, [
    createElement('ul', { class: 'list' }, ['a']),
    createElement('ul', { class: 'list' }, ['b']),
    createElement('ul', { class: 'list' }, ['c']),
])
let el = render(vertualDom);
document.body.appendChild(el);

在这里插入图片描述

  • 现在有了节点和节点上面的属性,下面需要渲染其子元素…
  • 很自然的想到了递归.
  • 遍历其子元素,如果是VNode类型,就在调用render,否则认为其是一个文本节点.使用document.createTextNode创之
const render = (vnode) => {
    let el = document.createElement(vnode.type);
    let props = vnode.props;
    for (let key in props) {
        el.setAttribute(key, props[key]);
    }
    vnode.children.forEach(child => {
        child = child instanceof VNode ? render(child) : document.createTextNode(child);
        el.appendChild(child);
    })
    return el;
}

在这里插入图片描述

发布了177 篇原创文章 · 获赞 22 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/piano9425/article/details/103648182