虛擬 DOM

来源:https://www.cnblogs.com/iceflorence/archive/2018/04/25/8944148.html
-Advertisement-
Play Games

虛擬DOM :virtual dom(以下簡稱vdom,是vue和react的核心),使用比較簡單。 一,vdom是什麼,為何會存在vdom 1,什麼是vdom:用js模擬DOM結構,DOM操作非常‘昂貴’,DOM變化的對比,放在JS層來做(圖靈完備語言),提高重繪性能 需求:根據給出的數據,將該數 ...


虛擬DOM :virtual dom(以下簡稱vdom,是vue和react的核心),使用比較簡單。

一,vdom是什麼,為何會存在vdom

1,什麼是vdom:用js模擬DOM結構,DOM操作非常‘昂貴’,DOM變化的對比,放在JS層來做(圖靈完備語言),提高重繪性能

需求:根據給出的數據,將該數據展示成一個表格, 隨便修改一個信息, 表格也跟著修改,下麵使用jquery實現demo:

    <div id="container"></div>
    <button id="btn-change">change</button>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
    var data = [{
        name: '張三',
        age: '20',
        address: '北京'
    }, {
        name: '李四',
        age: '21',
        address: '上海'
    }, {
        name: '王五',
        age: '22',
        address: '廣州'
    }];

    // 渲染函數
    function render(data) {
        var $container = $('#container');
        // 清空容器,重要
        $container.html('');
        // 拼接table
        var $table = $('<table>');
        $table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>'));
        data.forEach(function(item) {
            $table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address + '</td></tr>'));
        })
        // 渲染到頁面
        $container.append($table);
    }
    // 修改信息
    $('#btn-change').click(function() {
        data[1].age = 30;
        data[2].address = '深圳';
        // re-render 再次渲染
        render(data)
    })

    // 頁面載入完立刻執行(初次渲染)
    render(data)

 

 

 

遇到的問題:DOM操作是昂貴的,改動後,整個container容器都重新渲染了一遍,相當於‘推倒重來’,如果項目複雜,非常影響性能

dom操作的屬性是非常多的,非常複雜,操作很昂貴,所以,儘量用js代替操作,例:

    var div = document.createElement('div');
    var item, result = '';
    for(item in div) {
        result += '|' + item;
    }
    console.log(result);

 


vdom可以解決這個問題

二,vdom如何應用,核心API是什麼

1,介紹snabbdom

   var vnode = h('ul#list', {}, [
        h('li.item', {}, 'Item 1'),
        h('li.item', {}, 'Item 2')
    ])
    {
        tag: 'ul',
        attrs: {
            id: 'list'
        },
        children: [{
            tag: 'li',
            attrs: { className: 'item' },
            children: ['Item 1']
        }, {
            tag: 'li',
            attrs: { className: 'item' },
            children: ['Item 2']
        }]
    }

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <div id="container"></div>
    <button id="btn-change">change</button>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
    <script>


    var snabbdom = window.snabbdom;

    // 定義patch
    var patch = snabbdom.init([
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners
    ])

    // 定義h
    var h = snabbdom.h;
    var container = document.getElementById('container');

    // 生成vnode
    var vnode = h('ul#list', {}, [
        h('li.item', {}, 'Item 1'),
        h('li.item', {}, 'Item 2'),
    ]);
    patch(container,vnode)

    // 模擬改變
    var btnChange = document.getElementById('btn-change');
    btnChange.addEventListener('click', function() {
        var newVnode = h('ul#list', {}, [
            h('li.item', {}, 'Item 1'),
            h('li.item', {}, 'Item 222'),
            h('li.item', {}, 'Item 333'),
        ]);
        patch(vnode,newVnode);
    })
    </script>
</body>

</html>

2,重做之前的demo

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <div id="container"></div>
    <button id="btn-change">change</button>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
    <script>
    var snabbdom = window.snabbdom;

    // 定義關鍵函數patch
    var patch = snabbdom.init([
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners
    ])

    // 定義關鍵函數 h
    var h = snabbdom.h;
    // 原始數據
    var data = [{
        name: '張三',
        age: '20',
        address: '北京'
    }, {
        name: '李四',
        age: '21',
        address: '上海'
    }, {
        name: '王五',
        age: '22',
        address: '廣州'
    }];
    // 把表頭也放在data中
    data.unshift({
        name: '姓名',
        age: '年齡',
        address: '地址',
    })

    var container = document.getElementById('container');

    
    var vnode;

    function render(data) {
        var newVnode = h('table',{},data.map(function(item){
            var tds = [];
            var i;
            for(i in item) {
                if(item.hasOwnProperty(i)) {
                    tds.push(h('td',{},item[i] + ''))
                }
            }
            return h('tr',{},tds)
        }))
        if(vnode) {
            // re-render
            patch(vnode,newVnode)
        } else {
            // 初次渲染
            patch(container,newVnode)
        }
        // 存儲當前vnode結果
        vnode = newVnode;

    }

    // 初次渲染
    render(data)

    var btnChange = document.getElementById('btn-change');
    btnChange.addEventListener('click', function() {
        data[1].age = 30;
        data[2].address = '深圳';
        // re-render
        render(data)
    })

