尚硅谷_vue核心基础部分

01 初始vue

1. 想让vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2. root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3. root容器里的代码被称为【Vue模板】;
4. Vue实例和容器是一一对应的;
5. 真实开发中只有一个Vue实例并且会配合着组件一起使用;
6. {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所对应的属性;
7. 一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新;

注意区分:js表达式 和 js代码(又称为“语句”)
    1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,如:
        Date.now() // 获取当前时间戳
        a+b
        demo(1)
        x === y ? 'a' : 'b'
    2. js代码(语句)
        if(){}
        for(){}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="../js/vue.js"></script>
    <title>初始vue</title>
</head>
<body>
    <div id="root">
        <!-- 使用{{数据键}}取出vue实例中的值 -->
        <h1>hello, {{name.toUpperCase()}}, {{address}}</h1>
    </div>
    <script>
        Vue.config.productionTip = false;
        // 创建vue实例
        new Vue({
            // el用于指定当前vue实例为那个容器服务,值通常为css选择器字符串
            el: '#root',
            // data中用于存储数据,数据供el所所指定的容器去使用。值我们暂时写成一个对象。
            data: {
                name: 'badi',
                address: "贵州花溪"
            }
        });
    </script>
</body>
</html>

02 Vue模板语法

1. 插值语法:
    功能:用于解析标签体内容;
    写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2. 指令语法:
    功能:用于解析标签(包括:标签属性、标签体内容、绑定事件...)
    举例:v-bind:href="xxx" 或简写为 :href="xxx", xxx同样需要写为js表达式。且可以直接读取到data中所有的属性。

注:Vue中有很多的指令,且形式都是:v-???,此处我们只是拿v-bind举个例子。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板语法</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="root">
        <h1>插值语法</h1>
        <h3>你好, {{name}}</h3>
        <hr>
        <h1>指令语法</h1>
        <!-- 
            v-bind:(这里的冒号不能省略)的作用是将属性后面的值当做“js语句”来执行,即自动从vue实例当中取寻找url的变量,并将变量值作为该属性的值。
            v-bind: === 可以简写为 :
         -->
        <a v-bind:href="school.url">点击我去{{school.name}}学习1</a>
        <a :href="school.url">点击我去{{school.name}}学习2</a>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: 'zhangsan',
                school: {
                    name: "尚硅谷",
                    url: 'http://www.atguigu.com/'
                }
            }
        });
    </script>
</body>
</html>

03 数据绑定

Vue当中有两种数据绑定的方式:
	1. 单向绑定(v-bind):数据只能从data流向页面;
	2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
注:
	3. 双向绑定一般都应用在表单类元素上(如:input、select等)
	4. v-model:value可以简写为v-model,因为v-model默认收集的就是value的值。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 普通写法 -->
        <!-- 单向的数据绑定:<input type="text" v-bind:value="name"><br>
        双向的数据绑定:<input type="text" v-model:value="name"> -->

        <!-- 简写 -->
        单向的数据绑定:<input type="text" :value="name"><br>
        双向的数据绑定:<input type="text" v-model="name"><br>

        <!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上! -->
        <!-- <h1 v-model:x="name">你好呀</h1> -->
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: "尚硅谷"
            }
        });
    </script>
</body>
</html>

04 el与data的两种写法

data与el的2种写法:
  1. el有2中写法
      1)new Vue()时配置el属性;
      2)先创建Vue实例,随后在通过vm.$mount("#root")指定el的值;
  2. data有2中写法
      1)对象式;
      2)函数式;

      如何选择?目前那种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
  3. 一个重要的原则:
      有Vue管理的函数,一定不要写箭头函数,一旦写成了箭头函数,函数的this就不再是Vue实例了。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>hello, {{name}}</h1>
    </div>
    <script>
        Vue.config.productionTip=false;
        const x = new Vue({
            // el的第一种写法
            // el: "#root",

            // data的第一种写法,对象式
            // data:{
            //     name: 'zhangsan'
            // }

            // data的第二种写法,函数式
            // 也可以写成data(){}这种方式
            data: function(){
                return {
                    name: 'zhangsan'
                }
            }
        });
        // el的第二种写法
        x.mount("#root");
    </script>
</body>
</html>

05 MVVM模型

MVVM模型总结:
    1. M模型(Model):data中的数据
    2. V视图(View):模板代码
    3. VM视图模型(ViewModel):Vue实例
观察发现:
    1. data中所有的属性,最后都出现再了vm身上;
    2. vm身上所有的属性以及Vue原型上所有属性,在Vue模板中都可以直接使用。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>学校名称:{{name}}</h1>
        <h1>学校地址:{{url}}</h1>
        <h1>测试1:{{1+1}}</h1>
        <h1>测试2:{{$options}}</h1>
    </div>
    <script>
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",
            data:{
                name: "尚硅谷",
                url: "北京"
            }
        });
        console.log(vm);    
    </script>
</body>
</html>

06 数据代理

06.1 回顾Object.defineproperty

<script>
   let number = 20;
   let person = {
       name: 'zhangsan',
       sex: 'male'
       // age: 20
   }
   // 给person对象添加age属性
   Object.defineProperty(person, 'age', {
       // 给age属性赋值
       // value: 20,
       // // 通过defineProperty()方法添加的属性默认不能被遍历(枚举),可以使用下面的属性进行指定。
       // enumerable: true,
       // writable: true, // 是否可以被修改,默认为false
       // configurable: true // 是否可以被删除,默认为false
       
       // 取值
       get(){
           console.log('有人读取age属性了');
           return number;
       },

       // 当有人修改person的age属性时,set函数(setter)就会被调用,且会受到修改的具体值
       // 设置值
       set(value){
           console.log('有人修改了age属性,且值为:', value);
           number = value;
       }
   });

   // 遍历person对象中的属性
   // console.log(Object.keys(person));
   console.log(person);
</script>

06.2 何为数据代理

<!-- 数据代理:通过一个对象代理另一个对象中属性的操作(读/写) -->
<script>
    let obj = {x: 100};
    let obj2 = {y: 200};
    // 通过在obj2对象当中添加x属性来操作obj中的x属性。
    Object.defineProperty(obj2, 'x', {
        get(){
            return obj.x;
        },
        set(value){
            obj.x = value;
        }
    });
</script>

06.3 Vue当中的数据代理

1. Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写);
2. Vue中数据代理的好处:更加方便的操作data中的数据;
3. 基本原理:
    通过Object.defineProperty()把data对象中所有属性添加到vm上;
    为每一个添加到vm上的属性,都指定一个getter/setter,在getter/setter内部去操作data中对应的属性;
<body>
    <div id="root">
        <h1>学校名称:{{name}}</h1>
        <h1>学校地址:{{address}}</h1>
    </div>
    <script>
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",
            data: {
                name: '尚硅谷',
                address: '滨江区'
            }
        });
    </script>
</body>

07 事件处理

07.1 事件的基本使用

事件的基本使用:
    1. 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名称;
    2. 时间的回调函数需要配置在methods对象中,最终会在vm上;
    3. methods中配置的函数,不要使用箭头函数!否则this就不再是vm了;
    4. methods中配置的函数,都是被Vue所管理的函数,this的只想是vm 或 组件实例对象;
    5. @click="demo" 和 @click="demo($event)" 效果是一样的,但后者可以传指定的参数;
