Vue基础(7)

简介: Vue基础(7)

虚拟 DOM 的好处


虚拟 DOM 就是为了解决浏览器性能问题而被设计出来的。如前,若一次操作中有 10 次更新 DOM 的动作,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地一个 JS 对象中,最终将这个 JS 对象一次性 attchDOM 树上,再进行后续操作,避免大量无谓的计算量。所以,用 JS 对象模拟 DOM 节点的好处是,页面的更新可以先全部反映在 JS 对象(虚拟 DOM )上,操作内存中的 JS 对象的速度显然要更快,等更新完成后,再将最终的 JS 对象映射成真实的 DOM,交由浏览器去绘制。

虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI。

回到最开始的问题,虚拟 DOM 到底是什么,说简单点,就是一个普通的 JavaScript 对象,包含了 tagpropschildren 三个属性。

接下来我们手动实现下 虚拟 DOM。

分两种实现方式:

一种原生 js DOM 操作实现;

另一种主流虚拟 DOM 库(snabbdom、virtual-dom)的实现(用h函数渲染)(暂时还不理解)

算法实现

(1) 用 JS 对象模拟 DOM 树:

<div id="virtual-dom">
    <p>Virtual DOM</p>
    <ul id="list">
      <li class="item">Item 1</li>
      <li class="item">Item 2</li>
      <li class="item">Item 3</li>
    </ul>
    <div>Hello World</div>
</div> 

我们用 JavaScript 对象来表示 DOM 节点,使用对象的属性记录节点的类型、属性、子节点等。

/**
 * Element virdual-dom 对象定义
 * @param {String} tagName - dom 元素名称
 * @param {Object} props - dom 属性
 * @param {Array<Element|String>} - 子节点
 */
function Element(tagName, props, children) {
    this.tagName = tagName;
    this.props = props;
    this.children = children;
    // dom 元素的 key 值,用作唯一标识符
    if (props.key) {
        this.key = props.key
    }
}
function el(tagName, props, children) {
    return new Element(tagName, props, children);
}

构建虚拟的 DOM ,用 javascript 对象来表示

let ul = el('div', { id: 'Virtual DOM' }, [    el('p', {}, ['Virtual DOM']),
    el('ul', { id: 'list' }, [        el('li', { class: 'item' }, ['Item 1']),
        el('li', { class: 'item' }, ['Item 2']),
        el('li', { class: 'item' }, ['Item 3'])
    ]),
    el('div', {}, ['Hello, World'])
])

现在 ul 就是我们用 JavaScript 对象表示的 DOM 结构,我们输出查看 ul 对应的数据结构如下:

1670073567377.jpg

(2) 将用 js 对象表示的虚拟 DOM 转换成真实 DOM:需要用到 js 原生操作 DOM 的方法。

/**
 * render 将virdual-dom 对象渲染为实际 DOM 元素
 */
Element.prototype.render = function () {
    // 创建节点
    let el = document.createElement(this.tagName);
    let props = this.props;
    // 设置节点的 DOM 属性
    for (let propName in props) {
        let propValue = props[propName];
        el.setAttribute(propName, propValue)
    }
    let children = this.children || []
    for (let child of children) {
        let childEl = (child instanceof Element)
        ? child.render() // 如果子节点也是虚拟 DOM, 递归构建 DOM 节点
        : document.createTextNode(child) // 如果是文本,就构建文本节点
        el.appendChild(childEl);
    }
    return el;
}

我们通过查看以上 render 方法,会根据 tagName 构建一个真正的 DOM 节点,然后设置这个节点的属性,最后递归地把自己的子节点也构建起来。

我们将构建好的 DOM 结构添加到页面 body 上面,如下:

let ulRoot = ul.render();
document.body.appendChild(ulRoot);

这样,页面 body 里面就有真正的 DOM 结构,效果如下图所示:

1670073585920.jpg

我们知道虚拟 DOM 的好处和虚拟 DOM 的实现后就要讲讲 key 的作用了。

贴一下上面实现地完整代码

