Vue学习(三)

一、列表渲染

v-for指令

  • 用于展示列表数据

  • 语法<li v-for="(item, index) in items" :key="index"></li>key可以是index,最好是遍历对象的唯一标识

  • 可遍历:数组、对象

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>列表渲染-基本列表</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h3>人员列表(遍历数组)</h3>
        <ul>
            <li v-for="(p, index) in persons" :key="index">{{p.name}}--{{index}}</li>
        </ul>
        <h3>遍历字符串</h3>
        <ul>
            <li v-for="(char, index) in str" :key="index">{{char}}--{{index}}</li>
        </ul>
        <h3>遍历指定次数</h3>
        <ul>
            <li v-for="(num, index) in 5" :key="index">{{num}}--{{index}}</li>
        </ul>

        <h3>遍历对象</h3>
        <ul>
            <li v-for="(car, index) in cars" :key="index">{{car}}--{{index}}</li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        new Vue({
            el: "#root",
            // 函数形式的data
            data() {
                return {
                    persons: [
                        { id: "001", name: "张三", age: 18 },
                        { id: "002", name: "李四", age: 20 },
                        { id: "003", name: "王五", age: 19 },
                    ],
                    str:"hanzhe",
                    cars:[
                        {name:"保时捷",rice:198},
                        {name:"宝马",rice:208}
                    ]
                }
            }
        })
    </script>
</body>
</html>

 

二、列表渲染中key的作用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>列表渲染-基本列表</title>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h3>遍历对象</h3>
        <button @click="addCar">添加一个汽车</button>
        <ul>
            <!-- :key用作固定值 -->
            <li v-for="(car, index) in cars" :key="1">{{car.name}}--{{car.rice}} <input type="text"></li> 
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        new Vue({
            el: "#root",
            // 函数形式的data
            data() {
                return {
                    cars: [
                        { name: "保时捷", rice: 198 },
                        { name: "宝马", rice: 208 }
                    ]
                }
            },
            methods: {
                addCar(){
                    // 在后面加个数据没问题
                    // this.cars.push({ name: "宝马1", rice: 203 })
                    this.cars.unshift({ name: "宝马1", rice: 203 })
                }
            },
        })
    </script>
</body>
</html>

 key用固定值后,看一个现象看一个现象

点击添加按钮后:

分析:

面试题:react、vue中key有什么作用?

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

  • 对比规则

  • 旧虚拟DOM中找到了新虚拟DOM相同的key

    • 若虚拟DOM中内容没变,直接使用之前的真实DOM

    • 若虚拟DOM内容改变了,则生成新的真实DOM,随后替换页面中之前的真实DOM

  • 旧虚拟DOM中未找到与新虚拟DOM相同的key

    创建新的真实DOM,随后渲染到页面上

  • 用index作为key可能会引发的问题

  • 若对数据进行逆序添加,删除等破坏顺序的操作,会产生没必要真实DOM更新,界面效果没问题,但是效率低下

  • 若结构中还包含输入类的DOM,会产生错误DOM更新,界面有问题

  • 开发中如何选择Key?

    最好每条数据的唯一标识作为key

    如果仅仅是在页面渲染,使用index作为key完全没问题。

三、列表过滤

分别使用计算属性和监听实现:

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>列表渲染-基本列表</title>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div id="root">
        <h3>遍历对象</h3>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <ul>
            <li v-for="(car, index) in filterCars" :key="index">{{car.name}}--{{car.rice}}</li>
        </ul>
    </div>

    <script>
        Vue.config.productionTip = false;
        new Vue({
            el: "#root",
            // 函数形式的data
            data() {
                return {
                    keyWord: '',
                    cars: [
                        { name: "保时捷", rice: 198 },
                        { name: "宝马", rice: 208 }
                    ],
                    filterCars: []
                }
            },
            // 使用计算属性
            // computed: {
            //     filterCars: {
            //         get() {
            //             return this.cars.filter((p) => {
            //                 // 过滤空格
            //                 return p.name.indexOf(this.keyWord) !== -1;
            //             })
            //         },
            //         set(value) {
            //             alert(value)
            //         }
            //     }
            // }

            // 使用监听属性
            // 需要在data中添加 filterCars:[]

            watch: {
                keyWord: {
                    immediate:true,
                    handler(newValue) {
                        this.filterCars = this.cars.filter((p) => {
                            return p.name.indexOf(this.keyWord) !== -1;
                        })
                    }
                }
            }
        })
    </script>
</body>

</html>

四、列表排序

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>列表渲染-基本列表</title>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div id="root">
        <h3>遍历对象</h3>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <button v-on:click="btn1">价格降序</button>
        <button @click="btn2">价格升序</button>
        <button @click="btn3">原来降序</button>
        <ul>
            <li v-for="(car, index) in filterCars" :key="index">{{car.name}}--{{car.rice}}</li>
        </ul>
    </div>

    <script>
        Vue.config.productionTip = false;
        new Vue({
            el: "#root",
            // 函数形式的data
            data() {
                return {
                    keyWord: '',
                    sortType: 0,
                    cars: [
                        { name: "保时捷", rice: 198 },
                        { name: "保马", rice: 208 },
                        { name: "保马2", rice: 309 },
                        { name: "雷克萨斯", rice: 500 }
                    ],
                    filterCars: []
                }
            },
            methods: {
                btn1(event) {
                    this.sortType = 2
                },

                btn2() {
                    this.sortType = 1
                },

                btn3() {
                    this.sortType = 0
                }
            },
            // 使用监听属性
            // 需要在data中添加 filterCars:[]
            watch: {
                keyWord: {
                    immediate: true,
                    handler(newValue) {
                        const arr = this.cars.filter((p) => {
                            return p.name.indexOf(this.keyWord) !== -1;
                        })

                        // 非 0 即true
                        if (this.sortType) {
                            arr.sort((p1, p2) => {
                                return this.sortType == 1 ? p2.rice - p1.rice : p1.rice - p2.rice
                            })
                        }
                        // 将排序后的数据赋值
                        this.filterCars = arr;
                    }
                }
            }
        })
    </script>
