虚拟DOM和diff算法 ,v-if和v-show区别

01-v-if指令

指令名称指令作用示例简写形式
v-if根据条件渲染数据v-if="score>=90"
v-show设置元素的displayv-show="age>=30"
<!-- 
    1.学习目标: v-if指令   
    2.学习路线:
        (1)作用: 根据条件渲染数据
        (2)语法: 
            单分支:     v-if="条件语句"
            双分支:     v-else
            多分支:      v-else-if="条件语句"
        (3)注意点
            v-else与v-else-if的前面  必须要有  v-if 或者 v-else-if
 -->

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>
​
<body>
​
    <!-- HTML结构 -->
    <div id="app">
        <!-- lazy :懒加载,输入框失去焦点vue才会渲染数据  -->
        <input type="text" v-model.lazy="score" placeholder="请输入考试分数">
        <h2>你的考试分数为:{{ score }}</h2>
        <hr>
        <h3 v-if="score>=90">爸爸给你买法拉利</h3>
        <h3 v-else-if="score>=80">爸爸给你买保时捷</h3>
        <h3 v-else-if="score>=60">爸爸给你买奥迪</h3>
        <h3 v-else>爸爸给你爱的掌声</h3>
    </div>
    <!-- 
        1.学习目标: v-if指令   
        2.学习路线:
            (1)作用: 根据条件渲染数据
            (2)语法: 
                单分支:     v-if="条件语句"
                双分支:     v-else
                多分支:      v-else-if="条件语句"
            (3)注意点
                v-else与v-else-if的前面  必须要有  v-if 或者 v-else-if
     -->
    <script>
        /* 创建vue实例 */
        let app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                score: '',
            }
        })
    </script>
</body>
</html>

02-v-show指令

<!-- 
    1.学习目标: v-show 指令   
    2.学习路线:
        (1)作用: 设置元素的display属性值
        (2)语法: v-show="属性值"
            属性值为true:   元素的display:block
            属性值为false:   元素的display:none
        (3)v-show与v-if区别
            v-if  : 条件渲染。  如果不满足条件,则该元素不会添加到DOM树中
            v-show: 显示与隐藏。 只是修改元素的display属性值
​
 -->

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>
​
<body>
​
    <!-- HTML结构 -->
    <div id="app">
        <p v-if="age>=30">我是v-if渲染出来的</p>
        <p v-show="age>=30">我是v-show渲染出来的</p>
    </div>
    <!-- 
        1.学习目标: v-show 指令   
        2.学习路线:
            (1)作用: 设置元素的display属性值
            (2)语法: v-show="属性值"
                属性值为true:   元素的display:block
                属性值为false:   元素的display:none
            (3)v-show与v-if区别
                v-if  : 条件渲染。  如果不满足条件,则该元素不会添加到DOM树中
                v-show: 显示与隐藏。 只是修改元素的display属性值
                
     -->
    <script>
        /* 创建vue实例 */
        let app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                age: 20,
            }
        })
    </script>
</body>
</html>

==知识点验收==

思考题1:v-if和v-show在功能上有什么区别?

v-show:一定会渲染,只是修改display属性

v-if:根据条件渲染