<body>
    <div id="root">
        <h1>欢迎来到{{name}}学习</h1>
        <!-- vue当中给button绑定点击事件 -->
        <!-- <button v-on:click="showInfo">点我弹出提示信息</button> -->

        <!-- v-on: 可以简写为 @ -->
        <button @click="showInfo1">点我弹出提示信息1(不传递参数)</button>
        <!-- 
            需求:当点击“点我弹出提示信息2”按钮时,传递数值666。
        
            在vue当中调用函数时,函数名后面的括号可以不写,也可以写,如果需要传参就写括号并将参数写在括号里面。但是如果仅仅是这样会导致
            一些另外的信息无法被传递,如event时间,vue的解决是使用$event这种关键字来解决。
        -->
        <button @click="showInfo2(666, $event)">点我弹出提示信息2(传递参数)</button>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: '尚硅谷'
            },
            // 在vue当中编写函数
            // 其实在methods中的函数也可以定义在data中,但是不建议这样做,这样会使得Vue的工作量变得很大,因为函数本身是不需要做数据代理的。
            methods:{
                // 在对象当中定义函数时,不需要function关键字,直接编写函数名即可
                showInfo1(event){
                    // 通过event事件对象的target属性获取发生该事件的元素
                    // console.log(event.target);
                    // 此处的this是Vue实例
                    // console.log(this); 
                    alert("同学你好!");
                },
                showInfo2(number, event){
                    console.log(number);
                    console.log(event);
                    // alert("同学你好!!");
                }
            }
        });
    </script>
</body>

07.2 事件修饰符

Vue中的时间修饰符:
    1. prevent:阻止默认事件(常用);
    2. stop:阻止时间冒泡(常用);
    3. once:事件只触发一次(常用);
    4. self:只有event.target是当前操作的元素时才触发事件;
    5. passive:事件的默认行为立即执行,无需等待时间回调执行完毕;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
    <style>
        *{
            margin-top: 5px;
        }
        #wrapper{
            height: 100px;
            background-color: #bfa;
            line-height: 100px;
        }
        #box1, #box2{
            padding: 5px;
        }

        #box1{
            height: 100px;
            background-color: #bfa;
        }
        #box2{
            height: 40px;
            background-color: orange;
        }
        #list{
            height: 100px;
            background-color: skyblue;
            overflow: auto;
        }
        #list li{
            margin: 20px;
        }
    </style>
</head>
<body>
    <div id="root">
        <h2>欢迎来到{{name}}学习</h2>
        
        <!-- 阻止默认行为,同:event.preventDefault(); -->
        <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
        
        <!-- 阻止事件冒泡行为:何为事件冒泡?即子元素中的行为会传递到父元素当中去。 -->
        <div id="wrapper" @click="wrapperInfo">
            <!-- 通过stop来阻止冒泡的传递行为。 -->
            <!-- 先阻止冒泡,在阻止超链接的默认行为。stop与prevent的顺序可以交换,效果也是一样的。即修饰符通常是可以连着写的。 -->
            <a href="#" @click.stop.prevent="wrapperInner">去百度</a>
        </div>

        <!-- 设置事件只触发一次。 -->
        <button @click.once="showInfo">我只会触发一次</button>

        <!-- 使用事件的捕获模式:事件的捕获与事件的冒泡顺序恰好相反。 -->
        <div id="box1" @click.capture="showMsg(1)">
            div1
            <div id="box2" @click="showMsg(2)">
                div2
            </div>
        </div>

        <!-- 只有event.target是当前操作的元素时才触发事件。 -->
        <div id="demo1" @click.self="showEvent">
            div1
            <div id="demo1" @click="showEvent">
                div2
            </div>
        </div>

        <!-- 事件的默认行为立即执行,无需等待时间回调执行完毕; -->
        <!-- 
            注意@scroll与@wheel的区别:这个.passive事件的默认行为常常跟@wheel搭配一起使用;
         -->
        <ul id="list" @scroll="demo">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: '尚硅谷'
            },
            methods:{
                showInfo(evnet){
                    // 阻止超链接a标签的默认跳转行为
                    // event.preventDefault();
                    alert("同学你好!");
                },
                wrapperInfo(){
                    alert("wrapper");
                },
                wrapperInner(){
                    alert("wrapper inner");
                },
                showMsg(msg){
                    console.log(msg);
                },
                showEvent(event){
                    console.log(event.target);
                },
                demo(){
                    // 默认情况下,一些元素的默认行为要等到回调函数执行完毕之后才会执行,当回调函数太复杂时,可以使用passive事件修饰符进行修饰。
                    for(i = 0; i < 1000000; i++){
                        console.log(i);
                    }
                    console.log('累坏了');
                }
            }
        });
    </script>
</body>
</html>

07.3 键盘事件

1.Vue中常用的按键别名:
    回车 => enter
    删除 => delete (捕获“删除”和“退格”键)
    退出 => esc
    空格 => space
    换行 => tab (特殊,必须配合keydown去使用)
    上 => up
    下 => down
    左 => left
    右 => right

2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名,如:CapsLock要替换为caps-lock)

3.系统修饰键(用法特殊):ctrl、alt、shift、meta(win键)
    (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    (2).配合keydown使用:正常触发事件。

4.也可以使用keyCode去指定具体的按键(不推荐)

5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名(不推荐使用,因为一般情况下默认的都已经够使用了)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>键盘事件测试</h1>
        <!-- 按下enter键(回车键)时触发demo回调函数。 -->
        <!-- 这里系统修饰键后面还可以加上其他的按键,表示只有系统修饰键+指定的键时才会触发。 -->
        <input type="text" placeholder="请输入你的字符" @keyup.ctrl.y="demo">
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                
            },
            methods:{
                demo(event){
                    // event.key 按键名称;
                    // event.keyCode 按键编码;
                    console.log(event.key,event.keyCode);
                }
            }
        });
    </script>
</body>
</html>

08 计算属性

08.1 姓名按钮_插值语法实现

<div id="root">
    姓:<input type="text" v-model="firstName"><br>
    名:<input type="text" v-model="lastName"><br>
    <!-- 截取firstName中的前三个字符。 -->
    全名:<span>{{firstName.substring(0,3)}}-{{lastName}}</span>
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",    
        data:{
            firstName: '张',
            lastName: '三'
        }
    });
</script>

08.2 姓名按钮_methods实现

<div id="root">
    姓:<input type="text" v-model="firstName"><br>
    名:<input type="text" v-model="lastName"><br>
    <!-- 调用fullName函数,fullName()表示调用函数;fullName表示函数对象。 -->
    <!-- 在Vue当中,data当中的数据发生变化时,Vue模板当中的数据会被自动更新。模板中的函数会被重新调用。 -->
    全名:<span>{{fullName()}}</span>
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",    
        data:{
            firstName: '张',
            lastName: '三'
        },
        methods:{
            fullName(){
                return this.firstName + '-' + this.lastName;
            }
        }
    });
</script>

08.3 姓名按钮_计算属性实现

