🍑Vue-Study

视频地址:https://www.bilibili.com/video/BV17h41137i4

课件链接:https://pan.baidu.com/s/1OQJdnVR74_SBr7BqUYQAbw?pwd=8023

Vue2官网:https://v2.cn.vuejs.org/

Vue3官网:https://cn.vuejs.org/

代码:

Vue-基础部分

前言:环境准备

VsCode插件:

  • techer.open-in-browser
  • ritwickdey.LiveServer

Chrom浏览器插件:

 1.1、Vue程序初体验

1.1.1、第一个Vue程序

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!--安装vue -->
    <script src="../js/vue.js"></script>
    <div id="app"></div>
    <script>
        let myVue = new Vue({
            template: "<h1>hello Vue</h1>"
        });
        myVue.$mount('#app');
    </script>
</body>
</html>

 1.1.2、模板引擎数据来源

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模板引擎数据来源</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <!-- 模板语句的数据来源:
            1. 谁可以给模板语句提供数据支持呢?data选项。
            2. data选项的类型是什么?Object | Function (对象或者函数)
            3. data配置项的专业叫法:Vue 实例的数据对象.(data实际上是给整个Vue实例提供数据来源的。)
            4. 如果data是对象的话,对象必须是纯粹的对象 (含有零个或多个的 key/value 对)
            5. data数据如何插入到模板语句当中?
                {{}} 这是Vue框架自己搞的一套语法,别的框架看不懂的,浏览器也是不能够识别的。
                Vue框架自己是能够看懂的。这种语法在Vue框架中被称为:模板语法中的插值语法。(有的人把他叫做胡子语法。)
                怎么用?
                    {{data的key}}
                插值语法的小细节:
                    {这里不能有其它字符包括空格{
                    }这里不能有其它字符包括空格}        
     -->
    <div id="app"></div>

    <script>
        let myVue = new Vue({
            template:
                    `
                        <h1>{{name}},年龄是:{{age}},兴趣爱好是:{{hoppy[0]}}、{{hoppy[1]}}、{{hoppy[2]}}</h1> 
                    `,
            data: {
                name: "zhangsan",
                "age": "23",
                "hoppy": [
                    "抽烟", "喝酒", "烫头"
                ]
            }
        });
        myVue.$mount("#app");
    </script>
</body>

</html>

1.1.3、template配置项详解

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>template配置项详解</title>
    <script src="../js/vue.js"></script>
</head>

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

    <script>
        // Vue.config是Vue的全局配置对象。
        // productionTip属性可以设置是否生成生产提示信息。
        // 默认值:true。如果是false则表示阻止生成提示信息。
        //Vue.config.productionTip = false
        // 或者直接在vue.js全局搜索: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({
            data: {
                name: "zhangzhixi",
                age: "23"
            },
            // 替换掉 .$mount()
            el: "#app"
        });
    </script>
</body>
</html>

1.1.4、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>
    <!-- 准备容器 -->
    <div class="app">
        <h1>{{msg}}</h1>
    </div>

    <div class="app">
        <h1>{{msg}}</h1>
    </div>

    <!-- 准备容器 -->
    <div id="app2">
        <h1>{{name}}</h1>
    </div>

    <!-- vue程序 -->
    <script>
        /* 
            验证:一个Vue实例可以接管多个容器吗?
                不能。一个Vue实例只能接管一个容器。一旦接管到容器之后,即使后面有相同的容器,Vue也是不管的。因为Vue实例已经“娶到媳妇”了。
        */
        new Vue({
            el : '.app',
            data : {
                msg : 'Hello Vue!'
            }
        })

        new Vue({
            el : '#app2',
            data : {
                name : 'zhangsan'
            }
        })

        // 这个Vue实例想去接管 id='app2'的容器,但是这个容器已经被上面那个Vue接管了。他只能“打光棍”了。
        new Vue({
            el : '#app2',
            data : {
                name : 'jackson'
            }
        })
        
    </script>


</body>
</html>

1.2、核心技术

1.2.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>
    <!-- 安装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.2.2、模板语法之指令:v-once(只执行一次)

<!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: 12,
                b: 11
            }
        })
    </script>
</body>

</html>

用Vue插件,修改文本

1.2.2、v-bind指令详解

<!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提供了以下简写方式:
                    <img :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实战 -->
        <img src="../img/1.jpg"> <br>
        <img v-bind:src="imgPath"> <br>

        <!-- v-bind简写形式 -->
        <img :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>

1.2.3、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>

1.2.4、MVVM分层思想

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>初识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>

 1.2.5、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>

1.2.6、解读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>
    <!-- 
        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>

1.2.7、data也可以是一个函数

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>data也可以是一个函数</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        <h1>{{msg}}</h1>
    </div>

    <script>
        new Vue({
            el: "#app",
            data() {
                return {
                    msg: "zhangsan"
                };
            }
        });

        // 关于配置项:enumerable、configurable
        let phone = {
            name: "IphoneX"
        }

        Object.defineProperty(phone, "color", {
            value: "red",
            // true表示可以进行遍历:Object.keys(phone)
            enumerable: true,
            // true表示是可以被删除的:delete phone.color
            configurable: true
        });
    </script>
</body>

</html>

 1.2.8、事件绑定:v-on,简写为:@

<!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在调用回调函数的时候,会自动给回调函数传递一个对象,这个对象是:当前发生的事件对象。
            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($event)">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)
                }
            }
        })
    </script>