思考题2:列举v-if和v-show的使用场景(结合目前学过的案例

v-show : 频繁切换。 tab栏,鼠标移入移出隐藏

03-【面试题】v-if和v-show区别

  • 1.v-if : 本质是在动态的创建 或者 删除元素节点

    • 应用场景:如果是不用频繁切换, 要么显示, 要么隐藏的情况, 适合于用 v-if

      • v-if 是惰性的, 如果初始值为 false, 那么这些元素就直接不创建了, 节省一些初始渲染开销

  • 2.v-show: 本质是在控制元素的 css 样式, display: none;

    • 应用场景:如果是频繁的切换显示隐藏, 用 v-show

      • v-if, 频繁切换会大量的创建和删除元素, 消耗性能

  • 面试题v-if和v-show区别面试题逐字稿参考:

    • 面试官你好,我是这么理解v-if和v-show的。 v-if本质其实是动态的创建 或者 删除元素节点。一般不用频繁切换, 要么显示, 要么隐藏的情况, 我都会用 v-if。因为v-if 是惰性的, 如果初始值为 false, 那么这些元素就直接不创建了, 这样就可以节省一些初始渲染开销。v-show本质是在控制元素的 css 样式,display: none;,一般元素需要频繁的切换显示隐藏, 用 v-show。因为v-if在频繁切换会大量的创建和删除元素, 消耗性能。

04-vue的key值作用(了解)

<!DOCTYPE html>
<html lang="zh-CN">
​
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>
​
<body>
​
    <!-- HTML结构 -->
    <div id="app">
        <button v-text="type" @click="flag=!flag;type=flag?'手机号注册':'邮箱注册'" ></button>
        <div v-if="flag" key="phone">
            手机号:<input type="text" placeholder="请输入手机号">
            <br>
            验证码:<input type="text" placeholder="请输入验证码">
        </div>
        <div v-else>
            邮箱:<input type="text" placeholder="请输入邮箱">
            <br>
            密码:<input type="text" placeholder="请输入邮箱密码">
        </div>
    </div>
​
    <script>
        /* 
        1.学习目标 : vue中key值作用
            * (1)让vue准确的识别DOM元素 (类似于给元素添加一个身份证)
            * (2)让vue强制更新DOM
        
        2.应用场景:  
            2.1 使用v-if 切换元素
                * 什么时候用key值 : 切换的元素dom结构一致
            2.2 使用v-for 渲染列表 
                * 什么时候用key值 :所有的v-for推荐全部加上key值
        */
​
        /* 创建vue实例 */
        var app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                flag: true,
                type: '手机号注册'
            }
        })
    </script>
</body>
​
</html>

==知识点验收==

  • 问题1: :key="值" key值的作用是什么?

  • 问题2:key值实际应用场景?

05-虚拟DOM和diff算法

1.1-虚拟DOM

  • 1.vue底层会把挂载点中的真实DOM结构,转变为虚拟DOM。

    • 真实DOM : 我们webapi学习的dom对象, 一个对象里面有几百个属性

    • 虚拟DOM : 本质是一个js对象,只处理一些关键的属性。(其他的不处理)

  • 这是vue的挂载dom

<template>
    <div id="app">
        <p class="my_p">123</p>
    </div>
</template>
  • 对应的虚拟DOM结构

const dom = {
    type: 'div',
    attributes: [{id: 'box'}],
    children: {
        type: 'p',
        attributes: [{class: 'my_p'}],
        text: '123'
    }
}
  • 2.vue是如何更新渲染数据呢

    • 生成新的虚拟DOM结构

      • 把几百个dom属性,变成虚拟dom十几个关键属性

    • 和旧的虚拟DOM结构对比(diff算法)

    • 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁

==好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)==

==好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)==

  • 3.总结

    • 虚拟DOM是什么?

      • 本质就是一个JS对象, 保存DOM关键信息

    • 虚拟DOM的好处?

      • 提高DOM更新的性能, 不频繁操作真实DOM, 在内存中找到变化部分, 更新真实DOM

1.2-diff算法

vue就地复用策略:Vue会尽可能的就地(同层级,同位置),对比虚拟dom,复用旧dom结构,进行差异化更新。

虚拟dom: 本质就是一个个保存节点信息, 属性和内容的 描述真实dom的 JS 对象

diff算法:

  • 策略1:

    先同层级根元素比较,如果根元素变化,那么不考虑复用,整个dom树删除重建

    先同层级根元素比较,如果根元素不变,对比出属性的变化更新,并考虑往下递归复用。

  • 策略2:

    对比同级兄弟元素时,默认按照下标进行对比复用。

    对比同级兄弟元素时,如果指定了 key,就会 按照相同 key 的元素 来进行对比。

06-v-for指令key值说明

v-for指令使用key值几种情况

1.没有key值: 就地更新

2.key值为下标 : 就地更新 (与不设置key值没有区别)

3.key值为id (唯一字符串): 复用相同的key, 更新不同的key

* 同级父元素中,子元素的key值必须是唯一的,否则vue会报错。(类似于相同作用于变量名不能重复)

总结: v-for指令的key值优先使用唯一字符串(性能最高), 实在没有就用下标index

<!DOCTYPE html>
<html lang="zh-CN">
​
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>
​
<body>
​
    <!-- HTML结构 -->
    <div id="app">
        <ul>
            <li v-for="(item,index) in list" :key="item.id"> 
                <span>姓名:{{ item.name }}</span>
                <span>年龄:{{ item.age }}</span>
            </li>
        </ul>
​
    </div>