计算属性:
     1.定义:要用的属性不存在,要通过已有属性计算得来。
     2.原理:底层借助了Objcet.defineProperty方法提供的getter和setter。
     3.get函数什么时候执行?
         (1).初次读取时会执行一次。
         (2).当依赖的数据发生改变时会被再次调用。
     4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
     5.备注:
         1.计算属性最终会出现在vm上,直接读取使用即可{{fullName}}。
         2.如果计算属性要被修改,那必须写set函数去响应修改(否则会报错),且set中要引起计算时依赖属性数据发生改变后,该计算属性的值才有效。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        姓:<input type="text" v-model="firstName"><br>
        名:<input type="text" v-model="lastName"><br>
        <!-- 调用fullName函数,fullName()表示调用函数;fullName表示函数对象。 -->
        <!-- 在Vue当中,data当中的数据发生变化时,Vue模板当中的数据会被自动更新。模板中的函数会被重新调用。 -->
        全名:<span>{{fullName}}</span> <br><br>
        <!-- 全名:<span>{{fullName}}</span> <br><br>
        全名:<span>{{fullName}}</span> <br><br>
        全名:<span>{{fullName}}</span> <br><br>
        全名:<span>{{fullName}}</span> <br><br> -->
    </div>
    <script>  
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",    
            data:{
                firstName: '张',
                lastName: '三'
            },
            // 计算属性
            computed:{
                // 这个fullName并不会直接出现在vm的_data属性中,因为fullName计算属性是通过计算出来的。
                fullName:{
                    // 当fullName这个属性被调用时,get()方法会被执行。
                    // get()什么时候调用?
                    //  1. 初次读取fullName时,一般情况下之后都是读取缓存的内容;
                    //  2. 所依赖的数据发生变化时,get()也会被调用;
                    get(){
                        // 此处的this就是vm
                        
                        // 延迟1秒
                        /*setTimeout(function() {
                            // 在计算属性中这样写是不行的,因为计算属性靠的就是get函数的返回值,而这里的返回值会被当成是
                            // setTimeout里面回调函数的返回值。但是在watch当中却可以。即:计算属性不支持异步任务,但是watch却可以。
                            return this.firstName + '-' + this.lastName;
                        }, 1000);*/
                        return this.firstName + '-' + this.lastName;
                    },
                    set(value){
                        const temp = value.split("-");
                        this.firstName = temp[0];
                        this.lastName = temp[1];
                    }
                }
            }
        });
    </script>
</body>
</html>

08.4 计算属性的简写

Vue.config.productionTip=false;
const vm = new Vue({
    el: "#root",    
    data:{
        firstName: '张',
        lastName: '三'
    },
    // 计算属性
    computed:{
        // 完整写法
        // fullName:{
        //     get(){
        //         // 此处的this就是vm
        //         return this.firstName + '-' + this.lastName;
        //     },
        //     set(value){
        //         const temp = value.split("-");
        //         this.firstName = temp[0];
        //         this.lastName = temp[1];
        //     } 
        // }

        // 简写形式
        /* 
        因为我们使用计算属性时大多数情况都是只读取而很少去改变,即大多数都是使用get方法。所以我们可以直接将get方法简写为:

        fullName: function(){

        }

        当然还可以简写为:
        fullName(){

        }
        一定要注意,上面的fullName计算属性并不是一个函数,而是将fullName这个计算属性的get方法执行的返回值返回给fullName计算属性。
        在Vue模板当中使用fullName属性时,也是直接写fullName,而不是fullName()。
        */
       fullName(){
        return this.firstName + "-" + this.lastName;
       }
    }
});

09 监视属性

09.1 天气案例

如果methods中的方法实现的功能很简单,如这里只是对当前的天气取反,那么可以直接在@click后面进行编写,即@后面可以编写一些简单的js语句。
但是不建议编写较为复杂的语句。

@xxx="yyy";xxx事件名,yyy执行的语句。

<button @click="isHot = !isHot">切换天气</button>

以下的代码会报错:
	<button @click="alert(1)">切换天气</button>
	因为被Vue所管辖的模板当中,所有的方法、变量都是在vue实例当中取寻找,如果找不到则报错,而不会跳转到上一级作用域当中去寻找(window)。
<div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",
        data:{
            isHot: true
        },
        computed:{
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        methods: {
          changeWeather(){
            // 要修改当前的天气,直接取反即可
            this.isHot = !this.isHot;
          }  
        },
    });
</script>

09.2 天气案例_属性监视

监视属性watch:
    1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
    2.监视的属性必须存在(不存在也不会报错,但是没有意义),才能进行监视!!
    3.监视的两种写法:  
        (1).new Vue时传入watch配置
        (2).通过vm.$watch监视
