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 前端开发
vue(1),小白看完都会了
vue(1),小白看完都会了
|
2天前
|
JavaScript 数据库
ant design vue日期组件怎么清空 取消默认当天日期
ant design vue日期组件怎么清空 取消默认当天日期
|
2天前
|
JavaScript C++
vue高亮显示组件--转载
vue高亮显示组件--转载
8 0
|
2天前
|
JavaScript 前端开发 数据安全/隐私保护
揭秘Vue中v-model的内部工作机制
揭秘Vue中v-model的内部工作机制
|
1天前
|
JavaScript 开发工具 git
Vue 入门系列:.env 环境变量
Vue 入门系列:.env 环境变量
7 1
|
2天前
|
缓存 监控 JavaScript
探讨优化Vue应用性能和加载速度的策略
【5月更文挑战第17天】本文探讨了优化Vue应用性能和加载速度的策略:1) 精简代码和组件拆分以减少冗余;2) 使用计算属性和侦听器、懒加载、预加载和预获取优化路由;3) 数据懒加载和防抖节流处理高频事件;4) 图片压缩和选择合适格式,使用CDN加速资源加载;5) 利用浏览器缓存和组件缓存提高效率;6) 使用Vue Devtools和性能分析工具监控及调试。通过这些方法,可提升用户在复杂应用中的体验。
9 0
|
2天前
|
JavaScript
vue知识点
vue知识点
10 0
|
2天前
|
JavaScript 前端开发 定位技术
Vue使用地图以及实现轨迹回放 附完整代码
Vue使用地图以及实现轨迹回放 附完整代码
Vue使用地图以及实现轨迹回放 附完整代码
|
2天前
|
JavaScript
Vue中避免滥用this去读取data中数据
Vue中避免滥用this去读取data中数据
|
2天前
|
JavaScript
vue中使用pinia及持久化
vue中使用pinia及持久化
5 0