​
    <script>
        
        /* v-for指令使用key值几种情况
​
        1.没有key值: 就地更新
        2.key值为下标 : 就地更新 (与不设置key值没有区别)
        3.key值为id (唯一字符串):  复用相同的key, 更新不同的key
            * 同级父元素中,子元素的key值必须是唯一的,否则vue会报错。(类似于相同作用于变量名不能重复)
​
        总结:  v-for指令的key值优先使用唯一字符串(性能最高), 实在没有就用下标index
        */
        /* 创建vue实例 */
        var app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                list:[
                    {id:'21378',name:'张三',age:20},
                    {id:'12456',name:'李四',age:22},
                    {id:'39862',name:'王五',age:30},
                ]
            }
        })
    </script>
</body>
​
</html>

07-v-for更新检测

1.数组的方法分为两种 第一种: 会改变原数组的元素值, 例如 reverse()、pop()、push()等 * 这种情况 v-for也会更新 第二种:不会改变原数组的元素值. 例如 slice() 、 filter() 、 concat()等 * 这种情况 v-for不会更新

2.总结 : 如果v-for没有更新. 可以覆盖整个数组, 或 使用 vue.$set() 强制更新

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>

<body>

    <!-- HTML结构 -->
    <div id="app">
        <ul>
            <li v-for="item in list" >
                {{ item }}
            </li>
        </ul>
        <button @click="revBtn">数组翻转</button>
        <button @click="sliceBtn">截取前2个</button>
        <button @click="updateBtn">更新第一个元素值</button>
    </div>

    <script>

        /* v-for更新检测
        1.数组的方法分为两种
            第一种: 会改变原数组的元素值, 例如  reverse()、pop()、push()等
                * 这种情况 v-for也会更新
            第二种:不会改变原数组的元素值. 例如  slice() 、 filter() 、 concat()等
                * 这种情况 v-for不会更新

        2.总结 : 如果v-for没有更新.  可以覆盖整个数组, 或 使用 vue.$set() 强制更新
        */
        /* 创建vue实例 */
        var app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                list: [
                    10,20,30,40,50
                ]
            },
            //methods:事件处理函数
            methods: {
                revBtn() {
                    // 1. 数组翻转可以让v-for更新
                    this.list.reverse()
                },
                sliceBtn() {
                    // 2. 数组slice方法不会造成v-for更新
                    /* 原因:slice没有改变原始数组,只是从原始数组中获取元素
                       解决方案: 覆盖原始数组
                    */
                    let newArr = this.list.slice(0, 2)
                    this.list = newArr
                },
                updateBtn() {
                    // 3. 替换元素值,不会造成v-for更新
                    // this.list[0] = 88

                    /* 解决方案: this.$set()
                    参数1: 更新目标结构
                    参数2: 更新位置
                    参数3: 更新值
                    */
                    this.$set(this.list, 0, 88)
                }
            }
        })
    </script>
</body>

</html>

这些方法会触发数组改变, v-for会监测到并更新页面

  • push()

  • pop()

  • shift()

  • unshift()

  • splice()

  • sort()

  • reverse()

这些方法不会触发v-for更新

  • slice()

  • filter()

  • concat()

注意: vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组

总结: 改变原数组的方法才能让v-for更新

08-vue计算属性

  • vue计算属性官方文档:计算属性和侦听器 — Vue.js

  • 1.计算属性作用 : 解决渲染数据的代码冗余问题

    • 某些数据在渲染的时候,可能要经过一些复杂逻辑的计算处理

  • 2.计算属性的语法:在vue实例的computed对象中声明一个函数

    • 这个函数名计算属性的属性名

    • 在这个函数中 return 返回值计算属性的属性值

  • 3.注意点

    • 这个函数一定要写在vue实例的computed对象中

    • 这个函数一定要有返回值,否则计算属性无法渲染

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>

