VUE-第二季-01

目录

1.Vue程序初体验

1.1 下载并安装vue.js

1.2 第一个Vue程序

1.3 Vue的data配置项

1.4 Vue的template配置项

1.5 Vue实例 和 容器 的关系是:一夫一妻制

2.Vue核心技术

2.0 Vue的模板语法

2.0.1 插值语法

插值语法总结:

2.0.2 指令语法

指令语法总结

v-bind指令详解总结

2.0.3 数据绑定v-bind和v-model

2.0.4 初始MVVM架构模式

2.0.4.1 MVVM架构模式

2.0.4.2 MVVM架构模式好处

2.0.4.3 Vue和MVVM的关系

2.0.4.4 vm总结

2.1 数据代理机制

2.1.1 Object.defineProperty()

2.1.1.1 Object.defineProperty总结

2.1.2 数据代理机制

数据代理机制总结:

2.1.3 Vue数据代理机制对属性名的要求

2.1.4 模拟Vue的数据代理

2.1.5 Vue数据代理源码跟踪

2.1.6 _date属性

2.1.7 data可以写成函数?

2.2 事件处理

2.2.1 事件处理的核心语法

2.2.2 事件修饰符

事件修饰符代码总结:

2.2.3 按键修饰符

按键修饰符总结

2.3 计算属性

1. 案例:用户输入信息,然后翻转用户输入的字符串。

js知识之-join()方法的使用

语法:

示例:

1. 默认(不传参数,使用逗号 , 连接)

2. 使用空字符串 ''(无分隔符,直接拼接)

3. 使用自定义分隔符

4. 处理包含非字符串元素(会自动调用 toString() 转换)

js知识之-split()方法的使用

split() 方法语法

参数说明

返回值

split() 的常见用法

1. 按字符拆分(split(''))

2. 按单词拆分(split(' '))

3. 按特定符号拆分(如逗号 ,)

4. 限制拆分数量(split(separator, limit))

5. 使用正则表达式拆分

更灵活的拆分方式(如按多个分隔符拆分):``js"2023/10-15".split(/[\/-]/);// 输出: ['2023', '10', '15'](同时匹配 `/` 和 `-`)``

特殊情况的处理

1. 不传参数(split())

2. 分隔符不存在于字符串中

3. 空字符串 split('')

4. 开头或结尾有分隔符

2. 什么是计算属性?

3. 计算属性的使用

2.4 侦听属性的变化

2.5 class与style绑定

2.4.1 class绑定

2.4.1.1 绑定字符串

2.4.1.2 绑定数组

2.4.1.3 绑定对象

2.5 条件渲染

2.5.1 v-if

2.5.2 v-else-if、v-else

2.5.3 template与v-if

2.5.5 v-if VS v-show

条件渲染总结

2.6 列表渲染

2.6.1 遍历数组、对象、字符串、指定次数

2.6.2 虚拟dom和diff算法

2.6.3 v-for的key的作用以及实现原理

1. 用index作为key

2. 用vip.id作为key

3. key的作用

4. diff算法是如何比较的?

5. index作为key存在两个问题

6. index作为key和vip.id作为key对比

2.7 列表过滤

2.8 列表排序

2.9 收集表单数据

2.10 过滤器

2.11 Vue的其它指令

2.11.1 v-text

2.11.2 v-html

2.11.3 v-cloak

2.11.4 v-once

2.11.5 v-pre

2.12 vue的自定义指令

函数式:

对象式:可以使用对象式完成更加细致的功能。

以上是局部指令,全局指令怎么定义:

2.13 响应式与数据劫持

1.什么是响应式?

2.Vue的响应式是如何实现的?

3.Vue会给data中所有的属性,以及属性中的属性,都会添加响应式。

4. 后期添加的属性,不会有响应式,怎么处理?

5.Vue没有给数组下标0,1,2,3....添加响应式,怎么处理?

数组相关方法

1. push() - 末尾添加元素

2. pop() - 移除末尾元素

3. reverse() - 反转数组

4. splice() - 添加/删除元素

5. shift() - 移除首元素

6. unshift() - 开头添加元素

7. sort() - 数组排序

重要注意事项

2.14 Vue的生命周期

2.14.1 什么是生命周期

2.14.2 掌握Vue的生命周期有什么用

2.14.3 Vue生命周期的4个阶段8个钩子

2.14.4 初始阶段做了什么事儿

2.14.5 挂载阶段做了什么事儿

2.14.6 更新阶段做了什么事儿

2.14.7 销毁阶段做了什么事儿


1.Vue程序初体验

我们可以先不去了解Vue框架的发展历史、Vue框架有什么特点、Vue是谁开发的,这些对我们编写Vue程序起不到太大的作用,更何况现在说了一些特点之后,我们也没有办法彻底理解它,因此我们可以先学会用,使用一段时间之后,我们再回头来熟悉一下Vue框架以及它的特点。现在你只需要知道Vue是一个基于JavaScript(JS)实现的框架。要使用它就需要先拿到Vue的js文件。从Vue官网(Vue.js)下载vue.js文件。

1.1 下载并安装vue.js

第一步:打开Vue2官网,点击下图所示的“起步”:

第二步:继续点击下图所示的“安装”

第三步:在“安装”页面向下滚动,直到看到下图所示位置:

第四步:点击开发版本,并下载,如下图所示:

第五步:安装Vue:
使用script标签引入vue.js文件。就像这样:<script src=”xx/vue.js”></script>

第六步:VsCode安装相关插件(仅参考)
 


还有一个liveserver,是在学习的过程中,主要作用是为静态网页(HTML/CSS/JavaScript)提供本地开发服务器,并支持实时刷新功能。liveserver如何使用呢?文件右键选择Open with Live Server,更多功能介绍到插件中选择liveserver学习

同时我们谷歌浏览器装一下,网页搜索极简插件,Vue.js Devtools_7.7.7_Chrome插件下载_极简插件
,然后搜索vue 选择旧版的,这个支持vue2,适合我们现在学习。下载安装以后,打开谷歌扩展程序,开发者模式,将下载的插件拖拽进行就可以了。

1.2 第一个Vue程序

集成开发环境使用VSCode,没有的可以安装一个:Visual Studio Code - Code Editing. Redefined
VS Code 也支持基础模板生成:

  1. 新建文件并保存为 .html 后缀。
  1. 输入 ! 后按 Tab 键(需文件类型识别为 HTML),生成效果如下。

第一个Vue程序如下:

<!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>第一个Vue程序</title>

    <!-- 安装vue:当你使用script进行Vue安装之后,上下文中就注册了一个全局变量:Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!-- 指定Vue实例的挂载位置。 -->

    <!-- <div id="app"></div> -->

    <hr>

    <div id="app"></div>

    <script>

        /*

        第一步:创建Vue实例

        1. 为什么要new Vue(),直接调用Vue()函数不行吗?

            不行,因为直接调用Vue()函数,不创建实例的话,会出现以下错误:

                Vue is a constructor and should be called with the `new` keyword

        2. 关于Vue构造函数的参数:options?

            option翻译为选项

            options翻译为多个选项

            Vue框架要求这个options参数必须是一个纯粹的JS对象:{}

            在{}对象中可以编写大量的key:value对。

            一个key:value对就是一个配置项。

            主要是通过options这个参数来给Vue实例指定多个配置项。

        3. 关于template配置项:

            template翻译为:模板。

            template配置项用来指定什么?用来指定模板语句,模板语句是一个字符串形式的。

            什么是模板语句?

                Vue框架自己制定了一些具有特殊含义的特殊符号。

                Vue的模板语句是Vue框架自己搞的一套语法规则。

                我们写Vue模板语句的时候,不能乱写,要遵守Vue框架的模板语法规则。

            模板语句可以是一个纯粹的HTML代码,也可以是Vue中的特殊规则。也可以是HTML代码 + Vue的特殊规则。

            template后面的模板语句会被Vue框架的编译器进行编译,转换成浏览器能够识别的HTML代码。

        */

        const myVue = new Vue({

            template : '<h1>Hello Vue!!!!!</h1>'

        })

  

        /*

        第二步:将Vue实例挂载到id='app'的元素位置。

        1. Vue实例都有一个$mount()方法,这个方法的作用是什么?

            将Vue实例挂载到指定位置。

        2. #app 显然是ID选择器。这个语法借鉴了CSS。

        */

        myVue.$mount('#app')

        //myVue.$mount(document.getElementById('app'))

    </script>

</body>

</html>

运行效果:

对第一个程序进行解释说明:

  1. 当使用script引入vue.js之后,Vue会被注册为一个全局变量。就像引入jQuery之后,jQuery也会被注册为一个全局变量一样。
  1. 我们必须new一个Vue实例,因为通过源码可以看到this的存在。

  1. Vue的构造方法参数是一个options配置对象。配置对象中有大量Vue预定义的配置。每一个配置项都是key:value结构。一个key:value就是一个Vue的配置项。
  1. template配置项:value是一个模板字符串。在这里编写符合Vue语法规则的代码(Vue有一套自己规定的语法规则)。写在这里的字符串会被Vue编译器进行编译,将其转换成浏览器能够识别的HTML代码。template称之为模板。
  1. Vue实例的$mount方法:这个方法完成挂载动作,将Vue实例挂载到指定位置。也就是说将Vue编译后的HTML代码渲染到页面的指定位置。注意:指定位置的元素被替换。
  1. ‘#app’的语法类似于CSS中的id选择器语法。表示将Vue实例挂载到id=’app’的元素位置。当然,如果编写原生JS也是可以的:vm.$mount(document.getElementById(‘app’))
  1. ‘#app’是id选择器,也可以使用其它选择器,例如类选择器:’.app’。类选择器可以匹配多个元素(位置),这个时候Vue只会选择第一个位置进行挂载(从上到下第一个)。

1.3 Vue的data配置项

观察第一个Vue程序,你会发现要完成这种功能,我们完全没有必要使用Vue,直接在body标签中编写以下代码即可:

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>没必要使用Vue呀</title>  
</head>  
<body>  
    <h1>Hello Vue!</h1>  
</body>  
</html> 

那我们为什么还要使用Vue呢?在Vue中有一个data配置项,它可以帮助我们动态的渲染页面。代码如下:

<!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>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!-- 指定挂载位置 -->

    <div id="app"></div>

    <!-- vue程序 -->

    <script>

        /*

        模板语句的数据来源:

            1. 谁可以给模板语句提供数据支持呢?data选项。

            2. data选项的类型是什么?Object | Function (对象或者函数)

            3. data配置项的专业叫法:Vue 实例的数据对象.(data实际上是给整个Vue实例提供数据来源的。)

            4. 如果data是对象的话,对象必须是纯粹的对象 (含有零个或多个的 key/value 对)

            5. data数据如何插入到模板语句当中?

                {{}} 这是Vue框架自己搞的一套语法,别的框架看不懂的,浏览器也是不能够识别的。

                Vue框架自己是能够看懂的。这种语法在Vue框架中被称为:模板语法中的插值语法。(有的人把他叫做胡子语法。)

                怎么用?

                    {{data的key}}

                插值语法的小细节:

                    {这里不能有其它字符包括空格{

                    }这里不能有其它字符包括空格}

        */

        new Vue({

            template : `<h1>最近非常火爆的电视剧{{name}},它的上映日期是{{releaseTime}}。主角是{{lead.name}},年龄是{{lead.age}}岁。

                其他演员包括:{{actors[0].name}}({{actors[0].age}}岁),{{actors[1].name}}({{actors[1].age}}岁)。{{a.b.c.d.e.name}}

                </h1>`,

            data : {

                name : '狂飙!!!',

                releaseTime : '2023年1月2日',

                lead : {

                    name : '高启强',

                    age : 41

                },

                actors : [

                    {

                        name : '安欣',

                        age : 41

                    },

                    {

                        name : '高启兰',

                        age : 29

                    }

                ],

                a : {

                    b : {

                        c : {

                            d : {

                                e : {

                                    name : '呵呵'

                                }

                            }

                        }

                    }

                }

            }

        }).$mount('#app')

    </script>

</body>

</html>

运行结果如下:

对以上程序进行解释说明:

  1. data是Vue 实例的数据对象。并且这个对象必须是纯粹的对象 (含有零个或多个的 key/value 对)。
  1. {{message}}是Vue框架自己搞的一个语法,叫做插值语法(或者叫做胡子语法),可以从data中根据key来获取value,并且将value插入到对应的位置。
  1. data可以是以下几种情况,但不限于这几种情况:
1.     data : {  

2.       name : '老杜',  

3.       age : 18  

4.     }  

5.     //取值:  

6.     {{name}}  

7.     {{age}}  

8.       

9.     data : {  

10.    user : {  

11.      name : '老杜',  

12.      age : 18  

13.    }  

14.  }  

15.  //取值:  

16.  {{user.name}}  

17.  {{user.age}}  

18.    

19.  data : {  

20.    colors : ['红色', '黄色', '蓝色']  

21.  }  

22.  //取值:  

23.  {{colors[0]}}  

24.  {{colors[1]}}  

25.  {{colors[2]}}
  1. 以上程序执行原理:Vue编译器对template进行编译,遇到胡子{{}}时从data中取数据,然后将取到的数据插到对应的位置。生成一段HTML代码,最终将HTML渲染到挂载位置,呈现。
  1. 当data发生改变时,template模板会被重新编译,重新渲染。

1.4 Vue的template配置项

(1) template只能有一个根元素。
请看如下代码:

<!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>Vue选项template</title>  
    <!-- 安装vue -->  
    <script src="../js/vue.js"></script>  
</head>  
<body>  
    <!-- 指定挂载位置 -->  
    <div id="app"></div>  
    <!-- vue程序 -->  
    <script>  
        new Vue({  
            template : '<h1>{{message}}</h1><h1>{{name}}</h1>',  
            data : {  
                message : 'Hello Vue!',  
                name : '动力节点老杜'  
            }  
        }).$mount('#app')  
    </script>  
</body>  
</html> 

template : '<h1>{{message}}</h1><h1>{{name}}</h1>', 这里如果是两个根元素,执行结果如下(报错):
 


控制台错误信息:组件模板应该只能包括一个根元素。
所以如果使用template的话,根元素只能有一个。
代码修改如下:用一个div标签包起来

1.     new Vue({  

2.         template : '<div><h1>{{message}}</h1><h1>{{name}}</h1></div>',  

3.         data : {  

4.             message : 'Hello Vue!',  

5.             name : '动力节点老杜'  

6.         }  

7.     }).$mount('#app')

运行结果如下:

(2) template编译后进行渲染时会将挂载位置的元素替换。
(3) template后面的代码如果需要换行的话,建议将代码写到``符号当中,不建议使用 + 进行字符串的拼接。
代码修改如下:

new Vue({  
    template : `  
        <div>  
            <h1>{{message}}</h1>  
            <h1>{{name}}</h1>  
        </div>  
    `,  
    data : {  
        message : 'Hello Vue!',  
            name : '动力节点老杜'  
        }  
}).$mount('#app') 

运行结果如下:
 


(4) template配置项可以省略,将其直接编写到HTML代码当中。
代码如下:

    <!-- 指定挂载位置 -->  
    <div id="app">  
        <div>  
            <h1>{{message}}</h1>  
            <h1>{{name}}</h1>  
        </div>  
    </div>  
    <!-- vue程序 -->  
    <script>  
        new Vue({  
            data : {  
                message : 'Hello Vue!',  
                name : '动力节点老杜'  
            }  
        }).$mount('#app')  
    </script> 

运行结果如下:
 


需要注意两点:
第一:这种方式不会产生像template那种的元素替换。

第二:虽然是直接写到HTML代码当中的,但以上程序中第3~6行已经不是HTML代码了,它是具有Vue语法特色的模板语句。这段内容在data发生改变后都是要重新编译的。

(5) 将Vue实例挂载时,也可以不用$mount方法,可以使用Vue的el配置项。
代码如下:

<!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>template配置项详解</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!-- 指定挂载位置 -->

    <!-- 注意:以下代码就是只有Vue框架能够看懂的代码了。下面的代码就是一个模板语句。这个代码是需要Vue框架编译,然后渲染的。 -->

    <div id="app">

        <div>

            <h1>{{msg}}</h1>

            <h1>{{name}}</h1>

        </div>

    </div>

  

    <!-- vue程序 -->

    <script>

        // Vue.config是Vue的全局配置对象。

        // productionTip属性可以设置是否生成生产提示信息。

        // 默认值:true。如果是false则表示阻止生成提示信息。

        //Vue.config.productionTip = false

  

        /*

        关于template配置项:

            1.template后面指定的是模板语句,但是模板语句中只能有一个根节点。

            2.只要data中的数据发生变化,模板语句一定会重新编译。(只要data变,template就会重新编译,重新渲染)

            3.如果使用template配置项的话,指定挂载位置的元素会被替换。

            4.好消息:目前我们可以不使用template来编写模板语句。这些模板语句可以直接写到html标签中。Vue框架能够找到并编译,然后渲染。

            5.如果直接将模板语句编写到HTML标签中,指定的挂载位置就不会被替换了。

        关于$mount('#app')?

            也可以不使用$mount('#app')的方式进行挂载了。

            在Vue中有一个配置项:el

            el配置项和$mount()可以达到同样的效果。

            el配置项的作用?

                告诉Vue实例去接管哪个容器。

                el : '#app',表示让Vue实例去接管id='app'的容器。

            el其实是element的缩写。被翻译为元素。

        */

        new Vue({

            // 错误的

            //template : '<h1>{{msg}}</h1><h1>动力节点老杜</h1>',

            /* template : `

            <div>

                <h1>{{msg}}</h1>

                <h1>{{name}}</h1>

            </div>

            `, */

            data : {

                msg : 'Hello Vue!!!!!!!',

                name : 'a动力节点老杜!!!!!!'

            },

            el : '#app'

            //el : document.getElementById('app')

        })

        //}).$mount('#app')

    </script>