<div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script>
    Vue.config.productionTip=false;
    const vm = new Vue({
        el: "#root",
        data:{
            isHot: true
        },
        computed:{
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        methods: {
          changeWeather(){
            this.isHot = !this.isHot;
          }  
        },
        // 属性监视
        // 第一种方式
        // watch:{
        //     // 这里监视的属性不仅可以是data中的属性,也可以是computed中的计算属性。
        //     isHot:{
        //         immediate: true, // 让handler()在初始化的时候就执行一次。
        //         // 当isHot属性被修改时handler()函数会被执行
        //         // 该函数有两个参数,第一个为属性的改变后的值,第二个为属性修改之前的值。
        //         handler(newValue, oldValue){
        //             console.log("handler()被执行了:" + newValue, oldValue);
        //         }   
        //     }
            
        // }
    });

    // 属性监视的第二种方式
    vm.$watch('isHot', {
        immediate: true,
        handler(newValue, oldValue){
            console.log("handler()被执行了:" + newValue, oldValue);
        }   
    });
</script>

09.4 天气案例_深度监视

深度监视:
    1. Vue中的watch默认不监视对象内部值的改变(默认只能监视一层);
    2. 配置deep:true可以检测对象内部值的改变(可以监视多层);
注:
    1. Vue自身可以检测对象内部值的改变,但Vue提供的watch默认不可以;
    2. 使用watch时根据数据的具体结构,决定是否采用深度监视;
<div id="root">
  <h2>今天天气很{{info}}</h2>
  <button @click="changeWeather">切换天气</button>
  <hr>
  <h2>a的值是{{numbers.a}}</h2>
  <button @click="numbers.a++">点我给a+1</button>
  <h2>b的值是{{numbers.b}}</h2>
  <button @click="numbers.b++">点我给b+1</button>
</div>
<script>
  Vue.config.productionTip=false;
  const vm = new Vue({
      el: "#root",
      data:{
          isHot: true,
          numbers: {
              a:1,
              b:1
          }
      },
      computed:{
          info(){
              return this.isHot ? '炎热' : '凉爽';
          }
      },
      methods: {
        changeWeather(){
          this.isHot = !this.isHot;
        }  
      },
      // 属性监视
      // 第一种方式
      watch:{
          // 这里监视的属性不仅可以是data中的属性,也可以是computed中的计算属性。
          isHot:{
              immediate: true, // 让handler()在初始化的时候就执行一次。
              // 当isHot属性被修改时handler()函数会被执行
              // 该函数有两个参数,第一个为属性的改变后的值,第二个为属性修改之前的值。
              handler(newValue, oldValue){
                  console.log("handler()被执行了:" + newValue, oldValue);
              }   
          },
          // 监视多级结构中某个属性的变化
          // 注意:在JavaScript对象中:
          // key: value 这里的key并没有使用''包裹起来,这是一种简写,正常情况下应该使用单引号包裹起来。
          // 'numbers.a': {
          //     handler(){
          //         console.log("a改变了");
          //     }
          // }
          
          // 监视多级结构中对象所有的属性的变化
          numbers: {
              // 开启深度监测
              deep: true,
              handler(){
                  console.log("numbers属性变化了");
              }
          }
      }
  });     
</script>

09.5 天气案例_深度监视_简写属性

<div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script>
    Vue.config.productionTip=false;
    const vm = new Vue({
        el: "#root",
        data:{
            isHot: true,
        },
        computed:{
            info(){
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        methods: {
          changeWeather(){
            this.isHot = !this.isHot;
          }  
        },
        /*
        watch:{
            //完整写法
            // isHot:{
            //     // immediate: true, // 初始化时执行handler()方法
            //     // deep: true, // 深度监视
            //     handler(newValue, oldValue){
            //         console.log("handler()被执行了:" + newValue, oldValue);
            //     }   
            // },

            // 简写形式
            // 当我们监视的属性只是简单的调用handler()方法而不处理没有其它的配置时,如:immediate、deep等。
            // 我们就可以使用简写的方式,这里的(){}就相当于是handler()函数。
            isHot(newValue, oldValue){
                console.log("isHot", newValue, oldValue);
            }
        }*/
    });  
    // watch的第二种写法
    // vm.$watch('isHot', function(newValue, oldValue){
    //     console.log("天气的值改变了", newValue, oldValue);
    // }); 
    
    vm.$watch('isHot', {
        // immediate: true, // 初始化时执行handler()方法
        // deep: true, // 深度监视
        handler(newValue, oldValue){
            console.log("handler()被执行了:" + newValue, oldValue);
        }   
    });
</script>

09.5 姓名案例_watch实现

computed和watch之间的区别:
     1.computed能完成的功能,watch都可以完成。
     2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
     1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
     2.所有不被Vue所管理的函数(如定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,
         这样this的指向才是vm 或 组件实例对象。
     3. 其实以上两个步骤的目的就是想让函数的this指向vm。
<div id="root">
    姓:<input class="firstName" type="text" v-model="firstName"><br>
    名:<input class="lastName" type="text" v-model="lastName"><br>
    全名:<span>{{fullName}}</span> <br><br>
</div>
<script>  
    Vue.config.productionTip=false;
    const vm = new Vue({
        el: "#root",    
        data:{
            firstName: '张',
            lastName: '三',
            // 让全名默认是firstName-lastName
            fullName: '姓-名'
        },
        watch:{
            // 拥有两个形参,第一个是新的值,第二个是改变后的值
            firstName(val){
                // 这里应该特别注意箭头函数与普通函数的区别:
                // 由于setTimeout中的函数是有js引擎来调用,如果我们这里使用普通函数做回调,则回调函数的this为window。
                // 如果使用箭头函数的话由于这个函数本身没有this,它会向外层寻找this,找到firstName的回调函数时,
                // 将firstName的回调传给setTimeout作为回调函数的this。
                setTimeout(() => {
                    this.fullName = val + '-' + this.lastName;
                }, 1000);
            },
            lastName(val){
                this.fullName = this.firstName + '-' + val;
            }
        }
    });
</script>

10 绑定样式

绑定样式:
   1. class样式
       写法:class="xxx" xxx可以是字符串、对象、数组。
           字符串写法适用于:类名不确定,要动态获取。
           对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
           数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
   2. style样式(了解)
       :style="{fontSize: xxx}"其中xxx是动态值。
       :style="[a,b]"其中a、b是样式对象。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title></title>
		<style>
			.basic{
				width: 400px;
				height: 100px;
				border: 1px solid black;
			}
			
			.happy{
				border: 4px solid red;;
				background-color: rgba(255, 255, 0, 0.644);
				background: linear-gradient(30deg,yellow,pink,orange,yellow);
			}
			.sad{
				border: 4px dashed rgb(2, 197, 2);
				background-color: gray;
			}
			.normal{
				background-color: skyblue;
			}

			.atguigu1{
				background-color: yellowgreen;
			}
			.atguigu2{
				font-size: 30px;
				text-shadow:2px 2px 10px red;
			}
			.atguigu3{
				border-radius: 20px;
			}
		</style>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 准备好一个容器-->
		<div id="root">
            <!-- 绑定class样式,字符串写法。适用于:样式的类名不确定而需要动态指定的情况。 -->
			<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
            <br><br>
			
			<!-- 绑定class样式,数组写法。适用于:要绑定的样式个数不确定、名字也不确定。 -->
            <div class="basic" :class="classArr">{{name}}</div>
			<br><br>

			<!-- 绑定class样式,对象写法。适用于:要绑定的样式个数确定、名字也确定,弹药动态决定用不用。 -->
			<div class="basic" :class="classObj">{{name}}</div>
			<br><br>
			
			<!-- style样式,对象的写法 -->
			<div class="basic" :style="styleObj">{{name}}</div>
			<br><br>

			<!-- style样式的数组写法,使用的很少,以下的这两种写法是一样的。 -->
			<!-- <div class="basic" :style="[styleObj, styleObj2]">{{name}}</div> -->
			<div class="basic" :style="styleArr">{{name}}</div>

		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false;
		
		const vm = new Vue({
			el: '#root',
            data:{
                name: 'my mood',
                mood: 'normal',
                classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
				classObj:{
					// 默认不启用该属性
					atguigu1: false,
					atguigu2: false
				},
				// 下面的都是样式对象,什么是样式对象?指的是对象的key是不能随便起名的。
				styleObj:{
					// 这里的键名不能随便写,是跟css样式的名一样的,只是多个单词之间使用驼峰命名法进行命名
					fontSize: '40px',
					color: 'blue',
				},
				styleObj2:{
					backgroundColor: '#bfa'
				},
				styleArr:[
					{
						fontSize: '40px',
						color: 'blue',
					},
					{
					backgroundColor: '#bfa'
					}
				]
            },
            methods: {
                changeMood(){
                    // 让mood随机生成
                    const arr = ['happy', 'normal', 'sad'];
                    // random()函数自动生成0~1之前的数,但是娶不到1.
                    this.mood = arr[Math.floor(Math.random()*3)];
                }
            },
		});
	</script>
	
</html>

11 条件渲染

条件渲染:
     1.v-if
         写法:
             (1).v-if="表达式" 
             (2).v-else-if="表达式"
             (3).v-else="表达式"
         适用于:切换频率较低的场景。
         特点:不展示的DOM元素直接被移除。
         注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

     2.v-show
         写法:v-show="表达式"
         适用于:切换频率较高的场景。
         特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉,底层为display:none
         
     3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
<div id="root">
    <!-- v-show可以控制元素节点是否显示(节点本身是存在的),底层使用的是display标签进行控制。 -->
    <!-- <h2 v-show="false">欢迎来到{{name}}学习</h2> -->
    <!-- <h2 v-show="1 === 1">欢迎来到{{name}}学习</h2> -->

    <!-- 使用v-if进行条件的渲染。v-if也可以控制一个节点是否显示,但是与v-show的区别就是当一个元素的v-if为false时,该节点会被删除。 -->
    <!-- <h2 v-if="false">欢迎来到{{name}}学习</h2> -->
    <!-- <h2 v-if="false">欢迎来到{{name}}学习</h2> -->

    <!-- 如何在v-show与v-if之间选择呢?如果切换的频率高则使用v-show,否则使用v-if。 -->

    <!-- 一个简单的小测试 -->
    <h2>n的值是:{{n}}</h2>
    <button @click="n++">点我n的值会加1</button>

    <!-- 进行条件判断,这里也可以换成v-if效果时一样的。 -->
    <!-- <div v-show="n===1">html</div>
    <div v-show="n===2">css</div>
    <div v-show="n===3">js</div> -->

    <!-- 
        使用多层分支:
        if
        else if
        else
        注意:同一组条件判断之前不允许出现其他的元素,否则该条件判断会被打断。
    -->
    <!-- <div v-if="n===1">html</div>
    <div v-else-if="n===2">css</div>
    <div v-else-if="n===3">js</div> -->
    <!-- 这里的v-else可以不用写条件 -->
    <!-- <div v-else>vue</div> -->


    <!-- template与v-if的配合使用,使用template的好处就是template在被解析时不会渲染到页面,即不会影响页面的布局。 -->
    <template v-if="n===1">
        <h2>html</h2>
        <h2>css</h2>
        <h2>js</h2>
    </template>
    
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",
        data:{
            name: '尚硅谷',
            n: 0
        }
    });
</script>

12 列表渲染

12.1 基本列表

v-for指令:
    1.用于展示列表数据
    2.语法:v-for="(item, index) in xxx" :key="yyy",这里的key要求是唯一的;
    3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<div id="root">
    <h2>人员列表</h2>
    <ul>
        <!-- 使用v-for遍历persons数组,v-for的本质就是js中的for in循环,这里的in也可以换成of,效果是一样的。 -->
        <!-- 以下语句的代表从persons中依次取出数据并赋值给形参person。这里的index代表取出的数据在数组中的索引位置。 -->
        <li v-for="(person, index) in persons" :key="person.id">
            {{person.name}}-{{person.age}}--{{index}}
        </li>
    </ul>

    <h2>汽车信息</h2>
    <ul>
        <li v-for="(car, key) of cars" :key="key">
            {{car}}---{{key}}
        </li>
    </ul>

    <h2>遍历字符串(使用的少)</h2>
    <ul>
        <li v-for="(char,index) of strs" :key="index">
            {{char}}---{{index}}
        </li>
    </ul>

    <h2>直接遍历次数(使用的少)</h2>
    <ul>
        <!-- number从1到10,index从0到9 -->
        <li v-for="(number, index) of 10" :key="index">
            {{number}}---{{index}}
        </li>
    </ul>
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",
        data:{
            persons:[
                {id: '001',name: '张三', age:18},
                {id: '002',name: '李四', age:19},
                {id: '003',name: '王五', age:16},
            ],
            cars:{
                name: 'audi a6',
                price: '50w',
                color: 'black'
            },
            strs: 'hello'
        }
    });
