1.vue虚拟DOM的作用

  • 具备跨平台的优势

由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。

  • 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。

因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)

  • 提升渲染性能

Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。

为了实现高效的DOM操作,一套高效的虚拟DOM diff算法显得很有必要。我们通过patch 的核心----diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新。比如修改某个model 100次,从1加到100,那么有了Virtual DOM的缓存之后,只会把最后一次修改patch到view上。

2.模拟实现虚拟DOM

VN虚拟dom:把html这个结构(名称,样式,属性等)转换成一个jsduixiang ,通过操作js对象操作虚拟dom。

怎么操作虚拟dom实现页面更新渲染:页面在初次渲染的时候,把页面html结构(DOM树 )转换成一个js对象,如果页面再有更新的时候,把更新之后的虚拟dom的js对象和之前的虚拟dom对象进行对比。通过vue提供的diff算法,找到需要更新的节点,针对需要更新的节点进行局部更新,提高渲染效率。

代码如下:

<body>
    <!-- VN虚拟dom:把html这个结构(名称,样式,属性等)转换成一个jsduixiang ,通过操作js对象操作虚拟dom -->
    <!-- 怎么操作虚拟dom实现页面更新渲染:页面在初次渲染的时候,把页面html结构(DOM树 )转换成一个js对象,如果页面再有更新的时候,把更新之后的虚拟dom的js对象和之前的虚拟dom对象进行对比。通过vue提供的diff算法,找到需要更新的节点,针对需要更新的节点进行局部更新,提高渲染效率  -->
    <div id="app">
        <button>点击更新年龄</button>
        <ul>
            <li>{{name}}</li>
            <li>{{age}}</li>
            <li>{{phone}}</li>
        </ul>
    </div>

</body>
<script src="../vue.js"></script>
<script>
    // 模拟虚拟dom更新的过程
    var app = new Vue({
        // el:"#app", 需要注释这句,
        data: {
            name: "张三",
            age: 20,
            phone: 110
        },
        // 1.先初次获取DOM结构,用来去模拟el:"#app"
        created() {
            // 用原生的dom操作来获取dom结构
            var appHtml = document.getElementById("app")
            var tempData = appHtml.innerHTML
            // 把html结构里面的{{}}替换成data数据
            tempData = tempData.replace("{{name}}", this.name)
            // replace() 替换原字符串的部分字符,原字符串不会改变的
            tempData = tempData.replace("{{age}}", this.age)
            tempData = tempData.replace("{{phone}}", this.phone)
            appHtml.innerHTML = tempData

            var btn = document.querySelector("#app button")
            btn.addEventListener("click", this.update)

            // 2.把初次获取dom结构转换成虚拟dom对象
            this.VN = {
                type: "div", //type代表的是元素的名称
                id: "app", // 把div的id属性转换成对象
                children: [  // children 元素的子元素
                    {
                        type: "button",
                        content: "点击更新年龄",// content标签内容
                        event: { //event 事件
                            name: "click",//事件名称
                            fun: this.update //事件函数
                        }
                    },
                    {
                        type: "ul",
                        children: [
                            {
                                type: "li",
                                content: this.name
                            },
                            {
                                type: "li",
                                content: this.age
                            },
                            {
                                type: "li",
                                content: this.phone
                            }
                        ]
                    }
                ]
            }

        },
        methods: {
            update() {
                // 3.更新数据并且生成新的虚拟dom对象
                this.age = 40
                this.newVN = {
                    type: "div", //type代表的是元素的名称
                    id: "app", // 把div的id属性转换成对象
                    children: [  // children 元素的子元素
                        {
                            type: "button",
                            content: "点击更新年龄",// content标签内容
                            event: { //event 事件
                                name: "click",//事件名称
                                fun: this.update //事件函数
                            }
                        },
                        {
                            type: "ul",
                            children: [
                                {
                                    type: "li",
                                    content: this.name
                                },
                                {
                                    type: "li",
                                    content: this.age
                                },
                                {
                                    type: "li",
                                    content: this.phone
                                }
                            ]
                        }
                    ]
                }
                console.log(this.VN,this.newVN);
                // 4.把更新之前的虚拟dom对象和更新之后的虚拟dom对象进行对比,通过diff算法进行对比,返回需要更新的节点
                function diff(oldVN,newVN){
                    return [{
                        path:"#app>ul>li:nth-child(2)",
                        value:this.age
                    }]
                }
                // 5.遍历所有需要更新的对象,然后针对性的去更新dom节点
                for (const obj of diff.call(this)) {
                    // 实现局部的更新
                    document.querySelector(obj.path).innerHTML = obj.value                    
                }
            }
        },
    })

</script>