</body>

</html>

el是element单词的缩写,翻译为“元素”,el配置项主要是用来指定Vue实例关联的容器。也就是说Vue所管理的容器是哪个。

1.5 Vue实例 和 容器 的关系是:一夫一妻制

在Vue中:一个Vue实例只服务于一个容器(一对一,一夫一妻制)。
编写程序做一个简单的测试:先来看一下,一个Vue实例,两个容器,结果会是怎样?

<!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>一个Vue实例服务于一个容器</title>

    <!-- 引入vue -->
    <script src="../js/vue.js"></script>

</head>

<body>

    <button onclick="hello()">hello</button>

    <!-- 准备两个容器 -->
    <div class="app">{{name}}</div>

    <div class="app">{{name}}</div>

    
    <script>
        
        Vue.config.productionTip = false

        function hello() {
            // 这是一个Vue实例
            new Vue({
                el : '.app',
                data : {
                    name : 'jackson'
                }
            })
        }

    </script>

</body>

</html>

执行结果:
 


测试结果:如果有多个容器的话,优先服务于第一个容器。
再来看一下,两个Vue实例,一个容器,结果会怎样?

<!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>两个Vue实例,1个容器</title>

    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>

</head>

<body>
    <button onclick="hello()">hello</button>

    <div id="app">姓名:{{name}},年龄:{{age}}</div>

    <script>
        function hello(){
            // 第一个Vue实例
            new Vue({
                el : '#app',
                data : {
                    name : 'jackson'
                }
            })
            // 第二个Vue实例
            new Vue({
                el : '#app',
                data : {
                    age : 30
                }
            })
        }
    </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>两个Vue实例,1个容器</title>

    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>

</head>

<body>
    <button onclick="hello()">hello</button>

    <div id="app">姓名:{{name}},年龄:{{age}}</div>

    <script>
        function hello(){
            // 第一个Vue实例
            new Vue({
                el : '#app',
                data : {
                    name : 'jackson'
                }
            })
            // 第二个Vue实例
            new Vue({
                el : '#app',
                data : {
                    age : 30
                }
            })
        }
    </script>

</body>

</html>

执行结果:
 


将两个Vue实例的先后顺序再次颠倒:

再次执行:

2.Vue核心技术

2.0 Vue的模板语法

2.0.1 插值语法

插值语法格式:{{值}}
插值语法就是我们之前所说的:Mustach,胡子语法。
插值语法出现在标签体当中。什么是标签体?html中开始标签和结束标签中间的那部分就是标签体。例如:

<p>{{值}}</p>

<div>{{值}}</div>

<h1>{{值}}</h1>

<span>{{值}}</span>

为什么只能放到标签体当中,这是Vue语法规定的,因为只有放到标签体当中,Vue才会把你当成模板进行解析。放在其他位置,它是不理会的,不解析的。例如:以上这个插值语法的代码出现在属性的位置上,Vue自然会忽略它的。可以测试一下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>插值语法</title>

    <!-- 引入Vue -->
    <script src="../js/vue.js"></script>

</head>

<body>
    <div id="app">
        <a href="{{url}}">{{name}}</a>

    </div>

    
    <script>
        Vue.config.productionTip = false
        new Vue({
            el : '#app',
            data : {
                name : '百度',
                url : 'http://www.baidu.com'
            }
        })
    </script>

</body>

</html>

提示错误了

以上报错信息翻译为:属性内的插值删掉。改用v-bind或冒号速记。例如,使用<div :id="val"></div>代替<div id="{{val}}"></div>

插值语法{{值}} 中的“值”都可以写什么?除了可以写Vue对象的data属性,其它的只要是JS表达式都可以写。还有Vue官网提到的一些内置的函数,例如Date、Math等,具体案例可以看本节插值语法最后面的代码演示

什么是JS表达式?

JS表达式:**可以被解析为值的代码单元。**

常见的JS表达式包括哪些?

原始表达式
算术表达式
字符串表达式
数组初始化器表达式
逻辑表达式
对象创建表达式
调用表达式
属性访问表达式

测试程序:

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>插值语法</title>

</head>

<body>

  

    <!-- 引入Vue -->

    <script src="../../js/vue.js"></script>

  

    <!-- 准备容器 -->

    <div id="app">

        <h1>真实姓名:{{realname}}</h1>

        <h1>城市:{{address.city}}</h1>

        <h1>街道:{{address.street}}</h1>

        <h1>邮编:{{address.zipcode}}</h1>

        <h1>原始表达式:{{100}}、{{'jackson'}}、{{true}}</h1>

        <h1>算术表达式:{{1 + 1}}、{{(10 + 2) / 2}}</h1>

        <h1>字符串表达式:{{realname + ',Mr.du'}}</h1>

        <h1>数组初始化器表达式:{{[1,2,3]}}</h1>

        <h1>逻辑表达式:{{true && false}}</h1>

        <h1>对象创建表达式:{{new String('字符串对象')}}</h1>

        <h1>调用表达式:{{'vue'.toUpperCase()}}</h1>

        <h1>属性访问表达式:{{'vue'.length}}</h1>

        <!-- 下面这个sum方法没有,浏览器控制台是会报错的 -->

        <h1>调用自定义的函数:{{sum(1, 2)}}</h1>

    </div>

  

    <script>

  

        function sum(a, b){

            return a + b

        }

  

        Vue.config.productionTip = false

        new Vue({

            el : '#app',

            data : {

                realname : '杜聚宾',

                address : {

                    city : '北京',

                    street : '大兴凉水河二街',

                    zipcode : '100176'

                }

            }

        })

    </script>

</body>

</html>

浏览器控制台输出结果:

错误提示信息翻译为:属性或方法“sum”未在实例上定义,但在渲染过程中被引用。通过初始化属性,确保该属性在data中或在基于类的组件中是激活的。

大致的意思是:sum要么在Vue对象的data属性上,要么在类的组件中是被激活的。

结论:当在插值语法中使用“调用表达式”的时候,调用的函数必须是JS内置函数,自定义的函数不好使。

通过测试可以得知:插值语法{{值}}中的“值”可以是:JS表达式、Vue对象data属性。

<!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>

    <!-- 安装Vue -->

    <script src="../../js/vue.js"></script>

</head>

<body>

    <!--

        主要研究:{{这里可以写什么}}

        1. 在data中声明的变量、函数等都可以。

        2. 常量都可以。

        3. 只要是合法的javascript表达式,都可以。

        4. 模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 等。

            'Infinity,undefined,NaN,isFinite,isNaN,'

            'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,'

            'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,'

            'require'

     -->

    <!-- 准备容器 -->

    <div id="app">

        <!-- 在data中声明的 -->

        <!-- 这里就可以看做在使用msg变量。 -->

        <h1>{{msg}}</h1>

        <h1>{{sayHello()}}</h1>

        <!-- <h1>{{i}}</h1> -->

        <!-- <h1>{{sum()}}</h1> -->

  

        <!-- 常量 -->

        <h1>{{100}}</h1>

        <h1>{{'hello vue!'}}</h1>

        <h1>{{3.14}}</h1>

  

        <!-- javascript表达式 -->

        <h1>{{1 + 1}}</h1>

        <h1>{{'hello' + 'vue'}}</h1>

        <h1>{{msg + 1}}</h1>

        <h1>{{'msg' + 1}}</h1>

        <h1>{{gender ? '男' : '女'}}</h1>

        <h1>{{number + 1}}</h1>

        <h1>{{'number' + 1}}</h1>

        <h1>{{msg.split('').reverse().join('')}}</h1>

  

        <!-- 错误的:不是表达式,这是语句。 -->

        <!-- <h1>{{var i = 100}}</h1> -->

  

        <!-- 在白名单里面的 -->

        <h1>{{Date}}</h1>

        <h1>{{Date.now()}}</h1>

        <h1>{{Math}}</h1>

        <h1>{{Math.ceil(3.14)}}</h1>

  

    </div>

  

    <!-- vue程序 -->

    <script>

  

        // 用户自定义的一个全局变量

        var i = 100

        // 用户自定义的一个全局函数

        function sum(){

            console.log('sum.....');

        }

  

        new Vue({

            el : '#app',

            data : {

                number : 1,

                gender : true,

                msg : 'abcdef',  // 为了方便沟通,以后我们把msg叫做变量。(这行代码就可以看做是变量的声明。)

                sayHello : function(){

                    console.log('hello vue!');

                }

            }

        })

    </script>

</body>

</html>
插值语法总结:

        主要研究:{{这里可以写什么}}

        1. 在data中声明的变量、函数等都可以。

        2. 常量都可以。

        3. 只要是合法的javascript表达式,都可以。

        4. 模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 等。

            'Infinity,undefined,NaN,isFinite,isNaN,'
            'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,'

            'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,'

            'require'

2.0.2 指令语法

指令语法总结

指令语法:

            1. 什么是指令?有什么作用?

                指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

            2. Vue框架中的所有指令的名字都以“v-”开始。

            3. 插值是写在标签体当中的,那么指令写在哪里呢?

                Vue框架中所有的指令都是以HTML标签的属性形式存在的,例如:

                    <span 指令是写在这里的>{{这里是插值语法的位置}}

                    注意:虽然指令是写在标签的属性位置上,但是这个指令浏览器是无法直接看懂的。

                    是需要先让Vue框架进行编译的,编译之后的内容浏览器是可以看懂的。

            4. 指令的语法规则:

                指令的一个完整的语法格式:

                    <HTML标签 v-指令名:参数="javascript表达式"></HTML标签>

                    表达式:

                        之前在插值语法中{{这里可以写什么}},那么指令中的表达式就可以写什么。实际上是一样的。

                        但是需要注意的是:在指令中的表达式位置不能外层再添加一个{{}}

                    不是所有的指令都有参数和表达式:

                        有的指令,不需要参数,也不需要表达式,例如:v-once

                        有的指令,不需要参数,但是需要表达式,例如:v-if="表达式"

                        有的指令,既需要参数,又需要表达式,例如:v-bind:参数="表达式"

            5. v-once 指令

                作用:只渲染元素一次。随后的重新渲染,元素及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

            6. v-if="表达式" 指令

                作用:表达式的执行结果需要是一个布尔类型的数据:true或者false

                    true:这个指令所在的标签,会被渲染到浏览器当中。

                    false:这个指令所在的标签,不会被渲染到浏览器当中。

<!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>模板语法之指令语法 v-??? </title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        指令语法:

            1. 什么是指令?有什么作用?

                指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

            2. Vue框架中的所有指令的名字都以“v-”开始。

            3. 插值是写在标签体当中的,那么指令写在哪里呢?

                Vue框架中所有的指令都是以HTML标签的属性形式存在的,例如:

                    <span 指令是写在这里的>{{这里是插值语法的位置}}</span>

                    注意:虽然指令是写在标签的属性位置上,但是这个指令浏览器是无法直接看懂的。

                    是需要先让Vue框架进行编译的,编译之后的内容浏览器是可以看懂的。

            4. 指令的语法规则:

                指令的一个完整的语法格式:

                    <HTML标签 v-指令名:参数="javascript表达式"></HTML标签>

                    表达式:

                        之前在插值语法中{{这里可以写什么}},那么指令中的表达式就可以写什么。实际上是一样的。

                        但是需要注意的是:在指令中的表达式位置不能外层再添加一个{{}}

                    不是所有的指令都有参数和表达式:

                        有的指令,不需要参数,也不需要表达式,例如:v-once

                        有的指令,不需要参数,但是需要表达式,例如:v-if="表达式"

                        有的指令,既需要参数,又需要表达式,例如:v-bind:参数="表达式"

            5. v-once 指令

                作用:只渲染元素一次。随后的重新渲染,元素及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

            6. v-if="表达式" 指令

                作用:表达式的执行结果需要是一个布尔类型的数据:true或者false

                    true:这个指令所在的标签,会被渲染到浏览器当中。

                    false:这个指令所在的标签,不会被渲染到浏览器当中。

     -->

    <!-- 准备一个容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

        <h1 v-once>{{msg}}</h1>

        <h1 v-if="a > b">v-if测试:{{msg}}</h1>

    </div>

    <!-- vue程序 -->

    <script>

        new Vue({

            el : '#app',

            data : {

                msg : 'Hello Vue!',

                a : 10,

                b : 11

            }

        })

    </script>

</body>

</html>
v-bind指令详解总结

v-bind指令详解

            1. 这个指令是干啥的?

                它可以让HTML标签的某个属性的值产生动态的效果。

            2. v-bind指令的语法格式:

                <HTML标签 v-bind:参数="表达式"></HTML标签>

            3. v-bind指令的编译原理?

                编译前:

                    <HTML标签 v-bind:参数="表达式"></HTML标签>

                编译后:

                    <HTML标签 参数="表达式的执行结果"></HTML标签>

                注意两项:

                    第一:在编译的时候v-bind后面的“参数名”会被编译为HTML标签的“属性名”

                    第二:表达式会关联data,当data发生改变之后,表达式的执行结果就会发生变化。

                    所以,连带的就会产生动态效果。

            4. v-bind因为很常用,所以Vue框架对该指令提供了一种简写方式:

                只是针对v-bind提供了以下简写方式:

                  <i/mg :src="imgPath">

            5. 什么时候使用插值语法?什么时候使用指令?

                凡是标签体当中的内容要想动态,需要使用插值语法。

                只要向让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>v-bind指令详解(它是一个负责动态绑定的指令)</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        v-bind指令详解

            1. 这个指令是干啥的?

                它可以让HTML标签的某个属性的值产生动态的效果。

            2. v-bind指令的语法格式:

                <HTML标签 v-bind:参数="表达式"></HTML标签>

            3. v-bind指令的编译原理?

                编译前:

                    <HTML标签 v-bind:参数="表达式"></HTML标签>

                编译后:

                    <HTML标签 参数="表达式的执行结果"></HTML标签>

                注意两项:

                    第一:在编译的时候v-bind后面的“参数名”会被编译为HTML标签的“属性名”

                    第二:表达式会关联data,当data发生改变之后,表达式的执行结果就会发生变化。

                    所以,连带的就会产生动态效果。

            4. v-bind因为很常用,所以Vue框架对该指令提供了一种简写方式:

                只是针对v-bind提供了以下简写方式:

                    <i/mg :src="imgPath">

            5. 什么时候使用插值语法?什么时候使用指令?

                凡是标签体当中的内容要想动态,需要使用插值语法。

                只要向让HTML标签的属性动态,需要使用指令语法。

     -->

    <!-- 准备一个容器 -->

    <div id="app">

        <!-- 注意:以下代码中 msg 是变量名。 -->

        <!-- 注意:原则上v-bind指令后面的这个参数名可以随便写。 -->

        <!-- 虽然可以随便写,但大部分情况下,这个参数名还是需要写成该HTML标签支持的属性名。这样才会有意义。 -->

        <span v-bind:xyz="msg"></span>

  

        <!-- 这个表达式带有单引号,这个'msg'就不是变量了,是常量。 -->

        <span v-bind:xyz="'msg'"></span>

  

        <!-- v-bind实战 -->

        <i/m/g s/r/c="../img/1.jpg"> <br>

        <i/m/g v-bind:src="imgPath"> <br>

  

        <!-- v-bind简写形式 -->

        <i/mg :src="imgPath"> <br>

  

        <!-- 这是一个普通的文本框 -->

        <input type="text" name="username" value="zhangsan"> <br>

        <!-- 以下文本框可以让value这个数据变成动态的:这个就是典型的动态数据绑定。 -->

        <input type="text" name="username" :value="username"> <br>

  

        <!-- 使用v-bind也可以让超链接的地址动态 -->

        <a href="https://www.baidu.com">走起</a> <br>

        <a :href="url">走起2</a> <br>

  

        <!-- 不能采用以下写法吗? -->

        <!--

            不能这样,报错了,信息如下:

            Interpolation inside attributes has been removed.

            Use v-bind or the colon shorthand instead. For example,

            instead of <div id="{{ val }}">, use <div :id="val">

            属性内部插值这种语法已经被移除了。(可能Vue在以前的版本中是支持这种写法的,但是现在不允许了。)

            请使用v-bind或冒号速记来代替。

            请使用 <div :id="val"> 来代替 <div id="{{ val }}">

  

         -->

        <!-- <a href="{{url}}">走起3</a>  -->

  

        <h1>{{msg}}</h1>

  

    </div>

    <!-- vue程序 -->

    <script>

        // 赋值的过程就可以看做是一种绑定的过程。

        //let i = 100

  

        new Vue({

            el : '#app',

            data : {

                msg : 'Hello Vue!',

                imgPath : '../img/1.jpg',

                username : 'jackson',

                url : 'https://www.baidu.com'

            }

        })

    </script>