</body>
</html>

 1.2.9、关于事件回调中的this,计数器例子(this就是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>关于事件回调函数中的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) :true
                    this.counter++;
                },
                add2: () => {
                    //this.counter++;
                    //console.log(this === vm)
                    //箭头函数中没有this,箭头函数中的this是从父级作用域当中继承过来的。
                    //对于当前程序来说,父级作用域是全局作用域:window
                    console.log(this)
                },
                sayHi() {
                    alert('hi...')
                }
            }
        })

    </script>
</body>

</html>

 1.2.10、事件修饰符

<!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">
            <!-- 此处是button按钮向上冒泡传递古来的事件,所以不执行er -->
            <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>

1.2.11、按键修饰符

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

1.2.12、计算属性-反转字符串案例(computed)

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性-反转字符串</title>
    <script src="../js/vue.js"></script>

</head>

<body>
    <!-- 
        执行步骤:
            1、v-model绑定的数据发生变化
            2、插值语法中的方法会重新执行,执行methods中的方法
            3、方法中的this指向的是Vue实例,获取到最新的数据
            4、方法中的代码会重新执行,获取到最新的数据,然后返回
     -->
    <div id="app">
        <h1>{{message}}</h1><br>
        输入字符串:<input type="text" v-model="reversedMessageInfo"><br>
        <br>
        <!-- 在插值语法中可以调用方法,小括号不能省略。这个方法需要是Vue实例所管理的。 -->
        反转字符串-methods方式:{{reverseMessage_methods()}}<br>
        <br>
        反转字符串-compared方式:{{reverseMessage_computed}}<br>
    </div>
    <script>
        new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue.js!',
                reversedMessageInfo: ''

                // 方法也可以直接放在data中,但是不推荐这样做
                // reverseMessage_methods(){
                //     return this.reversedMessageInfo.split('').reverse().join('');
                // }
            },
            methods: {
                // 反转字符串的方法
                reverseMessage_methods() {
                    return this.reversedMessageInfo.split('').reverse().join('');
                }
            },
            computed: {
                // 反转字符串的方法
                reverseMessage_computed() {
                    return this.reversedMessageInfo.split('').reverse().join('');
                }
            }
        })
    </script>
</body>

</html>

1.2.13、侦听属性-比较数字大小案例(watch)

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>监听属性变化-数字大小比较案例</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="app">
        数字1:<input type="number" v-model="num1"><br>
        数字2:<input type="number" v-model="num2"><br>
        <hr>
        比较数值大小-watch方式: {{compareResult}}
        <br>
        <!-- 此处需要注意的是,computed中的是计算属性,不是方法,所以不需要加括号。 -->
        比较数值大小-computed方式: {{compareResult_computed}}
    </div>

    <script>
        new Vue({
            el: "#app",
            data: {
                msg: "监听属性变化-数值大小比较案例",
                num1: 0,
                num2: 0,
                compareResult: ''
            },
            computed: {
                compareResult_computed() {
                    let result = this.num1 - this.num2
                    if (result == 0) {
                        return this.num1 + ' = ' + this.num2
                    } else if (result > 0) {
                        return this.num1 + ' > ' + this.num2
                    } else {
                        return this.num1 + ' < ' + this.num2
                    }
                }
            },
            watch: {
                num1: {
                    // 设置immediate为true,可以在初始化的时候,调用一次handler方法。
                    immediate: true,
                    handler(val) {
                        let result = val - this.num2
                        if (result == 0) {
                            this.compareResult = val + ' = ' + this.num2
                        } else if (result > 0) {
                            this.compareResult = val + ' > ' + this.num2
                        } else {
                            this.compareResult = val + ' < ' + this.num2
                        }
                    }
                },
                num2: {
                    immediate: true,
                    handler(val) {
                        let result = this.num1 - val
                        if (result == 0) {
                            this.compareResult = this.num1 + ' = ' + val
                        } else if (result > 0) {
                            this.compareResult = this.num1 + ' > ' + val
                        } else {
                            this.compareResult = this.num1 + ' < ' + val
                        }
                    }
                }
            }
        });
    </script>
</body>

</html>

1.2.14、class样式绑定

分别有三种形式:字符串形式、数组形式、对象形式

字符串形式:适用场景(如果确定动态绑定的样式个数只有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>

 数组形式:适用场景(当样式的个数不确定,并且样式的名字也不确定的时候)

<!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>
        <!-- 数组写法1 -->
        <div class="static" :class="[c1, c2]">{{msg}}</div>
        <br>
        <!-- 数组写法1 -->
        <!-- 适用场景:当样式的个数不确定,并且样式的名字也不确定的时候,可以采用数组形式。 -->
        <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>

对象形式:适用场景(样式的个数是固定的,样式的名字也是固定的,但是需要动态的决定样式用还是不用)

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

1.2.15、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>

1.2.16、条件渲染v-if、v-else-if、v-else

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

        <!-- ------------------------------------------------------------------------------- -->
        <button @click="counter++">点我加1</button>
        <h3>{{counter}}</h3>

        <!-- 提醒:v-if和v-else之间不能断开。 -->
        <img :src="imgPath1" v-if="counter % 2 === 1">
        <img :src="imgPath2" v-else>
        <hr>
        <!-- ------------------------------------------------------------------------------- -->
        <!-- ------------------------------------------------------------------------------- -->
        温度:<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>
        <br>
        <!-- 写法一:简洁 -->
        天气:<span v-if="temprature <= 10">寒冷</span>
        <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>

1.2.17、列表渲染:v-for

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

1.2.18、列表过滤(文本检索):computed、watch

computed

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

<head>
    <meta charset="UTF-8">
    <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="keyword" placeholder="请输入想要查找的姓名,">
        <table>
            <tr>
                <th>编号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>地址</th>
            </tr>
            <tr v-for="user in filterUser" ::key="user.id">
                <td>{{user.id}}</td>
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
                <td>{{user.address}}</td>
            </tr>
        </table>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                keyword: '',
                msg: '列表过滤',
                users: [
                    { id: '100', name: '张三', age: '23', address: '北京丰台' },
                    { id: '200', name: '李四', age: '24', address: '北京朝阳' },
                    { id: '300', name: '王五', age: '25', address: '北京海淀' },
                    { id: '400', name: '赵六', age: '26', address: '北京昌平' },
                    { id: '500', name: '田七', age: '27', address: '北京大兴' },
                    { id: '600', name: '张小三', age: '28', address: '北京大兴' }
                ]
            },
            // 字符串搜索之computed计算属性实现
            computed: {
                filterUser() {
                    return this.users.filter((user) => {
                        return user.name.indexOf(this.keyword) >= 0
                    })
                }
            }
        });
    </script>