<body>
    <!-- HTML结构 -->
    <div id="app">
        <p>{{ msg }}</p>

        <p>{{ revMsg }}</p>
        <p>{{ revMsg }}</p>
        <p>{{ revMsg }}</p>
        <p>{{ revMsg }}</p>
        <p>{{ revMsg }}</p>
    </div>
    <!-- 
        1.学习目标: vue计算属性
        2.学习路线:
            (1)计算属性作用:  解决模板语法代码冗余问题
                * 场景 : 数据需要通过计算才能得到
            (2)计算属性语法 
                computed : {
                    计算属性名(){
                        return 计算属性值
                    }
                }
            (3)注意点
                a. 计算属性要写在vue实例computed中
                b. 计算属性函数一定要有返回值
            (4)计算属性缓存机制
                a. 当第一次使用计算属性的时候, vue会调用一次函数。然后把函数名和返回值平铺到vue实例中。
                b. 之后使用计算属性, vue不会调用这个函数,而是从缓存中直接读取。
                c. 只有当计算属性内部的数据发生变化的时候,才会重新执行一次这个函数,然后又放入缓存
     -->
    <script>
        /* 创建vue实例 */
        let app = new Vue({
            // 1. el:挂载点
            el: '#app',
            // 2.data: 要渲染的数据
            data: {
                msg: '我爱敲代码',
            },
            // 3.methods:事件方法
            // 4.computed:计算属性
            computed:{
                revMsg(){
                    console.log(1)
                    return this.msg.split('').reverse().join('')
                }
            }
        })
    </script>
</body>
</html>

==知识点验收==

  • 问题1:计算属性主要是用于解决什么问题的?

    • 解决data中数据渲染冗余问题

  • 问题2:计算属性好处

    • 缓存功能

      • 当计算属性依赖值不变,直接从缓存读取

      • 当计算属性依赖值发生变化,vue会重新执行一次函数,并且将返回值放入缓存中

09-计算属性的set与get方法

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>
</head>

<body>

    <!-- HTML结构 -->
    <div id="app">
        全名:<input type="text" placeholder="请输入你的全名" v-model="fullName">
        <br>
        姓氏:<input type="text" placeholder="请输入你的姓氏" v-model="firstName">
        <br>
        名字:<input type="text" placeholder="请输入你的名字" v-model="lastName">
    </div>

    <script>
        /* 
        1. 默认情况下,计算属性只有get方法。只能获取,不能修改。否则程序会报错
        2. 如果希望计算属性可以修改,则可以实现set方法
        */

        /* 创建vue实例 */
        var app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                firstName:'',
                lastName:'',
            },
            //计算属性
            computed:{
                /*计算属性两种写法 
                (1)fullName(){}  : 默认就是get函数, 无法添加set函数
                (2)fullName:{ get(){},set(val){} }   : 如果想要添加set函数,计算属性就需要写成对象格式
                */
                fullName:{
                    //获取计算属性
                    get(){
                        return `${this.firstName}${this.lastName}`
                    },
                    //设置计算属性
                    set(val){
                        console.log(val)//设置的值
                        this.firstName = val[0] || ''
                        this.lastName = val.substr(1,val.length-1)  
                    }
                }
            }
        })
    </script>
</body>
</html>

11-侦听器使用

watch:{

'要侦听的属性名'(newVal,oldVal){ }

}

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>

</head>

<body>

    <!-- HTML结构 -->
    <div id="app">
        用户名: <input type="text" placeholder="请输入用户名" v-model="username"><span>{{ info }}</span>
        <br>
        密码: <input type="text" placeholder="请输入密码" v-model="password">
    </div>

    <script>
        /* 
        1.侦听器作用 : 监听某一个数据变化
        2.侦听器语法 : 在vue实例的watch对象中
            '要侦听的属性名'(newVal,oldVal){

            }
        3.面试点 : 侦听器与计算属性区别
            计算属性 :  监听多个属性 (只要计算属性内部的任何属性变化,都会执行函数)
            侦听器 : 监听单个属性
        */

        /* 创建vue实例 */
        let app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                username: '',
                password: '',
                info:''
            },
            //计算属性
            watch: {
                username(newVal, oldVal) {
                    console.log(newVal, oldVal)
                    this.info = '验证中...'
                    //模拟网络请求
                    setTimeout(()=>{
                        this.info = '该用户已被注册'
                    },500)
                }
            }
        })


    </script>
</body>

</html>

12-深度侦听

watch: {

"要侦听的属性名": {

deep: true, // 深度侦听复杂类型内变化

handler (newVal, oldVal) {}

}

}

  • 深度侦听:侦听引用类型内部数据变化

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 导包 -->
    <script src="./vue.js"></script>

</head>