</body>

</html>

2.0.3 数据绑定v-bind和v-model

Vue关于数据绑定,提供了单向绑定和双向绑定两种方式。

  • v-bind指令进行的数据绑定是单向的,当数据data更新时,视图会更新。但当视图更新后,数据data并不会更新。
  • v-model指令进行数据绑定是双向的,当数据data更新时,视图会更新。当视图更新后,数据data也会随之更新。

v-bind和v-model除了一个单向一个双向之外,还有其它区别吗?有

  • v-bind使用在标签的任意属性上,包括自定义的属性。
  • v-model只能使用在具有value属性的input标签以及文本域textarea中。(因为只有input和textarea控件提供了用户输入的界面。只有提供了用户的输入界面,才可以让用户完成视图的更新。视图更新了数据data才会更新。另外也只有这种输入界面才能够触发键盘事件,通过键盘事件Vue底层才能更新去执行代码,更新数据data。
  • 需要注意:input标签有很多,例如文本框,单选按钮,复选框,下拉列表等。
  • v-bind和v-model都有简写方式:
                    v-bind简写方式:
                        v-bind:参数="表达式"    简写为      :参数="表达式"
                    v-model简写方式:
                        v-model:value="表达式"  简写为      v-model="表达式"

代码学习:

<!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>v-model指令详解</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        v-bind和v-model的区别和联系

            1. v-bind和v-model这两个指令都可以完成数据绑定。

            2. v-bind是单向数据绑定。

                data ===> 视图

            3. v-model是双向数据绑定。

                data <===> 视图

            4. v-bind可以使用在任何HTML标签当中。v-model只能使用在表单类元素上,例如:

                input标签、select标签、textarea标签。

                为什么v-model的使用会有这个限制呢?

                    因为表单类的元素才能给用户提供交互输入的界面。

                v-model指令通常也是用在value属性上面的。

            5. v-bind和v-model都有简写方式:

                v-bind简写方式:

                    v-bind:参数="表达式"    简写为      :参数="表达式"

                v-model简写方式:

                    v-model:value="表达式"  简写为      v-model="表达式"

     -->

    <!-- 准备一个容器 -->

    <div id="app">

        v-bind指令:<input type="text" v-bind:value="name1"><br>

        v-model指令:<input type="text" v-model:value="name2"><br>

  

        <!-- 以下报错了,因为v-model不能使用在这种元素上。 -->

        <!-- <a v-model:href="url">百度</a> -->

  

        v-bind指令:<input type="text" :value="name1"><br>

        v-model指令:<input type="text" v-model="name2"><br>

  

        消息1:<input type="text" :value="msg"><br>

        消息2:<input type="text" v-model="msg"><br>

    </div>

  

    <!-- vue程序 -->

    <script>

        new Vue({

            el : '#app',

            data : {

                name1 : 'zhangsan',

                name2 : 'wangwu',

                url : 'https://www.baidu.com',

                msg : 'Hello Vue!'

            }

        })

    </script>

</body>

</html>

2.0.4 初始MVVM架构模式

MVVM是一种架构模式(一种系统分层的思想),是Model-View-ViewModel的缩写。

MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。

2.0.4.1 MVVM架构模式

MVVM 的核心是 ViewModel 层,它就像是一个中转站,负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示:

  1. Model:数据模型,由服务端(后端)提供,它是数据的来源。
  1. View:视图,和用户交互的界面。视图负责将数据展示到界面。
  1. ViewModel:简称vm,是MVVM的核心。它的存在,让Model和View达到了解耦合(它的存在让系统分层了,分为Model层和View层,并且这两层是解耦合的),这个解耦合就是前后端分离。
  1. Model和View是无法直接联系的,ViewModel是Model和View沟通的“桥梁”。
  1. ViewModel的核心主要是实现了Dom监听和数据绑定,通过它达到了双向数据绑定的效果。
  1. DOM监听:ViewModel可以监听页面DOM元素的变化,例如DOM元素的value被更新,DOM元素被点击等。当监听到DOM元素发生变化时,会对应更新数据data。(View更新-->data则更新)
  1. 数据绑定:ViewModel可以将数据data和页面元素进行绑定,当数据data发生改变时,页面元素也同步更新。(data更新-->View则更新)
  1. 所谓的双向数据绑定就是:数据data更新时,View随之更新。当View更新时,数据data也随之更新。
  1. ViewModel帮我们做了很多事情,像以前没有ViewModel的时候,第一:我们拿到后端返回的数据之后,亲自编写操作DOM的代码来更新视图。第二:当我们获取表单中的数据时,需要亲自编写操作DOM的代码来获取表单数据。有了ViewModel之后,这些代码都在ViewModel中提前封装好了,不需要我们写。大大提高了开发效率。同时也让我们程序员更加专注业务逻辑的处理。
  1. ViewModel是一个观察者。(MVVM符合观察者模式。)

为了帮助大家理解MVVM,画了一张图来说明MVVM各层技术的组成部分:

后端业务处理再复杂跟前端也没有半毛钱关系,只要后端保证对外接口足够简单就行了,我请求api,后端你把数据返回即可。

ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型。需要注意的是ViewModel所封装的数据模型包括视图的状态和行为两部分,而Model层的数据模型只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进行时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了ViewModel里。这样的封装使得ViewModel可以完整地去描述View层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

2.0.4.2 MVVM架构模式好处
  1. 低耦合: 视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  1. 可重用性: 你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
  1. 独立开发: 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面设计。
  1. 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
2.0.4.3 Vue和MVVM的关系

Vue和Angular都是实现MVVM架构模式的框架。

Vue中的Vue实例“new Vue()”就是MVVM中的VM,也就是ViewModel。所以一般会给Vue实例起名:vm,代码如下:

const vm = new Vue({
  el : '#app',
  data : {
    username : 'zhangsan',
    age : 20
  }
})

为了提高编码速度,可以在VSCode当中配置用户代码片段,将以上代码配置为模板,例如:

{
    "create json obj": {
        "prefix": "jsonobj",
        "body": [
            "let obj = {",
            "    $1",
            "}",
            "$2"
        ]
    },
    "create vue instance":{
        "prefix": "vm",
        "body": [
            "const vm = new Vue({",
            "    el : '#app',",
            "    data : {",
            "        $1",
            "    }",
            "})",
        ]
    }
}

然后在VSCode工具中需要使用Vue实例的位置直接输入vm,代码片段自动生成:

实际代码学习:

<!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>初识MVVM分层思想</title>

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        1. MVVM是什么?

            M:Model(模型/数据)

            V:View(视图)

            VM:ViewModel(视图模型):VM是MVVM中的核心部分。(它起到一个核心的非常重要的作用。)

            MVVM是目前前端开发领域当中非常流行的开发思想。(一种架构模式。)

            目前前端的大部分主流框架都实现了这个MVVM思想,例如Vue,React等。

        2. Vue框架遵循MVVM吗?

            虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。

            Vue框架基本上也是符合MVVM思想的。

  

        3. MVVM模型当中倡导了Model和View进行了分离,为什么要分离?

            假如Model和View不分离,使用最原始的原生的javascript代码写项目:

                如果数据发生任意的改动,接下来我们需要编写大篇幅的操作DOM元素的JS代码。

  

            将Model和View分离之后,出现了一个VM核心,这个VM把所有的脏活累活给做了,

            也就是说,当Model发生改变之后,VM自动去更新View。当View发生改动之后,

            VM自动去更新Model。我们再也不需要编写操作DOM的JS代码了。开发效率提高了很多。

     -->

     <!-- 准备容器 -->

     <!-- View V-->

     <div id="app">

        姓名:<input type="text" v-model="name">

     </div>

  

     <!-- vue程序 -->

     <script>

        // ViewModel  VM

        const vm = new Vue({

            el : '#app',

            // Model  M

            data : {

                name : 'zhangsan'

            }

        })

     </script>

</body>

</html>
2.0.4.4 vm总结
  1. 通过Vue实例都可以访问哪些属性?(通过vm都可以vm. 什么。)

            Vue实例中的属性很多,有的以 $ 开始,有的以 _ 开始

            所有以 $ 开始的属性,可以看做是公开的属性,这些属性是供程序员使用的。

            所有以 _ 开始的属性,可以看做是私有的属性,这些属性是Vue框架底层使用的。一般我们程序员很少使用。
            通过vm也可以访问Vue实例对象的原型对象上的属性,例如:vm.$delete...

<!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>认识vm</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        1. 通过Vue实例都可以访问哪些属性?(通过vm都可以vm. 什么。)

            Vue实例中的属性很多,有的以 $ 开始,有的以 _ 开始。

            所有以 $ 开始的属性,可以看做是公开的属性,这些属性是供程序员使用的。

            所有以 _ 开始的属性,可以看做是私有的属性,这些属性是Vue框架底层使用的。一般我们程序员很少使用。

            通过vm也可以访问Vue实例对象的原型对象上的属性,例如:vm.$delete...

        2.

     -->

    <div id="app">

        <h1>{{msg}}</h1>

    </div>

    <script>

  

        let dataObj = {

            msg : 'Hello Vue!'

        }

  

        const vm = new Vue({

            el : '#app',

            data : dataObj

        })

  

        // 按说msg是dataObj对象的属性。

        console.log('dataObj的msg', dataObj.msg);

  

        // 为什么msg属性可以通过vm来访问呢?

        // 这是因为Vue框架底层使用了数据代理机制。

        // 要想搞明白数据代理机制,必须有一个基础知识点要学会:Object.defineProperty()。

        console.log('vm的msg', vm.msg);

  

    </script>

</body>

</html>

2.1 数据代理机制

2.1.1 Object.defineProperty()

这是ES5的特性,这个方法的作用是:给对象定义新属性或修改原有的属性。
语法如下:

Object.defineProperty(对象, 属性名, 配置项)

对象:指定给哪个对象定义新属性。

属性名:新属性的属性名。

配置项:给该属性设置一些配置项,采用 {} 进行配置

编写程序,测试该方法是否能够为对象添加新属性:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6新特性:Object.definedProperty(),给对象定义新属性或修改属性</title>

</head>

<body>
    <script>
        // 对象
        let phone = {
            name : 'iPhone 14',
            price : 11000
        }

        // 给对象phone定义新属性颜色color
        Object.defineProperty(phone, 'color', {})

        // 读取color属性
        console.log('手机颜色:' + phone.color) 
    </script>

</body>

</html>

执行结果:

结果是undefined,这是因为没有给color属性指定值,默认值是undefined,可以通过配置项value给属性指定值:

Object.defineProperty(phone, 'color', {
  value : '太空灰'
})


可以对新增的color属性重新赋值吗?尝试一下:
 


执行结果:
 


重新赋值失败,怎么样才能支持重新赋值呢?需要添加一个配置项writable,将其设置为true:
 


再次执行:
 


重点来了:在defineProperty方法中的配置项中有getter和setter方法,当修改对象的属性值时,setter方法会被自动调用,当读取对象的属性值时,getter方法会被自动调用,测试一下:

Object.defineProperty(phone, 'color', {
  value : '太空灰', // 给属性指定值
  writable : true, // 设置该属性的值是否可以被重写,默认false,true表示支持重写
  // getter
  get : function(){
    console.log('getter执行了')
  },
  // setter
  set : function(){
    console.log('setter执行了')
  }
})

执行结果:

这个错误原因是:当你使用getter和setter的时候,不能使用writable和value。把这两个配置项注释掉:

Object.defineProperty(phone, 'color', {
  //value : '太空灰', // 给属性指定值
  //writable : true, // 设置该属性的值是否可以被重写,默认false,true表示支持重写
  // getter
  get : function(){
    console.log('getter执行了')
  },
  // setter
  set : function(){
    console.log('setter执行了')
  }
})


所以在使用getter和setter的时候,配置项writable和value是不能出现的。

setter在什么时候执行的?getter又是在什么时候执行的?

当你给对象属性赋值的时候setter方法会被自动调用。

当你读取对象属性的时候getter方法会被自动调用。

为什么执行结果是:“手机颜色:undefined”,显然是因为setter方法中什么也没做,并没有在该方法中完成赋值,要赋值的话,首先要拿到这个值,也就是拿到'夕阳红',怎么拿?setter方法上有一个参数,这个参数是专门用来接收'夕阳红'这个值的,测试一下:
 


注意:setter方法的参数名是随意的,只是一个变量名而已,都能接收赋值时传过来的值。

接下来,在setter方法和getter方法中编写代码,来完成属性值的修改以及读取属性值,代码如下:

为什么运行结果报错了,而且还是栈内存溢出?显然是因为递归导致的。这是因为我们在setter方法中执行了this.color = val,而这个操作执行时setter方法会被调用,setter方法调用后,又执行this.color = val,此时又会去执行setter方法。getter方法也是同理的。不再赘述。结论是:不要在getter中再次获取该属性值,也不要在setter中再次设置该属性,会发生无限递归,导致栈内存溢出错误的发生。

那该怎么办?可以在外面定义一个临时的变量,当getter方法执行时返回该临时变量的值,当setter方法执行时修改临时变量的值,代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>ES6新特性:Object.definedProperty(),给对象定义新属性或修改属性</title>

  </head>

  <body>
    <script>
      // 对象
      let phone = {
        name : 'iPhone 14',
        price : 11000
      }
      
      // 临时变量,用来存储属性的值
      let temp
      
      // 给对象phone定义新属性颜色color
      Object.defineProperty(phone, 'color', {
        //value : '太空灰', // 给属性指定值
        //writable : true, // 设置该属性的值是否可以被重写,默认false,true表示支持重写
        // getter
        get : function(){
          console.log('getter执行了')
          return temp
        },
        // setter
        set : function(val){
          console.log('setter执行了')
          console.log('拿到的值:' + val)
          temp = val
        }
      })
      
      // 给新增的color属性重新赋值
      phone.color = '夕阳红'
      
      // 读取color属性
      console.log('手机颜色:' + phone.color) 
    </script>

  </body>

</html>


根据ES6新特性来说,在对象中的函数是可以简写的,其中“:function”是可以省略的,也就是说以上的代码可简写为如下的代码:
 


以后大家在编写代码的时候,直接采用简写形式即可。
那么了解了Object.defineProperty()方法之后,它和数据代理机制有什么关系呢?

代码学习:

<!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>Object.defineProperty()</title>

</head>

<body>

    <!--

        Object.defineProperty()

        1. 这个方法是ES5新增的。

        2. 这个方法的作用是:给对象新增属性,或者设置对象原有的属性。

        3. 怎么用?

            Object.defineProperty(给哪个对象新增属性, '新增的这个属性名叫啥', {给新增的属性设置相关的配置项key:value对})

        4. 第三个参数是属性相关的配置项,配置项都有哪些?每个配置项的作用是啥?

            value 配置项:给属性指定值

            writable 配置项:设置该属性的值是否可以被修改。true表示可以修改。false表示不能修改。

            getter方法 配置项:不需要我们手动调用的。当读取属性值的时候,getter方法被自动调用。

                * getter方法的返回值非常重要,这个返回值就代表这个属性它的值。

            setter方法 配置项:不需要我们手动调用的。当修改属性值的时候,setter方法被自动调用。

                * setter方法上是有一个参数的,这个参数可以接收传过来的值。

            注意:当配置项当中有setter和getter的时候,value和writable配置项都不能存在。

     -->

     <script>

  

        // 这是一个普通的对象

        let phone = {}

  

        // 临时变量

        let temp

  

        // 给上面的phone对象新增一个color属性

        Object.defineProperty(phone, 'color', {

            //value : '太空灰',

            //writable : true,

            // getter方法配置项

            get : function(){

                console.log('getter方法执行了@@@');

                //return '动态'

                //return this.color

                return temp

            },

            // setter方法配置项

            set : function(val){

                console.log('setter方法执行了@@@',val);

                //this.color = val

                temp = val

            }

        })

  

     </script>

</body>

</html>
2.1.1.1 Object.defineProperty总结

        1. 这个方法是ES5新增的。

        2. 这个方法的作用是:给对象新增属性,或者设置对象原有的属性。

        3. 怎么用?

            Object.defineProperty(给哪个对象新增属性, '新增的这个属性名叫啥', {给新增的属性设置相关的配置项key:value对})

        4. 第三个参数是属性相关的配置项,配置项都有哪些?每个配置项的作用是啥?

            value 配置项:给属性指定值

            writable 配置项:设置该属性的值是否可以被修改。true表示可以修改。false表示不能修改。

            getter方法 配置项:不需要我们手动调用的。当读取属性值的时候,getter方法被自动调用。

                * getter方法的返回值非常重要,这个返回值就代表这个属性它的值。

            setter方法 配置项:不需要我们手动调用的。当修改属性值的时候,setter方法被自动调用。

                * setter方法上是有一个参数的,这个参数可以接收传过来的值。

            注意:当配置项当中有setter和getter的时候,value和writable配置项都不能存在。

2.1.2 数据代理机制

所谓的数据代理就是:通过访问代理对象的数据来间接访问目标对象的数据。可以写一个程序,给大家演示一下数据代理:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>数据代理</title>

</head>

<body>
    <script>
        // 目标对象
        var target = {
            name : 'zhangsan'
        }
        // 代理对象
        var proxy = {}
        // 为代理对象添加新属性name
        Object.defineProperty(proxy, 'name', {
            get(){
                return target.name
            },
            set(val){
                target.name = val
            }
        })
        // 通过访问代理对象的name属性来间接访问目标对象的name属性
        proxy.name = 'jackson'
        console.log('proxy.name = ' + proxy.name)
        console.log('target.name = ' + target.name)
    </script>

</body>

</html>

执行结果:
通过测试得知,当我们修改代理的name时,目标的name也被修改了。这就是数据代理的实现原理。
 


我们可以把proxy在控制台输出一下,看看是什么样子
通过测试得知,当我们修改代理的name时,目标的name也被修改了。这就是数据代理的实现原理。

我们可以把proxy在控制台输出一下,看看是什么样子
 


你会看到一个name属性,并且这个name属性对应了getter和setter方法。

注意:凡是在控制台输出的时候,属性的值是:(...),就表示该属性使用了数据代理机制,(...) 表示目前未知,必须通过调用getter方法来动态获取该属性的值,鼠标停留到"(...)"上面,你会看到如下提示信息:


当我们点击"(...)"的时候会调用getter方法:

代码学习:

<!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="app">

        <h1>{{msg}}</h1>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Hello Vue!'

            }

        })

    </script>

  

    <!--

        1. 什么是数据代理机制?

            通过访问 代理对象的属性 来间接访问 目标对象的属性。

            数据代理机制的实现需要依靠:Object.defineProperty()方法。

        2. ES6新特性:

            在对象中的函数/方法 :function 是可以省略的。

     -->

     <script>

  

        // 目标对象

        let target = {

            name : 'zhangsan'

        }

  

        // 代理对象

        let proxy = {}

  

        // 如果要实现数据代理机制的话,就需要给proxy新增一个name属性。

        // 注意:代理对象新增的这个属性的名字 和 目标对象的属性名要一致。

        Object.defineProperty(proxy, 'name', {

            // get : function(){

            //     // 间接访问目标对象的属性

            //     return target.name

            // },

            // set : function(val){

            //     target.name = val

            // }

  

            get(){

                console.log('getter方法执行了@@@@');

                return target.name

            },

            set(val){

                target.name = val

            }

        })

  

        // let target = {

        //     name : 'zhangsan'

        // }

  

        // const vm = new Vue({

        //     el : '#app',

        //     data : target

        // })

     </script>