</body>

</html>

watch 

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

<head>
    <meta charset="UTF-8">
    <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="keyword" placeholder="请输入想要查找的姓名">
        <table>
            <tr>
                <th>编号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>地址</th>
            </tr>
            <tr v-for="user in filterUser" ::key="user.id">
                <td>{{user.id}}</td>
                <td>{{user.name}}</td>
                <td>{{user.age}}</td>
                <td>{{user.address}}</td>
            </tr>
        </table>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                keyword: '',
                msg: '列表过滤',
                users: [
                    { id: '100', name: '张三', age: '23', address: '北京丰台' },
                    { id: '200', name: '李四', age: '24', address: '北京朝阳' },
                    { id: '300', name: '王五', age: '25', address: '北京海淀' },
                    { id: '400', name: '赵六', age: '26', address: '北京昌平' },
                    { id: '500', name: '田七', age: '27', address: '北京大兴' },
                    { id: '600', name: '张小三', age: '28', address: '北京大兴' }
                ],
                filterUser:[]
            },
            // 字符串搜索之watch监听属性实现
            watch: {
                // 监听keyword的变化
                keyword: {
                    immediate: true,
                    handler(val) {
                        this.filterUser = this.users.filter((user) => {
                            return user.name.indexOf(val) >= 0
                        })
                    }
                }
            }
        });
    </script>
</body>

</html>

1.2.19、表单数据收集

<!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>
        <!-- 阻止表单的默认提交行为,第一种:@submit.prevent  -->
        <form @submit.prevent="send">
            用户名:<input type="text" v-model.trim="user.username"><br><br>
            密码:<input type="password" v-model="user.password"><br><br>
            年龄:<input type="number" v-model.number="user.age"><br><br>
            性别:
            男<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>
            简介:
            <!--  -->
            <textarea cols="50" rows="15" v-model.lazy="user.introduce"></textarea><br><br>
            <input type="checkbox" v-model="user.accept">阅读并接受协议<br><br>

        <!-- 阻止表单的默认提交行为,第二种:@click.prevent  -->
            <!-- <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: '',
                    introduce: '',
                    accept: ''
                },
                msg: '表单数据的收集'
            },
            methods: {
                send() {
                    // 将数据收集好,发送给服务器。
                    console.log(JSON.stringify(this.$data))
                    console.log(JSON.stringify(this.user))
                }
            }
        })
    </script>
</body>

</html>

1.2.20、过滤器(在Vue3被弃用)

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

1.2.21、Vue的其他指令(v-text、v-html、v-once)

<!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>
         <br><br>
         使用了v-once指令,那么这个元素只会渲染一次。
         <ul>
            <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的其它指令',
                name : 'jack',
                s1 : '<h1>欢迎大家学习Vue!</h1>',
                users : ['jack', 'lucy', 'james']
            }
        })
    </script>
</body>
</html>

1.2.22、自定义指令(directives)

例:

  • text-danger:将属性值设置为红色
  • bind-blue:设置属性的父级元素背景色

关于自定义指令的区别:

  • 函数式:功能简洁
  • 对象式:功能更加细粒化
