第三节:Vue3向下兼容2(v-for、数组方法、v-model、计算属性、监听器)

一. 基本指令

 1. v-for

数据准备

    data() {
                    return {
                        userInfo: { name: 'ypf', age: 18, school: '北大' },
                        movies: ["星际穿越", "盗梦空间", "大话西游", "教父", ],
                        userList: [
                            { name: 'ypf1', age: 18, school: '北大1' },
                            { name: 'ypf2', age: 19, school: '北大2' },
                            { name: 'ypf3', age: 20, school: '北大3' }
                        ]
                    }
             }

(1). 遍历对象

格式:

            <h4>1.遍历对象</h4>
            <ul>
                <li v-for="(value,key,index) in userInfo" :key='index'>
                    {{index}}-{{key}}--{{value}}
                </li>
            </ul>

(2). 遍历数字

格式: item in 数字

            <h4>2.遍历数字</h4>
            <ul>
                <li v-for="value in 5">
                    {{value}}
                </li>
            </ul>

(3). 遍历数组【重点】

格式:item in 数组

           (item, index) in 数组

A. 普通数组

            <h5>3.1 普通数组</h5>
            <ul>
                <li v-for="(item,index) in movies" :key='index'>{{index}}-{{item}}</li>
            </ul>

B. 数组对象

            <h5>3.2 数组对象</h5>
            <ul>
                <li v-for="(item,index) in userList" :key='index'>
                    {{index}}-{{item.name}}-{{item.age}}-{{item.school}}
                </li>
            </ul>

补充:

  我们发现,每个v-for,我们都赋值了一个key属性,key属性里的内容建议用唯一值,这里涉及到diff算法,提高效率,后面章节重点剖析。

2. 数组方法

(1).变异方法:

  背景:在 Vue 中,直接修改对象属性的值无法触发响应式。当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变。所以:Vue中引入变异数组方法,即保持数组方法原有功能不变的前提下对其进行功能拓展,使其支持响应式。

常用方法:

  a. push: 往数组最后面添加一个元素,成功返回当前数组的长度。

  b. pop: 删除数组的最后一个元素,成功返回删除元素的值

  c. shift: 删除数组的第一个元素,成功返回删除元素的值

  d. unshift: 往数组最前面添加一个元素,成功返回当前数组的长度

  e. splice:  删除,有三个参数,第一个要删除元素的下标(必选),第二个要删除元素的个数(必选),第三个删除后想在原位置替换的值

  f. sort: 将数组按照字符编码从小到大排序,成功返回排序后的数组

  g. reverse: 将数组倒序排列,并返回倒叙后的数组

(2).替换数组

  含义: 不会影响原始的数组数据,而是形成一个新的数组.

常用方法:

  a. filter: 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

  b. concat: 用于连接两个或多个数组,该方法不会改变现有的数组。

  c. slice: 从已有的数组中返回选定的元素。该方法并不会修改数组,而是返回一个子数组

  eg:从第0个开始,获取两个元素 this.list = this.list.slice(0, 2);

案例参考:

  https://www.cnblogs.com/yaopengfei/p/12315704.html  底部的图书管理代码。 

二. v-model

1. 基本使用和原理

 v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定;它会根据控件类型自动选取正确的方法来更新元素;尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理;

<body>
        <div id="app"></div>
        <template id="myApp">
            <div>
                <input v-model="msg" />
                <h5>等价于</h5>
                <!-- 等价于 -->
                <input :value="msg" @input="change1">
            </div>
        </template>

        <script src="../js/vue3.js" ></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        msg: 'Hello Vue3!'
                    }
                },
                methods:{
                    change1(event){
                        this.msg=event.target.value;
                    }
                }
            }).mount('#app');
        </script>
    </body>

剖析本质:v-model的原理其实是背后有两个操作:

     (1). v-bind绑定value属性的值;

     (2). v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

2. 绑定常用标签

 v-model还可以绑定:textarea、checkbox、radio、select。