</body>

</html>
数据代理机制总结:
  1. 什么是数据代理机制?

            通过访问 代理对象的属性 来间接访问 目标对象的属性。

            数据代理机制的实现需要依靠:Object.defineProperty()方法。

        2. ES6新特性:

            在对象中的函数/方法 :function 是可以省略的。

2.1.3 Vue数据代理机制对属性名的要求

学习代码:

<!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>Vue数据代理机制对属性名的要求</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        1. Vue实例不会给以_和$开始的属性名做数据代理。

        2. 为什么?

            如果允许给_或$开始的属性名做数据代理的话。

            vm这个Vue实例上可能会出现_xxx或$xxx属性,

            而这个属性名可能会和Vue框架自身的属性名冲突。

  

        3. 在Vue当中,给data对象的属性名命名的时候,不能以_或$开始。

     -->

    <!-- 容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

    </div>

    <!-- vue程序 -->

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Hello Vue!',

                _name : 'zhangsan',

                $age : 20

            }

        })

    </script>

</body>

</html>

执行结果,这样写完以后只要是我们以$和_开头的自定义的属性,都不会被Vue识别,其他自定义的属性都可以。

2.1.4 模拟Vue的数据代理

Vue的代码是这样写的:

const vm = new Vue({
  el : '#app',
  data : {
    name : 'jackson'
  }
})

// 读取name
console.log('name = ' + vm.name)
// 修改name
vm.name = 'lucy'
// 读取name
console.log('name = ' + vm.name)

我们可以自己写一个程序,模拟一下Vue,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue的数据代理</title>

</head>

<body>
    <script>
        // ES6新特性(用class关键字定义类)
        class Vue {
            // ES6新特性(构造函数)
            constructor(options) {
                // 获取目标对象的所有属性(options.data就是目标对象)
                let target = options.data
                // 给代理对象扩展属性(在构造方法中this就是当前对象,当前对象是Vue实例,所以this就是代理对象)
                let proxy = this
                // 遍历目标对象的所有属性(属性可能有多个)
                // Object.keys(obj) 可以获取obj对象的所有属性名,并最终得到一个数组。【ES6新特性】
                Object.keys(target).forEach((propertyName) => {
                    Object.defineProperty(this, propertyName, {
                        get(){
                            return target[propertyName]
                        },
                        set(val){
                            target[propertyName] = val
                        }
                    })
                })
            }
        }
        // 目标对象
        let options = {
            data : {
                name : 'xiaoming',
                age : 20
            }
        }
        // 代理对象
        const vm = new Vue(options)
        // 通过代理vm访问目标options.data中的属性
        console.log('vm.name = ' + vm.name)
        vm.name = 'xiaogang'
        console.log('vm.name = ' + vm.name)

        console.log('vm.age = ' + vm.age)
        vm.age = 18
        console.log('vm.age = ' + vm.age)

    </script>

</body>

</html>

2.1.5 Vue数据代理源码跟踪

以上是我们自己写的,真正的Vue框架底层是如何实现数据代理的呢?一起跟踪一下源码:

以下源码跟踪方式是在chrome浏览器中添加断点的方式进行跟踪:

<!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>解读Vue框架源代码</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        Vue框架源代码中关键性代码:

        1. var data = vm.$options.data;

            注意:这是获取data。程序执行到这里的时候vm上还没有 _data 属性。

        2. data = vm._data = isFunction(data) ? getData(data, vm) : data || {};

            程序执行完这个代码之后,vm对象上多了一个_data这样的属性。

            通过以上源码解读,可以得知data不一定是一个{},也可以是一个函数。

            代码含义:

                如果data是函数,则调用getData(data, vm)来获取data。

                如果data不是函数,则直接将data返回,给data变量。并且同时将data赋值给vm._data属性了。

            有一个疑问?

                程序执行到这里,为什么要给vm扩展一个_data属性呢?

                    _data属性,以"_"开始,足以说明,这个属性是人家Vue框架底层需要访问的。

                    Vue框架底层它使用vm._data这个属性干啥呢?

                        vm._data是啥?

                            vm._data 是:{

                                            name : 'jackson',

                                            age : 35

                                        }

                vm._data 这个属性直接指向了底层真实的data对象。通过_data访问的name和age是不会走数据代理机制的。

                通过vm._data方式获取name和age的时候,是不会走getter和setter方法的。

            注意:对于Vue实例vm来说,不仅有_data这个属性,还有一个$data这个属性。

                   _data 是框架内部使用的,可以看做私有的。

                   $data 这是Vue框架对外公开的一个属性,是给我们程序员使用。

  

        3. 重点函数:

            function isReserved(str) {

                var c = (str + '').charCodeAt(0);

                return c === 0x24 || c === 0x5f;

            }

            这个函数是用来判断字符串是否以 _ 和 $ 开始的。

            true表示以_或$开始的。

            false表示不是以_或$开始的。

        4. proxy(vm, "_data", key);

            通过这行代码直接进入代理机制(数据代理)。

        5. 重点函数proxy

            function proxy(target, sourceKey, key) { // target是vm,sourceKey是"_data",key是"age"

                sharedPropertyDefinition.get = function proxyGetter() {

                    return this["_data"]["age"];

                };

                sharedPropertyDefinition.set = function proxySetter(val) {

                    this["_data"]["age"] = val;

                };

                Object.defineProperty(vm, 'age', sharedPropertyDefinition);

            }

     -->

    <!-- 容器 -->

    <div id="app">

        <h1>姓名:{{name}}</h1>

        <h1>年龄:{{age}}岁</h1>

    </div>

  

    <!-- vue代码 -->

    <script>

  

        function isReserved(str) {

            var c = (str + '').charCodeAt(0);

            return c === 0x24 || c === 0x5f;

        }

  

        const vm = new Vue({

            el : '#app',

            data : {

                name : 'jackson',

                age : 35

            }

        })

  

        // 如果我们程序员不想走代理的方式读取data,想直接读取data当中的数据,可以通过_data和$data属性来访问。

        // 建议使用$data这个属性。

        console.log('name = ' + vm.$data.name)

        console.log('age = ' + vm.$data.age)

    </script>

</body>

</html>

2.1.6 _date属性

在之前的源码追踪过程中,我们可以看到Vue实例定义了一个_data属性:

可以在控制台查看一下,是否存在这样的属性:

果然是存在的,那么也就是说,通过_data属性也可以访问data的属性?

_data属性这个了解一下就行,到目前为止,访问data的属性可以通过两种方式:

  • 第一种方式(常用):vm.name
  • 第二种方式(了解):vm._data.name

在这里再给大家透露一个小技巧,大家在查看Vue实例的时候,发现Vue实例有很多属性,有的以“$”开始,有的以“_”开始,这里要记住哦:

  • "$"开始的属性一般都是公开的,供我们开发人员使用的。
  • "_"开始的属性一般都是私有的,是供Vue核心程序使用的,支撑Vue运行的,这些属性是不建议我们使用的。(所以_data属性了解一下即可。)

2.1.7 data可以写成函数?

如果data是一个函数的话,必须返回一个{}对象
另外还有一点需要大家记住,data如果写成函数的话,这个函数不能够使用箭头函数。因为箭头函数中的this是window对象。我们需要让这个this是当前的Vue实例。

到目前为止,大家知道的defineProperty方法的配置项的属性有两个:

  • 一个是value:给新增的属性赋值
  • 一个是writable:默认值false,如果是true表示该属性的值是可修改的

如果setter和getter使用的话,以上两个属性都是不能出现的。这一点还是要记住。
那么除了writable和value之外,还有其他的属性吗?当然有,例如:

  • enumerable:值为布尔类型true或者false。默认值false。true表示该属性可枚举,可遍历。
  • configurable:值为布尔类型true或者false。默认值false。true表示该属性可删除。
<!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>data也可以是一个函数</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!-- 容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

    </div>

    <!-- vue代码 -->

    <script>

        const vm = new Vue({

            el : '#app',

            // data : {

            //     msg : 'Hello Vue!'

            // }

            // data functions should return an object:data函数应该返回一个对象。

            // data 也可以是一个函数。

            // 如果是函数的话,必须使用return语句返回{}对象。

            // data可以是直接的对象,也可以是一个函数,什么时候使用直接的对象?什么时候使用函数呢?(等你学到组件的时候自然就明白了。)

            // data : function(){

            //     return {

            //         msg : 'Hello Vue!'

            //     }

            // }

  

            // 在对象当中,函数的 :function 可以省略

            data(){

                return {

                    msg : 'Hello Zhangsan!'

                }

            }

        })

  

        // 关于配置项:enumerable、configurable

        let phone = {

            name : '苹果X'

        }

  

        // 给phone对象新增一个color属性

        Object.defineProperty(phone, 'color', {

            value : '奶奶灰',

            // true表示该属性是可以遍历的。(可枚举的,可迭代的。)

            // false表示该属性是不可遍历的。

            enumerable : false,

  

            // true表示该属性是可以被删除的。

            // false表示该属性是不可以被删除的。

            configurable : false

        })

  

    </script>

</body>

</html>

2.2 事件处理

2.2.1 事件处理的核心语法

(1) 指令的语法格式:<标签 v-指令名:参数=”表达式”></标签>

(2) 事件绑定的语法格式:v-on:事件名。例如鼠标单击事件的绑定使用v-on:click。
(3) 绑定的回调函数需要在Vue实例中使用methods进行注册。methods可以配置多个回调函数,采用逗号隔开。
(4) 绑定回调函数时,如果回调函数没有参数,( )可以省略。
(5) 每一个回调函数都可以接收一个事件对象event。
(6) 如果回调函数有参数,并且还需要获取事件对象,可以使用$event进行占位。
(7) v-on:click可以简写为@click。简写的语法格式:@事件名

(8) 回调函数中的this是vm。如果回调函数是箭头函数的话,this是window对象,因为箭头函数没有自己的this,它的this是继承过来的,默认这个this是箭头函数所在的宿主对象。这个宿主对象其实就是它的父级作用域。而对象又不能构成单独的作用域,所以这个父级作用域是全局作用域,也就是window。
(9) 回调函数并没有在vm对象上,为什么通过vm可以直接调用函数呢?尝试手写Vue框架。
(10) 可以在函数中改变data中的数据,例如:this.counter++,这样会联动页面上产生动态效果。

<!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>Vue的事件绑定</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!--

        Vue事件处理:

            1.指令的语法格式:

                <标签 v-指令名:参数名="表达式">{{插值语法}}</标签>

                “表达式”位置都可以写什么?

                    常量、JS表达式、Vue实例所管理的XXX

            2. 在Vue当中完成事件绑定需要哪个指令呢?

                v-on指令。

                语法格式:

                    v-on:事件名="表达式"

                例如:

                    v-on:click="表达式" 表示当发生鼠标单击事件之后,执行表达式。

                    v-on:keydown="表达式" 表示当发生键盘按下事件之后,执行表达式。

            3. 在Vue当中,所有事件所关联的回调函数,需要在Vue实例的配置项methods中进行定义。

                methods是一个对象:{}

                在这个methods对象中可以定义多个回调函数。

            4. v-on指令也有简写形式

                v-on:click 简写为 @click

                v-on:keydown 简写为 @keydown

                v-on:mouseover 简写为 @mouseover

                ....

            5. 绑定的回调函数,如果函数调用时不需要传递任何参数,小括号()可以省略。

            6. Vue在调用回调函数的时候,会自动给回调函数传递一个对象,这个对象是:当前发生的事件对象,并且这个时候是不能带小括号的,否则event将不会被识别。

            7. 在绑定回调函数的时候,可以在回调函数的参数上使用 $event 占位符,Vue框架看到这个 $event 占位符之后,会自动将当前事件以对象的形式传过去。

     -->

    <!-- 容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

        <!-- 使用javascript原生代码如何完成事件绑定。 -->

        <button onclick="alert('hello')">hello</button>

        <!-- 使用Vue来完成事件绑定 -->

        <!-- 以下是错误的,因为alert()并没有被Vue实例管理。 -->

        <!-- <button v-on:click="alert('hello')">hello</button> -->

        <!-- 以下是错误的,因为sayHello()并没有被Vue实例管理。 -->

        <!-- <button v-on:click="sayHello()">hello</button> -->

        <!-- 正确的写法 -->

        <button v-on:click="sayHello()">hello</button>

        <!-- v-on指令的简写形式 -->

        <button @click="sayHi()">hi button</button>

        <!-- <button @click="sayHi($event, 'jack')">hi button2</button> -->

        <!-- 绑定的回调函数,如果不需要传任何参数,小括号() 可以省略 -->

        <button @click="sayWhat">what button</button>

    </div>

    <!-- vue代码 -->

    <script>

        // 自定义一个函数

        // function sayHello(){

        //     alert('hello')

        // }

  

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Vue的事件绑定'

            },

            methods : {

                // 回调函数

                // sayHello : function(){

                //     alert('hello')

                // }

                // : function 可以省略

                sayHello(){

                    alert('hello2')

                },

                sayHi(event, name){

                    console.log(name, event)

                    //alert("hi " + name)

                },

                sayWhat(event){

                    //console.log(event)

                    //console.log(event.target)

                    //console.log(event.target.innerText)

                    //alert('what...')

                }

            }

        })

    </script>

</body>

</html>

#### 事件处理总结

Vue事件处理:

            1.指令的语法格式:

                <标签 v-指令名:参数名="表达式">{{插值语法}}</标签>

                “表达式”位置都可以写什么?

                    常量、JS表达式、Vue实例所管理的XXX

            2. 在Vue当中完成事件绑定需要哪个指令呢?

                v-on指令。

                语法格式:

                    v-on:事件名="表达式"

                例如:

                    v-on:click="表达式" 表示当发生鼠标单击事件之后,执行表达式。

                    v-on:keydown="表达式" 表示当发生键盘按下事件之后,执行表达式。

            3. 在Vue当中,所有事件所关联的回调函数,需要在Vue实例的配置项methods中进行定义。

                methods是一个对象:{}

                在这个methods对象中可以定义多个回调函数。

            4. v-on指令也有简写形式

                v-on:click 简写为 @click

                v-on:keydown 简写为 @keydown

                v-on:mouseover 简写为 @mouseover

                ....

            5. 绑定的回调函数,如果函数调用时不需要传递任何参数,小括号()可以省略。

            6. Vue在调用回调函数的时候,会自动给回调函数传递一个对象,这个对象是:当前发生的事件对象,并且这个时候是不能带小括号的,否则event将不会被识别。

            7. 在绑定回调函数的时候,可以在回调函数的参数上使用 event 占位符,Vue框架看到这个 event 占位符之后,会自动将当前事件以对象的形式传过去。