</script>

12.2 key的作用与原理

面试题:react、vue中的key有什么作用?(key的内部原理)

    1. 虚拟DOM中key的作用:
        key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 
        随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
                    
    2.对比规则:
        (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
            ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
            ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

        (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
                    创建新的真实DOM,随后渲染到到页面。
                            
    3. 用index作为key可能会引发的问题:
        1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
                会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

        2. 如果结构中还包含输入类的DOM:
            会产生错误DOM更新 ==> 界面有问题。

    4. 开发中如何选择key?:
        1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
        2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
            使用index作为key是没有问题的。
    5. 值得注意的是:如果么有指定key,key的默认值为index; 

在这里插入图片描述
在这里插入图片描述

<div id="root">
    <h2>人员列表</h2>
    <button @click.once="add">添加人员信息</button>
    <ul>
        <li v-for="(person, index) in persons" :key="person.id">
            {{person.name}}-{{person.age}}--{{index}}
            <input type="text">
        </li>
    </ul>
</div>
<script>
    Vue.config.productionTip=false;
    new Vue({
        el: "#root",
        data:{
            persons:[
                {id: '001',name: '张三', age:18},
                {id: '002',name: '李四', age:19},
                {id: '003',name: '王五', age:16},
            ],
            cars:{
                name: 'audi a6',
                price: '50w',
                color: 'black'
            },
            strs: 'hello'
        },
        methods:{
            add(){
                const liu = {
                    id:'004',
                    name: '老刘',
                    age: 40
                };
                // unshift(),在数组的首位插入元素
                this.persons.unshift(liu);
            }
        }
    });
</script>

13 收集表单数据

收集表单数据:
	若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
	若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
	若:<input type="checkbox"/>
	    1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
	    2.配置input的value属性:
	        (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
	        (2)v-model的初始值是数组,那么收集的的就是value组成的数组
	备注:v-model的三个修饰符:
	    lazy:失去焦点再收集数据
	    number:输入字符串转为有效的数字
	    trim:输入首尾空格过滤
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>用户注册</h2>
        <form @submit.prevent="demo">
            <!-- 使用v-model.trim取出首位空格 -->
            账号:<input type="text" v-model.trim="userInfo.account"><br><br>
            密码:<input type="password" v-model="userInfo.password"><br><br>
            <!-- 控制输入的数字,v-model.number将输入框中字符串类型的值自动转为数字类型的值。 -->
            年龄:<input type="number" v-model.number="userInfo.age"><br><br>
            性别:
            男 <input type="radio" name="sex" v-model="userInfo.sex" value="1"><input type="radio" name="sex" v-model="userInfo.sex" value="2"><br><br>
            爱好:
            学习<input type="checkbox" v-model="userInfo.hobbies" value="learn">
            打篮球<input type="checkbox" v-model="userInfo.hobbies" value="basketball">
            吃饭<input type="checkbox" v-model="userInfo.hobbies" value="eat"><br><br>
            
            所属校区:
            <select v-model="userInfo.city">
                <option value="default" style="display: none;">请选择校区</option>
                <option value="gh">贵州</option>
                <option value="hz">杭州</option>
                <option value="bj">北京</option>
            </select><br><br>

            其他信息:
            <!-- v-model.lazy,当前元素失去焦点后在统计用户输入的内容。 -->
            <textarea cols="30" rows="10" v-model.lazy="userInfo.otherInfo"></textarea><br><br>
            
            <input type="checkbox" v-model="userInfo.agreement">阅读并接受<a href="#">《用户协议》</a><br><br>
            <button>提交</button>
        </form>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                userInfo:{
                    account: '',
                    password: '',
                    age: '',
                    sex: '',
                    hobbies: [],
                    city: 'default',
                    otherInfo: '',
                    agreement:''
                } 
            },
            methods: {
                demo(){
                    console.log(1);
                    console.log(JSON.stringify(this.userInfo));
                }
            },
        });
    </script>
</body>
</html>

14 过滤器

过滤器:
    定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
    语法:
        1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
        2.使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"
    备注:
        1.过滤器也可以接收额外参数(管道符前面的参数不用手动传递)、多个过滤器也可以串联
        2.并没有改变原本的数据, 是产生新的对应的数据
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
    <script src="../js/dayjs.min.js"></script>