</script> </body> </html>

 


3,核心API

h('標簽名',{...屬性...},[...子元素...]) //多個子元素
h('標簽名',{...屬性...},'...') //只有一個子元素
patch(container,vnode) //初次渲染,會把外層容器替代掉
patch(vnode,newVnode) //re-render

三,介紹diff演算法(vdom核心演算法)

1,vdom為何用diff演算法

diff 是linux的基礎命令,可以比較兩個文本文件的不同 git diff xxx;  vdom中應用diff演算法是為了找出需要更新的節點
比如新建兩個文本文件,log1.txt log2.txt

diff log1.txt log2.txt

diff線上對比:http://tool.oschina.net/diff 

使用vdom原因:DOM操作是昂貴的,因此儘量減少DOM操作

找出本次DOM必須更新的節點來更新,其他的不更新
這個找出的過程,就需要diff演算法 找出前後兩個vdom的差異

2,diff演算法的實現流程

vdom核心函數:h生成dom節點,patch函數-進行對比和渲染的
patch(container,vnode)   初次渲染,會把外層容器替代掉
patch(vnode,newVnode)   re-render

3,如何用vnode生成真是的dom節點

diff實現:
1,patch(container,vnode) 
2,patch(vnode,newVnode) 

核心邏輯:createElement 和 updateChildren

    // patch(container,vnode)
    function createElement(vnode) {
        var tag = vnode.tag;
        var attrs = vnode.attrs || {};
        var children = vnode.children || [];
        if (!tag) {
            return null;
        }
        // 創建真實的DOM元素
        var elem = document.createElement(tag);
        // 屬性
        var attrName;
        for (attrName in attrs) {
            if (attrs.hasOwnProperty(attrName)) {
                // 給elem添加屬性
                elem.setAttribute(attrName, attrs[attrName]);
            }
        }
        // 子元素
        children.forEach(function(childNode) {
            // 遞歸調用 createElement 給elem添加子元素
            elem.appendChild(createElement(childVnode)); //遞歸
        })
        // 返回真實的DOM元素
        return elem;
    }

    // patch(vnode,newVnode)
    function updateChildren(vnode, newVnode) {
        var children = vnode.children || [];
        var newChildren = newVnode.children || [];

        // 遍歷現有的children
        children.forEach(function(child, index) {
            var newChild = newChildren[index];
            if (newChild == null) {
                return;
            }
            if (child.tag === newChild.tag) {
                // 兩者tag一樣 深層次對比
                updateChildren(child, newChild);
            } else {
                // 兩者tag不一樣 替換
                replaceNode(child, newChild)
            }
        })
    }

    function replaceNode(vnode, newVnode) {
        var elem = vnode.elem; //真實的DOM節點
        var newElem = createElement(newVnode);
        // 替換
    }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • HTML5+CSS3從入門到精通是通過基礎知識+中小實例+綜合案例的方式,講述了用HTML5+ CSS3設計構建網站的必備知識,相對於專業指南、高級程式設計、開髮指南同類圖書,本書是一本適合快速入手的自學教程。內容有:創建HTML5文檔,實戰HTML5表單,實戰HTML5繪畫,HTML5音頻與視頻, ...
  • HTML是什麼,HTML5是什麼 HTML元素標簽、屬性都是什麼概念? 文檔類型是什麼概念,起什麼作用? meta標簽都用來做什麼的? Web語義化是什麼,是為瞭解決什麼問題 鏈接是什麼概念,對應什麼標簽? 常用標簽都有哪些,都適合用在什麼場景 表單標簽都有哪些,對應著什麼功能,都有哪些屬性 ol, ...
  • 1、console.log是最常用的輸入方法,正常化輸出語句,還具有print占位符整數(%d||%i),浮點數(%f),對象(%o),字元(%s); 2、console.error輸出錯誤化的語句 3、console.info輸出一條信息化語句 4、console.warn輸出警告化的語句 5、c ...
  • 正則表達式在程式應用中的使用是非常廣泛的。 首先正則表達式一般要寫兩個斜杠之間:/正則表達式/ 其次有兩個符號 ^ 和 $ ,符號 ^ 代表一個字元串的開始,而 $ 則代表一個字元串的結束。 舉個例子: 這裡聲明一個字元串str,聲明一個select,表示所有以asd開頭的字元串。 表示所有以jkl ...
  • 隊列實現 堆棧實現 ...
  • 圖片預覽 圖片預覽 --> ...
  • 示例 安裝 npm install form-create OR git clone https://github.com/xaboy/form-create.git cd form-create npm install 引入 <!-- import Vue --> <script src="nod ...
  • function isExists(obj, objarr) { if (JSON.stringify(objarr).indexOf(JSON.stringify(obj)) >= 0) { return true; } return false; } function enclosure(obj ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...