代码分享:

    <body>
        <div id="app"></div>
        <template id="myApp">
            <!-- 1.绑定textarea -->
            <label for="intro">
                自我介绍
                <textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
            </label>
            <h2>intro: {{intro}}</h2>

            <!-- 2.checkbox -->
            <!-- 2.1.单选框 -->
            <label for="agree">
                <input id="agree" type="checkbox" v-model="isAgree"> 同意协议
            </label>
            <h2>isAgree: {{isAgree}}</h2>

            <!-- 2.2.多选框 -->
            <span>你的爱好: </span>
            <label for="basketball">
                <input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
            </label>
            <label for="football">
                <input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
            </label>
            <label for="tennis">
                <input id="tennis" type="checkbox" v-model="hobbies" value="tennis"> 网 球
            </label>
            <h2>hobbies: {{hobbies}}</h2>

            <!-- 3.radio -->
            <span>你的爱好: </span>
            <label for="male">
                <input id="male" type="radio" v-model="gender" value="male"></label>
            <label for="female">
                <input id="female" type="radio" v-model="gender" value="female"></label>
            <h2>gender: {{gender}}</h2>

            <!-- 4.select -->
            <span>喜欢的水果: </span>
            <select v-model="fruit" multiple size="2">
                <option value="apple">苹果</option>
                <option value="orange">橘子</option>
                <option value="banana">香蕉</option>
            </select>
            <h2>fruit: {{fruit}}</h2>
        </template>

        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        intro: "Hello World",
                        isAgree: false,
                        hobbies: ["basketball", "football"],
                        gender: "",
                        fruit: "orange"
                    }
                }
            }).mount('#app');
        </script>
    </body>
View Code

3. 修饰符

 

lazy修饰符的作用:

 默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步;

 如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发

number修饰符

代码分享:

<body>
        <div id="app"></div>
        <template id="myApp">
            <!-- 1.lazy修饰符 敲击回车才有效-->
            <input type="text" v-model.lazy="message">
            <br>
            <!-- 2.number修饰符 -->
            <input type="text" v-model.number="message">
            <h2>{{message}}</h2>
            <button @click="showType">查看类型</button>

            <!-- 3.trim修饰符 -->
            <input type="text" v-model.trim="message">
            <button @click="showResult">查看结果</button>
        </template>

        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        message: "Hello World"
                    }
                },
                methods: {
                    showType() {
                        console.log(this.message, typeof this.message);
                    },
                    showResult() {
                        console.log(this.message);
                    }
                }
            }).mount('#app');
        </script>
    </body>

 

三. 计算属性computed

1. 什么时候用计算属性?

 对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性;

 计算属性看起来像是一个函数,但是我们在使用的时候不需要加(),这个后面讲setter和getter时会讲到;

2. 基本用法

<body>
        <div id="app"></div>
        <template id="myApp">
            <div> {{fullName}} </div>
            <div> {{finalResult}} </div>
        </template>
        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        firstName: 'BBB',
                        lastName: 'AAA',
                        message: "Hello Vue3"
                    }
                },
                computed: {
                    fullName() {
                        return this.lastName + ' ' + this.firstName;
                    },
                    finalResult() {
                        return this.message.split(' ').reverse().join(' ');
                    }
                }
            }).mount('#app');
        </script>
    </body>

3. get 和 set方法

 计算属性在大多数情况下,只需要一个get方法即可,所以我们会将计算属性直接写成一个函数

    但是,如果我们确实想设置计算属性的值呢?这个时候我们也可以给计算属性设置一个set的方法

    <body>
        <div id="app"></div>
        <template id="myApp">
            <div> {{fullName1}} </div>
            <div> {{fullName2}} </div>
        </template>
        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        firstName: 'BBB',
                        lastName: 'AAA',
                    }
                },
                computed: {
                    // 相当于get方法
                    fullName1() {
                        return this.lastName + ' ' + this.firstName;
                    },
                    fullName2: {
                        get() {
                            return this.lastName + ' ' + this.firstName;
                        },
                        set(newValue) {
                            this.firstName = newValue + this.firstName;
                        }
                    }
                }
            }).mount('#app');
            
        </script>
    </body>
View Code

4. 计算属性的缓存

 计算属性computed是有缓存的,计算属性会基于它们的依赖关系进行缓存;

 在数据不发生变化时,计算属性是不需要重新计算的;但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算

PS. 与method的本质区别:method每次调用都需要重新计算,没有缓存。

代码分享:

<body>
        <div id="app"></div>
        <template id="myApp">
            <!-- 1. 计算属性调用-->
            <div>{{getFullName1()}}</div>
            <div>{{getFullName1()}}</div>
            <div>{{getFullName1()}}</div>
            <!-- 2. 方法调用 -->
            <div>{{getFullName2}}</div>
            <div>{{getFullName2}}</div>
            <div>{{getFullName2}}</div>
            <!-- 3.修改数据内容 -->
            <input type="text" v-model='firstName' />
        </template>

        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        firstName: 'BBB',
                        lastName: 'AAA'
                    }
                },
                methods: {
                    getFullName1() {
                        console.log('我是方法调用');
                        return this.lastName + ' ' + this.firstName;
                    }
                },
                computed:{
                    getFullName2(){
                        console.log('我是计算属性调用');
                        return this.lastName + ' ' + this.firstName;
                    }
                }

            }).mount('#app');
        </script>
    </body>

运行结果:

 计算属性只调用了1次,方法调用了三次。

 

   修改数据值,计算属性再次被调用了1次,方法调用了3次。

 

 5. 扩展get和set的源码

  详见章节:https://www.cnblogs.com/yaopengfei/p/15251328.html

四. 监听器

1. 什么是监听器(侦听器)?

 

 PS: 可以监听data和computed中的数据。

2.  普通用法

    <body>
        <div id="app"></div>
        <template id="myApp">
            <h3>{{userInfo1.userName}}</h3>
            <div><button @click="Edit1">修改对象</button></div>
            <div><button @click="Edit2">修改userInfo1.userName</button></div>
        </template>
        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        userInfo1: { userName: 'test1', age: 20, },
                    }
                },
                watch: {
                    // 普通监听
                    // 默认情况下我们的侦听器只会针对监听的数据本身的改变(内部发生的改变是不能侦听)
                    userInfo1(newValue, oldValue) {
                        console.log(oldValue, newValue);
                    },
                },
                methods: {
                    Edit1() {
                        this.userInfo1 = { userName: 'test2' };
                    },
                    Edit2() {
                        this.userInfo1.userName = 'test2';
                    }
            }).mount('#app');
        </script>
    </body>

剖析:

 Edit1方法中直接修改了对象,则普通用法中能监听到,但像Edit2中的修改对象中的内容,是监听不到的!!!

3.  深度监听 和 立即执行

(1). 深度监听

 采用另一种写法,并且需要配置 deep: true ,就可以监听到对象内任何一个属性的修改了。 

<body>
        <div id="app"></div>
        <template id="myApp">
            <h3>{{userInfo2.userName}}</h3>
            <div><button @click="Edit3">修改对象</button></div>
            <div><button @click="Edit4">修改userInfo2.userName</button></div>
            <div><button @click="Edit5">修改userInfo2.childInfo.cName</button></div>
        </template>

        <script src="../js/vue3.js"></script>
        <script>
            Vue.createApp({
                template: '#myApp',
                data() {
                    return {
                        userInfo2: { userName: 'test1', age: 20, childInfo: { cName: 'ypf' } },
                    }
                },
                watch: {// 深度监听
                    // 内部发生的改变也侦听到
                    // 发现一问题:新对象和旧对象中的userName值是相同的,这是因为引用类型赋值的问题,都是指向同一个内存地址(详见后面的知识补充)
                    userInfo2: {
                        handler(newValue, oldValue) {
                            console.log(oldValue, newValue);
                        },
                        deep: true
                    },             
                },
                methods: {
                    Edit3() {
                        this.userInfo2 = { userName: 'test2' };
                    },
                    Edit4() {
                        this.userInfo2.userName = 'test2';
                    },
                    Edit5() {
                        this.userInfo2.childInfo.cName = 'lmr';
                    }

                }

            }).mount('#app');
        </script>
    </body>

剖析:

  无论是修改  userInfo2.userName 还是  userInfo2.childInfo.cName ,都可以监听到。

发现一问题:

  新对象和旧对象中的userName值是相同的,这是因为引用类型赋值的问题,都是指向同一个内存地址(详见后面的知识补充)

(2). 立即执行

  通过设置参数immediate: true,即不发生变化前,默认执行一次。

4.  其它写法

(1). 直接监听对象中某个属性值

    watch: {
                    // 特殊的监听方式
                    // 直接监听userName的值
            'userInfo2.userName'(newValue, oldValue) {
                console.log(oldValue, newValue);
            }
    },

(2).  在created生命周期中进行监听

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
原文地址:https://www.cnblogs.com/yaopengfei/p/15247883.html