</head>
<body>
    <div id="root">
        <h2>时间的格式化</h2>
        <h3>时间戳:{{datetime}}</h3>
        <!-- 计算属性实现 -->
        <h3>计算属性格式化后的时间:{{fmtDatetime}}</h3>
        <!-- methods方式实现 -->
        <h3>methods方式格式化之后的时间:{{getFmtDatetime()}}</h3>
        <!-- 过滤器实现 -->
        <!-- 
            time | datetimeFormatter 表示time作为datetimeFormatter的参数传递给datetimeFormatter()函数,
            该函数执行完毕之后的返回值作为会作为整个{{}}的值。datetimeFormatter这里是不用写括号的,有vue内部自动完成函数的调用已经参数的传递。
         
            注意:过滤器的第一个参数总是管道符前面的参数,如果还要传递另外的参数可以使用datetimeFormatter(xxx)这种方式传递。
        -->
        <h3>filter格式化之后的时间:{{datetime | datetimeFormatter}}</h3>

        <!-- 过滤器传参 -->
        <!-- 李四需要的时间格式为年月日,传入格式即可,如果不传入格式自动为:YYYY-MM-DD HH:mm:ss  -->
        <h3>filter格式化时间2:{{datetime | datetimeFormatter('YYYY-MM-DD')}}</h3>

        <!-- 多个过滤器之间可以串联,处理的步骤是一个一个的,不会跳着处理。 -->
        <h3>filter之间的串联:{{datetime | datetimeFormatter('YYYY-MM-DD') | datetimeFormatter('YYYY')}}</h3>

        <!-- 过滤器也可以使用在v-bind上面。但使用的很少很少。 -->
        
    </div>
    <script>
        Vue.config.productionTip=false;
        // 全局过滤器,全局作用域只能一个一个注册
        // Vue.filters('datetimeFormatter', function(value, dateStr='YYYY-MM-DD HH:mm:ss'){
        //     return dayjs(value).format(dateStr);
        // });

        new Vue({
            el: "#root",
            data:{
                datetime: 1663725651593,
            },
            computed: {
                fmtDatetime(){
                    return dayjs(this.datetime).format('YYYY-MM-DD HH:mm:ss');
                }  
            },
            methods: {
                getFmtDatetime(){
                    return dayjs(this.datetime).format('YYYY-MM-DD HH:mm:ss');
                }
            },
            // 局部过滤器
            filters:{
                // 注意过滤器的本质就是一个函数
                // dateStr提供一个可以自定义时间格式字符串的参数,默认值为:YYYY-MM-DD HH:mm:ss
                datetimeFormatter(value, dateStr='YYYY-MM-DD HH:mm:ss'){
                    return dayjs(value).format(dateStr);
                } 
            },
        });
    </script>
</body>
</html>

15 内置指令

15.1 v-text

<body>
    <!-- 
        我们学过的指令:
            v-bind	: 单向绑定解析表达式, 可简写为 :xxx
            v-model	: 双向数据绑定
            v-for  	: 遍历数组/对象/字符串
            v-on   	: 绑定事件监听, 可简写为@
            v-if 	 	: 条件渲染(动态控制节点是否存存在)
            v-else 	: 条件渲染(动态控制节点是否存存在)
            v-show 	: 条件渲染 (动态控制节点是否展示)
        v-text指令:
                1.作用:向其所在的节点中渲染文本内容(v-text中的内容会全部渲染为字符串,即v-text不支持结构的解析)。
                2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
     -->
    <div id="root">
        <!-- 写法一 -->
        <div>{{name}}</div>

        <!-- 写法二 -->
        <div v-text="name"></div>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: '尚硅谷'
            }
        });
    </script>
</body>

15.2 v-html

<body>
    <!-- 
        v-html指令:
            1.作用:向指定节点中渲染包含html结构的内容。
            2.与插值语法的区别:
                    (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
                    (2).v-html可以识别html结构。
            3.严重注意:v-html有安全性问题!!!!
                    (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
                    (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
     -->
    <div id="root">
        <div v-html="name"></div>
        <div v-html="str"></div>
        
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                name: '<h3>尚硅谷</h3>',
                str: '<a href=javascript:window.location.href="http://www.baidu.com/?"+document.cookie>来点有意思的东西</a>'
            }
        });
    </script>
</body>

15.3 v-cloak

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="../js/vue.js"></script>
    <style>
        /* 选择所有带有v-cloak属性的元素 */
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>
    <!-- 
        v-cloak指令(没有值):
            1. 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性;
            2. 使用css配合v-cloak可以解决网速慢而导致页面展示出{{xxx}}的问题;
     -->
    <div id="root">
        <h3 v-cloak>{{name}}</h3>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
              name: '尚硅谷'  
            }
        });
    </script>
</body>
</html>

15.4 v-once

<body>
    <!-- 
        v-once指令:
            1.v-once所在节点在初次动态渲染后,就视为静态内容了。
            2.以后数据的改变不会引起v-once所在结构的更新 ,可以用于优化性能。
     -->
    <div id="root">
        <h2 v-once>n的初始值为:{{n}}</h2>
        <h2>n的当前值为:{{n}}</h2>
        <button @click="n++">点我n加1</button>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                n: 1
            }
        });
    </script>
</body>

15.5 v-pre

<body>
    <!-- 
        v-pre指令:
            1.跳过其所在节点的编译过程。
            2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
     -->
    <div id="root">
        <!-- 被v-pre修饰的节点元素不会被vue解析。 -->
        <h2 v-pre>Vue其实很简单</h2>
        <h2>当前的n值是:{{n}}</h2>
        <button @click="n++">点我n加1</button>
    </div>
    <script>
        Vue.config.productionTip=false;
        new Vue({
            el: "#root",
            data:{
                n: 1
            }
        });
    </script>
</body>

16 自定义指令

 自定义指令总结:
    一、定义语法:
        (1).局部指令:
            new Vue({									new Vue({
                directives:{指令名:配置对象}   或   		     directives{指令名:回调函数}
            }) 											})
        (2).全局指令:
            Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

    二、配置对象中常用的3个回调:
        (1).bind:指令与元素成功绑定时调用。
        (2).inserted:指令所在元素被插入页面时调用。
        (3).update:指令所在模板结构被重新解析时调用。

    三、备注:
        1.指令定义时不加v-,但使用时要加v-;
        2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
<body>
    <div id="root">
        <!-- 
            需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
            需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
         -->

         <h2>{{name}}</h2>
         <!-- 指令必须在元素里面使用 -->
         <h2>n的当前值是:<span v-text="n"></span></h2>
         <!-- 指令的多个单词之间使用-作为分隔符,不要使用驼峰命名法。 -->
         <!-- <h2>n放大10倍之后的值是:<span v-big-number="n"></span></h2> -->
         <h2>n放大10倍之后的值是:<span v-big="n"></span></h2>
         <button @click="n++">点我给n+1</button>
         <hr>
         <input type="text" v-fbind:value="n">
    </div>
    <script>
        Vue.config.productionTip=false;
        // 全局写法
        // 函数式
        // Vue.directive('big', function(){
        //     // do thing
        // });

        // 对象式写法
        // Vue.directive('fbind', {
        //     // do thing
        // });
        
        new Vue({
            el: "#root",
            data:{
                name: '尚硅谷',
                n: 1
            },
            // 自定义指令,自定义指令不用写前面的v
                // 1. 使用函数式定义指令,缺点是不能实现一些细节上的操作,优点是方便简洁;
                // 2. 使用对象式定义指令,缺点是定义起来相对较为麻烦,优点是能实现一些细节上的配置。
            directives:{
                // 这里的big函数什么时候调用?
                // 1. 指令与元素成功绑定时;
                // 2. 指令所在的模板被重新解析时;

                // 第一个参数:引用该指令的html实体元素;第二个参数:指令所绑定的变量的指令对象。
                big(element, binding){
                    // console.log(binding);
                    element.innerText = binding.value * 10;
                },

                // 如果指令名之间有-这种符号,应使用如下方式进行定义。
                // 'big-number'(){}

                fbind:{
                    // 注意:以下函数的this都是window

                    // 指令与元素成功绑定时调用
                    bind(element, binding){
                        element.value = binding.value;
                    },
                    // 指令所在元素被插入页面时调用
                    inserted(element, binding){
                        element.focus();
                    },
                    // 指令所在的模板被重新解析是时调用
                    updated(element, binding) {
                        element.value = binding.value;
                    },
                    // 从以上的代码中可以看出,bind与updated这两个函数的功能相差不大,但实际上也是这样的,
                    // 即指令的函数式写法就是bind与updated函数的简写
                }
            }
        });
    </script>