<!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>自定义指令</h1>
        <div v-text="msg"></div>
        <div v-text-danger="msg"></div>
        用户名:<input type="text" v-bind:value="username">
        <!-- 
            需要一个指令,可以和v-bind指令完成相同的功能,同时将该元素的父级元素的背景色设置为蓝色。
         -->
        <div>
            用户名:<input type="text" v-bind-blue="username">
        </div>
    </div>
    <hr>
    <hr>
    <div id="app2">
        <div v-text-danger="msg"></div>
        <div>
            用户名:<input type="text" v-bind-blue="username">
        </div>
    </div>

    <script>
        // 定义全局的指令
        // 函数式
        Vue.directive('text-danger', function (element, binding) {
            //对于自定义指令来说,函数体当中的this是window,而不是vue实例。
            console.log(this)
            element.innerText = binding.value
            element.style.color = 'red'
        })

        // 对象式
        Vue.directive('bind-blue', {
            // 元素与指令初次绑定的时候,自动调用bind
            bind(element, binding) {
                element.value = binding.value
                console.log(this)
            },
            // 元素被插入到页面之后,这个函数自动被调用。
            inserted(element, binding) {
                element.parentNode.style.backgroundColor = 'skyblue'
                console.log(this)
            },
            // 当模板重新解析的时候,这个函数会被自动调用。
            update(element, binding) {
                element.value = binding.value
                console.log(this)
            }
        })

        const vm2 = new Vue({
            el: '#app2',
            data: {
                msg: '欢迎学习Vue框架!',
                username: 'lucy'
            }
        })

        const vm = new Vue({
            el: '#app',
            data: {
                msg: '自定义指令',
                username: 'jackson'
            },
            directives: {
                // 指令1
                // 指令2
                // ...
                // 关于指令的名字:1. v- 不需要写。 2. Vue官方建议指令的名字要全部小写。如果是多个单词的话,请使用 - 进行衔接。
                // 这个回调函数的执行时机包括两个:第一个:标签和指令第一次绑定的时候。第二个:模板被重新解析的时候。
                // 这个回调函数有两个参数:第一个参数是真实的dom元素。 第二个参数是标签与指令之间绑定关系的对象。
                // 这种方式属于函数式方式。
                /* 'text-danger' : function(element, binding){
                    console.log('@')
                    element.innerText = binding.value
                    element.style.color = 'red'
                }, */
                /* 'bind-blue' : function(element, binding){
                    element.value = binding.value
                    console.log(element)
                    // 为什么是null,原因是这个函数在执行的时候,指令和元素完成了绑定,但是只是在内存当中完成了绑定,元素还没有被插入到页面当中。
                    console.log(element.parentNode)
                    element.parentNode.style.backgroundColor = 'blue'
                } */


                // 对象式
                /* 'bind-blue' : {
                    // 这个对象中三个方法的名字不能随便写。
                    // 这三个函数将来都会被自动调用。
                    // 元素与指令初次绑定的时候,自动调用bind
                    // 注意:在特定的时间节点调用特定的函数,这种被调用的函数称为钩子函数。
                    bind(element, binding){
                        element.value = binding.value
                    },
                    // 元素被插入到页面之后,这个函数自动被调用。
                    inserted(element, binding){
                        element.parentNode.style.backgroundColor = 'blue'
                    },
                    // 当模板重新解析的时候,这个函数会被自动调用。
                    update(element, binding){
                        element.value = binding.value
                    }
                } */
            }
        })
    </script>
</body>

</html>

 1.2.23、响应式与数据劫持

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

数组的响应式处理

<!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():在数组的末尾添加一个或多个元素,并返回新的长度。
                vm.users.push('jackson')
            pop():删除数组的最后一个元素,并返回该元素。
                vm.users.pop()
            reverse():颠倒数组中元素的顺序。
                vm.users.reverse()
            splice():从数组中替换元素。
                vm.users.splice(下标, 替换的数量, 替换的元素),
                vm.users.splice(1, 1, 'jackson')
            shift():删除数组的第一个元素,并返回该元素。
                vm.users.shift()
            unshift():在数组的开头添加一个或多个元素,并返回新的长度。
                vm.users.unshift('jackson')
            sort():对数组的元素进行排序,按照字母或数字的顺序。
                vm.users.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> 

1.2.24、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>{{msg}}</h1>
        <h3>计数器:{{counter}}</h3>
        <h3 v-text="counter"></h3>
        <button @click="add()">点我加1</button>
        <button @click="destroy()">点我销毁</button>
    </div>
    <script>
        const vm = new Vue({
            el : '#app',
            data : {
                msg : 'Vue生命周期',
                counter : 1
            },
            methods: {
                add(){
                    console.log('add....')
                    this.counter++
                },
                destroy(){
                    // 销毁vm
                    this.$destroy()
                },
                /* m(){
                    console.log('m....')
                } */
            },
            watch : {
                counter(){
                    console.log('counter被监视一次!')
                }
            },
            /*
            1.初始阶段
                el有,template也有,最终编译template模板语句。
                el有,template没有,最终编译el模板语句。
                el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果template有,最终编译template模板语句。
                el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果没有template,最终编译el模板语句。

                结论:
                    流程要想继续:el必须存在。
                    el和template同时存在,优先选择template。如果没有template,才会选择el。
            */
            beforeCreate() {
                // 创建前
                // 创建前指的是:数据代理和数据监测的创建前。
                // 此时还无法访问data当中的数据。包括methods也是无法访问的。
                console.log('beforeCreate', this.counter)
                // 调用methods报错了,不存在。
                //this.m()
            },
            created() {
                // 创建后
                // 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。
                console.log('created', this.counter)
                // 可以访问methods了。
                //this.m()
            },
            // 2.挂载阶段
            beforeMount() {
                // 挂载前
                console.log('beforeMount')
            },
            mounted() {
                // 挂载后
                console.log('mounted')
                console.log(this.$el)
                console.log(this.$el instanceof HTMLElement)
            },
            // 3.更新阶段
            beforeUpdate() {
                // 更新前
                console.log('beforeUpdate')
            },
            updated() {
                // 更新后
                console.log('updated')
            },
            // 4.销毁阶段
            beforeDestroy() {
                // 销毁前
                console.log('beforeDestroy')
                console.log(this)
                this.counter = 1000
            },
            destroyed() {
                // 销毁后
                console.log('destroyed')
                console.log(this)
            },
        })
    </script>
</body>
</html>

1.3、Vue组件化开发

1.3.1、第一个组件程序

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