<body>

    <!-- HTML结构 -->
    <div id="app">
        用户名: <input type="text" placeholder="请输入用户名" v-model="user.username"><span>{{ info }}</span>
        <br>
        密码: <input type="text" placeholder="请输入密码" v-model="user.password">
    </div>

    <script>
        /* 
        1.深度侦听作用 : 侦听引用类型内部数据变化
        2.语法:
            watch: {
                "要侦听的属性名": {
                    deep: true, // 深度侦听复杂类型内变化
                    handler (newVal, oldVal) {}
                }
            }
        */

        /* 创建vue实例 */
        let app = new Vue({
            //el:挂载点
            el: '#app',
            //data: 要渲染的数据
            data: {
                user: {
                    username: '',
                    password: ''
                },
                info: ''
            },
            //计算属性
            watch: {
                user: {
                    deep: true,
                    handler(newVal, oldVal) {
                        console.log(newVal, oldVal)
                        this.info = '验证中...'
                        //模拟网络请求
                        setTimeout(() => {
                            this.info = '该用户已被注册'
                        }, 500)
                    }
                }
            }
        })


    </script>
</body>

</html>

:vue虚拟dom算法与key值工作原理

1.1-虚拟DOM

  • 1.vue底层会把挂载点中的真实DOM结构,转变为虚拟DOM。

    • 真实DOM : 我们webapi学习的dom对象, 一个对象里面有几百个属性

    • 虚拟DOM : 本质是一个js对象,只处理一些关键的属性。(其他的不处理)

  • 这是vue的挂载dom

<template>
    <div id="app">
        <p class="my_p">123</p>
    </div>
</template>
  • 对应的虚拟DOM结构

const dom = {
    type: 'div',
    attributes: [{id: 'box'}],
    children: {
        type: 'p',
        attributes: [{class: 'my_p'}],
        text: '123'
    }
}
  • 2.vue是如何更新渲染数据呢

    • 生成新的虚拟DOM结构

      • 把几百个dom属性,变成虚拟dom十几个关键属性

    • 和旧的虚拟DOM结构对比(diff算法)

    • 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁

==好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)==

==好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)==

  • 3.总结

    • 虚拟DOM是什么?

      • 本质就是一个JS对象, 保存DOM关键信息

    • 虚拟DOM的好处?

      • 提高DOM更新的性能, 不频繁操作真实DOM, 在内存中找到变化部分, 更新真实DOM

1.2.key作用与原理

无key - 就地更新

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

<ul id="myUL">
    <li v-for="str in arr">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

旧 - DOM结构 和 新 - DOM结构 对比过程

==性能不高, 从第二个li往后都更新了==

有key - 值为索引 - 就地更新

  • 还是就地更新

因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的

<ul id="myUL">
    <li v-for="(str, index) in arr" :key="index">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

key为索引-图解过程 (又就地往后更新了)

  1. v-for先循环产生新的DOM结构, key是连续的, 和数据对应

  2. 然后比较新旧DOM结构, 找到区别, 打补丁到页面上

    最后补一个li, 然后从第二个往后, 都要更新内容

口诀: key的值有id用id, 没id用索引

有key - 值为id - 性能最高

key的值只能是唯一不重复的, 字符串或数值

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签

新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建

旧DOM结构的key, 在新的DOM结构里没有了, 则==移除key所在的标签==

<template>
  <div>
    <ul>
      <li v-for="obj in arr" :key="obj.id">
        {{ obj.name }}
        <input type="text">
      </li>
    </ul>
    <button @click="btn">下标1位置插入新来的</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        {
          name: '老大',
          id: 50
        },
        {
          name: '老二',
          id: 31
        },
        {
          name: '老三',
          id: 10
        }
      ],
    };
  },
  methods: {
    btn(){
      this.arr.splice(1, 0, {
        id: 19, 
        name: '新来的'
      })
    }
  }
};
</script>

<style>
</style>

图解效果:

总结: 不用key也不影响功能(就地更新), 添加key可以提高更新的性能