还有一点:通过methods注册的函数,每一个函数同时也会作为Vue实例的属性,也就是说这些函数也可以通过vm来直接访问:

const vm = new Vue({
  el : '#app',
  methods : {
    doSome(){
      alert('hello vue!')
    }
  }
})

// 可以直接通过Vue实例来访问doSome()方法
vm.doSome()

#### 关于事件回调函数中的this

<!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>关于事件回调函数中的this</title>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

</head>

<body>

    <!-- 容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

        <h1>计数器:{{counter}}</h1>

        <button @click="counter++">点击我加1</button>

        <button @click="add">点击我加1</button>

        <button @click="add2">点击我加1(箭头函数)</button>

    </div>

    <!-- vue代码 -->

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '关于事件回调函数中的this',

                counter : 0

            },

            // 1.methods对象中的方法可以通过vm去访问吗?可以。

            // 2.methods对象中的方法有没有做数据代理呢?没有。

            methods : {

                add(){

                    //counter++; // 错误的。

                    // 在这里需要操作counter变量?怎么办?

                    //console.log(vm === this)

                    //console.log(this)

                    this.counter++;

                    //vm.counter++;

                },

                add2:()=>{

                    //this.counter++;

                    //console.log(this === vm)

                    //箭头函数中没有this,箭头函数中的this是从父级作用域当中继承过来的。

                    //对于当前程序来说,父级作用域是全局作用域:window

                    console.log(this)

                },

                sayHi(){

                    alert('hi...')

                }

            }

        })

  

    </script>

</body>

</html>

2.2.2 事件修饰符

学习代码

<!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>

    <!-- 安装Vue -->

    <script src="../js/vue.js"></script>

    <style>

        .divList{

            width: 300px;

            height: 200px;

            background-color: aquamarine;

            overflow: auto;

        }

        .item{

            width: 300px;

            height: 200px;

        }

    </style>

</head>

<body>

    <!--

        Vue当中提供的事件修饰符:

        .stop : 停止事件冒泡,等同于 event.stopPropagation()。

        .prevent : 等同于 event.preventDefault() 阻止事件的默认行为。

        .capture :添加事件监听器时使用事件捕获模式

                    添加事件监听器包括两种不同的方式:

                        一种是从内到外添加。(事件冒泡模式)

                        一种是从外到内添加。(事件捕获模式)

        .self :这个事件如果是“我自己元素”上发生的事件,这个事件不是别人给我传递过来的事件,则执行对应的程序。

        .once : 事件只发生一次

        .passive :passive翻译为顺从/不抵抗。无需等待,直接继续(立即)执行事件的默认行为。

                    .passive 和 .prevent 修饰符是对立的。不可以共存。(如果一起用,就会报错。)

                    .prevent:阻止事件的默认行为。

                    .passive:解除阻止。

     -->        

    <!-- 容器 -->

    <div id="app">

        <h1>{{msg}}</h1>

  

        <!-- 阻止事件的默认行为 -->

        <a href="https://www.baidu.com" @click.prevent="yi">百度</a> <br><br>

  

        <!-- 停止事件冒泡 -->

        <div @click="san">

            <div @click.stop="er">

                <button @click="yi">事件冒泡</button>

            </div>

        </div>

  

        <br><br>

  

        <!-- 添加事件监听器时使用事件捕获模式 -->

        <div @click.capture="san">

            <!-- 这里没有添加.capture修饰符,以下这个元素,以及这个元素的子元素,都会默认采用冒泡模式。 -->

            <div @click="er">

                <button @click="yi">添加事件监听器的时候采用事件捕获模式</button>

            </div>

        </div>

        <br>

        <br>

        <!-- .self修饰符 -->

        <div @click="san">

            <div @click.self="er">

                <button @click="yi">self修饰符</button>

            </div>

        </div>

  

        <br>

        <br>

        <!-- 在Vue当中,事件修饰符是可以多个联合使用的。

            但是需要注意:

                @click.self.stop:先.self,再.stop

                @click.stop.self:先.stop,再.self

         -->

        <div @click="san">

            <div @click="er">

                <button @click.self.stop="yi">self修饰符</button>

            </div>

        </div>

  

        <br>

        <br>

        <!-- .once修饰符:事件只发生一次 -->

        <button @click.once="yi">事件只发生一次</button>

  

        <!-- .passive修饰符 -->

        <div class="divList" @wheel.passive="testPassive">

            <div class="item">div1</div>

            <div class="item">div2</div>

            <div class="item">div3</div>

        </div>

  

    </div>

    <!-- vue代码 -->

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '事件修饰符'

            },

            methods : {

                yi(event){

                    //alert('去百度!!!!!!')

                    // 手动调用事件对象的preventDefault()方法,可以阻止事件的默认行为。

                    // 在Vue当中,这种事件的默认行为可以不采用手动调用DOM的方式来完成,可以使用事件修饰符:prevent。

                    //event.preventDefault();

                    alert(1)

                },

                er(){

                    alert(2)

                },

                san(){

                    alert(3)

                },

                testPassive(event){

                    for(let i = 0; i < 100000; i++){

                        console.log('test passive')

                    }

                    // 阻止事件的默认行为

                    //event.preventDefault()

                }

            }

        })

    </script>

</body>

</html>
事件修饰符代码总结:

Vue当中提供的事件修饰符:

        .stop : 停止事件冒泡,等同于 event.stopPropagation()。

        .prevent : 等同于 event.preventDefault() 阻止事件的默认行为。

        .capture :添加事件监听器时使用事件捕获模式

                    添加事件监听器包括两种不同的方式:

                        一种是从内到外添加。(事件冒泡模式)

                        一种是从外到内添加。(事件捕获模式)

        .self :这个事件如果是“我自己元素”上发生的事件,这个事件不是别人给我传递过来的事件,则执行对应的程序。

        .once : 事件只发生一次

        .passive :passive翻译为顺从/不抵抗。无需等待,直接继续(立即)执行事件的默认行为。

                    .passive 和 .prevent 修饰符是对立的。不可以共存。(如果一起用,就会报错。)

                    .prevent:阻止事件的默认行为。

                    .passive:解除阻止。

2.2.3 按键修饰符

代码学习:

<!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>

    <!--

        9个比较常用的按键修饰符:

            .enter

            .tab (必须配合keydown事件使用。)

            .delete (捕获“删除”和“退格”键)

            .esc

            .space

            .up

            .down

            .left

            .right

        怎么获取某个键的按键修饰符?

            第一步:通过event.key获取这个键的真实名字。

            第二步:将这个真实名字以kebab-case风格进行命名。

                PageDown是真实名字。经过命名之后:page-down

        按键修饰符是可以自定义的?

            通过Vue的全局配置对象config来进行按键修饰符的自定义。

            语法规则:

                Vue.config.keyCodes.按键修饰符的名字 = 键值

        系统修饰键:4个比较特殊的键

            ctrl、alt、shift、meta

            对于keydown事件来说:只要按下ctrl键,keydown事件就会触发。

            对于keyup事件来说:需要按下ctrl键,并且加上按下组合键,然后松开组合键之后,keyup事件才能触发。

     -->

    <div id="app">

        <h1>{{msg}}</h1>

        回车键:<input type="text" @keyup.enter="getInfo"><br>

        回车键(键值):<input type="text" @keyup.13="getInfo"><br>

        delete键:<input type="text" @keyup.delete="getInfo"><br>

        esc键:<input type="text" @keyup.esc="getInfo"><br>

        space键:<input type="text" @keyup.space="getInfo"><br>

        up键:<input type="text" @keyup.up="getInfo"><br>

        down键:<input type="text" @keyup.down="getInfo"><br>

        left键:<input type="text" @keyup.left="getInfo"><br>

        right键:<input type="text" @keyup.right="getInfo"><br>

        <!-- tab键无法触发keyup事件。只能触发keydown事件。 -->

        tab键: <input type="text" @keyup.tab="getInfo"><br>

        tab键(keydown): <input type="text" @keydown.tab="getInfo"><br>

        PageDown键: <input type="text" @keyup.page-down="getInfo"><br>

        huiche键: <input type="text" @keyup.huiche="getInfo"><br>

        ctrl键(keydown): <input type="text" @keydown.ctrl="getInfo"><br>

        ctrl键(keyup): <input type="text" @keyup.ctrl="getInfo"><br>

        ctrl键(keyup): <input type="text" @keyup.ctrl.i="getInfo"><br>

    </div>

  

    <script>

  

        // 自定义了一个按键修饰符:.huiche 。代表回车键。

        Vue.config.keyCodes.huiche = 13

  

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '按键修饰符'

            },

            methods : {

                getInfo(event){

                    // 当用户键入回车键的时候,获取用户输入的信息。

                    //if(event.keyCode === 13){

                        console.log(event.target.value)

                    //}

                    console.log(event.key)

                }

            }

        })

    </script>

</body>

</html>
按键修饰符总结

9个比较常用的按键修饰符:

            .enter

            .tab (必须配合keydown事件使用。)

            .delete (捕获“删除”和“退格”键)

            .esc

            .space

            .up

            .down

            .left

            .right

        怎么获取某个键的按键修饰符?

            第一步:通过event.key获取这个键的真实名字。

            第二步:将这个真实名字以kebab-case风格进行命名。

                PageDown是真实名字。经过命名之后:page-down

        按键修饰符是可以自定义的?

            通过Vue的全局配置对象config来进行按键修饰符的自定义。

            语法规则:

                Vue.config.keyCodes.按键修饰符的名字 = 键值

        系统修饰键:4个比较特殊的键

            ctrl、alt、shift、meta

            对于keydown事件来说:只要按下ctrl键,keydown事件就会触发。

            对于keyup事件来说:需要按下ctrl键,并且加上按下组合键,然后松开组合键之后,keyup事件才能触发。

2.3 计算属性

1. 案例:用户输入信息,然后翻转用户输入的字符串。

(1) 插值语法可以实现,但是有三个问题
① 代码可读性差
② 代码不可复用
③ 代码难以维护

<!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="app">

        <h1>{{msg}}</h1>

        输入的信息:<input type="text" v-model="info"> <br>

        <!--

            三个问题:

                1. 可读性差。

                2. 代码没有得到复用。

                3. 难以维护。

         -->

        反转的信息:{{info.split('').reverse().join('')}} <br>

        反转的信息:{{info.split('').reverse().join('')}} <br>

        反转的信息:{{info.split('').reverse().join('')}} <br>

        反转的信息:{{info.split('').reverse().join('')}} <br>

        反转的信息:{{info.split('').reverse().join('')}} <br>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '计算属性-反转字符串案例',

                info : ''

            }

        })

    </script>

</body>

</html>

(2) 可以使用methods方式实现,存在1个问题
① 效率低,即使数据没有发生变化,但每一次调用方法都会调用method一次。

<!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>反转字符串methods实现</title>

    <script src="../../js/vue.js"></script>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        输入的信息:<input type="text" v-model="info"> <br>

        <!-- 在插值语法中可以调用方法,小括号不能省略。这个方法需要是Vue实例所管理的。 -->

        反转的信息:{{reverseInfo()}} <br>

        反转的信息:{{reverseInfo()}} <br>

        反转的信息:{{reverseInfo()}} <br>

        反转的信息:{{reverseInfo()}} <br>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '计算属性-反转字符串案例',

                info : ''

            },

            methods : {

                // 反转信息的方法

                reverseInfo(){

                    console.log('@')

                    return this.info.split('').reverse().join('');

                }

            }

        })

    </script>

</body>

</html>
js知识之-join()方法的使用
语法:

array.join([separator])

  • separator(可选):指定连接数组元素的分隔符,默认是 ,(逗号)。
  • 返回值:返回连接后的字符串。
示例:
1. 默认(不传参数,使用逗号 , 连接)
const arr = ['a', 'b', 'c'];
const str = arr.join();
console.log(str); // 输出: "a,b,c"
2. 使用空字符串 ''(无分隔符,直接拼接)
const arr = ['2', '2', '2', '2'];
const str = arr.join('');
console.log(str); // 输出: "2222"
3. 使用自定义分隔符
const arr = ['2023', '10', '15'];
const str1 = arr.join('-'); // "2023-10-15"
const str2 = arr.join('/'); // "2023/10/15"
console.log(str1, str2);
4. 处理包含非字符串元素(会自动调用 toString() 转换)
const arr = [1, true, null, { name: 'Tom' }];
const str = arr.join(' | ');
console.log(str); // 输出: "1 | true |  | [object Object]"
js知识之-split()方法的使用
split() 方法语法
str.split([separator[, limit]])
参数说明

参数

说明

separator(可选)

指定拆分字符串的分隔符,可以是字符串正则表达式
如果省略或传入 undefined,整个字符串会作为数组的唯一元素。
如果传入 ''(空字符串),则每个字符都会被拆分成数组元素。

limit(可选)

限制返回数组的最大长度(超出部分会被丢弃)。

返回值

返回一个新数组,包含拆分后的子字符串。