<head>
    <meta charset="UTF-8">
    <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>

        <!-- 3、使用组件 -->
        <my-component></my-component>
        <userlist></userlist>
        <userlogin></userlogin>
    </div>

    <hr>
    <div id="app2">
        <userlogin></userlogin>
    </div>

    <script>
        // ##########################1、创建组件###########################################
        /* let myComponent = Vue.extend(
            {
                template: `
                    <ul>
                        <li v-for="user,index in users" :key="user.id">
                            {{user.id}}--{{user.name}}--{{user.age}}
                        </li>
                    </ul>
                `,
                data() {
                    return {
                        users: [
                            { id: '001', name: '张三', age: 18 },
                            { id: '002', name: '李四', age: 19 },
                            { id: '003', name: '王五', age: 20 },
                        ]
                    }
                }
            }
        ); */
        // 创建组件简写
        let myComponent = {
            template: `
                <ul>
                    <li v-for="user,index in users" :key="user.id">
                        {{user.id}}--{{user.name}}--{{user.age}}
                    </li>
                </ul>
            `,
            data() {
                return {
                    users: [
                        { id: '001', name: '张三', age: 18 },
                        { id: '002', name: '李四', age: 19 },
                        { id: '003', name: '王五', age: 20 },
                    ]
                }
            }
        }
        
        let myUserLogin = Vue.extend({
            template: `
                <div>
                    <h1>用户登录</h1>
                    <form @submit.prevent="login">
                        用户名:<input type="text" v-model="username"> <br>
                        密码:<input type="password" v-model="password" autocomplete><br>
                        <input type="submit" value="登录">
                    </form>
                </div>
            `,
            data() {
                return {
                    username: '',
                    password: ''
                }
            },
            methods: {
                // 阻止表单默认提交
                login() {
                    alert(this.username + "," + this.password)
                }
            }

        });
        // ##########################创建组件###########################################

        // 2、注册组件(全局),写在组件创建之后
        Vue.component('userlogin', myUserLogin)

        // ##########################创建Vue实例###########################################
        let myVue = new Vue({
            el: '#app',
            data: {
                msg: '第一个组件程序',
                users: [
                    { id: '001', name: '张三', age: 18 },
                    { id: '002', name: '李四', age: 19 },
                    { id: '003', name: '王五', age: 20 },
                ]
            },
            // 2、注册组件(局部)
            components: {
                myComponent,
                // 起别名
                userlist: myComponent
            }
        })

        // 创建Vue实例
        let myVue2 = new Vue({
            el: '#app2'
        })
        // ##########################创建Vue实例###########################################
    </script>
</body>

</html>

1.3.2、组件嵌套

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>组件的嵌套</title>
    <script src="../js/vue.js"></script>
</head>

<body>
    <div id="root">
        <!-- 3、使用组件 -->
        <app></app>
    </div>
    <script>


        let x1 = {
            template: `
                <div>
                    <h1>x1组件</h1>
                </div>
            `
        }
        let x = {
            template: `
                <div>
                    <h1>x组件</h1>
                    <!--使用组件-->
                    <x1></x1>
                </div>
            `,
            components: {
                x1
            }
        }
        let y1 = {
            template: `
                <div>
                    <h1>y1组件</h1>
                </div>
            `
        }
        let y = {
            template: `
                <div>
                    <h1>y组件</h1>
                    <!--使用组件-->
                    <y1></y1>
                </div>
            `,
            components: {
                y1
            }
        }

        // 1、创建组件
        let app = {
            template: `
                <div>
                    <h1>app组件</h1>
                    <!--使用组件-->
                    <x></x>
                    <y></y>
                </div>
            `,
            components: {
                x, y
            }
        };
        new Vue({
            el: '#root',
            // 2、注册组件
            components: {
                app
            }
        });
    </script>
</body>

</html>

 1.4、Vue脚手架

1.4.1、NodeJs安装

https://www.cnblogs.com/zhangzhixi/p/17642325.html

1.4.2、Vue脚手架安装

Vue的脚手架(Vue CLI: Command Line Interface)是Vue官方提供的标准化开发平台。

它可以将我们.vue的代码进行编译生成html css js代码,并且可以将这些代码自动发布到它自带的服务器上,为我们Vue的开发提供了一条龙服务。

脚手架官网地址:https://cli.vuejs.org/zh

安装脚手架

npm install -g @vue/cli

创建项目(自带HelloWorld案例)

vue create vue_pro

这里选择Vue2,

  • babel:负责ES6语法转换成ES5。
  • eslint:负责语法检查的。

回车之后,就开始创建项目,创建脚手架环境(内置了webpack loader),自动生成HelloWorld案例。

编译Vue程序  

cd vue_pro
npm run serve

访问程序:

 

 1.4.3、脚手架目录结构

  • package.json:包的说明书(包的名字,包的版本,依赖哪些库)。该文件里有webpack的短命令:
    • serve(启动内置服务器)
    • build命令是最后一次的编译,生成html css js,给后端人员
    • lint做语法检查的。

 1.4.4、分析脚手架HelloWorld程序

 

可以看到在index.html中只有一个容器。没有引入vue.js,也没有引入main.js

Vue脚手架可以自动找到main.js文件。(所以main.js文件名不要修改,位置也不要随便移动)

 

接下来就是将之前写的程序拷贝到脚手架中,进行测试。

需要拷贝过来的是:App.vue、X.vue、Y.vue、X1.vue、Y1.vue。

main.js和index.html都不需要了,因为脚手架中有。

 

只需要将App.vue中的路径修改一下即可:

 