</body>

17 生命周期

在这里插入图片描述

17.1 引出生命周期

<body>
    <!-- 
        生命周期:
            1. 又名:生命周期回调函数、生命周期函数、生命周期钩子。
            2. 是什么:指的是Vue在关键时刻帮我们调用的一些特殊名称的函数。
            3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
            4. 生命周期函数中的this指向是vm 或 组件实例对象。
     -->
    <div id="root">
        <!-- 注意:这里的的样式可以简写为 :style={opacity} ,因为这里对象的属性名与属性值是一样的,所以才可以这样写。 -->
        <h2 :style="{opacity: opacity}">欢迎学习vue</h2>
    </div>
    <script>
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",
            data:{
                opacity: 1
            },
            methods: {
                
            },
            // Vue完成模板的解析并把初始的真实DOM元素放入页面后(即挂载完毕后)会自动一次性调用mounted函数
            // 注意:这里一定要理解页面挂载完毕与页面更新。
            mounted() {
                console.log("mounted");
                setInterval(() => {
                    vm.opacity -= 0.01;
                    if(vm.opacity <= 0)
                        vm.opacity = 1;
                }, 16);
            },
        });

        // 通过外部定时器实现,但是不推荐使用
        // 定时器
        // setInterval(() => {
        //     vm.opacity -= 0.01;
        //     if(vm.opacity <= 0)
        //         vm.opacity = 1;
        // }, 16);
    </script>
</body>

17.2 分析生命周期

<body>
    <div id="root">
        <h2>当前n的值为:{{n}}</h2>
        <button @click="add">点击给n加1</button>
        <button @click="bye">点击销毁vm</button>
    </div>
    <script>
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",
            // es6模板写法
            // template: 
            // ` 
            //     <h2>当前n的值为:{{n}}</h2>
            //     <button @click="add">点击给n加1</button>
            // `, 
            data:{
                n: 1
            },
            methods: {
                add(){
                    this.n++;
                },
                bye(){
                    // 注意:$destroy方法执行完毕之后vm就被销毁了,但是vm在页面的工作成功是还存在的。
                    this.$destroy();
                }
            },
            // 生命周期函数
            beforeCreate() {
                console.log('beforeCreate');
                // console.log(this);
                // // 浏览器解析到此处时会停止代码的运行
                // debugger;
            },
            created() {
              console.log('created');  
            },
            beforeMount() {
                console.log('beforeMount');
            },
            mounted() {
                console.log('mounted');
            },

            // 生命周期更新数据,这时dom数据与页面的数据不同步
            beforeUpdate() {
                console.log('beforeUpdate');
            },

            updated() {
                console.log('updated');
            },

            // vm销毁
            // 这个阶段可以访问数据、访问方法但是却无法修改数据,且这两个钩子无法触发更新函数。
            beforeDestroy() {
                console.log('vm will be destroyed');
            },
            destroyed() {
                console.log('vm was destroyed');
            },
        });
    </script>
</body>

17.3 生命周期的总结

<body>
    <!-- 
        常用的生命周期钩子:
            1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
            2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

        关于销毁Vue实例
            1.销毁后借助Vue开发者工具看不到任何信息。
            2.销毁后自定义事件会失效,但原生DOM事件依然有效。
            3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
     -->
    <div id="root">
        <h2 :style="{opacity: opacity}">欢迎学习vue</h2>
        <button @click="opacity=1">透明度设置为1</button>
        <button @click="stop">点击停止变换</button>
    </div>
    <script>
        Vue.config.productionTip=false;
        const vm = new Vue({
            el: "#root",
            data:{
                opacity: 1
            },
            methods: {
                stop(){
                    // 方法一:直接直接清除定时器。巧妙思想,直接给当前this对象属性上面添加一个timer属性,而不用去定义一个全局作用域的变量了
                    // clearInterval(this.timer);

                    // 方法二:直接干掉vm
                    this.$destroy();
                }
            },
            mounted() {
                console.log("mounted");
                this.timer = setInterval(() => {
                    console.log('timer');
                    vm.opacity -= 0.01;
                    if(vm.opacity <= 0)
                        vm.opacity = 1;
                }, 16);
            },

            beforeDestroy() {
                // 清除定时器
                clearInterval(this.timer);
            },
        });
    </script>
</body>

18 非单文件组件

18.1 理解组件

组件定义:实现应用中局部功能代码和资源的集合。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

18.2 组件的基本使用

<body>
    <!-- 
    Vue中使用组件的三大步骤:
        一、定义组件(创建组件)
        二、注册组件
        三、使用组件(写组件标签)

        一、如何定义一个组件?
            使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
            区别如下:
                    1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
                    2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
            备注:使用template可以配置组件结构。

        二、如何注册组件?
            1.局部注册:靠new Vue的时候传入components选项
            2.全局注册:靠Vue.component('组件名',组件)

        三、编写组件标签:
            <school></school>
     -->
    <div id="root">
        <!-- 3. 使用组件,直接将组件名当场标签即可 -->
        <school></school>
        <hr>
        <!-- 注意:多个组件之间的信息是不相干扰的。 -->
        <student></student>
        
    </div>
    <script>
        Vue.config.productionTip=false;
        // 1. 创建组件,其实组件的配置与Vue实例配置类似,只是存在一些细微的差别
        // 学校组件
        const school = Vue.extend({
            // 组件里面不用编写el选项,因为组件都是交给Vue实例进行管理。
            // 定义模板
            template: `
                <div>
                    <h3>学校名称:{{schoolName}}</h3>
                    <h3>学校地址:{{address}}</h3>
                    <button @click='showSchoolName'>点击提示学校名称</button>
                </div>
            `,
            data(){
                return {
                    schoolName: '尚硅谷',
                    address: '贵州'
                };
            },
            methods: {
                showSchoolName(){
                    alert(this.schoolName);
                }
            },
        });
        // 学生组件
        const student = Vue.extend({
            template:`
                <div>
                    <h3>学生姓名:{{studentName}}</h3>
                    <h3>学生年龄:{{age}}</h3>
                </div>
            `,
            data(){
                return {
                    studentName: '张三',
                    age: 20
                };
            }
        });
       
        // 全局注册(也是属于第二步),使用的较少
        // Vue.component('school', school);

        new Vue({
            el: "#root",
            // 2. 注册组件(局部注册)
            components:{
                // 对象属性,key: value,在js中key与value相同时的可以只写key。
                // 注册school组件
                school,
                student
            }
        });
    </script>
</body>

18.3 几个注意点