<script>
    /**
         * Element virdual-dom 对象定义
         * @param {String} tagName - dom 元素名称
         * @param {Object} props - dom 属性
         * @param {Array<Element|String>} - 子节点
         */
    function Element(tagName, props, children) {
        this.tagName = tagName;
        this.props = props;
        this.children = children;
        // dom 元素的 key 值,用作唯一标识符
        if (props.key) {
            this.key = props.key
        }
    }
    function el(tagName, props, children) {
        return new Element(tagName, props, children);
    }
    let ul = el('div', { id: 'Virtual DOM' }, [
        el('p', {}, ['Virtual DOM']),
        el('ul', { id: 'list' }, [
            el('li', { class: 'item' }, ['Item 1']),
            el('li', { class: 'item' }, ['Item 2']),
            el('li', { class: 'item' }, ['Item 3'])
        ]),
        el('div', {}, ['Hello, World'])
    ])
    /**
             * render 将virdual-dom 对象渲染为实际 DOM 元素
             */
    Element.prototype.render = function () {
        // 创建节点
        let el = document.createElement(this.tagName);
        let props = this.props;
        // 设置节点的 DOM 属性
        for (let propName in props) {
            let propValue = props[propName];
            el.setAttribute(propName, propValue)
        }
        let children = this.children || []
        for (let child of children) {
            let childEl = (child instanceof Element)
            ? child.render() // 如果子节点也是虚拟 DOM, 递归构建 DOM 节点
            : document.createTextNode(child) // 如果是文本,就构建文本节点
            el.appendChild(childEl);
        }
        return el;
    }
    let ulRoot = ul.render();
    document.body.appendChild(ulRoot);
    console.log(ul);
</script>

虚拟DOM中key的作用

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
  • ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
  • ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
  • 旧虚拟DOM中未找到与新虚拟DOM相同的key
  • 创建新的真实DOM,随后渲染到到页面。
相关文章
|
2天前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
10 3
|
4天前
|
JavaScript 前端开发 开发者
Vue v-for 进阶指南:in 与 of 的区别及应用场景 | 笔记
Vue.js 中的 v-for 是强大的遍历指令,但其中的 in 和 of 关键字往往被开发者忽视。尽管它们的用法相似,但适用的场景和数据结构却各有不同。本文将详细探讨 v-for 中 in 和 of 的区别、适用场景以及在实际开发中的最佳使用时机。通过理解它们的差异,你将能够编写更加高效、简洁的 Vue.js 代码,灵活应对各种数据结构的遍历需求。
40 6
|
2天前
|
缓存 JavaScript
Vue 中 computed 与 method 的区别
【10月更文挑战第15天】computed 和 method 是 Vue 中两个重要的选项,它们在功能和特点上存在着明显的区别。理解并合理运用它们的区别,可以帮助我们构建更高效、更具可维护性的 Vue 应用。在实际开发中,要根据具体情况灵活选择使用,以满足不同的需求。
5 2
|
2天前
|
JavaScript 搜索推荐 UED
vue的自定义指令
【10月更文挑战第14天】Vue 自定义指令为我们提供了一种强大的工具,使我们能够更灵活地控制和扩展 Vue 应用的行为。通过合理地使用自定义指令,可以提高开发效率,增强应用的功能和用户体验。
|
3天前
|
JavaScript
|
4天前
|
缓存 JavaScript 前端开发
Vue 中动态导入的注意事项
【10月更文挑战第12天】 在 Vue 项目中,动态导入是一种常用的按需加载模块的技术,可以提升应用性能和效率。本文详细探讨了动态导入的基本原理及注意事项,包括模块路径的正确性、依赖关系、加载时机、错误处理、缓存问题和兼容性等,并通过具体案例分析和解决方案,帮助开发者更好地应用动态导入技术。
|
4天前
|
JavaScript API
vue 批量自动引入并注册组件或路由等等
【10月更文挑战第12天】 vue 批量自动引入并注册组件或路由等等
|
5天前
|
JavaScript 算法 前端开发
深入剖析Vue中v-for的使用及index作为key的弊端
深入剖析Vue中v-for的使用及index作为key的弊端
13 2
|
4天前
|
JavaScript UED
Vue + ElementUI 实现动态添加和删除表单项的多层嵌套表单
【10月更文挑战第5天】本示例展示了如何在 Vue.js 中使用 Element UI 组件实现动态添加和删除嵌套表单项。该表单包含设备信息、设备部位及其对应的任务列表,支持用户动态添加设备部位和任务,并提供相应的表单验证规则。
23 0
Vue + ElementUI 实现动态添加和删除表单项的多层嵌套表单
|
4天前
|
JavaScript
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
10 1