打开VSCode终端:ctrl + `

在终端中执行:npm run serve

报错了:

 

导致这个错误的原因是:组件的名字应该由多单词组成。这是eslint进行的es语法检测。

解决这个问题有两种方案:

第一种:把所有组件的名字修改一下。

第二种:在vue.config.js文件中进行脚手架的默认配置。配置如下:

 

在终端中ctrl + c 两次,终止之前的服务,再次运行命令:npm run serve

 

 1.4.5、props配置项

使用props配置可以接收其他组件传过来的数据,让组件的数据变为动态数据,三种接收方式:

(1)     简单接收

props : [‘name’,’age’,’sex’]

(2)     接收时添加类型限制

props: {
    name: String,
    age: Number,
    sex: String
}

(3)     接收时添加类型限制,必要性限制,默认值

props : {
    name : {
        type : Number,
        required : true
    },
    age : {
        type : Number,
        default : 10
    },
    sex : {
        type : String,
        default : '男'
    }
}

 

 在上面的案例中,通常我们给属性赋值是通过在data中给属性赋值,如下所示:

 可以看到上面的程序是有问题,如果我们想要数据是动态的话,这时候需要一个个修改其属性值,比较繁琐麻烦。

有没有可以在标签使用时动态给属性传参呢?这时候就可以通过props配置项:

car.vue:

<template>
    <div>
        <h3>品牌:{{brand}}</h3>
        <h3>价格:{{price}}</h3>
        <h3>颜色:{{color}}</h3>
    </div>
</template>

<script>
export default {
    /* data() {
        return {
            brand: '奔驰',
            price: 1000000,
            color: '黑色'
        }
    } */
    // props接收父组件传递过来的数据
    props: ['brand', 'price', 'color']
}
</script>

<style>

</style>

App.vue:

<template>
  <div>
    <h1>我是App组件</h1>
    <!-- 传递属性值 -->
    <car brand="奔驰" :price="100000" color="黑色"></car>
    <hr />
    <car brand="宝马" :price="200000" color="白色"></car>
    <hr />
    <car brand="奥迪" :price="300000" color="红色"></car>
  </div>
</template>

<script>
import car from "./components/car.vue";
export default {
  components: {
    car,
  },
};
</script>

<style>
</style>

 1.4.6、在父组件中获取子组件信息:ref

比如上个例子,App.vue可以看做父组件、car.vue看作是子组件,想在父组件中获取子组件中的属性值等信息,就可以使用到标签体中的ref属性。

1、打标记

<car ref="benzCar" brand="奔驰" :price="100000" color="黑色"></car>

2、获取值

// 获取子组件
 console.log(this.$refs.benzCar);
// 获取子组件的属性
console.log(this.$refs.benzCar.brand);
 // 获取dom元素
console.log(this.$refs.h1Msg.innerText);

App.vue

<template>
  <div>
    <h1 ref="h1Msg">{{ msg }}</h1>
    <!-- 传递属性值 -->
    <car ref="benzCar" brand="奔驰" :price="100000" color="黑色"></car>
    <hr />
    <car ref="bmwCar" brand="宝马" :price="200000" color="白色"></car>
    <hr />
    <car brand="奥迪" :price="300000" color="红色"></car>
    <button @click="printCarInfo()" value="点我获取汽车属性">
      点我获取汽车属性
    </button>
  </div>
</template>

<script>
import car from "./components/car.vue";
export default {
  data() {
    return {
      msg: "我是App组件",
    };
  },
  components: {
    car,
  },
  methods: {
    printCarInfo() {
      // 获取子组件
      console.log(this.$refs.benzCar);
      console.log(this.$refs.bmwCar);
      // 获取子组件的属性
      console.log(this.$refs.benzCar.brand);
      console.log(this.$refs.bmwCar.brand);

      // 获取dom元素
      console.log(this.$refs.h1Msg.innerText);
    },
  },
};
</script>

<style>
</style>

 1.4.7、mixins配置(混入)(代码复用)

如图所示,其中两段方法是一样的,是否可以进行优化,将代码复用呢?

 运行效果:

可以看到以上vip.vue和user.vue代码中都有相同的methods,这个代码可以复用吗?可以使用mixins配置进行混入。实现步骤:

第一步:创建混入

单独定义一个mixin.js(一般和main.js在同级目录),代码如下:

// 1、定义一个混入对象
export const mix1 = {
    methods: {
        printInfo() {
            console.log(this.name, this.age);
        }
    }
}

2、引入和使用混入

全局混入:

1.4.8、局部样式(scoped)

如下所示定义了两个相同名称的class,并分别设置背景颜色为红色和蓝色

这时候在页面上面显示的是红色的背景色,原因是因为在App.vue中先引入了user.vue,如果先引入vip.vue,那么背景色就会都变为蓝色。

 如何解决呢?想要每个组件拥有自己的样式:只需要在style中添加scoped标签即可

Vue-高级部分

2.1、Vue与Ajax

2.1.1、回顾发送AJax异步请求的方式

发送AJAX异步请求的常见方式包括:

1. 原生方式,使用浏览器内置的JS对象XMLHttpRequest

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){}
xhr.open()
xhr.send()

2. 原生方式,使用浏览器内置的JS函数fetch

fetch(‘url’, {method : ‘GET’}).then().then()

3. 第三方库方式,JS库jQuery(对XMLHttpRequest进行的封装)

$.get()
$.post()

4. 第三方库方式,基于Promise的HTTP库:axios (对XMLHttpRequest进行的封装),axios是Vue官方推荐使用的。

axios.get('').then(
    response => {
        console.log('服务器响应回来的数据:', response.data)
    },
    error => {
        console.log('错误信息:', error.message)
    }
)

2.1.2、回顾AJax跨域

1.什么是跨域访问

(1)	在a页面中想获取b页面中的资源,如果a页面和b页面所处的协议、域名、端口不同(只要有一个不同),所进行的访问行动都是跨域的。
(2)	哪些跨域行为是允许的?
    ①	直接在浏览器地址栏上输入地址进行访问
    ②	超链接
    ③	<img src=”其它网站的图片是允许的”>
    ④	<link href=”其它网站的css文件是允许的”>
    ⑤	<script src=”其它网站的js文件是允许的”>
    ⑥	......
(3)	哪些跨域行为是不允许的?
    ①	AJAX请求是不允许的
    ②	Cookie、localStorage、IndexedDB等存储性内容是不允许的
    ③	DOM节点是不允许的

2.AJax无法跨域的原因:同源策略

(1)	
    同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。
    同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
(2)	
    AJAX请求不允许跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

3.解决AJax跨域的方案包括哪些

(1)	CORS方案(工作中常用的)
    这种方案主要是后端的一种解决方案,被访问的资源设置响应头,告诉浏览器我这个资源是允许跨域访问的:
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
(2)	jsonp方案(面试常问的)
    采用的是<script src=””>不受同源策略的限制来实现的,但只能解决GET请求。
(3)	代理服务器方案(工作中常用的)
    Nginx反向代理
    Node中间件代理
    vue-cli(Vue脚手架自带的8080服务器也可以作为代理服务器,需要通过配置vue.config.js来启用这个代理)
(4)	postMesssage
(5)	websocket
(6)	window.name + iframe
(7)	location.hash + iframe
(8)	document.domain + iframe
(9)	......

2.1.3、演示跨域

Vue脚手架内置服务器的地址:http://localhost:8080

SpringBoot1项目-User地址:http://localhost:8000/getUserList

package com.zhixi.controller;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhangzhixi
 * @version 1.0
 * @description
 * @date 2023-09-01 9:10
 */
@RestController
public class UserController {

    @GetMapping("/getUserList")
    public List<User> getUserList(){
        List<User> userList = new ArrayList<>();
        userList.add(new User("张三", 23, "河南信阳"));
        userList.add(new User("李四", 24, "河南南阳"));
        userList.add(new User("王五", 25, "河南濮阳"));
        return userList;

    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class User{
    private String name;
    private Integer age;
    private String address;
}
UserController

SpringBoot2项目-Vip地址:http://localhost:8001/getVipList

package com.zhixi.controler;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhangzhixi
 * @version 1.0
 * @description
 * @date 2023-09-01 9:17
 */
@RestController
public class VipController {
    @GetMapping("/getVipList")
    public List<Vip> getVipList() {
        List<Vip> vipList = new ArrayList<>();
        vipList.add(new Vip("马云", 58, "浙江杭州"));
        vipList.add(new Vip("刘强东", 49, "江苏宿迁"));
        vipList.add(new Vip("雷军", 53, "湖北仙桃"));
        return vipList;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Vip {
    private String name;
    private Integer age;
    private String address;
}
VipController


 1.安装axios库

npm i axios

2.引入axios库

3.发送ajax请求  

import axios from "axios";

mounted() {
    // 发送ajax请求
    axios.get("http://localhost:8000/getUserList").then(
      (response) => {
        console.log(response.data);
      },
      (error) => {
        console.log(error.message);
      }
);

以上的访问表示:在8080服务器(Vue)中发送AJAX请求访问8000服务器,必然会出现AJAX跨域问题:

2.1.4、解决跨域

在Vue中解决跨域的方法是:启用Vue脚手架内置服务器8080的代理功能。

简单开启:

vue.config.js文件中添加如下配置:

devServer: {
  // 后端服务器:协议、IP、端口
  // 含义:Vue脚手架内置的8080服务器负责代理访问8000服务器
  proxy: 'http://localhost:8000'
}

发送ajax时,地址需要修改为如下:

刷新页面,可以看到成功打印出来了user信息:


 如何验证会先走vue项目8080的请求呢?在public下新建一个文件,文件名为getUserList,写入内容随意,然后再次刷新,可以看到走的是vue服务器8080的请求了:

高级开启(常用)

vue.config.js:

devServer: {
  proxy: {
    // 后端服务,访问前缀
    '/api': {
      // 后端服务,协议、IP、端口
      target: 'http://localhost:8000',
      pathRewrite: { '^/api': '' }, // 重写路径,正则表达式去除前缀,因为后端服务器是没有/api访问前缀的
      ws: true, // 支持websocket
      changeOrigin: true // true表示改变起源(让目标服务器不知道真正的起源)
    },
    '/abc': {
      target: 'http://localhost:8001',
      pathRewrite: { '^/abc': '' },
      ws: true, // 默认值true
      changeOrigin: true // 默认值true
    }
  }
}

页面:新建按钮,分别访问8000和8001服务器资源:

<button @click="getUserList()">获取普通用户列表信息</button>
<br />
<button @click="getVipList()">获取Vip用户列表信息</button>

methods: {
  getUserList() {
    // 当前按钮这个页面就是在8080服务器上,又去访问8080服务器上的资源,所以http://localhost:8080可以省略。
    axios.get("/api/getUserList").then(
      (response) => {
        console.log("服务器响应回来的数据:", response.data);
      },
      (error) => {
        console.log("错误信息:", error.message);
      }
    );
  },
  getVipList() {
    axios.get("/abc/getVipList").then(
      (response) => {
        console.log("服务器响应回来的数据:", response.data);
      },
      (error) => {
        console.log("错误信息:", error.message);
      }
    );
  }
}

访问到资源,成功解决跨域问题:

2.2、实现城市搜索展示天气预报功能

 代码地址:https://gitee.com/zhang-zhixi/vue-demo-bjpowernode/tree/master/%E9%AB%98%E7%BA%A7%E9%83%A8%E5%88%86/vue_pro/16-Weather-%E5%A4%A9%E6%B0%94%E9%A2%84%E6%8A%A5%E6%A1%88%E4%BE%8B

效果:

 2.3、vuex

2.3.1、vuex概述

1. vuex是实现数据集中式状态管理的插件。数据由vuex统一管理。其它组件都去使用vuex中的数据。只要有其中一个组件去修改了这个共享的数据,其它组件会同步更新。

一定要注意:全局事件总线和vuex插件的区别:

  • (1)     全局事件总线关注点:组件和组件之间数据如何传递,一个绑定$on,一个触发$emit。数据实际上还是在局部的组件当中,并没有真正的让数据共享。只是数据传来传去。
  • (2)     vuex插件的关注点:共享数据本身就在vuex上。其中任何一个组件去操作这个数据,其它组件都会同步更新。是真正意义的数据共享。

  • (3)     使用vuex的场景是:多个组件之间依赖于同一状态。来自不同组件的行为需要变更同一状态。

 

2.3.2、vuex环境搭建

1. 安装vuex

# 因为Vue和vuex版本没有对应上,这里是需要注意的
vue2安装vuex3版本:npm i vuex@3
vue3安装vuex4版本:npm i vuex@4

2. 创建目录和js文件(目录和文件名不是必须叫这个)

(1)     目录:vuex

(2)     js文件:store.js

vuex/store.js

// 引入Vue,因为下面使用Vuex插件的时候需要Vue
import Vue from 'vue'

// 引入vuex插件
import Vuex from 'vuex'

// 使用插件
Vue.use(Vuex)

// 创建三个vuex插件的核心对象:actions对象、mutations对象、state对象
const actions = {}
const mutations = {}
const state = {}

// 简写形式
export default new Vuex.Store({actions,mutations,state})


/* // 创建store对象(这个store对象是vuex插件中的老大,最核心的对象,这个对象store是用来管理actions对象、mutations对象、state对象。)
const store = new Vuex.Store({
    // 它是一个负责执行某个行为的对象
    actions : actions,
    // 它是一个负责更新的对象
    mutations : mutations,
    // 它是一个状态对象
    state : state
})

// 导出store对象(暴露之后,别人想用可以使用import进行引入)
export default store */

3.暴漏vuex核心对象store

在store.js文件中创建核心store对象,并暴露

 

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 引入Vuex插件中的核心对象store
import store from './vuex/store'

new Vue({
  el : '#app',
  // 一个全新的配置项,以前没有学过:store
  // 加上这个配置项之后,vm以及所有的vc对象上都会多一个属性:$store
  // 以后通过vm.$store或者vc.$store来获取store对象。
  //store : store,
  store,
  render : h => h(App),
})

2.3.3、vuex实现点我加一功能

<template>
    <div>
        <h1>数字:{{$store.state.num}}</h1>
        <button @click="plusOne()">点我加1</button>
    </div>
</template>

<script>
    export default {
        name : 'App',
        data() {
            return {
                startNum : 0
            }
        },
        methods: {
            plusOne(){
                // 这里的代码在实际开发中可能会比较复杂。
                // 业务逻辑复杂,代码比较多。
                // 如果你将这些代码放到这里的话,这些业务逻辑代码无法得到复用。
                // 无法在其他组件中使用,在其他组件中使用的时候,你还需要把这些代码再写一遍。
                //this.$store.state.num++
                // 调用vuex的API。
                // dispatch是vuex的API。调用这个方法之后,store对象中的plusOne这个action回调函数会被自动调用。
                // dispatch:分发
                // 交给plusOne这个action去处理这个事儿。
                this.$store.dispatch('plusOne', this.startNum)
            }
        },
    }
</script>
App.vue
// 引入Vue,因为下面使用Vuex插件的时候需要Vue
import Vue from 'vue'

// 引入vuex插件
import Vuex from 'vuex'

// 使用插件
Vue.use(Vuex)

// 创建三个vuex插件的核心对象:actions对象、mutations对象、state对象
const actions = {
    // N多个action
    // 每一个action都是一个callback(回调函数)
    // 在action这种回调函数中编写复杂的业务逻辑
    // 有个原则:action是专门用来处理业务逻辑,或者说发送AJAX请求的。
    //plusOne : function(){}
    // 简写
    // context参数:context是vuex的上下文(context可以看做是store对象的缩小版)
    // value参数:传过来的数据
    plusOne(context, value){ 
        // 处理业务
        value = value + 1
        // 调用其它的action这个回调函数
        //context.dispatch('otherAction', value)
        // 业务逻辑处理完成之后,继续向下一个环节走,就轮到了数据的更新。
        // 提交上下文环境(所有的事儿都做完了,该最后一步了,更新数据了,怎么办?提交)
        context.commit('PLUS_ONE', value)
    },

    // 这里可能还会有其它的action
    // ...
    /* otherAction(context, value){        
        console.log(6666)
    } */
}
const mutations = {
    // N多个mutation
    // 每一个mutation都是一个callback(回调函数)
    // 每一个mutation这个回调函数的作用就是:更新state
    // 只要state一更新,因为是响应式的,所以页面就重新渲染了。
    // state参数:状态对象
    // value参数:上一环节传过来的数据
    PLUS_ONE(state, value){
        state.num += value
    }
}

// 等同于Vue当中的data(只不过这里我们不叫做数据,叫做状态)
// 状态对象(数据对象),已经做了响应式处理的。
const state = {
    num : 0
}

// 简写形式
export default new Vuex.Store({actions,mutations,state})
vuex/store.js

 

posted @ 2023-08-16 10:36  Java小白的搬砖路  阅读(58)  评论(0编辑  收藏  举报