split() 的常见用法
1. 按字符拆分(split('')

将字符串拆分成单个字符的数组

"hello".split(''); 
// 输出: ['h', 'e', 'l', 'l', 'o']
2. 按单词拆分(split(' ')

空格拆分字符串(适用于句子分词):

"JavaScript is fun".split(' '); 
// 输出: ['JavaScript', 'is', 'fun']
3. 按特定符号拆分(如逗号 ,

常用于处理 CSV 数据

"apple,banana,orange".split(','); 
// 输出: ['apple', 'banana', 'orange']

4. 限制拆分数量(split(separator, limit)

只拆分前 n 个匹配项:

"a-b-c-d-e".split('-', 3); 
// 输出: ['a', 'b', 'c'](只取前 3 个)
5. 使用正则表达式拆分

更灵活的拆分方式(如按多个分隔符拆分):
``js
"2023/10-15".split(/[\/-]/);
// 输出: ['2023', '10', '15'](同时匹配 `/` 和 `-`)
``

特殊情况的处理
1. 不传参数(split()

直接返回包含整个字符串的数组:

"hello".split(); 
// 输出: ['hello']
2. 分隔符不存在于字符串中

返回包含整个字符串的数组:

"hello".split('x'); 
// 输出: ['hello']
3. 空字符串 split('')

每个字符单独拆分成数组元素:

"123".split(''); 
// 输出: ['1', '2', '3']
4. 开头或结尾有分隔符

会生成空字符串元素:

",a,b,".split(','); 
// 输出: ['', 'a', 'b', '']

(3) 使用计算属性可以解决以上问题。

2. 什么是计算属性?

data中的是属性。用data的属性经过计算得出的全新的属性就是计算属性。
注意了:计算属性是全新的属性

3. 计算属性的使用

代码学习:

<!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>

    <!--

        计算属性:

        1. 什么是计算属性?

            使用Vue的原有属性,经过一系列的运算/计算,最终得到了一个全新的属性,叫做计算属性。

            Vue的原有属性: data对象当中的属性可以叫做Vue的原有属性。

            全新的属性: 表示生成了一个新的属性,和data中的属性无关了,新的属性也有自己的属性名和属性值。

        2. 计算属性怎么用?

            语法格式:需要一个新的配置项 computed

                computed : {

                    // 这是一个计算属性

                    计算属性1 : {

                        // setter 和 getter方法。

                        // 当读取计算属性1的值的时候,getter方法被自动调用。

                        get(){

  

                        },

                        // 当修改计算属性1的值的时候,setter方法被自动调用。

                        set(val){

  

                        }

                    },

                    // 这是另一个计算属性

                    计算属性2 : {},

                }

        3. 计算属性的作用?

            代码得到了复用。

            代码更加便于维护了。

            代码的执行效率高了。

     -->

    <div id="app">

        <h1>{{msg}}</h1>

        输入的信息:<input type="text" v-model="info"> <br>

        反转的信息:{{reversedInfo}}<br>

        反转的信息:{{reversedInfo}}<br>

        反转的信息:{{reversedInfo}}<br>

        反转的信息:{{reversedInfo}}<br>

        反转的信息:{{reversedInfo}}<br>

        {{hehe}} <br>

        {{hehe}} <br>

        {{hehe}} <br>

        {{hehe}} <br>

        {{hehe}} <br>

        {{hello()}} <br>

        {{hello()}} <br>

        {{hello()}} <br>

        {{hello()}} <br>

        {{hello()}} <br>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '计算属性-反转字符串案例',

                info : ''

            },

            methods : {

                hello(){

                    console.log('hello方法执行了')

                    return 'hello'

                }

            },

            computed : {

                // 可以定义多个计算属性

                hehe : {

                    // get方法的调用时机包括两个

                    // 第一个时机:第一次访问这个属性的时候。

                    // 第二个时机:该计算属性所关联的Vue原有属性的值发生变化时,getter方法会被重新调用一次。

                    get(){

                        console.log('getter方法调用了')

                        //console.log(this === vm)

                        return 'haha' + this.info

                    },

                    // 不能使用箭头函数,使用箭头函数会导致this的指向是:window

                    // get:()=>{

                    //     console.log('getter方法调用了')

                    //     console.log(this === vm)

                    //     return 'haha'

                    // },

                    set(val){

                        console.log('setter方法调用了')

                        //console.log(this === vm)

                    }

                },

                // 完整写法

                /* reversedInfo : {

                    get(){

                        return this.info.split('').reverse().join('')

                    },

                    // 当修改计算属性的时候,set方法被自动调用。

                    set(val){

                        //console.log('setter方法被调用了。')

                        // 不能这么做,这样做就递归了。

                        //this.reversedInfo = val

                        // 怎么修改计算属性呢?原理:计算属性的值变还是不变,取决于计算属性关联的Vue原始属性的值。

                        // 也就是说:reversedInfo变还是不变,取决于info属性的值变不变。

                        // 本质上:修改计算属性,实际上就是通过修改Vue的原始属性来实现的。

                        this.info = val.split('').reverse().join('')

                    }

                } */

  

                // 简写形式:set不需要的时候。

                reversedInfo(){

                    return this.info.split('').reverse().join('')

                }

            }

        })

    </script>

</body>

</html>

(1) 计算属性需要使用:computed
(2) 计算属性通过vm.data 是无法访问的。计算属性不能通过vm.data访问。
(3) 计算属性的getter/setter方法中的this是vm。
(4) 计算属性的getter方法的调用时机:
① 第一个时机:初次访问该属性。

        <font color="#ff0000">	②	第二个时机:计算属性所依赖的数据发生变化时。</font>

(5) 计算属性的setter方法的调用时机:
① 当计算属性被修改时。(在setter方法中通常是修改属性,因为只有当属性值变化时,计算属性的值就会联动更新。注意:计算属性的值被修改并不会联动更新属性的值。)
(6) 计算属性没有真正的值,每一次都是依赖data属性计算出来的
(7) 计算属性的getter和setter方法不能使用箭头函数,因为箭头函数的this不是vm。而是window。
4. 计算属性的简写形式
只考虑读取,不考虑修改时,可以启用计算属性的简写形式。

2.4 侦听属性的变化

代码学习:

<!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="app">

        <h1>{{msg}}</h1>

        数字:<input type="text" v-model="number"><br>

        数字:<input type="text" v-model="a.b"><br>

        数字:<input type="text" v-model="a.c"><br>

        数字:<input type="text" v-model="a.d.e.f"><br>

        数字(后期添加监视):<input type="text" v-model="number2"><br>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                number2 : 0,

                msg : '侦听属性的变化',

                number : 0,

                // a属性中保存的值是一个对象的内存地址。

                // a = 0x2356

                a : {

                    b : 0,

                    c : 0,

                    d : {

                        e : {

                            f : 0

                        }

                    }

                }

            },

            computed : {

                hehe(){

                    return 'haha' + this.number

                }

            },

            watch : {

                // 可以监视多个属性

                // 监视哪个属性,请把这个属性的名字拿过来即可。

                // 可以监视Vue的原有属性

                /* number : {

                    // 初始化的时候,调用一次handler方法。

                    immediate : true,

                    // 这里有一个固定写死的方法,方法名必须叫做:handler

                    // handler方法什么时候被调用呢?当被监视的属性发生变化的时候,handler就会自动调用一次。

                    // handler方法上有两个参数:第一个参数newValue,第二个参数是oldValue

                    // newValue是属性值改变之后的新值。

                    // oldValue是属性值改变之前的旧值。

                    handler(newValue, oldValue){

                        console.log(newValue, oldValue)

                        // this是当前的Vue实例。

                        // 如果该函数是箭头函数,这个this是window对象。不建议使用箭头函数。

                        console.log(this)

                    }

                }, */

                // 无法监视b属性,因为b属性压根不存在。

                /* b : {  

                    handler(newValue, oldValue){

                        console.log('@')

                    }

                } */

                // 如果监视的属性具有多级结构,一定要添加单引号:'a.b'

                /* 'a.b' : {  

                    handler(newValue, oldValue){

                        console.log('@')

                    }

                },

  

                'a.c' : {  

                    handler(newValue, oldValue){

                        console.log('@')

                    }

                }, */

  

                a : {

                    // 启用深度监视,默认是不开启深度监视的。

                    // 什么时候开启深度监视:当你需要监视一个具有多级结构的属性,并且监视所有的属性,需要启用深度监视。

                    deep : true,  

  

                    handler(newValue, oldValue){

                        console.log('@')

                    }

                },

  

                // 注意:监视某个属性的时候,也有简写形式,什么时候启用简写形式?

                // 当只有handler回调函数的时候,可以使用简写形式。

                number(newValue, oldValue){

                    console.log(newValue, oldValue)

                }

  

                // 也可以监视计算属性

                /* hehe : {

                    handler(a , b){

                        console.log(a, b)

                    }

                } */

            }

        })

  

        // 如何后期添加监视?调用Vue相关的API即可。

        // 语法:vm.$watch('被监视的属性名', {})

        /* vm.$watch('number2', {

            immediate : true,

            deep : true,

            handler(newValue, oldValue){

                console.log(newValue, oldValue)

            }

        }) */

  

        // 这是后期添加监视的简写形式。

        vm.$watch('number2', function(newValue, oldValue){

            console.log(newValue, oldValue)

        })

  

    </script>

</body>

</html>

1. 侦听属性的变化其实就是监视某个属性的变化。当被监视的属性一旦发生改变时,执行某段代码。

2. 监视属性变化时需要使用watch配置项。

使用watch实现:比较数字大小的案例

<!DOCTYPE html>

<html lang="en">

  

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>

    <script src="../../js/vue.js"></script>

</head>

  

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        数值1:<input type="text" v-model="number1"><br>

        数值2:<input type="text" v-model="number2"><br>

        比较大小:{{compareResult}}

    </div>

    <script>

        const vm = new Vue({

            el: '#app',

            data: {

                msg: '侦听属性的变化',

                number1: 1,

                number2: 1,

                compareResult: ''

            },

            watch: {

                number1: {

                    immediate: true,

                    handler(newVal, oldVal) {

                        let result = newVal - this.number2

                        if (result > 0) {

                            this.compareResult = newVal + '>' + this.number2

                        } else if (result < 0) {

                            this.compareResult = newVal + '<' + this.number2

                        } else {

                            this.compareResult = newVal + '=' + this.number2

                        }

                    }

                },

                number2: {

                    immediate: true,

                    handler(newVal, oldVal) {

                        let result = this.number1 - newVal

                        if (result > 0) {

                            this.compareResult = this.number1 + '>' + newVal

                        } else if (result < 0) {

                            this.compareResult = this.number1 + '<' + newVal

                        } else {

                            this.compareResult = this.number1 + '=' + newVal

                        }

                    }

                }

            }

        })  

    </script>

  
  
  

</body>

  

</html>

运行效果:
 


3. 如何深度监视:

(1) 监视多级结构中某个属性的变化,写法是:’a.b.c’ : {}。注意单引号哦。
(2) 监视多级结构中所有属性的变化,可以通过添加深度监视来完成:deep : true
4. 如何后期添加监视:

(1) 调用API:vm.$watch(‘number1’, {})
5. watch的简写:

(1) 简写的前提:当不需要配置immediate和deep时,可以简写。
(2) 如何简写?
① watch:{ number1(newVal,oldVal){}, number2(newVal, oldVal){} }
(3) 后期添加的监视如何简写?
① vm.$watch(‘number1’, function(newVal, oldVal){})
6. computed和watch如何选择?

(1) 以上比较大小的案例可以用computed完成,并且比watch还要简单。所以要遵守一个原则:computed和watch都能够完成的,优先选择computed。
(2) 如果要开启异步任务,只能选择watch。因为computed依靠return。watch不需要依赖return。
7. 关于函数的写法,写普通函数还是箭头函数?

(1) 不管写普通函数还是箭头函数,目标是一致的,都是为了让this和vm相等。
(2) 所有Vue管理的函数,建议写成普通函数。
(3) 所有不属于Vue管理的函数,例如setTimeout的回调函数、Promise的回调函数、AJAX的回调函数,建议使用箭头函数。

2.5 class与style绑定

数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。

2.4.1 class绑定
2.4.1.1 绑定字符串

适用于样式的名字不确定,需要动态指定。
适用场景:如果确定动态绑定的样式个数只有1个,但是名字不确定

<!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>Class绑定之字符串形式</title>

    <script src="../../js/vue.js"></script>

    <style>

        .static{

            border: 1px solid black;

            background-color: aquamarine;

        }

        .big{

            width: 200px;

            height: 200px;

        }

        .small{

            width: 100px;

            height: 100px;

        }

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <!-- 静态写法 -->

        <div class="static small">{{msg}}</div>

        <br><br>

        <button @click="changeBig">变大</button>

        <button @click="changeSmall">变小</button>

        <!-- 动态写法:动静都有 -->

        <!-- 适用场景:如果确定动态绑定的样式个数只有1个,但是名字不确定。 -->

        <div class="static" :class="c1">{{msg}}</div>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Class绑定之字符串形式',

                c1 : 'small'

            },

            methods: {

                changeBig(){

                    this.c1 = 'big'

                },

                changeSmall(){

                    this.c1 = 'small'

                }

            },

        })

    </script>

</body>

</html>

运行效果:
 


通过测试可以看到样式完成了动态的切换。

2.4.1.2 绑定数组

适用于绑定的样式名字不确定,并且个数也不确定。

<!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>Class绑定之数组形式</title>

    <script src="../../js/vue.js"></script>

    <style>

        .static {

            border: 1px solid black;

            width: 100px;

            height: 100px;

        }

        .active {

            background-color: green;

        }

        .text-danger {

            color: red;

        }

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <!-- 静态写法 -->

        <div class="static active text-danger">{{msg}}</div>

        <br>

        <!-- 动态写法:动静结合 -->

        <div class="static" :class="['active','text-danger']">{{msg}}</div>

        <br>

        <div class="static" :class="[c1, c2]">{{msg}}</div>

        <br>

        <!-- 适用场景:当样式的个数不确定,并且样式的名字也不确定的时候,可以采用数组形式。 -->

        <div class="static" :class="classArray">{{msg}}</div>

  

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Class绑定之数组形式',

                c1 : 'active',

                c2 : 'text-danger',

                classArray : ['active', 'text-danger']

            }

        })

    </script>

</body>

</html>

运行效果:

2.4.1.3 绑定对象

适用于样式名字和个数都确定,但是要动态决定用或者不用。

<!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>Class绑定之对象形式</title>

    <script src="../../js/vue.js"></script>

    <style>

        .static {

            border: 1px solid black;

            width: 100px;

            height: 100px;

        }

        .active {

            background-color: green;

        }

        .text-danger {

            color: red;

        }

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <!-- 动态写法:动静结合 -->

        <!-- 对象形式的适用场景:样式的个数是固定的,样式的名字也是固定的,但是需要动态的决定样式用还是不用。 -->

        <div class="static" :class="classObj">{{msg}}</div>

        <br>

        <div class="static" :class="{active:true,'text-danger':false}">{{msg}}</div>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Class绑定之对象形式',

                classObj : {

                    // 该对象中属性的名字必须和样式名一致。

                    active : false,

                    'text-danger' : true

                }

            }

        })

    </script>

</body>

</html>

运行效果:
 


#### 2.4.2 style绑定

代码学习:注意数组绑定和对象绑定都是以对象的形式写的, 数组里面写对象格式

<!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>Style绑定</title>

    <script src="../js/vue.js"></script>

    <style>

        .static {

            border: 1px solid black;

            width: 100px;

            height: 100px;

        }

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <!-- 静态写法 -->

        <div class="static" style="background-color: green;">{{msg}}</div>

        <br>

        <!-- 动态写法:字符串形式 -->

        <div class="static" :style="myStyle">{{msg}}</div>

        <br>

        <!-- 动态写法:对象形式 -->

        <div class="static" :style="{backgroundColor: 'gray'}">{{msg}}</div>

        <br>

        <div class="static" :style="styleObj1">{{msg}}</div>

        <br>

        <!-- 动态写法:数组形式 -->

        <div class="static" :style="styleArray">{{msg}}</div>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Style绑定',

                myStyle : 'background-color: gray;',

                styleObj1 : {

                    backgroundColor: 'green'

                },

                // 数组形式的话里面也必须是对象,这里的格式写法一定要注意

                styleArray : [

                    {backgroundColor: 'green'},

                    {color : 'red'}

                ]

            }

        })

    </script>

</body>

</html>

2.5 条件渲染

2.5.1 v-if

指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true时才被渲染

<div id="app">  
    <h1>{{msg}}</h1>  
    温度:<input type="number" v-model="temprature"><br>  
    天气:  
    <span v-if="temprature <= 10">寒冷</span>  
    <span v-if="temprature > 10 && temprature <= 25">凉爽</span>  
    <span v-if="temprature > 25">炎热</span>  
</div>  
<script>  
    const vm = new Vue({  
        el : '#app',  
        data : {  
            msg : '条件渲染',  
            temprature : 10  
        }  
    })  
</script> 

运行效果:

2.5.2 v-else-if、v-else

顾名思义,v-else-if 提供的是相应于 v-if 的“else if 区块”。它可以连续多次重复使用。
一个使用 v-else-if 的元素必须紧跟在一个 v-if 或一个 v-else-if元素后面
你也可以使用 v-else 为 v-if 添加一个“else 区块”,当然,v-else元素也是必须紧跟在一个 v-if 或一个 v-else-if元素后面。

    <div id="app">  
        <h1>{{msg}}</h1>  
        温度:<input type="number" v-model="temprature"><br>  
        天气:  
        <span v-if="temprature <= 10">寒冷</span>  
        <span v-else-if="temprature <= 25">凉爽</span>  
        <span v-else>炎热</span>  
    </div>  
    <script>  
            const vm = new Vue({  
                el : '#app',  
                data : {  
                    msg : '条件渲染',  
                    temprature : 10  
                }  
            })  
        </script>
2.5.3 template与v-if

因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 <template> 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含个 <template> 元素。v-else 和 v-else-if 也可以在 <template>上使用。


 <div id="app">  
     <h1>{{msg}}</h1>  
     温度:<input type="number" v-model="temprature"><br>  
     天气:  
     <template v-if="temprature <= 10">  
         <span>寒冷</span>  
     </template>  
     <template v-else-if="temprature <= 25">  
         <span>凉爽</span>  
         </template>  
         <template v-else>  
             <span>炎热</span>  
         </template>  
     </div>  
     <script>  
         const vm = new Vue({  
             el : '#app',  
             data : {  
                 msg : '条件渲染',  
                 temprature : 10  
             }  
         })  
     </script>```

#####  <font color="#245bdb">2.5.4 v-show</font>

另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:
​```html
1.	<div id="app">  
2.	    <h1>{{msg}}</h1>  
3.	    温度:<input type="number" v-model="temprature"><br>  
4.	    天气:  
5.	    <span v-show="temprature <= 10">寒冷</span>  
6.	    <span v-show="temprature > 10 && temprature <= 25">凉爽</span>  
7.	    <span v-show="temprature > 25">炎热</span>  
8.	</div>  
9.	<script>  
10.	    const vm = new Vue({  
11.	        el : '#app',  
12.	        data : {  
13.	            msg : '条件渲染',  
14.	            temprature : 10  
15.	        }  
16.	    })  
17.	</script>  

不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。

v-show 不支持在 template元素上使用,也不能和 v-else 搭配使用。

2.5.5 v-if VS v-show

v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

条件渲染总结

学习代码:

<!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="app">

        <h1>{{msg}}</h1>

        <!--

            v-if指令的值:true/false

                true: 表示该元素会被渲染到页面上。

                false: 表示该元素不会被渲染到页面上。(注意:不是修改了CSS样式,是这个元素压根没有加载)

         -->

        <div v-if="false">{{msg}}</div>

  

        <div v-if="2 === 1">{{msg}}</div>

  

        <button @click="counter++">点我加1</button>

  

        <h3>{{counter}}</h3>

  

        <i/mg :src="imgPath1" v-if="counter % 2 === 1">

        <!-- 提醒:v-if和v-else之间不能断开。 -->

        <!-- <div></div> -->

        <!-- <i/mg :src="imgPath2" v-if="counter % 2 === 0"> -->

        <!-- 为了提高效率,可以使用v-else指令 -->

        <i/mg :src="imgPath2" v-else>

  

        <br><br>

  

        温度:<input type="number" v-model="temprature"><br><br>

  

        <!-- 天气:<span v-if="temprature <= 10">寒冷</span>

        <span v-if="temprature > 10 && temprature <= 25">凉爽</span>

        <span v-if="temprature > 25">炎热</span> -->

  

        天气:<span v-if="temprature <= 10">寒冷</span>

        <!-- v-if v-else-if v-else三者在使用的时候,中间不能断开。 -->

        <!-- <br> -->

        <span v-else-if="temprature <= 25">凉爽</span>

        <span v-else>炎热</span>

  

        <br><br><br>

  

        <!--

            v-show指令是通过修改元素的CSS样式的display属性来达到显示和隐藏的。

            v-if和v-show应该如何选择?

                1. 如果一个元素在页面上被频繁的隐藏和显示,建议使用v-show,因为此时使用v-if开销比较大。

                2. v-if的优点:页面加载速度快,提高了页面的渲染效率。

         -->

        <div v-show="false">你可以看到我吗?</div>

  

        <!-- template标签/元素只是起到占位的作用,不会真正的出现在页面上,也不会影响页面的结构。 -->

        <template v-if="counter === 10">

            <input type="text">

            <input type="checkbox">

            <input type="radio">            

        </template>

  

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '条件渲染',

                counter : 1,

                imgPath1 : '../img/1.jpg',

                imgPath2 : '../img/2.jpg',

                temprature : 0

            }

        })

    </script>