<think>好的,用户想了解DOM算法的实现方式,特别是与虚拟DOMDiff算法相关的部分。我需要结合提供的引用内容来组织回答。 首先,用户提到的“DOM算法”可能指的是虚拟DOM及其Diff算法,这在引用中多次出现。比如引用[1]提到虚拟DOM通过JS对象表示真实DOM树,减少直接操作DOM的开销。引用[5]详细说明了虚拟DOM的结构,比如使用h函数创建节点,这可能需要解释。 接下来,Diff算法是关键部分。引用[2]引用[3]讨论了Diff算法的优化策略,比如同层比较、key的使用等。需要说明Diff的基本步骤:同层比较、节点对比、递归子节点。同时要提到key的重要性,如引用[3]中建议使用唯一id而非索引。 然后,实现方式部分需要分点说明。首先虚拟DOM的表示,用JS对象描述节点类型、属性子节点。然后是Diff过程,比较新旧虚拟DOM树的差异,这里要提到同层比较节点的四种情况(创建、删除、更新、复用)。此外,引用[5]中的函数重载节点创建示例可以作为例子。 性能优化方面,引用[3]给出了具体建议,比如合理使用key、避免不必要的更新、用v-show代替v-if。这些需要整合到回答中,说明如何配合Diff算法提升效率。 最后,生成相关问题需要考虑用户可能的后续疑问,比如Diff算法的细节、虚拟DOM的应用场景、性能优化技巧等。需要确保问题覆盖回答中的主要点,帮助用户深入理解。 需要检查引用标记是否正确,确保每个引用来源对应正确的内容。例如,引用[1]对应虚拟DOM的基本概念,引用[2][3]对应Diff的优化机制性能建议,引用[5]对应虚拟DOM的结构实现示例。 整体结构需要清晰,分步骤解释,语言简洁,使用中文。同时避免使用Markdown,用自然的分段标号。确保LaTeX公式正确,如时间复杂度用$O(n)$格式。</think>### DOM算法实现方式详解 DOM算法通常指**虚拟DOM(Virtual DOM)及其Diff算法**,核心目标是通过减少直接操作真实DOM的开销来提升性能。以下分步骤说明其实现方式: --- #### 一、虚拟DOM的基本结构 虚拟DOM是用JavaScript对象模拟真实DOM树的抽象表示,包含以下关键属性: - **节点类型**:如`div`、`p`等标签名(对应`sel`属性)[^5]; - **属性**:如`class`、`style`、事件等(对应`data`对象); - **子节点**:子虚拟节点或文本内容(对应`children`数组或文本字符串)[^5]。 示例代码表示: ```javascript // 通过h函数创建虚拟DOM节点 const vdom = h('div', { class: 'vdom' }, [ h('p', { class: 'text' }, ['Hello World']), h('input', { class: 'ipt', value: '内容' }) ]); ``` --- #### 二、Diff算法核心流程 Diff算法通过比较新旧虚拟DOM树的差异,生成最小化的DOM操作指令。实现步骤如下: 1. **同层比较** Diff算法仅比较同层级的节点,若发现节点类型不同,则直接替换整个子树,不递归比较子节点(减少时间复杂度)[^2]。 2. **节点对比** - **相同节点**(选择器与`key`均相同):更新属性或内容; - **不同节点**:创建新节点并替换旧节点; - **无旧节点**:插入新节点; - **无新节点**:删除旧节点[^2][^5]。 3. **子节点递归** 对子节点列表进行递归Diff,采用**双指针算法**优化遍历效率[^3]。 --- #### 三、关键优化机制 1. **Key的作用** 为列表项分配唯一`key`(如`item.id`),帮助Diff算法识别节点的稳定性复用逻辑。避免使用索引作为`key`,否则可能导致错误复用[^3]。 2. **批量更新** 将多次DOM操作合并为一次,减少重排(Reflow)重绘(Repaint)次数[^1]。 3. **差异化更新策略** - **静态节点跳过**:标记静态子树,避免重复Diff- **组件级优化**:通过组件复用减少子树的Diff范围[^3]。 --- #### 四、时间复杂度分析 - **最优情况**:仅需$O(n)$时间复杂度(如仅修改文本内容); - **最坏情况**:仍需$O(n^2)$时间复杂度,但通过`key`同层比较等优化,实际性能接近线性[^2][^3]。 --- #### 五、性能优化建议 1. **合理使用`key`** ```html <!-- 错误:使用索引 --> <div v-for="(item, index) in list" :key="index"> <!-- 正确:使用唯一标识 --> <div v-for="item in list" :key="item.id"> ``` 2. **避免不必要的渲染** 使用计算属性缓存复杂计算结果,减少虚拟DOM生成频率[^3]。 3. **优先使用`v-show`** 频繁切换可见性时,用`v-show`代替`v-if`以减少节点销毁/重建开销[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值