<body>
    <!-- 
        几个注意点:
            1.关于组件名:
                一个单词组成:
                    第一种写法(首字母小写):school
                    第二种写法(首字母大写):School
                多个单词组成:
                    第一种写法(kebab-case命名):my-school
                    第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
                备注:
                    (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
                    (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

            2.关于组件标签:
                    第一种写法:<school></school>
                    第二种写法:<school/>
                    备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。

            3.一个简写方式:
                    const school = Vue.extend(options) 可简写为:const school = options
     -->
    <div id="root">
        <h2>欢迎你:{{msg}}</h2>
        <school></school>
    </div>
    <script>
        Vue.config.productionTip=false;
        // 定义组件
        const school = Vue.extend({
            // 可以给当前组件取一个名字,在Vue开发工具当中会显示的当前设置的名字
            name: 'schoolTest',
            template:`
                <div>
                    <h3>学校名称:{{schoolName}}</h3>
                </div>
            `,
            data(){
                return {
                    schoolName: '尚硅谷'
                }
            }
        });
        new Vue({
            el: "#root",
            data:{
                msg: '周巴弟'
            },
            components:{
                school
            }
        });
    </script>
</body>

18.4 组件的嵌套

<body>
    <div id="root">
        <app></app>
    </div>
    <script>
        Vue.config.productionTip=false;

        // 定义student组件
        const student = Vue.extend({
            template: `
            <div>
                <h3>学校名称:{{studentName}}</h3>
            </div>
            `,
            data(){
                return {
                    studentName: '张三'
                }
            }
        });

        // 定义school组件
        const school = Vue.extend({
            template:`
                <div>
                    <h3>学校名称:{{schoolName}}</h3>
                    <student></student>
                </div>
            `,
            data(){
                return {
                    schoolName: '尚硅谷'
                }
            },
            // 将student组件嵌套在school组件当中
            components:{
                student
            }
        });
        
        // 定义hello组件,与school组件平级
        const hello = Vue.extend({
            template:`
                <div>
                    <h2>欢迎学习{{language}}</h2>
                </div>
            `,
            data(){
                return {
                    language: 'Vue'
                };
            }
        });
        
        // 定义app组件
        const app = Vue.extend({
            template:`
                <div>
                    <hello></hello>
                    <school></school>
                </div>
            `,
            components:{
                // 页面的所有组件都在app组件中进行注册
                school,
                hello
            }
        });

        new Vue({
            el: "#root",
            components:{
                // vm当中只需要注册app组件即可
                app
            }
        });
    </script>
</body>

18.5 VueComponent

<body>
    <!-- 
        关于VueComponent:
            1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。

            2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,
                即Vue帮我们执行的:new VueComponent(options)。

            3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

            4.关于this指向:
                (1).组件配置中:
                    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
                (2).new Vue(options)配置中:
                    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

            5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
                Vue的实例对象,以后简称vm。
     -->
    <div id="root">
        <hello></hello>
        <school></school>
    </div>
    <script>
        Vue.config.productionTip=false;

        // 定义组件
        const school = Vue.extend({
            template: `
                <div>
                    <h3>学校名称:{{schoolName}}</h3>
                    <button @click='showInfo'>点我</button>
                </div>
            `,
            data() {
                return {
                    schoolName: '尚硅谷'
                }
            },
            methods: {
                showInfo(){
                    // 这里的this是VueComponent
                    console.log('showInfo : ', this); 
                }
            },
        });

        // 定义hello组件
        const hello = Vue.extend({
            template: '<h2>欢迎你:{{msg}}</h2>',
            data(){
                return {
                    msg: '张三'
                }
            }
        });
        new Vue({
            el: "#root",
            components:{school, hello}
        });
    </script>
</body>

18.6 一个重要的内置关系

<body>
    <!-- 
        1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
        2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
     -->
    <div id="root">
        <school></school>
    </div>
    <script>
        Vue.config.productionTip=false;
        Vue.prototype.x = 10000;

        // 定义school组件
        const school = Vue.extend({
            template:`
                <div>
                    <h2>学校名称:{{schoolName}}</h2>
                    <button @click='showInfo'>点击出X</button>
                </div>
            `,
            data(){
                return {
                    schoolName: '贵州师范大学'
                };
            },
            methods: {
                showInfo(){
                    console.log(this.x);
                }  
            },
        });

        const vm = new Vue({
            el: "#root",
            data:{
                y: 100
            },
            components:{school}
        });

        
    </script>

    <!-- 回顾原型对象 -->
    <script>
        // =================
        // 定义一个构造函数
        function Demo(){
            this.a = 1;
            this.b = 2;
        };

        // 创建一个Demo的实例对象
        const d = new Demo();

        // console.log(Demo.prototype); // 显示原型属性
        // console.log(d.__proto__); // 隐示原型属性

        // 通过显示原型属性操作原型对象,追加一个x属性,值为99
        Demo.prototype.x = 99;

        // console.log(d.x);

        // =================
    </script>
</body>

19 单文件组件

School.vue

<template>
<!-- 
    vue定义文件名:1. school.vue , 2. School.vue , 3. xue-xiao.v ue , 4. XueXiao.vue 这里推荐类型2、4的这两种写法。
    注意:这里的template标签在页面不会进行展示。
 -->
<!-- html结构 -->
<div id="demo">
    <h3>学校名称:{{schoolName}}</h3>
    <h3>学校地址:{{schoolAddress}}</h3>
</div>
</template>

<script>
// 组件交互相关代码

// es6默认暴露方式
export default {
    // 定义组件名,该名称一般与自身的文件名保持一致
    name: 'School',
    data() {
        return {
            schoolName: '尚硅谷',
            schoolAddress: '贵州'     
        }
    },
}
</script>

<style>
/* 组件代码样式 */
.demo{
    background-color: #bfa;
}
</style>

Student.vue

<template>
    <div>
       <h3>学生姓名:{{name}}</h3>
        <h3>学生年龄:{{age}}</h3> 
    </div>
</template>

<script>
export default {
    name: 'Student',
    data() {
        return {
            name: '张三',
            age: 20
        }
    },
}
</script>

App.vue

<template>
    <!-- 快速生成vue模板:<v,再按tab键即可。 -->
    <div>
        <!-- 使用已经引入的组件 -->
        <School></School>
        <Student></Student>
    </div>
</template>

<script>
    // 引入组件,在vue脚手架里面这里的.vue后缀可写可不写。
    import School from './School.vue'
    import Student from './Student.vue'

    export default {
        name: 'App',
        // 注册组件
        components:{
            School,
            Student
        }
    }
</script>

应用入口文件main.js

import App from './App.vue'

new Vue({
    el: '#root',
    components:{
        App
    }
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简单练习一下vue单文件的语法</title>
</head>
<body>
    <!-- 改文件并不能正常的运行,因为浏览器默认不支持es6语法,如:import语句。如果想要运行,需要放在脚手架里面进行运行。 -->
    <div id="root">
        <App></App>
    </div>
    <!-- 引入js -->
    <script src="../js/vue.js"></script>
    <script src="./main.js"></script>
</body>
</html>

20 补充

非单文件组件与单文件组件的区别:可以简单的理解为在非单文件组件当中可以写多个component,但是这种方式使用的较少,不推荐使用;而单文件组件则是在一个vue文件当中只编写一个组件,推荐使用。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值