</body>

</html>

v-if指令的值:true/false

                true: 表示该元素会被渲染到页面上。

                false: 表示该元素不会被渲染到页面上。(注意:不是修改了CSS样式,是这个元素压根没有加载)
               
v-if v-else-if v-else三者在使用的时候,中间不能断开。
v-show指令是通过修改元素的CSS样式的display属性来达到显示和隐藏的。

            v-if和v-show应该如何选择?

                1. 如果一个元素在页面上被频繁的隐藏和显示,建议使用v-show,因为此时使用v-if开销比较大。

                2. v-if的优点:页面加载速度快,提高了页面的渲染效率。

2.6 列表渲染

语法格式:v-for指令。该指令用在被遍历的标签上。
1. v-for="(element, index) in elements" :key="element.id"
或者
1. v-for="(element, index) of elements" :key="element.id"

2.6.1 遍历数组、对象、字符串、指定次数

代码演示:

<!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="app">

        <h1>{{msg}}</h1>

  

        <h2>遍历对象的属性</h2>

        <ul>

            <li v-for="(value, propertyName) of user">

                {{propertyName}},{{value}}

            </li>

        </ul>

  

        <h2>遍历字符串</h2>

        <ul>

            <li v-for="(c,index) of str">

                {{index}},{{c}}

            </li>

        </ul>

  

        <h2>遍历指定的次数</h2>

        <ul>

            <li v-for="(num,index) of counter">

                {{index}}, {{num}}

            </li>

        </ul>

  
  

        <h2>遍历数组</h2>

        <!-- 静态列表 -->

        <ul>

            <li>张三</li>

            <li>李四</li>

            <li>王五</li>

        </ul>

  

        <!-- 动态列表 -->

        <ul>

            <!--

                1. v-for要写在循环项上。

                2. v-for的语法规则:

                    v-for="(变量名,index) in/of 数组"

                    变量名 代表了 数组中的每一个元素

             -->

            <li v-for="fdsafds in names">

                {{fdsafds}}

            </li>

        </ul>

  

        <ul>

            <li v-for="name of names">

                {{name}}

            </li>

        </ul>

  

        <ul>

            <li v-for="(name,index) of names">

                {{name}}-{{index}}

            </li>

        </ul>

  

        <ul>

            <li v-for="(vip,index) of vips">

                会员名:{{vip.name}},年龄:{{vip.age}}岁

            </li>

        </ul>

  

        <table>

            <tr>

                <th>序号</th>

                <th>会员名</th>

                <th>年龄</th>

                <th>选择</th>

            </tr>

            <tr v-for="(vip,index) in vips">

                <td>{{index+1}}</td>

                <td>{{vip.name}}</td>

                <td>{{vip.age}}</td>

                <td><input type="checkbox"></td>

            </tr>

        </table>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '列表渲染',

                names : ['jack','lucy','james'],

                vips : [

                    {id:'111',name:'jack',age:20},

                    {id:'222',name:'lucy',age:30},

                    {id:'333',name:'james',age:40}

                ],

                user : {

                    id : '111',

                    name : '张三',

                    gender : '男'

                },

                str : '动力节点',

                counter : 10

            }

        })

    </script>

</body>

</html>

2.6.2 虚拟dom和diff算法

所谓的虚拟dom就是内存当中的dom对象。vue为了提高渲染的效率,只有真正改变的dom元素才会重新渲染。

2.6.3 v-for的key的作用以及实现原理

1. 用index作为key
1.	<!DOCTYPE html>  
2.	<html lang="en">  
3.	<head>  
4.	    <meta charset="UTF-8">  
5.	    <title>key的原理</title>  
6.	    <script src="../js/vue.js"></script>  
7.	</head>  
8.	<body>  
9.	    <div id="app">  
10.	        <h1>{{msg}}</h1>  
11.	        <button @click="addFirst">在数组第一个位置添加tom</button>  
12.	        <button @click="addLast">在数组最后位置添加vue</button>  
13.	        <table>  
14.	            <tr>  
15.	                <th>序号</th>  
16.	                <th>姓名</th>  
17.	                <th>邮箱</th>  
18.	                <th>选择</th>  
19.	            </tr>  
20.	            <tr v-for="(vip,index) of vips" :key="index">  
21.	                <td>{{index + 1}}</td>  
22.	                <td>{{vip.name}}</td>  
23.	                <td>{{vip.email}}</td>  
24.	                <td><input type="checkbox"></td>  
25.	            </tr>  
26.	        </table>  
27.	    </div>  
28.	    <script>  
29.	        const vm = new Vue({  
30.	            el : '#app',  
31.	            data : {  
32.	                msg : 'key原理(虚拟dom与diff算法)',  
33.	                vips : [  
34.	                    {id:'100',name:'jack',email:'jack@123.com'},  
35.	                    {id:'200',name:'lucy',email:'lucy@123.com'},  
36.	                    {id:'300',name:'james',email:'james@123.com'}  
37.	                ]  
38.	            },  
39.	            methods : {  
40.	                addFirst(){  
41.	                    this.vips.unshift({id:'400',name:'tom',email:'tom@123.com'})  
42.	                },  
43.	                addLast(){  
44.	                    this.vips.push({id:'500',name:'vue',email:'vue@123.com'})  
45.	                }  
46.	            }  
47.	        })  
48.	    </script>  
49.	</body>  
50.	</html> 

可以看到错乱了。思考这是为什么?

2. 用vip.id作为key

运行和测试结果正常,没有出现错乱。为什么?

3. key的作用

key存在于虚拟dom元素中,代表该虚拟dom元素的唯一标识(身份证号)。我们用数据库中每条数据的id,这个id一般在数据库中都是唯一的,所以正好适合用来作为key使用。

4. diff算法是如何比较的?

新的虚拟dom和旧的虚拟dom比较时,先拿key进行比较:

(1) 如果key相同:则继续比较子元素:
① 子元素不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
② 子元素相同:直接复用之前的真实dom。
(2) 如果key不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。

5. index作为key存在两个问题

(1) 效率较低。
(2) 对数组的非末尾元素进行增删时,容易错乱。

6. index作为key和vip.id作为key对比

当index作为key时:

当vip.id作为key时:

2.7 列表过滤

使用watch和computed分别进行实现:

watch实现

<!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>

    <style>

        th,td{border: 1px solid black;}

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <input type="text" placeholder="请输入搜索关键字" v-model="keyword">

        <table>

            <tr>

                <th>序号</th>

                <th>英雄</th>

                <th>能量值</th>

                <th>选择</th>

            </tr>

            <tr v-for="(hero,index) in filteredHeros" :key="hero.id">

                <td>{{index+1}}</td>

                <td>{{hero.name}}</td>

                <td>{{hero.power}}</td>

                <td><input type="checkbox"></td>

            </tr>

        </table>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                keyword : '',

                msg : '列表过滤',

                heros : [

                    {id:'101',name:'艾格文',power:10000},

                    {id:'102',name:'麦迪文',power:9000},

                    {id:'103',name:'古尔丹',power:8000},

                    {id:'104',name:'萨尔',power:6000}

                ],

                filteredHeros : []

            },

            watch : {

                /* keyword(val){

                    // 执行过滤规则

                    this.filteredHeros = this.heros.filter((hero) => {

                        return hero.name.indexOf(val) >= 0

                    })

                } */

                keyword : {

                    immediate : true,

                    handler(val){

                        this.filteredHeros = this.heros.filter((hero) => {

                            return hero.name.indexOf(val) >= 0

                        })

                    }

                }

            }

        })

  

        // 回顾filter

        let arr = [1,2,3,4,5,6,7,8,9]

  

        // filter不会破坏原数组的结构,会生成一个全新的数组。

        let newArr = arr.filter((num) => {

            //return 过滤规则

            return num < 5

        })

  

        console.log(newArr)

    </script>

</body>

</html>

注意filter过滤使用的特点:filter不会破坏原数组的结构,会生成一个全新的数组。

computed实现

<!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>

    <style>

        th,td{border: 1px solid black;}

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <input type="text" placeholder="请输入搜索关键字" v-model="keyword">

        <table>

            <tr>

                <th>序号</th>

                <th>英雄</th>

                <th>能量值</th>

                <th>选择</th>

            </tr>

            <tr v-for="(hero,index) in filteredHeros" :key="hero.id">

                <td>{{index+1}}</td>

                <td>{{hero.name}}</td>

                <td>{{hero.power}}</td>

                <td><input type="checkbox"></td>

            </tr>

        </table>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                keyword : '',

                msg : '列表过滤',

                heros : [

                    {id:'101',name:'艾格文',power:10000},

                    {id:'102',name:'麦迪文',power:9000},

                    {id:'103',name:'古尔丹',power:8000},

                    {id:'104',name:'萨尔',power:6000}

                ]

            },

            computed : {

                filteredHeros(){

                    // 执行过滤

                    return this.heros.filter((hero) => {

                        return hero.name.indexOf(this.keyword) >= 0

                    })

                }

            }

        })

    </script>

</body>

</html>

2.8 列表排序

<!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>

    <style>

        th,td{border: 1px solid black;}

    </style>

</head>

<body>

    <div id="app">

        <h1>{{msg}}</h1>

        <input type="text" placeholder="请输入搜索关键字" v-model="keyword">

        <br>

        <button @click="type = 1">升序</button>

        <button @click="type = 2">降序</button>

        <button @click="type = 0">原序</button>

        <table>

            <tr>

                <th>序号</th>

                <th>英雄</th>

                <th>能量值</th>

                <th>选择</th>

            </tr>

            <tr v-for="(hero,index) in filteredHeros" :key="hero.id">

                <td>{{index+1}}</td>

                <td>{{hero.name}}</td>

                <td>{{hero.power}}</td>

                <td><input type="checkbox"></td>

            </tr>

        </table>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                type : 0,

                keyword : '',

                msg : '列表排序',

                heros : [

                    {id:'101',name:'艾格文',power:10000},

                    {id:'102',name:'麦迪文',power:9000},

                    {id:'103',name:'古尔丹',power:8000},

                    {id:'104',name:'萨尔',power:11000}

                ]

            },

            computed : {

                filteredHeros(){

                    // 执行过滤

                    const arr = this.heros.filter((hero) => {

                        return hero.name.indexOf(this.keyword) >= 0

                    })

                    // 排序

                    if(this.type === 1){

                        arr.sort((a, b) => {

                            return a.power - b.power

                        })

                    }else if(this.type == 2){

                        arr.sort((a, b) => {

                            return b.power - a.power

                        })

                    }

                    // 返回

                    return arr

                }

            }

        })

  

        // 回顾sort方法

        let arr = [8,9,5,4,1,2,3]

  

        // sort方法排序之后,不会生成一个新的数组,是在原数组的基础之上进行排序,会影响原数组的结构。

        arr.sort((a, b) => {

            return b - a

        })

  

        console.log(arr)

    </script>

</body>

</html>

看代码中是执行了一下过滤方法,然后再执行的排序方法,我本地写的时候是没有执行过滤方法的,导致点击原序按钮的时候界面是没有任何反应的,因为我只执行了sort方法,导致原来的数据结构也跟着变化了,所以不会再返回原来的数据显示到界面。


除了先使用过滤方法外,还有另外的方式,就是先把这个数组赋值给另外一个数组,我们上面的写法


 

sort方法特点:sort方法排序之后,不会生成一个新的数组,是在原数组的基础之上进行排序,会影响原数组的结构。所以在使用的过程中一定要注意不要直接用原对象进行排序

2.9 收集表单数据

<!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="app">

        <h1>{{msg}}</h1>

        <!-- form表单点击提交会直接放松请求所以我们使用@submit.prevent  禁用属性原有的事件,适用于向后台提交数据发送ajax请求 -->

        <form @submit.prevent="send">

            <!-- 输入框前后去空字符串的写法 用v-model.trim -->

            用户名:<input type="text" v-model.trim="user.username"><br><br>

            密码:<input type="password" v-model="user.password"><br><br>

            <!-- 字符串转数字的写法  v-model.number -->

            年龄:<input type="number" v-model.number="user.age"><br><br>

            性别:

            <!-- 单选框一定要写value -->

                男<input type="radio" name="gender" value="1" v-model="user.gender">

                女<input type="radio" name="gender" value="0" v-model="user.gender"><br><br>

            爱好:

            <!-- 注意:对于checkbox来说,如果没有手动指定value,那么会拿这个标签的checked属性的值作为value -->

                旅游<input type="checkbox" v-model="user.interest" value="travel">

                运动<input type="checkbox" v-model="user.interest" value="sport">

                唱歌<input type="checkbox" v-model="user.interest" value="sing"><br><br>

            学历:

                <select v-model="user.grade">

                    <option value="">请选择学历</option>

                    <option value="zk">专科</option>

                    <option value="bk">本科</option>

                    <option value="ss">硕士</option>

                </select><br><br>

            简介:

            <!-- v-model.lazy 懒加载,适用于当你这个输入框写完以后才响应数据 -->

                <textarea cols="50" rows="15" v-model.lazy="user.introduce"></textarea><br><br>

            <input type="checkbox" v-model="user.accept">阅读并接受协议<br><br>

            <!-- <button @click.prevent="send">注册</button> -->

            <button>注册</button>

        </form>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                user : {

                    username : '',

                    password : '',

                    age : '',

                    gender : '1',

                    interest : ['travel'],

                    grade : 'ss',

                    introduce : '',

                    accept : ''

                },

                msg : '表单数据的收集'

            },

            methods : {

                send(){

                    alert('ajax...!!!!')

                    // 将数据收集好,发送给服务器。

                    //console.log(JSON.stringify(this.$data))

                    console.log(JSON.stringify(this.user))

                }

            }

        })

    </script>

</body>

</html>

页面展示效果:

运行结果:

2.10 过滤器

过滤器filters适用于简单的逻辑处理,例如:对一些数据进行格式化显示。他的功能完全可以使用methods,computed来实现。过滤器可以进行全局配置,也可以进行局部配置:
① 全局配置:在构建任何Vue实例之前使用Vue.filter(‘过滤器名称’, callback)进行配置。
② 局部配置:在构建Vue实例的配置项中使用filters进行局部配置。
过滤器可以用在两个地方:插值语法和v-bind指令中。
多个过滤器可以串联:{{msg | filterA | filterB | filterC}} ,' | ' 代表管道符
过滤器也可以接收额外的参数,但过滤器的第一个参数永远接收的都是前一个过滤器的返回值。具体使用看下面代码

代码学习:

<!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>

    <!--

        需求:

            从服务器端返回了一个商品的价格price,这个price的值可能是这几种情况:''、null、undefined、60.5

            要求:

                如果是''、null、undefined ,页面上统一显示为 -

                如果不是 ''、null、undefined,则页面上显示真实的数字即可。

        在Vue3当中,已经将过滤器语法废弃了。

     -->

    <div id="app">

        <h1>{{msg}}</h1>

        <h2>商品价格:{{formatPrice}}</h2>

        <h2>商品价格:{{formatPrice2()}}</h2>

        <h2>商品价格:{{price | filterA | filterB(3)}}</h2>

        <input type="text" :value="price | filterA | filterB(3)">

    </div>

  

    <hr>

  

    <div id="app2">

        <h2>商品价格:{{price | filterA | filterB(3)}}</h2>

    </div>

  

    <script>

  

        // 配置全局的过滤器。

        Vue.filter('filterA', function(val){

            if(val === null || val === undefined || val === ''){

                return '-'

            }

            return val

        })

  

        Vue.filter('filterB', function(val, number){

            return val.toFixed(number)

        })

  

        const vm2 = new Vue({

            el : '#app2',

            data : {

                price : 20.3

            }

        })

  

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '过滤器',

                price : 50.6

            },

            methods: {

                formatPrice2(){

                    if(this.price === '' || this.price === undefined || this.price === null){

                        return '-'

                    }

                    return this.price

                }

            },

            computed : {

                formatPrice(){

                    if(this.price === '' || this.price === undefined || this.price === null){

                        return '-'

                    }

                    return this.price

                }

            },

            /* filters : {

                // 局部过滤器

                filterA(val){

                    if(val === null || val === undefined || val === ''){

                        return '-'

                    }

                    return val

                },

                filterB(val, number){

                    // 确保传递过来的数据val,保留两位小数。

                    return val.toFixed(number)

                }

            } */

        })

    </script>

</body>

</html>

2.11 Vue的其它指令

2.11.1 v-text

将内容填充到标签体当中,并且是以覆盖的形式填充,而且填充的内容中即使存在HTML标签也只是会当做一个普通的字符串处理,不会解析。功能等同于原生JS中的innerText。

2.11.2 v-html

将内容填充到标签体当中,并且是以覆盖的形式填充,而且将填充的内容当做HTML代码解析。功能等同于原生JS中的innerHTML。
v-html不要用到用户提交的内容上。可能会导致XSS攻击。XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript。
例如:用户在留言中恶意植入以下信息:

其他用户上当了:如果点击了以上的留言,就会将cookie发送给恶意的服务器。

<!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>Vue的其它指令</title>

    <script src="../../js/vue.js"></script>

</head>

<body>

    <div id="app">

        <h1>{{msg}},test</h1>

        <!--

            v-text指令:

                可以将指令的内容拿出来填充到标签体当中。和JS的innerText一样。

                这种填充是以覆盖的形式进行的。先清空标签体当中原有的内容,填充新的内容。

                即使内容是一段HTML代码,这种方式也不会将HTML代码解析并执行。只会当做普通

                文本来处理。

         -->

        <h1 v-text="msg">test</h1>

        <h1 v-text="name">test</h1>

        <h1 v-text="s1"></h1>

  

        <!--

            v-html指令:

                和v-text一样,也是填充标签体内容。也是采用覆盖的形式进行。

                只不过v-html会将内容当做一段HTML代码解析并执行。

         -->

         <h1 v-html="s1"></h1>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Vue的其它指令',

                name : 'jack',

                s1 : '<h1>欢迎大家学习Vue!</h1>'

            }

        })

    </script>

</body>

</html>
2.11.3 v-cloak

v-cloak配置css样式来解决胡子的闪现问题。
v-cloak指令使用在标签当中,当Vue实例接管之后会删除这个指令。
这是一段CSS样式:当前页面中所有带有v-cloak属性的标签都隐藏起来。

注意:如果使用这个指令的话必须搭配以下代码使用

    <style>

        [v-cloak] {

            display: none;

        }

    </style>

完整代码:

<!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>Vue的其它指令</title>

    <style>

        [v-cloak] {

            display: none;

        }

    </style>

</head>

<body>

    <div id="app">

        <h1 v-cloak>{{msg}}</h1>

    </div>

  

    <script>

        setTimeout(() => {

            let scriptElt = document.createElement('script')

            scriptElt.src = '../js/vue.js'

            document.head.append(scriptElt)

        }, 3000)

  

        setTimeout(() => {

            const vm = new Vue({

                el : '#app',

                data : {

                    msg : 'Vue的其它指令'

                }

            })

        }, 4000)

    </script>

</body>

</html>

也就是说按照上面代码的方式渲染到页面,如果<h1 v-cloak>{{msg}}</h1>没有加v-cloak的话,我们使用setTimeout方式延迟渲染Vue,就会导致这个h1标签内容直接显示在页面上(没有经过Vue处理的),如果加上了这个指令,那么最开始初始化的时候就会隐藏这个h1标签,如果不加v-cloak的话,打开页面会显示这样,


然后等几秒中才会正常显示

2.11.4 v-once

初次接触指令的时候已经学过了。只渲染一次。之后将被视为静态内容。没有动态效果了

2.11.5 v-pre

使用该指令可以提高编译速度。带有该指令的标签将不会被编译。可以在没有Vue语法规则的标签中使用可以提高效率。不要将它用在带有指令语法以及插值语法的标签中。

<!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>Vue的其它指令</title>

    <script src="../../js/vue.js"></script>

</head>

<body>

    <div id="app">

        <h1 v-cloak>{{msg}}</h1>

        <h1 v-pre>欢迎学习Vue框架!</h1>

        <h1 v-pre>{{msg}}</h1>

        <ul>

            <!-- 这里只会渲染一次,如果users的值有所改变,这里是不会变的,因为已经变成静态资源了 -->

            <li v-for="user,index of users" :key="index" v-once>

                {{user}}

            </li>

        </ul>

  

        <ul>

            <li v-for="user,index of users" :key="index">

                {{user}}

            </li>

        </ul>

    </div>

  

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : 'Vue的其它指令',

                users : ['jack', 'lucy', 'james']

            }

        })

    </script>

</body>

</html>

2.12 vue的自定义指令

函数式:

directives : {
    ‘text-reverse’ : function(element, binding){
        // element 是真实dom对象(可以通过 element instanceof HTMLElement 判断)
        // binding 是绑定的对象
        element.innerText = binding.value.split(‘’).reverse().join(‘’)
    }
}

函数调用时机:

第一时机:模板初次解析时(元素与指令初次绑定)。
第二时机:模板重新解析时。

对象式:可以使用对象式完成更加细致的功能。

directives : {
    ‘bind-parent’ : {
        // 元素与指令初次绑定时自动调用。
        bind(element, binding){},
        // 元素已经被插入页面后自动调用。
        inserted(element, binding){},
        // 模板重新解析时被自动调用。
        update(element, binding){}
    }
}

自定义指令的函数中的this是window。

以上是局部指令,全局指令怎么定义:

对象式:

Vue.directive(‘bind-parent’, {
    bind(element, binding){},
    inserted(element, binding){},
    update(element, binding){}
})

函数式:

    Vue.directive(‘text-reverse’, function(element, binding){})

2.13 响应式与数据劫持

1.什么是响应式?

修改data后,页面自动改变/刷新。这就是响应式。就像我们在使用excel的时候,修改一个单元格中的数据,其它单元格的数据会联动更新,这也是响应式。

2.Vue的响应式是如何实现的?

数据劫持:Vue底层使用了Object.defineProperty,配置了setter方法,当去修改属性值时setter方法则被自动调用,setter方法中不仅修改了属性值,而且还做了其他的事情,例如:重新渲染页面。setter方法就像半路劫持一样,所以称为数据劫持。

3.Vue会给data中所有的属性,以及属性中的属性,都会添加响应式。
4. 后期添加的属性,不会有响应式,怎么处理?
① Vue.set(目标对象, ‘属性名’, 值)
② vm.$set(目标对象, ‘属性名’, 值)

代码学习:

<!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="app">

        <h1>{{msg}}</h1>

        <div>姓名:{{name}}</div>

        <div>年龄:{{age}}岁</div>

        <div>数字:{{a.b.c.e}}</div>

        <div>邮箱:{{a.email}}</div>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '响应式与数据劫持',

                name : 'jackson',

                age : 20,

                a : {

                    b : {

                        c : {

                            e : 1

                        }

                    }

                }

            }

        })

  

        // 测试:后期给Vue实例动态的追加的一些属性,会添加响应式处理吗?

        // 目前来看,通过这种方式后期给vm追加的属性并没有添加响应式处理。

        //vm.$data.a.email = 'jack@126.com'

  

        // 如果你想给后期追加的属性添加响应式处理的话,调用以下两个方法都可以:

        // Vue.set() 、 vm.$set()

        //Vue.set(目标对象, 属性名, 属性值)

        //Vue.set(vm.$data.a, 'email', 'jack@126.com')

        //Vue.set(vm.a, 'email', 'jack@123.com')

        vm.$set(vm.a, 'email', 'jack@456.com')

  

        // 避免在运行时向Vue实例或其根$data添加响应式

        // 不能直接给vm / vm.$data 追加响应式属性。只能在声明时提前定义好。

        //Vue.set(vm, 'x', '1')

        //Vue.set(vm.$data, 'x', '1')

  

    </script>

</body>

</html>
5.Vue没有给数组下标0,1,2,3....添加响应式,怎么处理?

① 调用Vue提供的7个API:
push()
pop()
reverse()
splice()
shift()
unshift()
sort()
或者使用:
Vue.set(数组对象, ‘index’, 值)
vm.$set(数组对象, ‘index’, 值)

数组相关方法

这些数组方法都会直接修改原数组(原地操作),使用时需要注意是否希望改变原数组:

1. push() - 末尾添加元素

作用:向数组末尾添加一个或多个元素
返回值:新数组长度
示例
javascript
const arr = [1, 2];
arr.push(3); // arr变为[1, 2, 3]
arr.push(4, 5); // arr变为[1, 2, 3, 4, 5]

2. pop() - 移除末尾元素

作用:移除并返回数组的最后一个元素
返回值:被移除的元素
示例

javascript
const arr = [1, 2, 3];
const last = arr.pop(); // last=3, arr变为[1, 2]

3. reverse() - 反转数组

作用:反转数组元素的顺序
返回值:反转后的数组(原数组也被反转)
示例

javascript
const arr = [1, 2, 3];
arr.reverse(); // arr变为[3, 2, 1]

4. splice() - 添加/删除元素

作用:在指定位置添加/删除元素 下标从0开始
返回值:包含被删除元素的数组
参数

  • start:开始位置
  • deleteCount:要删除的元素个数
  • items:要添加的元素
    示例
  • javascript
    const arr = [1, 2, 3, 4];
    arr.splice(1, 2); // 从索引1删除2个元素,arr变为[1, 4]
    arr.splice(1, 0, 'a', 'b'); // 在索引1处插入元素,arr变为[1, 'a', 'b', 4]
5. shift() - 移除首元素

作用:移除并返回数组的第一个元素
返回值:被移除的元素
示例

javascript
const arr = [1, 2, 3];
const first = arr.shift(); // first=1, arr变为[2, 3]

6. unshift() - 开头添加元素

作用:向数组开头添加一个或多个元素
返回值:新数组长度
示例

javascript
const arr = [1, 2];
arr.unshift(0); // arr变为[0, 1, 2]
arr.unshift(-2, -1); // arr变为[-2, -1, 0, 1, 2]

7. sort() - 数组排序

作用:对数组元素进行排序
返回值:排序后的数组(原数组也被排序)
参数:比较函数(可选)
示例

javascript
const arr = [3, 1, 2];
arr.sort(); // arr变为[1, 2, 3]
arr.sort((a, b) => b - a); // 降序排列,arr变为[3, 2, 1]

重要注意事项
  1. 这些方法都会修改原数组,如果不想修改原数组:
    javascript
    // 先创建副本再操作
    const newArr = [...arr].sort();
    const newArr = arr.slice().reverse();
  1. 在React/Vue等框架中,直接修改状态数组可能导致问题,应该: javascript
    // Vue示例
    this.items = [...this.items, newItem]; // 而不是this.items.push(newItem)
    // React示例
    setItems(prev => [...prev, newItem]);
  1. 性能考虑:对于大型数组,创建副本会有性能开销,在不需要保持原数组时可以直接修改。
  1. 特殊行为
    • sort()默认按字符串Unicode排序,对数字排序需要提供比较函数
    • splice()可以同时实现删除和插入操作

这些方法都是数组操作中最常用的方法,理解它们的修改行为对编写可预测的代码非常重要。

代码学习:

<!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>

    <!--

        1. 通过数组的下标去修改数组中的元素,默认情况下是没有添加响应式处理的。怎么解决?

        2. 第一种方案:

            vm.$set(数组对象, 下标, 值)

            Vue.set(数组对象, 下标, 值)

  

        3. 第二种方案:

            push()

            pop()

            reverse()

            splice()

            shift()

            unshift()

            sort()

  

            在Vue当中,通过以上的7个方法来给数组添加响应式处理。

     -->

    <div id="app">

        <h1>{{msg}}</h1>

        <ul>

            <li v-for="user in users">

                {{user}}

            </li>

        </ul>

        <ul>

            <li v-for="vip in vips" :key="vip.id">

                {{vip.name}}

            </li>

        </ul>

    </div>

    <script>

        const vm = new Vue({

            el : '#app',

            data : {

                msg : '数组的响应式处理',

                users : ['jack', 'lucy', 'james'],

                vips : [

                    {id:'111', name:'zhangsan'},

                    {id:'222', name:'lisi'}

                ]

            }

        })

    </script>

</body>

</html>

2.14 Vue的生命周期

2.14.1 什么是生命周期

所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。
例如人的生命周期:
(1) 出生:打疫苗
(2) 3岁了:上幼儿园
(3) 6岁了:上小学
(4) 12岁了:上初中
(5) ......
(6) 55岁了:退休
(7) ......
(8) 临终:遗嘱
(9) 死亡:火化

可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿。

Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。
(1) 虚拟DOM在内存中就绪时:去调用一个a函数
(2) 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
(3) Vue的data发生改变时:去调用一个c函数
(4) ......
(5) Vue实例被销毁时:去调用一个x函数
在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。

2.14.2 掌握Vue的生命周期有什么用

研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。
例如:在vm被销毁之前,我需要将绑定到元素上的自定义事件全部解绑,那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数。

2.14.3 Vue生命周期的4个阶段8个钩子

Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:
(1) 初始阶段
① beforeCreate() 创建前
② created() 创建后
(2) 挂载阶段
① beforeMount() 挂载前
② mounted() 挂载后
(3) 更新阶段
① beforeUpdate() 更新前
② updated() 更新后
(4) 销毁阶段
① beforeDestroy() 销毁前
② destroyed() 销毁后
8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。
Vue官方的生命周期图:

翻译后的生命周期图:

2.14.4 初始阶段做了什么事儿

做了这么几件事:
(1) 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
(2) 初始化事件对象和生命周期(接产大夫正在给他洗澡)
(3) 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
(4) 初始化数据代理和数据监测
(5) 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
(6) 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)
该阶段适合做什么?
beforeCreate:可以在此时加一些loading效果。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。

2.14.5 挂载阶段做了什么事儿

做了这么几件事:
(1) 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成)
(2) 给vm追加el属性,用它来代替”el”,el代表了真实的DOM元素(此时真实DOM生成,页面渲染完成)
(3) 调用mounted()钩子函数
该阶段适合做什么?
mounted:可以操作页面的DOM元素了。

2.14.6 更新阶段做了什么事儿

做了这么几件事:
(1) data发生变化(这是该阶段开始的标志)
(2) 调用beforeUpdate()钩子函数(此时只是内存中的数据发生变化,页面还未更新)
(3) 虚拟DOM重新渲染和修补
(4) 调用updated()钩子函数(此时页面已更新)
该阶段适合做什么?
beforeUpdate:适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
updated:页面更新后,如果想对数据做统一处理,可以在这里完成。

2.14.7 销毁阶段做了什么事儿

做了这么几件事:
(1) vm.$destroy()方法被调用(这是该阶段开始的标志)
(2) 调用beforeDestroy()钩子函数(此时Vue实例还在。虽然vm上的监视器、vm上的子组件、vm上的自定义事件监听器还在,但是它们都已经不能用了。此时修改data也不会重新渲染页面了)
(3) 卸载子组件和监视器、解绑自定义事件监听器
(4) 调用destroyed()钩子函数(虽然destroyed翻译为已销毁,但此时Vue实例还在,空间并没有释放,只不过马上要释放了,这里的已销毁指的是vm对象上所有的东西都已经解绑完成了)
该阶段适合做什么?
beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器。

【四轴飞行器】非线性三自由度四轴飞行器模拟器研究(Matlab代码实现)内容概要:本文围绕非线性三自由度四轴飞行器模拟器的研究展开,重点介绍了基于Matlab的建模与仿真方法。通过对四轴飞行器的动力学特性进行分析,构建了非线性状态空间模型,并实现了姿态与位置的动态模拟。研究涵盖了飞行器运动方程的建立、控制系统设计及数值仿真验证等环节,突出非线性系统的精确建模与仿真优势,有助于深入理解飞行器在复杂工况下的行为特征。此外,文中还提到了多种配套技术如PID控制、状态估计与路径规划等,展示了Matlab在航空航天仿真中的综合应用能力。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的高校学生、科研人员及从事无人机系统开发的工程技术人员,尤其适合研究生及以上层次的研究者。; 使用场景及目标:①用于四轴飞行器控制系统的设计与验证,支持算法快速原型开发;②作为教学工具帮助理解非线性动力学系统建模与仿真过程;③支撑科研项目中对飞行器姿态控制、轨迹跟踪等问题的深入研究; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注动力学建模与控制模块的实现细节,同时可延伸学习文档中提及的PID控制、状态估计等相关技术内容,以全面提升系统仿真与分析能力。
你可以使用 `el-date-picker` 的 `shortcuts` 属性来实现季度选择。具体步骤如下: 1. 在 `el-date-picker` 上添加 `shortcuts` 属性,并将其值设置为一个数组,数组中每个元素代表一个季度。 2. 每个季度的配置对象应包含 `text` 和 `onClick` 两个属性,分别表示显示在季度选择列表中的文本和点击季度后的回调函数。 3. 在 `onClick` 回调函数中,你可以使用 `moment.js` 或其他日期处理库来计算出选择的季度的起始日期和结束日期,并将其传递给父组件。 下面是一个示例代码: ``` <el-date-picker v-model="date" type="daterange" :shortcuts="[ { text: '第一季度', onClick: () => { const start = moment().quarter(1).startOf('quarter') const end = moment().quarter(1).endOf('quarter') this.date = [start, end] } }, { text: '第二季度', onClick: () => { const start = moment().quarter(2).startOf('quarter') const end = moment().quarter(2).endOf('quarter') this.date = [start, end] } }, { text: '第三季度', onClick: () => { const start = moment().quarter(3).startOf('quarter') const end = moment().quarter(3).endOf('quarter') this.date = [start, end] } }, { text: '第四季度', onClick: () => { const start = moment().quarter(4).startOf('quarter') const end = moment().quarter(4).endOf('quarter') this.date = [start, end] } } ]" ></el-date-picker> ``` 在上述代码中,我们使用了 `moment.js` 来计算选择的季度的起始日期和结束日期。你也可以使用其他日期处理库或手动计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值