</body>
</html>

五、数据监视

data属性是对象时,修改对象可能不能触发MVVM的变化:

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>列表渲染-基本列表</title>
    <script src="./js/vue.js"></script>
</head>

<body>
    <div id="root">
        <h3>遍历对象</h3>
        <button v-on:click="updatebmw2">修改宝马2的数据</button>
        <ul>
            <li v-for="(car, index) in cars" :key="index">{{car.name}}--{{car.rice}}</li>
        </ul>
    </div>

    <script>
        Vue.config.productionTip = false;
        new Vue({
            el: "#root",
            // 函数形式的data
            data() {
                return {
                    cars: [
                        { name: "保时捷", rice: 198 },
                        { name: "保马", rice: 208 },
                        { name: "保马2", rice: 309 },
                        { name: "雷克萨斯", rice: 500 }

                    ]
                }
            },
            methods: {
                updatebmw2() {
                    // 将数组中存下标2开始,删除一个,并添加后面的数据
                    // 数组改变了,能触发mvvm模型的变化
                    // this.cars.splice(2,1,{ name: "保马2", rice: 109 }) 


                    // 数据虽然变化了,但是没有触发data上数据的变化
                    // this.cars[2] = { name: "保马2", rice: 109 }

                    // 生效的
                    this.cars[2].name = "名称修改了";
                    console.log(JSON.stringify(this.cars))
                }
            },
            // 使用监听属性
            // 需要在data中添加 filterCars:[]
            watch: {
                keyWord: {
                    immediate: true,
                    handler(newValue) {
                        const arr = this.cars.filter((p) => {
                            return p.name.indexOf(this.keyWord) !== -1;
                        })
                        // 将排序后的数据赋值
                        this.filterCars = arr;
                    }
                }
            }
        })
    </script>
</body>

</html>

模拟一个数据监视:

let data = {
  name: '尚硅谷',
  address: '北京',
}

function Observer(obj) {
  // 汇总对象中所有的属性形成一个数组
  const keys = Object.keys(obj)
  // 遍历
  keys.forEach((k) => {
    Object.defineProperty(this, k, {
      get() {
        return obj[k]
      },
      set(val) {
        console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
        obj[k] = val
      }
    })
  })
}

// 创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)

// 准备一个vm实例对象
let vm = {}
vm._data = data = obs

原理:

  • vue会监视data中所有层次的数据

  • 如何监测对象中的数据?

  • 通过setter实现监视,且要在new Vue()时传入要检测的数据

    • 兑现创建后追加的属性vue默认不做响应式处理

    • 如果需要给后添加的属性做响应式,使用如下API
      Vue.set(target,propertyName/index,value)
      vm.$set(target,propertyName/index,value)

  • 如何检测数组中的数据
    通过包裹数组新元素的方法实现,本质做了两件事
    调用原生对应的方法对数组进行更新
    重新解析模板,进而更新页面

  • 在vue修改数组的某个元素时,用如下方法
    push,pop,unshift,shift,splice,sort,reverse这几个方法被Vue重写了
    特别注意:vue.set()和vm.$set()不能给vm或vm的根对象(data)添加属性

<title>总结数据监视</title>
<style>button {margin-top: 10px;}</style>
<script type="text/javascript" src="../js/vue.js"></script>

<div id="root">
  <h1>学生信息</h1>
  <button @click="student.age++">年龄+1岁</button> <br />
  <button @click="addSex">添加性别属性,默认值:男</button> <br />
  <button @click="student.sex = '未知' ">修改性别</button> <br />
  <button @click="addFriend">在列表首位添加一个朋友</button> <br />
  <button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br />
  <button @click="addHobby">添加一个爱好</button> <br />
  <button @click="updateHobby">修改第一个爱好为:开车</button> <br />
  <button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
  <h3>姓名:{{ student.name }}</h3>
  <h3>年龄:{{ student.age }}</h3>
  <h3 v-if="student.sex">性别:{{student.sex}}</h3>
  <h3>爱好:</h3>
  <ul>
    <li v-for="(h,index) in student.hobby" :key="index">{{ h }} </li>
  </ul>
  <h3>朋友们:</h3>
  <ul>
    <li v-for="(f,index) in student.friends" :key="index">{{ f.name }}--{{ f.age }}</li>
  </ul>
</div>

<script type="text/javascript">
  Vue.config.productionTip = false

  const vm = new Vue({
    el: '#root',
    data: {
      student: {
        name: 'tom',
        age: 18,
        hobby: ['抽烟', '喝酒', '烫头'],
        friends: [
          { name: 'jerry', age: 35 },
          { name: 'tony', age: 36 }
        ]
      }
    },
    methods: {
      addSex() {
        // Vue.set(this.student,'sex','男')
        this.$set(this.student, 'sex', '男')
      },
      addFriend() {
        this.student.friends.unshift({ name: 'jack', age: 70 })
      },
      updateFirstFriendName() {
        this.student.friends[0].name = '张三'
      },
      addHobby() {
        this.student.hobby.push('学习')
      },
      updateHobby() {
        // this.student.hobby.splice(0,1,'开车')
        // Vue.set(this.student.hobby,0,'开车')
        this.$set(this.student.hobby, 0, '开车')
      },
      removeSmoke() {
        this.student.hobby = this.student.hobby.filter((h) => {
          return h !== '抽烟'
        })
      }
    }
  })

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一季春秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值