vue 基础
vue: 是一套用于构建用户界面的渐进式JavaScript框架
渐进式: Vue可以自底向上逐层的应用:
简单应用: 只需一个轻量小巧的核心库
复杂应用: 可以引入各式各样的Vue插件
vue特点:
1. 采用组件化(一个.vue文件就是一个组件,直接引用组件即可)模式, 提高代码复用率,且让代码更好维护
2. 声明式编码,让编码人员无需直接操作DOM, 提高开发效率

3. 使用虚拟DOM + 优秀的Diff算法,尽量复用Dom节点
 

4. 学习Vue之前要掌握的JavaScript基础知识?
ES6语法规范:结构赋值、箭头函数、模板字符串。。。
ES6模块化:默认暴露、分别暴露、统一暴露。。。
包管理器:npm
原型、原型链
数组常用方法:过滤、筛选
axios
promise
...
一. vue基础
js中设置颜色:
        <style>
            *{
                margin-top: 20px;
            }
            
            .demo1{
                height: 50px;
                background-color: skyblue;
            }
            .box1{
                padding: 5px;
                background-color: skyblue;
            }
            .box2{
                padding: 5px;
                background-color: orange;
            }
            .list{
                width: 200px;
                height: 200px;
                background-color: peru;
                overflow: auto;
            }
            li{
                height: 100px;
            }
        
        </style>
1.2 插值语法
初识Vue:
1. 想让Vue工作,就必须创建一个Vue实例(new Vue()),且要传入一个配置对象;
2. root容器里的代码依然符合Html规范,只不过混入了一些特殊的Vue语法;
3. root容器里的代码被称为【Vue模板】;
4. Vue实例和容器是一一对应的;
5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6. {{xxx}} 中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7. 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>Hello {{name}}</h1>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //创建Vue实例
            const x = new Vue({
                el:'#root',  //el(element) 用于指定当前Vue实例为哪个容器服务
                data:{    //data 中用于存储数据,数据供el所指定的容器去使用,  值我们暂时先写成一个对象 
                    name:'Hello' 
                }
            })
                
        //调用后端
            // axios({
            //     URL: http://127.0.0.1:5500/
            // })
        </script>
    </body>
</html>
  
细节1: 一个实例,如果有2个容器;
第二个容器解析不了 ------>一个实例不能对应多个容器
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div class = "root">
            <h1>Hello {{name}} 1</h1>
        </div>
        <div class = "root">
            <h1>Hello {{name}} 2</h1>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //创建Vue实例
            new Vue({
                el:'.root',  //el(element) 用于指定当前Vue实例为哪个容器服务
                data:{    //data 中用于存储数据,数据供el所指定的容器去使用,  值我们暂时先写成一个对象 
                    name:'Hello' 
                }
            })
        </script>
    </body>
</html>
  
控制台不报错,但是第二个容器解析不了;一个实例不能对应多个容器
细节2 : 一个容器对应多个实例
一个容器不能对应多个实例;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>Hello {{name}} , {{address}}, {{1+1}}, {{ Date.now() }}</h1>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //创建Vue实例
            new Vue({
                el:'#root',  //el(element) 用于指定当前Vue实例为哪个容器服务
                data:{    //data 中用于存储数据,数据供el所指定的容器去使用,  值我们暂时先写成一个对象 
                    name:'Hello' 
                }
            })
            new Vue({
                el:'#root',  //el(element) 用于指定当前Vue实例为哪个容器服务
                data:{    //data 中用于存储数据,数据供el所指定的容器去使用,  值我们暂时先写成一个对象 
                    address:'上海' 
                }
            })
        </script>
    </body>
</html>
解析结果:vue.js:634 [Vue warn]: Property or method "address" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property

第二个实例解析不了;控制台报错
总结:
注意区分:js表达式 和 js代码(语句)
1. 表达式: 一个表达式会生成一个值,可以放在任何一个需要值得地方
(1). a ;变量
(2). a + b 运算;例如:{{1+1}}
(3). demo(1) 函数表达式:函数调用(name.toUpperCase() )
(4). x === y ? 'a' : 'b' (相等:====; 不相等:!== )
2. js代码(语句)
(1). if (){}
(2). for (){}
(3).
1.3 模板语法
vue模板语法有2大类:
1. 插值语法:
功能:用于解析标签体内容;
写法:{{xxx}}, xxx是js表达式,且可以直接读取到data中的所有属性;
2. 指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件。。。)
举例:v-bind:href="xxx" 或 简写为 : href="xxx", xxx同样要写js表达式,且可以直接读取到data中的所有属性;
备注:Vue中有很多的指令,且形式都是:v-??? , 此处只是拿v-bind举个例子;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
           <h1>插值语法</h1>
           <h3>你好,{{name}}</h3>
           <hr/>
           <h1>指令语法</h1>
           <!-- v-bind:使后面的变量成为一个表达式 
                v-bind:还可以简写成:
            -->
           <a v-bind:href="school.url.toUpperCase()">学校名称:{{school.name}}</a>
           <a :href="url">学习2</a>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                name:'java',
                school:{
                    name:'南翔技校',
                    url:'http://baidu.com'
                }
               }
           })
        </script>
    </body>
</html>
1.4 数据绑定
Vue中有2种数据绑定的方式:
1. 单向绑定(v-bind): 数据只能从data流向页面;
2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data;
备注:a. 双向绑定一般都应用在表单类元素上(如: input、select。。)
b. v-model:value 可以简写为 v-model, 因为 v-model默认收集的就是value值;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- v-bind:是一个单向数据绑定 -->
           单向数据绑定:<input type="text" v-bind:value="name" />
           <!-- v-model:是一个双向数据绑定 -->
           双向数据绑定:<input type="text" v-model:value="name" />
          <!-- 如下代码是错误的,因为v-model 只能应用在表单类元素(单选、多选、文本框、下拉框。。。) -->
           <h2 v-model:x="name">你好</h2>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                name:'java'
               }
           })
        </script>
    </body>
</html>
或简写:
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            
           <!-- 单向数据绑定:<input type="text" v-bind:value="name" /> -->
           <!-- 双向数据绑定:<input type="text" v-model:value="name" /> -->
          <!-- 简写代码 -->
           单向数据绑定:<input type="text" :value="name" />
           双向数据绑定:<input type="text" v-model="name" />
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                name:'java'
               }
           })
        </script>
    </body>
</html>
1.5 el与data的两种写法
el与data的2种写法:
a. el有2种写法:
(1)new Vue时候配置el属性
(2)先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
b. data有2种写法
(1)对象式
(2)函数式
如何选择:目前哪种写法都可以,一旦用组件时,data必须使用函数式,否则会报错;
c. 一个重要的原则
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了;
1.5.1 el的两种写法
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>你好,{{name}}</h1>
          
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //el的2种写法
           const x = new Vue({
            // el:'#root', //第一种写法
               data:{
                name:'hello'
               }
           })
        
           console.log(x)
        //    第二种写法
        //    x.$mount('#root')
        //    定时器 ,1s之后执行
        setTimeout(() => {
            x.$mount('#root')
        }, 1000);
        </script>
    </body>
</html>
1.5.2 data的两种写法
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>你好,{{name}}</h1>
          
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //data的两种写法
            new Vue({
                el:'#root',
                // data的第一种写法
                // data:{
                //     name:'java'
                // }
                // 写组件时,只能用下面的方式
                //data的第二种写法:函数式--返回一个对象
                data:function(){
                    console.log('@@', this) //此处的this是Vue实例对象
                    return {
                        name:'Hello'
                    }
                }
                //或
                /* data(){
                    console.log('@@', this) //此处的this是Vue实例对象
                    return {
                        name:'Hello'
                    }
                } */
                //如果是下面的写法;this代表是window;=>中没有this
                /* data:()=>{
                    console.log('@@', this) //此处的this是Vue实例对象
                    return {
                        name:'Hello'
                    }
                } */
            })
        
        </script>
    </body>
</html>
1.6 MVVM模型

 
MVVM模型:
1. M:模型(Model) --- data中的数据
2.V: 视图(View) --- 模板代码
3.VM: 视图模型(viewModel) --- Vue实例
观察发现:a. data中所有的属性,最后都出现在了VM身上
b. vm身上所有的属性,及 Vue原型上所有属性,在Vue模板中都可以直接使用
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- {{xxx}} xxx是vm上的东西,vm上的都能用 -->
            <h1>学校名称:{{name}}</h1>
            <h1>学校地址:{{address}}</h1>
          
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //data
            const vm = new Vue({
                el:'#root',
                data:{
                    name:'java',
                    address:'上海'
                }
            })
            console.log('#@@', vm)
        </script>
    </body>
</html>
1.7 数据代理
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Object.defineproperty</Object></title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
        </div>
        <script type="text/javascript">
            //下面的person属性,是可以直接被修改或删除的
            let num = 18
            let person = {
                name:'张三',
                sex:'男'
            }
            //age 不可枚举
            // Object.defineProperty(person, 'age',{
            //     value:16
            // })
            // 如果希望age可枚举; enumerable 控制属性是否可以枚举,默认值是false;
            //如果enumerable为true, age属性是不可以被修改的;如果希望修改age: writable设置为true
            //writable 控制属性是否可以被修改,默认值是false;
            //configurable 控制属性是否可以被删除,默认值是false;
            Object.defineProperty(person, 'age',{  //追加属性 
                // value:16,
                // enumerable:true,
                // writable:true,
                // configurable:true,
                
                //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
                get:function(){
                    console.log('有人读取age属性了')
                    return num
                },
                //或
                /* get(){
                    return num
                } */
                //当有人修改person的age属性时,set函数(setter)就会被调用,且会受到修改的具体值
                set(value){
                    console.log('有人修改了age属性,且值是:', vaue)
                    num = value
                }
            })
            console.log(person)
        </script>
    </body>
</html>
什么是数据代理?
数据代理: 通过一个对象代理对另一个对象中属性的操作(读或写)
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>什么是数据代理</title>
    </head>
    <body>
       <!-- 数据代理: 通过一个对象代理对另一个对象中属性的操作(读或写) -->
        <script type="text/javascript">
            let obj = {x:10}
            let obj2 = {y:20}
            //通过一个obj2代理对obj中属性x的操作
            Object.defineProperty(obj2, 'x',{
                get(){
                    return obj.x
                },
                set(value){
                    obj.x = value
                }
            })
        </script>
    </body>
</html>
vue中的数据代理:
1. vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2. vue中数据代理的好处
更加方便的操作data中的数据
3. 基本原理
通过Object.defineProperty()方法把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter或setter。
在getter或setter内部去操作(读或写)data中对应的属性;
 
1.8 事件处理
1.8.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)" 效果一致,但后者可以传参;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Vue中的数据代理</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
           <h2>欢迎学习:{{name}}</h2>
           <!-- v-on:当什么时候 -->
           <!-- <button v-on:click="showInfo">点我提示信息</button> -->
           <!-- 上面的简写 -->
           <button @click="showInfo">点我提示信息1(不传参)</button>
           <button @click="showInfo2($event, 66)">点我提示信息2(传参)</button>
 
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            
            const vm = new Vue({
               el:'#root',
               data:{
                  name:'java',
                  
               },
               //函数回调只能写在Vue实例里面
               methods:{
                   showInfo(event){
                       //event.target 表示事件的标签
                       console.log(event.target) //打印: <button>点我提示信息</button>
                       console.log(this == vm);  //this = vm
                       alert('欢迎学习java')
                   },
                   showInfo2(event, num){
                       console.log(event, num)
                       alert('Hello')
                   }
               }
            })
        </script>
    </body>
</html>
1.8.2 事件的修饰符
vue中的事件修饰符:
1. prevent : 阻止默认事件(常用) ;
2. stop : 阻止事件冒泡(常用);
3. once : 事件只触发一次(常用);
4. capture : 使用事件的捕获模式;
5. self : 只有event.target是当前操作的元素时才触发事件;
6. passive : 事件的默认行为立即执行。无需等待事件回调执行完毕;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Vue中的数据代理</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <style>
       <!-- 每个模块之间隔开20px -->
            *{
                margin-top: 20px;
            }
            
            .demo1{
                height: 50px;
                background-color: skyblue;
            }
            .box1{
                padding: 5px;
                background-color: skyblue;
            }
            .box2{
                padding: 5px;
                background-color: orange;
            }
            .list{
                width: 200px;
                height: 200px;
                background-color: peru;
                overflow: auto;
            }
            li{
                height: 100px;
            }
        
        </style>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>欢迎学习:{{name}}</h2>
            <!-- 阻止默认调用事件 -->
           <a href="http://baidu.com" @click.prevent="showInfo">点我提示信息</a>
  
           <!-- 阻止事件冒泡 -->
            <div class="demo1" @click="showInfo">
                <button @click.stop="showInfo">点我提示信息</button>
          <!-- 修饰符可以连续写 -->
          <a href="http://baidu.com" @click.stop.prevent="showInfo">点我提示信息</a>
            </div>
           <!-- 事件只触发一次 -->
           <div class="box1" @click.capture="showMsg(1)">
               div1 
               <div class="box2" @click="showMsg(2)">
                    div2
               </div>
           </div>
           <!-- 只有event.target是当前操作的元素时才触发事件 -->
           <div class="demo1" @click.self="showInfo">
                <button @click="showInfo">点我提示信息</button>
            </div>
           <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
            <ul @scroll="demo" class="list">
                <li>1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
            </ul>
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              name:'java',
              
           },
           methods:{
                showInfo(e){
                    alert("同学好")
                },
                showMsg(msg){
                    console.log(msg);
                },
                demo(e){
                    for (let i = 0; i < 100000; i++) {
                        console.log('#');
                    }
                    alert("累坏了")
                }
            }
        })
        
    </script>
</html>
1.8.3 键盘事件
1. vue中常用的按键别名
回车 => enter
删除 => delete(捕获“删除” 和 “退格”键)
退出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown使用)
上 => up
下 => down
左 => left
右 => right
2. Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab - case (短横线命名)
3. 系统修饰符(用法特殊):ctrl、alt、shift、meta(win)
a. 配合keyup使用:按下修饰键的同时+按下其他键,随后释放其他键,事件才被触发;
b. 配合keydown使用:正常触发事件;
c. 系统修饰符配合其他键,可以用来做一些限制
<!-- 需求:按下ctrl + y 才触发函数的调用,ctrl + 其他键不能触发函数 --> <input type="text" placeholder="按下回车提示输入" @keyup.ctrl.y="showInfo" />
4. 也可以使用keyCode 去指定具体的按键(不推荐);
5. Vue.config.keyCodes.自定义键名 = 键码, 可以去定制按键别名;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>键盘事件</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>欢迎学习:{{name}}</h2>
            <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo" />
            <!-- 大小写按键CapsLock,像这种多个单词组成的,需要像下面这样用"-"分隔单词,单词都要小写 -->
            <input type="text" placeholder="按下回车提示输入" @keyup.caps-lock="showInfo" />
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              name:'java',
              
           },
           methods:{
                showInfo(e){
                   // 可以显示按键和按键对应的编码
                   // console.log(e.key, e.keyCode);
                   //获取input输入的值
                   console.log(e.target.value);
                   
                }
            }
        })
    </script>
</html>
1.9 计算属性
计算属性:
定义:要用的属性不存在,要通过已经有得属性计算得来;
原理: 底层借助了Object.defineproperty方法提供的getter和setter;
get函数什么时候执行?
a.初次读取时会执行一次
b.当依赖的数据发生改变时会被再次调用;
优势: 与methods实现相比,内部有缓存机制(复用),效率更高
备注:
a.计算属性最终会出现在vm上,直接读取使用即可;
b.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变;
1.9.1. 姓名案例_插值语法实现
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>插值语法</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>欢迎学习:{{name}}</h2>
            姓: <input type="text" v-model="firstName"/> <br/><br/><br/>
            名: <input type="text" v-model="lastName"/><br/><br/>
            全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
            
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              firstName:'张',
              lastName:'三'
           },
           methods:{
                
            }
        })
    </script>
</html>
1.9.2.姓名案例_methods实现
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>methods</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            
            姓: <input type="text" v-model="firstName"/> <br/><br/><br/>
            名: <input type="text" v-model="lastName"/><br/><br/>
            全名:<span>{{fullName()}}</span>
            
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              firstName:'张',
              lastName:'三'
           },
           methods:{
                fullName(){
                    return this.firstName+'-'+this.lastName
                }
            }
        })
    </script>
</html>
1.9.3.姓名案例_计算属性实现
data中的属性和methods中的方法都是vm实例本身的;但是计算属性不是,不能用vm实例直接调用
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>计算属性</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            
            姓: <input type="text" v-model="firstName"/> <br/><br/><br/>
            名: <input type="text" v-model="lastName"/><br/><br/>
            全名:<span>{{fullName}}</span>
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              firstName:'张',
              lastName:'三'
           },
           computed:{
                fullName:{
                    // get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
                    /* get什么时候调用? 1. 初识读取fullName时
                                      2. 所依赖的数据发生变化时 
                                        
                    */
                    get(){
                        return this.firstName+'-'+this.lastName //this代表vm
                    },
                    // set什么时候调用?fullName被修改时
                    set(value){
                        const arr = value.split('-')
                        this.fistName = arr[0]
                        this.lastName = arr[1]
                    }
                }
            }
        })
    </script>
</html>
计算属性简写
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>计算属性</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            
            姓: <input type="text" v-model="firstName"/> <br/><br/><br/>
            名: <input type="text" v-model="lastName"/><br/><br/>
            全名:<span>{{fullName}}</span>
           
         </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              firstName:'张',
              lastName:'三'
           },
           computed:{
                /* 
                //完整写法
                fullName:{
                    // get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
                    // get什么时候调用? 1. 初识读取fullName时
                    //                 2. 所依赖的数据发生变化时 
                                        
                    get(){
                        return this.firstName+'-'+this.lastName //this代表vm
                    },
                    // set什么时候调用?fullName被修改时
                    set(value){
                        const arr = value.split('-')
                        this.fistName = arr[0]
                        this.lastName = arr[1]
                    }
                } */
                //计算属性简写(如果确定fullName只被读,不被写)
                fullName(){
                    return this.firstName+'-'+this.lastName //this代表vm
                }
            }
        })        
    </script>
</html>
2.0 监视属性-watch
监视属性-watch
1. 当被监测的属性变化时,回调函数自动调用,进行相关操作;
2. 监视的属性必须存在,才能进行监视;
3. 监视的两种写法:
a. new Vue()实例时传入watch配置;
b. 通过vm.$watch监视
2.0.1.天气案例
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>监视属性--天气案例</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>今天天气很{{info}}</h2>
            <!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 -->
            <!-- <button @click="isHot = !isHot">切换天气</button> -->
            <button @click="changeWeather">切换天气</button>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        new Vue({
            el:'#root',
            data:{
             isHot:true 
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
        })
        
    </script>
</html>
2.0.2.天气案例_监视属性
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>天气案例_监视属性</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>今天天气很{{info}}</h2>
            <button @click="changeWeather">切换天气</button>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
             isHot:true 
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
        
            //watch是一个配置对象: 对属性进行监测
       //创建vm实例时,就知道要监视的属性
            watch:{
                //下面的值是被监测的属性
                isHot:{
                    //初始化时让handler调用一下
                    immediate:true,
                    //handler什么时候调用? 当isHot发生改变时
                    handler(newValue, oldValue){
                        console.log(newValue, oldValue)
                    }
                }
            }
        
        })
        //watch的简写(一开始创建vm实例的时候,不知道要监视的属性)
        vm.$watch('isHot', {
            //初始化时让handler调用一下
            immediate:true,
            //handler 什么时候调用? 当isHot发生改变时
            handler(newValue, oldValue){
                console.log(newValue, oldValue)
            }
        })
        
    </script>
</html>
2.0.3 深度监视
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>天气案例_深度监视</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 
            深度监视
                1. Vue中的watch默认不监视对象内部值得改变(一层)
                2. 配置deep:true可以监测对象内部值改变(多层)
            备注:
                1. Vue自身可以监测对象内部值得改变。但Vue提供的watch默认不可以;
                2. 使用watch时根据数据的具体结构。决定是否采用深度监视;
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>今天天气很{{info}}</h2>
            <button @click="changeWeather">切换天气</button>
           <hr/>
           <h3>a的值是:{{numbers.a}}</h3>
           <!-- 监视a的变化 -->
           <button @click="numbers.a++">点我让a加1</button>
           <h3>a的值是:{{numbers.b}}</h3>
           <!-- 监视b的变化 -->
           <button @click="numbers.b++">点我让b加1</button>
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        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是一个配置对象: 对属性进行监测
            //创建vm实例时,就知道要监视的属性
            watch:{
                //下面的值是被监测的属性
                isHot:{
                    //初始化时让handler调用一下
                    immediate:true,
                    //handler 什么时候调用? 当isHot发生改变时
                    handler(newValue, oldValue){
                        console.log(newValue, oldValue)
                    }
                },
                //监视多级机构中某个属性的变化
                /* 'numbers.a':{
                    handler(){
                        console.log('a被改变了');
                        
                    }
                } */
                //监视多级机构中所有属性的变化
                'numbers':{
                    deep:true,
                    handler(){
                        console.log('numbers被改变了');
                    }
                }
            }
        
        })
    </script>
</html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>天气案例_监视属性_简写</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
       
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>今天天气很{{info}}</h2>
            <button @click="changeWeather">切换天气</button>
           <hr/>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true
                
            },
            computed:{
                info(){
                    return this.isHot ? "炎热" : "凉爽"
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
        
            //watch是一个配置对象: 对属性进行监测
            //创建vm实例时,就知道要监视的属性
            watch:{
                //正常写法
                /* isHot:{
                    //初始化时让handler调用一下
                    immediate:true,
                    //handler 什么时候调用? 当isHot发生改变时
                    handler(newValue, oldValue){
                        console.log(newValue, oldValue)
                    }
                }, */
                //简写
                /* isHot(newValue, oldValue){
                    console.log(newValue, oldValue);
                } */
            }
        })
        //正常写法
        /* vm.$watch('isHot', {
            //初始化时让handler调用一下
            immediate:true,
            //handler 什么时候调用? 当isHot发生改变时
            handler(newValue, oldValue){
                console.log(newValue, oldValue)
            }
        }) */
        vm.$watch('isHot', function(newValue, oldValue){
            console.log(newValue, oldValue)
        })
    </script>
</html>
计算属性 vs 侦听属性
computed和watch之间的区别:
1. computed能完成的功能,watch都可以完成;
2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作;
两个重要的小原则:
a. 被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象;
b. 所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象;
侦听属性
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>天气案例_监视属性_简写</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
       
        <!-- 准备一个容器-->
        <div id = "root">
            姓: <input type="text" v-model="firstName"/> <br/><br/><br/>
            名: <input type="text" v-model="lastName"/><br/><br/>
            全名:<span>{{fullName}}</span>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
           el:'#root',
           data:{
              firstName:'张',
              lastName:'三',
              fullName:'张-三'
           },
           methods:{
                fullName(){
                    return this.firstName+'-'+this.lastName
                }
            },
        
            //watch是一个配置对象: 对属性进行监测
            //创建vm实例时,就知道要监视的属性
            watch:{
                //val:最新的姓
                firstName(val) {
                    //watch属性可以异步执行
                    setTimeout(() =>{
                        this.fullName = val + ' ' + this.lastName
                    }, 1000)
                },
               //val: 最新的名
                lastName(val) {
                    this.fullName = this.firstName + ' ' + val
                }
               
            }
        })
    </script>
</html>
计算属性
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
2.1 绑定样式
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>绑定样式</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <style>
            .basic{
            }
            .happy{
                
            }
            .sad{
                
            }
            .normal{
                
            }
            .atguigu1{
                
            }
            .atguigu2{
                
            }
            .atguigu3{
                
            }
        </style>
    </head>
    <body>
        <!-- 
            绑定样式:
                1. class样式
                    写法: class="xxx" , xxx可以是字符串、对象、数组;
                         字符串写法适用于:类名不确定,要动态获取;
                         对象写法适用于: 要绑定多个样式,个数不确定,名字也不确定
                         数组写法适用于: 要绑定多个样式,个数确定,名字也确定,但不确定用不用
                2. style样式
                    :style="{fontSize: xxx}" 其中xxx是动态值
                    :style="[a,b]" 其中a、b是样式对象
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- :class 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br/>
            <!-- :class 绑定class样式-数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
            <div class="basic" :class="classArr" >{{name}}</div>
            <!-- :class 绑定class样式-对象写法,适用于:要绑定的样式个数确定、名字也确定, 但要动态决定用不用 -->
            <div class="basic" :class="classObj" >{{name}}</div>
            <!-- :class 绑定style样式-对象写法 -->
            <div class="basic" :style="styleObj" >{{name}}</div>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        new Vue({
            el:'#root',
            data:{
                name:'Java',
                mood:'normal',
                classArr:['atguigu1','atguigu2','atguigu3'],
                classObj:{
                    atguigu1:false,
                    atguigu2:true
                },
                styleObj:{
                    fontSize: '40px',
                    color:'red',
                    backgroundColor:'orange'
                }
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal']
                    const index = Math.floor(Math.random()*3)
                    this.mood = arr[index]
                }
            }
        })
    </script>
</html>
2.2 条件渲染
条件渲染:
1. v-if
写法:
a. v-if="表达式"
b. v-else-if="表达式
c. v-else="表达式
适用于:切换频率较低的场景;
特点:不展示的DOM元素直接被移除;
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”;
2. v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景;
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉;
3. 备注
使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到;
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>条件渲染</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>当前的n值是:{{n}}</h2>
            <button @click="n++">点我n+1</button>
            <!-- 使用v-show做条件渲染 -->
            <!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
            <!-- <h2 v-show="1===1">欢迎来到{{name}}</h2> -->
            <!-- 使用v-if 做条件渲染 -->
            <!-- <h2 v-if="false">欢迎来到{{name}}</h2>
            <h2 v-if="1===1">欢迎来到{{name}}</h2> -->
            
            <!-- <div v-show="n===1">Angular</div>
            <div v-show="n===2">React</div>
            <div v-show="n===3">Vue</div> -->
            <!-- 使用v-if 和 v-else-if 做条件渲染:中间不能被打断 -->
            <div v-if="n===1">Angular</div>
            <div v-else-if="n===2">React</div>
            <div v-else-if="n===3">Vue</div>
            <div v-else>Java</div>
           
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
             name:'Java' ,
             n:0
            }
           
        })
    </script>
</html>
2.3 列表渲染
2.3.1 基本列表
v-for 指令:
1. 用于展示列表数据
2. 语法:v-for="(item, index) in xxx" :key="yyy"
3. 可遍历:数组、对象、字符串(用的少)、指定次数(用的很少)
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>基本列表</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <ul>
                <li v-for="(p, index) in persons" ::key="p.id">
                    {{p.name}}-{{p.age}}
                </li>
            </ul>
            <!-- 遍历对象 -->
            <h2>汽车列表</h2>
            <ul>
                <li v-for="(val, key) in car" ::key="key">
                    {{key}}-{{val}}
                </li>
            </ul>
            <!-- 遍历字符串 -->
            <h2>测试遍历字符串</h2>
            <ul>
                <li v-for="(char, index) in str" ::key="index">
                    {{char}}-{{index}}
                </li>
            </ul>
            <!-- 遍历指定次数 -->
            <h2>测试遍历指定次数(用的少)</h2>
            <ul>
                <li v-for="(num, index) in 5" ::key="index">
                    {{index}}-{{num}}
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
             persons:[
                 {id:'001',name:'张三',age:18},
                 {id:'002',name:'李四',age:20},
                 {id:'003',name:'王五',age:45}
             ],
             car:{
                 name:'奥迪A8',
                 price: 457,
                 color:'白色'
             },
             str:'hello'
            }
           
        })
    </script>
</html>
2.3.2 key的原理
面试题:react、vue中的key有什么作用?(key的内部原理)
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1)若虚拟DOM中内容没变,直接使用之前的真实DOM;
(2)若虚拟DOM中内容改变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM;
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到页面;
2. 用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==>界面效果没问题,但效率低;
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==>界面有问题
3. 开发中如何选择key ?
(1)最好使用每条数据的唯一标识作为key,比如Id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表,用于展示,使用index作为key是没有问题的;
对数据进行逆序添加列表:
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>key的原理</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <button @click.once="add">添加一个老刘</button>
            <ul>
                <li v-for="(p, index) in persons" :key="index">
                    {{p.name}}-{{p.age}}
                    <input type="text" />
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'张三',age:18},
                    {id:'002',name:'李四',age:20},
                    {id:'003',name:'王五',age:45}
                ]
            },
            methods: {
                add(){
                    const p = {id:'004',name:'老刘',age:18}
                    //逆序添加: 会导致页面错误
                    this.persons.unshift(p)
                }
            },
           
        })
    </script>
</html>
逆序添加数据,并用index作为key的原理图:

页面结果:

逆序添加数据,用id作为key的原理图:
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>key的原理</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <button @click.once="add">添加一个老刘</button>
            <ul>
                <li v-for="(p, index) in persons" :key="p.id">
                    {{p.name}}-{{p.age}}
                    <input type="text" />
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'张三',age:18},
                    {id:'002',name:'李四',age:20},
                    {id:'003',name:'王五',age:45}
                ]
            },
            methods: {
                add(){
                    const p = {id:'004',name:'老刘',age:18}
                    //正序添加
                    // this.persons.push(p)
                    //逆序添加: 会导致页面错误
                    this.persons.unshift(p)
                }
            }
           
        })
    </script>
</html>
 
页面结果:

2.3.3 列表过滤

输入关键字 "冬"

<html>
    <head>
        <meta charset="UTF-8"/>
        <title>列表过滤</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <input type="text" v-model="keyWord" placeholder="请输入名字">
            <ul>
                <li v-for="(p, index) in filPersons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        //#region  折叠符开始
        //用watch实现
        // new Vue({
        //     el:'#root',
        //     data:{
        //         keyWord:'',
        //         persons:[
        //             {id:'001',name:'马冬梅',age:18, sex:'女'},
        //             {id:'002',name:'周冬雨',age:20, sex:'女'},
        //             {id:'003',name:'周杰伦',age:23, sex:'男'},
        //             {id:'004',name:'温兆伦',age:45, sex:'男'}
        //         ],
        //         filPersons:[]
        //     },
        //     watch:{
        //         keyWord:{
        //             immediate:true,
        //             handler(newVal){
        //                 this.filPersons = this.persons.filter((p)=>{
        //                     return p.name.indexOf(newVal) !==-1
        //                 })
        //             }
        //         }
        //     }
        // })
        //#endregion 折叠符结束
        //用computed实现
        new Vue({
            el:'#root',
            data:{
                keyWord:'',
                persons:[
                    {id:'001',name:'马冬梅',age:18, sex:'女'},
                    {id:'002',name:'周冬雨',age:20, sex:'女'},
                    {id:'003',name:'周杰伦',age:23, sex:'男'},
                    {id:'004',name:'温兆伦',age:45, sex:'男'}
                ]
            },
            computed:{
                filPersons(){
                    return this.persons.filter((p)=>{
                        return p.name.indexOf(this.keyWord) !==-1
                    })
                }
            }
        })
        
    </script>
</html>
2.3.4 列表排序
 
 
也可以先筛选后排序
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>列表排序</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <input type="text" v-model="keyWord" placeholder="请输入名字"> 
            <button @click="sortType = 2">按年龄升序排列</button>
            <button @click="sortType = 1">按年龄降序排列</button>
            <button @click="sortType = 0">按原顺序</button>
            <ul>
                <li v-for="(p, index) in filPersons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        //用computed实现
        new Vue({
            el:'#root',
            data:{
                keyWord:'',
                sortType:0, //0-原顺序;1-降序;2-升序
                persons:[
                    {id:'001',name:'马冬梅',age:45, sex:'女'},
                    {id:'002',name:'周冬雨',age:18, sex:'女'},
                    {id:'003',name:'周杰伦',age:23, sex:'男'},
                    {id:'004',name:'温兆伦',age:40, sex:'男'}
                ]
            },
            computed:{
                filPersons(){
                    const arr = this.persons.filter((p)=>{
                        return p.name.indexOf(this.keyWord) !==-1
                    })
                    //判断是否需要排序
                    if(this.sortType){ //this.sortType为真才进入
                        arr.sort((p1, p2)=>{
                            return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age 
                        })
                    }
                    return arr
                }
            }
        })
        
    </script>
</html>
2.3.5 Vue.set的使用
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Vue.set的使用</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>学校名称信息</h1>
            <h2>学校名称:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>
            <hr/>
            <h1>学生信息</h1>
            <button @click="addSex">添加一个性别属性,默认值是男</button>
            <h2>姓名:{{student.name}}</h2>
            <h2 v-if="student.sex">性别:{{student.sex}}</h2>
            <h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2>
            <h2>朋友们</h2>
            <ul>
                <li v-for="(f, index) in student.friends" :key="index">
                    {{f.name}}--{{f.age}}
                </li>
            </ul>
       </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        //用computed实现
        new Vue({
            el:'#root',
            data:{
               name:'清华',
               address:'北京',
               student:{
                   name:'tom',
                   age:{
                       rAge:45,
                       aAge:20
                   },
                   friends:[
                       {name:'jerry', age:35},
                       {name:'tony', age:35}
                   ]
               }
            },
            methods: {
                addSex(val){
                    // Vue.set(this.student, 'sex', '男')
                    //或
                    this.$set(this.student, 'sex', '男')
                }
            },  
        })
    </script>
</html>
2.3.6 Vue监测数据改变的原理_数组
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>Vue监测数据改变的原理_数组</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 
            Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
            let arr = [1,2,3,4,5]
            如下数组的操作方法被调用,vue才认可数组被修改
            push : 新增最后一个
            pop :删除
            shift    :删除第一个元素
            unshift  :新增第一个元素
            splice: 指定位置替换/删除
            sort : 排序
            reverse : 反转
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>学校名称信息</h1>
            <h2>学校名称:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>
            <hr/>
            <h1>学生信息</h1>
            <button @click="addSex">添加一个性别属性,默认值是男</button>
            <h2>姓名:{{student.name}}</h2>
            <h2 v-if="student.sex">性别:{{student.sex}}</h2>
            <h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2>
            <h2>爱好</h2>
            <ul>
                <li v-for="(h, index) in student.hobbys" :key="index">
                    {{h}}
                </li>
            </ul>
            <h2>朋友们</h2>
            <ul>
                <li v-for="(f, index) in student.friends" :key="index">
                    {{f.name}}--{{f.age}}
                </li>
            </ul>
        </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        //用computed实现
        new Vue({
            el:'#root',
            data:{
               name:'清华',
               address:'北京',
               student:{
                   name:'tom',
                   age:{
                       rAge:45,
                       aAge:20
                   },
                   hobbys:['喝酒','抽烟','烫头'],
                   friends:[
                       {name:'jerry', age:35},
                       {name:'tony', age:35}
                   ]
               }
            },
            methods: {
                //向学生中追加属性sex
                addSex(val){
                    // Vue.set(this.student, 'sex', '男')
                    //或
                    this.$set(this.student, 'sex', '男')
                }
            },  
        })
 
        
    </script>
</html>
2.3.7 总结Vue监测数据
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的根数据对象 添加属性!!!
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>总结Vue监测数据</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h1>学生信息</h1>
            <button @click="student.age++">年龄加1岁</button> <br/>
            <button @click="addSex">添加性别属性,默认值:男</button><br/>
            <button @click="addFriend">在列表首位添加一个朋友</button><br/>
            <button @click="updateFriend">修改第一个朋友的名字为:张三</button><br/>
            <button @click="addHobby">添加一个爱好</button><br/>
            <button @click="updateHobby">修改第一个爱好为:开车</button>
            <h2>姓名:{{student.name}}</h2>
            <h2>年龄:{{student.age}}</h2>
            <h2 v-if="student.sex">性别:{{student.sex}}</h2>
            <h2>爱好</h2>
            <ul>
                <li v-for="(h, index) in student.hobbys" :key="index">
                    {{h}}
                </li>
            </ul>
            <h2>朋友们</h2>
            <ul>
                <li v-for="(f, index) in student.friends" :key="index">
                    {{f.name}}--{{f.age}}
                </li>
            </ul>
        </div>
   </body>
   <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        
        //用computed实现
        new Vue({
            el:'#root',
            data:{
                student:{
                   name:'tom',
                   age:18,
                   hobbys:['喝酒','抽烟','烫头'],
                   friends:[
                       {name:'jerry', age:35},
                       {name:'tony', age:35}
                   ]
               }
            },
            methods: {
                addSex(){
                    Vue.set(this.student, 'sex', '男')
                },
                addFriend(){
                    const obj = {name:'lisi', age:20}
                    this.student.friends.unshift(obj)
                },
                updateFriend(){
                    Vue.set(this.student.friends[0], 'name', '张三')
                    //或 this.student.friends[0].name = '张三'
                },
                addHobby(){
                    this.student.hobbys.push('打麻将')
                },
                updateHobby(){
                    // this.student.hobbys[0]='开车' 不能直接操作数组中的对象,因为没有对应的setter和getter
                    this.student.hobbys.splice(0,1,'开车')
                    // 或 Vue.set(this.student.hobbys, 0, '开车')
                    // 或 this.$set(this.student.hobbys, 0, '开车')
                }
            }
            
        })   
    </script>
</html>
2.4 收集表单数据
效果如下:

收集表单数据:
若:<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属性:
a. v-model的初始值是非数组,那么收集的就是checked (勾选 or 未勾选,是布尔值)
b. v-model的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符:
lazy : 失去焦点再收集数据
number : 输入字符串转为有效的数字
trim : 输入首尾空格过滤
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>初识Vue</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
          <form @click="submit">
                <!-- 点击账号获取焦点:<label for="demo"></label> demo是input的id -->
                账号:<input type="text" v-model.trim="userInfo.account" /><br/><br/>
                密码:<input type="password" v-model.trim="userInfo.password" /> <br/><br/>
                年龄<input type="number" v-model.number="userInfo.age" /> <br/><br/>
                性别:
                    男<input type="radio" name="sex" v-model="userInfo.sex" value="male" />
                    女<input type="radio" name="sex" v-model="userInfo.sex" value="female" /><br/><br/>
                爱好:
                    喝酒:<input type="checkbox"  v-model="userInfo.hobby" value="hejiu"/>
                    抽烟:<input type="checkbox"  v-model="userInfo.hobby" value="chouyan"/>
                    烫头:<input type="checkbox" v-model="userInfo.hobby" value="tangtou"/><br/><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/><br/>
                其他信息:
                <textarea v-model="userInfo.other"></textarea><br/><br/>
                <input type="checkbox" v-model="userInfo.agree"> 阅读并接受<a href="http://baidu.com">《用户协议》</a><br/><br/>
                <button>提交</button>
          </form>  
          
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   userInfo:{
                        account:'',
                        password:'',
                        age:'',
                        sex: 'female',
                        hobby:[],
                        city:'beijing',
                        other:'',
                        agree:''
                   }
               },
               methods: {
                submit(){
                    console.log(JSON.stringify(this.userInfo))
                }
                   
               },
           })
        </script>
    </body>
</html>
2.5 过滤器
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理);
语法:
(1)注册过滤器:Vue.filter(name, callback) 或 new Vue(filters:{})
(2)使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
a. 过滤器也可以接收额外参数、多个过滤器也可以串联
b.并没有改变原本的数据,是产生新的对应的数据
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>过滤器</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>显示格式化后的时间</h2>
            <!-- 计算属性实现 -->
            <h3>现在是:{{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>现在是:{{getFmtTime()}}</h3>
            <!-- 过滤器实现 -->
            <!-- 1. 过滤器不传参,默认参数是time -->
            <h3>现在是:{{time | timeFormater}}</h3>
            <!-- 2. 过滤器传参,默认参数是time,传参format -->
            <h3>现在是:{{time | timeFormater('YYYY_MM_DD')}}</h3>
            <!-- 3. 过滤器链-->
            <h3>现在是(过滤器串联):{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
            <h3 :x="msg | mySlice">javascript</h3>
        </div>
        <hr/>
        <div id = "root2">
          <h2>消息:{{msg | mySlice}}</h2>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
            //全局过滤器
            Vue.filter('mySlice', function(val){
                return val.slice(0,4)
            } )
           new Vue({
               el:'#root',
               data:{
                   time:1621561377603, //时间戳
                   msg:'你好,JavaScript'
               },
               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:{
                   //str如果不为null,就用str;否则用默认值:YYYY年MM月DD日 HH:mm:ss
                    timeFormater(val, str='YYYY年MM月DD日 HH:mm:ss'){
                        return dayjs(val).format(str)
                    }
                    /* ,
                    mySlice(val){
                        return val.slice(0,4)
                    } */
                
               }
           }),
           new Vue({
                el:'#root2',
                data:{
                    msg:'helloWorld'
                }
           })
        </script>
    </body>
</html>
2.6 Vue的内置指令
2.6.1 v-text_指令
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>v-text_指令</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <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">
            <h2>{{name}}</h2>
            <div v-text="name"></div>
            
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   name:'你好,JavaScript'
               }
           })
        </script>
    </body>
</html>
2.6.2 v-html_指令
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>v-html_指令</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <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">
            <h2>你好:{{name}}</h2>
            <div v-html="str"></div>
            <div v-html="str2"></div>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   name:'JavaScript',
                   str:'<h3>你好啊</h3>',
                   str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>点击一下看看</a>'
               }
           })
        </script>
    </body>
</html>
2.6.3 v-once_指令
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>v-once_指令</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <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 type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   n:1
               }
           })
        </script>
    </body>
</html>
2.6.4 v-cloak_指令
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>v-cloak_指令</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
        <style>
            [v-cloak]{
                display: none;
            }
        </style>
    </head>
    <body>
        <!-- 
            v-cloak:指令(没有值):
                1. 本质是一个特殊属性。Vue实例创建完毕并接管容器后,会删掉v-cloak属性
                2. 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2 v-cloak>{{name}}</h2>
        </div>
        //这个位置延迟加载vue实例
    </body>
    <script type="text/javascript">
        Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
        new Vue({
            el:'#root',
            data:{
                name:'你好,JavaScript'
            }
        })
    </script>
    
</html>
2.6.5 v-pre_指令
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>v-pre_指令</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 
            v-pre:指令:
                1. 作用:跳过其所在节点的编译过程
                2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2 v-pre>初始化的n值</h2>
            <h2>当前的n值是:{{n}}</h2>
            <button @click="n++">点我n+1</button>
            
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   n:1
               }
           })
        </script>
    </body>
</html>
2.7 自定义指令
2.8 生命周期
生命周期图示

 
2.8.1 引出生命周期
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>引出生命周期</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 
            生命周期:
                1. 别名:生命周期回调函数、生命周期函数、生命周期钩子
                2. 是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数
                3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
                4. 生命周期函数中的this指向是vm 或 组件实例对象
         -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2 :style="{opacity}">欢迎学习Vue</h2>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   opacity:1
                    
               },
               methods: {
               },
               //Vue完成模板的解析,并把初始的真实的DOM元素放入页面后(挂载完毕)调用mounted
               mounted() {
                setInterval(() =>{
                        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>
</html>
2.8.2 生命周期_挂载流程
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>分析生命周期</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 
            生命周期:
                1. 别名:生命周期回调函数、生命周期函数、生命周期钩子
                2. 是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数
                3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
                4. 生命周期函数中的this指向是vm 或 组件实例对象
         -->
         <!-- P50 -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>当前的n值是: {{n}}</h2>
            <button @click="add">点我n + 1</button>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   n:1
               },
               methods: {
                   add(){
                       this.n++
                   }
               },
               //此时:无法通过vm访问到data中的数据、methods中的方法
               beforeCreate() {
                   console.log('beforeCreate');
                   
               },
               //此时:可以通过vm访问到data中的数据、methods中配置的方法
               created() {
                   console.log('created');
                   
               },
               //此时:1. 页面呈现的是未经Vue编译的DOM结构;2. 所有对DOM的操作,最终都不奏效
               beforeMount() {
                console.log('beforeMount');
               },
               //此时: 
               // 1. 页面中呈现的是经过Vue编译的DOM;
               // 2. 对DOM的操作均有效,至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
               mounted() {
                console.log('mounted');
                
               }
           })
        </script>
    </body>
</html>
2.8.3 生命周期_更新流程
beforeUpdate:当前data中的数据改变时触发;此时数据是改变的,但页面是旧数据,即:页面尚未和数据保持同步
updated : 此时:数据是最新的,页面也是最新的;即:页面和数据保持同步
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>分析生命周期</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 
            生命周期:
                1. 别名:生命周期回调函数、生命周期函数、生命周期钩子
                2. 是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数
                3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
                4. 生命周期函数中的this指向是vm 或 组件实例对象
         -->
         <!-- P50 -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>当前的n值是: {{n}}</h2>
            <button @click="add">点我n + 1</button>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   n:1
               },
               methods: {
                   add(){
                       this.n++
                   }
               },
               //此时:无法通过vm访问到data中的数据、methods中的方法
               beforeCreate() {
                   console.log('beforeCreate');
                   
               },
               //此时:可以通过vm访问到data中的数据、methods中配置的方法
               created() {
                   console.log('created');
                   
               },
               //此时:1. 页面呈现的是未经Vue编译的DOM结构;2. 所有对DOM的操作,最终都不奏效
               beforeMount() {
                console.log('beforeMount');
               },
               //此时: 
               // 1. 页面中呈现的是经过Vue编译的DOM;
               // 2. 对DOM的操作均有效,至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
               mounted() {
                console.log('mounted');
                
               },
               //当前data中的数据改变时触发;此时数据是改变的,但页面是旧数据,即:页面尚未和数据保持同步
               beforeUpdate() {
                   console.log('beforeUpdate');
               },
               //此时:数据是最新的,页面也是最新的;即:页面和数据保持同步
               updated() {
                   console.log('updated');
               }
           })
        </script>
    </body>
</html>
2.8.4 生命周期_销毁流程
destroy :
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>分析生命周期</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 
            生命周期:
                1. 别名:生命周期回调函数、生命周期函数、生命周期钩子
                2. 是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数
                3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
                4. 生命周期函数中的this指向是vm 或 组件实例对象
         -->
         <!-- P50 -->
        <!-- 准备一个容器-->
        <div id = "root">
            <h2>当前的n值是: {{n}}</h2>
            <button @click="add">点我n + 1</button>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                   n:1
               },
               methods: {
                   add(){
                       this.n++
                   }
               },
               //监听
               watch:{
                   n(){
                       console.log('n变了');
                   }
               },
               //此时:无法通过vm访问到data中的数据、methods中的方法
               beforeCreate() {
                   console.log('beforeCreate');
                   
               },
               //此时:可以通过vm访问到data中的数据、methods中配置的方法
               created() {
                   console.log('created');
                   
               },
               //此时:1. 页面呈现的是未经Vue编译的DOM结构;2. 所有对DOM的操作,最终都不奏效
               beforeMount() {
                console.log('beforeMount');
               },
               //此时: 
               // 1. 页面中呈现的是经过Vue编译的DOM;
               // 2. 对DOM的操作均有效,至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
               mounted() {
                console.log('mounted');
                
               },
               //当前data中的数据改变时触发;此时数据是改变的,但页面是旧数据,即:页面尚未和数据保持同步
               beforeUpdate() {
                   console.log('beforeUpdate');
               },
               //此时:数据是最新的,页面也是最新的;即:页面和数据保持同步
               updated() {
                   console.log('updated');
               },
               //此时:vm中所有的:data、methods、指令等等,都处于可用状态,马上要执行销毁过程,
               //一般在此阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
               beforeDestroy() {
                   console.log('beforeDestroy');
                   this.add()
                   debugger
               },
               destroyed() {
                   console.log('destroyed');
               }
           })
        </script>
    </body>
</html>
vm.$destroy()
用法: 完全销毁一个实例,清理它与其他实例的连接, 解绑它的全部指令及事件监听器;
触发 baforeDestroy 和 destroy 的钩子;
注意: 在大多数的场景中,你不应该调用这个方法。最好使用 v-if 和 v-for 指令以数据驱动的方式控制子组件的生命周期
生命周期总结:
常用的生命周期钩子:
1. mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
2. beforeDestroy: 清楚定时器、解绑自定义事件、取消订阅消息等【收尾工作】
关于销毁Vue实例:
1. 销毁后借助Vue开发者工具看不到任何信息
2. 销毁后自定义事件会失效,但原生DOM事件依然有效
3. 一般不会再beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>总结生命周期</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <h2  :style="{opacity}">欢迎学习Vue</h2>
            <button @click="opacity = 1">透明度设置为1</button>
            <button @click="stop">点我停止变换</button>
            
        </div>
        <script type="text/javascript">
            Vue.config.productionTip=false //阻止Vue在启动时生成生产提示
           new Vue({
               el:'#root',
               data:{
                    opacity:1
               },
               methods: {
                   stop(){
                       this.$destroy()
                   }
               },
               //监听
               watch:{
                   n(){
                       console.log('n变了');
                   }
               },
               //此时:无法通过vm访问到data中的数据、methods中的方法
               beforeCreate() {
                   console.log('beforeCreate');
               },
               //此时:可以通过vm访问到data中的数据、methods中配置的方法
               created() {
                   console.log('created');
                   
               },
               //此时:1. 页面呈现的是未经Vue编译的DOM结构;2. 所有对DOM的操作,最终都不奏效
               beforeMount() {
                console.log('beforeMount');
               },
               //此时: 
               // 1. 页面中呈现的是经过Vue编译的DOM;
               // 2. 对DOM的操作均有效,至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
               mounted() {
                console.log('mounted');
          //给vm实例新增timer属性
                this.timer = setInterval(() =>{
                        this.opacity -= 0.01
                        if(this.opacity <= 0){
                            this.opacity = 1
                        }
                    }, 16)
               },
               //当前data中的数据改变时触发;此时数据是改变的,但页面是旧数据,即:页面尚未和数据保持同步
               beforeUpdate() {
                   console.log('beforeUpdate');
               },
               //此时:数据是最新的,页面也是最新的;即:页面和数据保持同步
               updated() {
                   console.log('updated');
               },
               //此时:vm中所有的:data、methods、指令等等,都处于可用状态,马上要执行销毁过程,
               //一般在此阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
               beforeDestroy() {
                   console.log('beforeDestroy');
                   clearInterval(this.timer)
               },
               destroyed() {
                    console.log('destroyed');
               }
           })
        </script>
    </body>
</html>
2.9 组件
2.9.1 对组件的理解
传统方式编写应用

组件方式编写应用:

组件: 用来实现局部(特定)功能效果的代码集合(html/css/js/...)
作用: 复用编码、简化项目编码、提高运行效率
模块化:当应用中的js都以模块来编写的,那这个应用就是一个模块化的应用
组件化:当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用
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>
2.9.2 非单文件组件
定义: 一个文件中包含有n个组件
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>非单文件组件</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <hello></hello>
            <hr>
            <!-- 第三步: 编写组件标签 -->
            <school></school>
            <hr>
            <!-- 第三步: 编写组件标签 -->
            <student></student>
        </div>
        <hr>
        <div id="root2">
            <hello></hello>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
            //第一步: 创建school组件
            const school = Vue.extend({
                template:`
                    <div>
                        <h2>学校姓名: {{schoolName}}</h2>
                        <h2>学校地址: {{address}}</h2>
                    </div>
                `,
                //el : '#root', //一定不要写el配置,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
                data(){
                    return {
                        schoolName: 'JAVA',
                        address: '上海'
                    }
                }
            })
            //第一步: 创建student组件
            const student = Vue.extend({
                template:`
                    <div>
                        <h2>学生姓名: {{studentName}}</h2>
                        <h2>学生年龄: {{age}}</h2>
                    </div>
                `,
                //el: '#root',
                data(){
                    return {
                        studentName: '张三',
                        age: 12
                    }
                }
            })
            //第一步: 创建 hello 组件
            const hello = Vue.extend({
                template:`
                    <div>
                        <h2>你好啊:{{name}}</h2>
                    </div>
                `,
                data(){
                    return {
                        name: '张三'
                    }
                }
            })
            
            //第二步: 全局注册组件
            Vue.component('hello', hello);
            new Vue({
                el:'#root',
                //第二步: 注册组件(局部注册)
                components:{
                    school: school,
                    student: student
                }
            })
            new Vue({
                el:'#root2'
            })
        </script>
    </body>
</html>
单文件组件: 一个文件就是一个组件(例如: .vue结尾的文件)
2.9.3 组件的几个注意点
几个注意点:
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}
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>几个注意点</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <my-school></my-school>
        </div>
        <hr>
        <script type="text/javascript">
            Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
            const s = Vue.extend({
                template:`
                    <div>
                        <h2>学校姓名: {{schoolName}}</h2>
                        <h2>学校地址: {{address}}</h2>
                    </div>
                `,
                data(){
                    return {
                        schoolName: 'JAVA',
                        address: '上海'
                    }
                }
            })
            new Vue({
                el:'#root',
                //第二步: 注册组件(局部注册)
                components:{
                    'my-school': s
                }
            })
        </script>
    </body>
</html>
2.9.4 组件的嵌套
 
组件嵌套:
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>组件嵌套</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <!-- <app></app> -->
        </div>
        <hr>
        <script type="text/javascript">
            Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
            const student = Vue.extend({
                template:`
                    <div>
                        <h2>学生姓名: {{studentName}}</h2>
                        <h2>学生年龄: {{age}}</h2>
                    </div>
                `,
                data(){
                    return {
                        studentName: 'JAVA',
                        age: 18
                    }
                }
            })
            const school = Vue.extend({
                template:`
                    <div>
                        <h2>学校姓名: {{schoolName}}</h2>
                        <h2>学校地址: {{address}}</h2>
                        <hr/>
                        <student></student>
                    </div>
                `,
                data(){
                    return {
                        schoolName: 'JAVA',
                        address: '上海'
                    }
                },
                components:{
                    student
                }
            })
            const hello = Vue.extend({
                template:`
                    <h1>{{msg}}</h1>
                `,
                data(){
                    return {
                        msg: '欢迎'
                    }
                }
            })
            const app = Vue.extend({
                template:`
                    <div>
                        <hello></hello>
                        <school></school>
                    </div>
                `,
                components:{
                    school,
                    hello
                }
            })
            
            new Vue({
                template: '<app></app>',
                el:'#root',
                //第二步: 注册组件(局部注册)
                components:{
                    app
                }
            })
        </script>
    </body>
</html>
2.9.5 VueComponent 构造函数
关于VueComponent;
1. school组件本质是一个名为VueComponent的构造函数, 且不是程序员定义的,是Vue.extend 生成的。
2. 我们只需要写<school /> 或 <school></school> , Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的: new VueComponent(options)
3. 特别注意 : 每次调用Vue.extend , 返回的都是一个全新的VueComponet !!!
4. 关于this 指向:
(1)组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数, 它们的this 均是【VueComponent实例对象】
(2) new Vue() 配置中:
data函数、methods中的函数、watch中的函数、computed中的函数, 它们的this 均是【Vue实例对象】
5. VueComponent的实例对象, 以后简称vc(也可称之为:组件实例对象)
Vue的实例对象,以后简称为: vm
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>VueComponent</title>
        <!-- 引入Vue-->
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="../js/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备一个容器-->
        <div id = "root">
            <school></school>
            
        </div>
        <hr>
        <script type="text/javascript">
            Vue.config.productionTip = false //阻止Vue在启动时生成生产提示
            const school = Vue.extend({
                template:`
                    <div>
                        <h2>学校姓名: {{schoolName}}</h2>
                        <h2>学校地址: {{address}}</h2>
                        <button @click="showName">点我提示学校名称</button>
                    </div>
                `,
                data(){
                    return {
                        schoolName: '北京大学',
                        address: '上海'
                    }
                },
                methods: {
                    showName(){
                        alert(this.schoolName)
                    }
                },
            })
            new Vue({
                el:'#root',
                //第二步: 注册组件(局部注册)
                components:{
                    school
                }
            })
 
        </script>
    </body>
</html>
2.9.6 Vue实例和组件实例
2.9.7 一个重要的内置关系
1. 一个重要的内置关系: VueComponent.prototype._ _proto_ _=Vue.prototype
2. 为什么要有这个关系: 让组件实例对象(vc) 可以访问到Vue原型上的属性、方法
分析Vue 与 VueComponent的关系:

2.9.8 单文件组件
一般的单文件组件包含:
index.html
main.js
App.vue : 组件总管(管理若干组件)
xxx.vue : 组件若干
index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>练习一下单文件组件的语法</title>
</head>
<body>
  <!-- 准备一个容器 -->
  <div id="root">
    <App></App>
  </div>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script type="text/javascript" src="./main.js"></script>
  
</body>
</html>
main.js
import App from './App.vue'
new Vue({
  el: '#root',
  // template:`<App></App>`,
  components:{App}
})
App.vue
<template>
  <!-- 模板里面必须有一个根元素 -->
  <div>
    <School></School>
    <Student></Student>
  </div>
  
</template>
<script>
  //引入组件
  import School from './School'
  import Student from './Student'
  export default {
    name:'App',
    components:{
      School,
      Student
    }
  }
</script>
<style>
</style>
若干组件
<template>
  <!-- 组件结构 -->
  <div class="demo">
    <h2>学校姓名: {{schoolName}}</h2>
    <h2>学校地址: {{address}}</h2>
    <button @click="showName">点我提示学校名</button>
  </div>
</template>
<script>
  // 组件交互相关的代码(数据、方法等等)
  const school = Vue.extend({
    name:'school', 
    data(){
      return {
          schoolName: 'JAVA',
          address: '上海'
      }
    },
    methods: {
      showName(){
        alert(this.schoolName)
      }
    },
  })
  //必须暴露组件(下面是默认暴露方式)
  export default school
</script>
  
<style>
  /* 组件的样式 */
  .demo{
    background-color: orange;
  }
</style>
<template>
  <!-- 组件结构 -->
  <div>
    <h2>学生姓名: {{name}}</h2>
    <h2>学生年龄: {{age}}</h2>
    <button @click="showName">点我提示学生名</button>
  </div>
</template>
<script>
  // 组件交互相关的代码(数据、方法等等)
  export default {
    name:'Student', 
    data(){
      return {
          name: '张三',
          age: 18
      }
    },
    methods: {
      showName(){
        alert(this.name)
      }
    },
  }
</script>
  
3.0 Vue脚手架
说明:
a. Vue脚手架是Vue管方提供的标准化开发工具(开发平台)
b. 最新版本是4.x
c. 文档地址: https://cli.vuejs.org/zh/
安装:
1. 第一步(仅第一次执行):全局安装@vue/cli
npm install -g @vue/cli
2. 第二步:切换到你要创建项目的目录,然后使用命令创建项目
vue create xxxx
3. 启动项目
npm run serve
备注:
a. 如出现下载缓慢的情况, 请先配置npm淘宝镜像:
npm config set registry https://registry.npm.taobao.org
b. Vue脚手架隐藏了所有webpack相关的配置, 若想查看具体的webpack配置, 请执行:
vue inspect > output.js
3.1 创建Vue脚手架
 
 
  
3.2 分析脚手架结构
3.3 render函数
关于不同版本的Vue:
1.vue.js 与 vue.runtime.xxx.js的区别:
(1) vue.js是完整版的Vue,包含:核心功能 + 模板解析器
(2)vue.runtime.xxx.js 是运行版的vue. 只包含:核心功能, 没有模板解析器
2. 因为vue.runtime.xxx.js 没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容;
/* 
  该文件是整个项目的入口文件
*/
//引入VUe
import Vue from 'vue'
//引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false
//创建Vue实例对象 ---vm
new Vue({
  el:'#app',
  render: h => h(App),
})
3.4 修改默认配置
3.5 ref属性
ref属性:
1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:
打标识:<h1 ref="xxx"> ...</h1> 或 <School ref="xxx"></School>
获取: this.$refs.xxx
<template>
  <!-- 模板里面必须有一个根元素 -->
  <div>
    <img src="./assets/logo.png" alt="logo">
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDom">点我输出上方的DOM元素</button>
    <School ref="sch"></School>
  </div>
  
</template>
<script>
  //引入组件
  import School from './components/School.vue'
  export default {
    name:'App',
    components:{
      School
    },
    data() {
      return {
        msg:'欢迎学习Vue'
      }
    },
    methods: {
      showDom(){
        console.log(this.$refs.title)
        console.log(this.$refs.btn)  //获取的DOM
        console.log(this.$refs.sch)  //school的vc实例对象
      } 
    },
  }
</script>
<style>
</style>
3.6 props配置
功能:让组件接收外部传过来的数据
(1)传递数据:
<Demo name="xxx">
(2)接收数据:
第一种方式(只接收)
props:['name']
第二种方式(限制类型)
props:{
name:Number
}
第三种方式:(限制类型、限制必传性、指定默认值)
props:{
name:{
type: String, //类型
required: true, //必传性
default: ‘老张’, //默认值
}
}
备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告;
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
<template>
  <!-- 模板里面必须有一个根元素 -->
  <div>
    <!-- 对组件进行属性传值,属性字段不给就是不传值 -->
    <Student name="李四" sex="男"  :age="18"/>
  </div>
  
</template>
<script>
  //引入组件
  import Student from './components/Student.vue'
  export default {
    name:'App',
    components:{
      Student
    }
  }
</script>
<template>
  <!-- 组件结构 -->
  <div>
    <h2> {{msg}}</h2>
    <h2>学生姓名: {{name}}</h2>
    <h2>学生性别: {{sex}}</h2>
    <!-- 
      需求:修改age的值,复制一份到data中,再修改
      <h2>学生性别: {{age}}</h2> -->
    <h2>学生性别: {{myAge}}</h2>
    <button @click="updateAge">点我修改学生年龄</button>
  </div>
</template>
<script>
  // 组件交互相关的代码(数据、方法等等)
  export default {
    name: 'Student', 
    data(){
      return {
         msg:'我是一个学生',
         myAge: this.age
      }
    },
    methods: {
      updateAge(){
        this.myAge++
      }
    },
    //简单声明接收, 但优先级很高
    //props:['name','age','sex']
    //接收的同是对数据类型进行限制
    /* props:{
      name:String,
      age:Number,
      sex:String
    } */
    //接收的同时对数据类型:进行限制 + 默认值的指定 + 必传性的限制
    props:{
      name:{
        type:String, //name的类型是字符串
        required:true //name的值是必传的
      },
      age:{
        type:Number,
        default:99 //type没有传值,给出的默认值
      },
      sex:{
        type:String,
        required:true
      }
    }
  }
</script>
  
3.7 mixin 混入
功能: 可以把多个组件共用(相同)的配置提取成一个混入对象
使用方式:
第一步定义混合,例如:
{
data(){...},
methods(){...},
....
}
第二步使用混入, 例如:
(1)全局混入: Vue.mixin(xxx)
(2)局部混入: mixins:['xxx']
mixin.js
// 复用配置 export const hunhe = { methods: { showName(){ alert(this.name) } } } export const hunhe2 = { data() { return { x:111, y:222 } } }
/* 该文件是整个项目的入口文件 */ //引入VUe import Vue from 'vue' //引入App组件,它是所有组件的父组件 import App from './App.vue' //引入全局mixin import { hunhe,hunhe2 } from './mixin' //关闭vue的生产提示 Vue.config.productionTip = false //配置全局mixin(所有的vc和vm 中都会引入mixin) Vue.mixin(hunhe) Vue.mixin(hunhe2) //创建Vue实例对象 ---vm new Vue({ el:'#app', render: h => h(App), })
<template>
  <!-- 组件结构 -->
  <div class="demo">
    <h2 @click="showName">学校姓名: {{name}}</h2>
    <h2>学校地址: {{address}}</h2>
  </div>
</template>
<script>
  //引入hunhe
  import { hunhe } from '../mixin'
  // 组件交互相关的代码(数据、方法等等)
  export default {
    name: 'School', 
    data(){
      return {
          name: 'JAVA',
          address: '上海'
      }
    },
    mixins:[hunhe,hunhe2 ]
  }
  
</script>
  
<style>
  /* 组件的样式 */
  .demo{
    background-color: orange;
  }
</style>
<template>
  <!-- 组件结构 -->
  <div>
    <h2 @click="showName">学生姓名: {{name}}</h2>
    <h2>学生性别: {{sex}}</h2>
  </div>
</template>
<script>
  //引入hunhe
  import { hunhe } from '../mixin'
  // 组件交互相关的代码(数据、方法等等)
  export default {
    name: 'Student', 
    data(){
      return {
         name:'张三',
         sex:'男'
      }
    },
    mixins:[hunhe]
  }
</script>
  
4.0 插件
功能:用于增强Vue
本质:包含install方法的一个对象, install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue, options){
//1. 添加全局过滤器
Vue.filter(...)
//2. 添加全局指令
Vue.directive(....)
//3. 配置全局 混入
Vue.mixin(....)
//4. 添加实例方法
Vue.prototype.$myMethod = function(){.....}
Vue.prototype.$myProperty= xxx
}
使用插件:1. 先引入插件文件xxx
2. Vue.use(xxx)
插件文件:plugins.js
export default {
  install(Vue){
    //全局过滤器
    Vue.filter('mySlice', function(value){
      return values.slice(0,4)
    })
    //定义全局指令
    Vue.directive('fbind',{
      //指令与元素成功绑定时(一上来)
      bind(element, binding){
        element.value = binding.value
      },
      //指令所在元素别插入页面时
      inserted(element, binding){
        element.focus
      },
      //指令所在的模板被重新解析时
      update(element, binding){
        element.value = binding.value
      }
    })
    //定义混入
    Vue.mixin({
      data() {
        return {
          x:100,
          y:200
        }
      }
    })
    //给Vue原型上添加一个方法(vm 和 vc就都能用了)
    Vue.prototype.hello = ()=>{
      alert('你好啊')
    }
  }
}
main.js
/* 该文件是整个项目的入口文件 */ //引入VUe import Vue from 'vue' //引入App组件,它是所有组件的父组件 import App from './App.vue' //引入插件 import plugins from './plugins' //关闭vue的生产提示 Vue.config.productionTip = false //应用插件 Vue.use(plugins) //创建Vue实例对象 ---vm new Vue({ el:'#app', render: h => h(App), })
组件中使用插件
<template>
  <!-- 组件结构 -->
  <div>
    <h2 @click="showName">学生姓名: {{name}}</h2>
    <h2>学生性别: {{sex}}</h2>
    <input type="text" v-fbind:value="name" />
  </div>
</template>
<script>
  //引入hunhe
  import { hunhe } from '../mixin'
  // 组件交互相关的代码(数据、方法等等)
  export default {
    name: 'Student', 
    data(){
      return {
         name:'张三',
         sex:'男'
      }
    },
    mixins:[hunhe]
  }
</script>
  
scoped样式:
作用: 让样式在局部生效, 防止冲突
写法: <style scoped>
案例: Todo-list
组件化编码的通用流程:
1. 实现静态组件:抽取组件,使用组件实现静态页面效果
2. 展示动态数据:
2.1 数据的类型、名称是什么?
2.2 数据保存在哪个组件?
3. 交互---从绑定事件监听开始
效果:
 
编码:
App.vue
<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :receive="receive"/>
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
        <MyFooter :todos="todos" :checkAllDone="checkAllDone" :clearAllTodo="clearAllTodo"/>
      </div>
    </div>
  </div>
</template>
<script>
  import MyHeader from './components/MyHeader.vue'
  import MyList from './components/MyList.vue'
  import MyFooter from './components/MyFooter.vue'
  
  export default {
    name:'App',
    components:{
      MyHeader,
      MyList,
      MyFooter
    },
    data() {
      return {
        todos: [
          {id: '001', title: '吃饭', done: false},
          {id: '002', title: "睡觉", done: true},
          {id: '003', title: '打代码', done: false}
        ]
      }
    },
    methods: {
      //接收Header组件给的数据,进行添加操作
      receive(val){
        console.log('val', val);
        this.todos.unshift(val)
      },
      //勾选或取消勾选一个todo
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if (todo.id === id) {
            todo.done = !todo.done
            console.log(todo.id, todo.done);
            
          }
        })
      },
      //删除todo
      deleteTodo(id){
        this.todos = this.todos.filter((todo)=>{
          return todo.id !== id
        })
      },
      //todo全选或全不选
      checkAllDone(done){
        this.todos.forEach((todo)=>{
          todo.done = done
        })
      },
      clearAllTodo(){
        this.todos = this.todos.filter((todo)=>{{
          return !todo.done
        }})
      }
    },
  }
</script>
<style>
/*base*/
body {
  background: #fff;
}
.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}
.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}
.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}
.btn:focus {
  outline: none;
}
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>
MyHeader.vue
<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add" />
  </div>
</template>
<script>
  import {nanoid} from 'nanoid'
  export default {
    name: 'MyHeader',
    props:['receive'],
    data() {
      return {
        title:''
      }
    },
    methods: {
      add(event){
        //校验输入
        if (!this.title.trim()) return alert('输入不能为空')
        console.log(event.target.value);
        //将用户输入的包装成一个todo对象
        const todoObj = {id: nanoid(), title: this.title, done: false}
        //将输入传给App组件
        this.receive(todoObj)
        this.title = ''
      }
    },
  }
</script>
<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}
.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
MyList.vue
<template>
  <ul class="todo-main">
    <MyItem 
      v-for="todoObj in todos" 
      :key="todoObj.id" 
      :todo="todoObj" 
      :checkTodo="checkTodo"
      :deleteTodo="deleteTodo"
    />
  </ul>
  
</template>
<script>
  import MyItem from '../components/MyItem.vue'
  export default {
    name: 'MyList',
    components:{MyItem},
    props:['todos', 'checkTodo', 'deleteTodo']
  }
</script>
<style scoped>
/*main*/
.todo-main {
  margin-left: 0;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>
MyItem.vue
<template>
  <li>
    <label>
<!--      这里勾选和取消勾选可以使用change和click作为事件处理-->
      <input type="checkbox" :checked="todo.done" @click="handleCheck(todo.id)"/>
      
      <!--v-model数据的双向绑定,checkbox使用v-model来双向绑定其是否被勾选,也可以实现效果但不推荐(因为其实修改了props中的数据)-->
      <!--这里修改了从List修改过来的props,这里的不允许改是浅层次,就是如果props是一个对象则这个修改这个对象的某一个属性vue是放行的-->
      <!-- <input type="checkbox" v-model="todo.done"/>-->
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>
<script>
  export default {
    name: 'MyItem',
    props:['todo', 'checkTodo', 'deleteTodo'],  //声明接收todo对象(props组件间传递数据)
    methods: {
      handleCheck(id){
        //通知App组件将对应的todo对象的done值取反
        this.checkTodo(id)
      },
      //删除
      handleDelete(id){
        console.log(id);
        if (confirm('确定要删除吗')) {
          this.deleteTodo(id)
        }
      }
    },
    
  }
</script>
<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
li label {
  float: left;
  cursor: pointer;
}
li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}
li button {
  float: right;
  display: none;
  margin-top: 3px;
}
li:before {
  content: initial;
}
li:last-child {
  border-bottom: none;
}
li:hover{
  background: #ddd;
}
li:hover button{
  display: block;
}
</style>
MyFooter.vue
<template>
  <!--隐式类型转换-->
  <div class="todo-footer" v-show="total">
    <label>
      <!--这里也可用v-model来替代,此时不需要计算属性了-->
      <!--<input type="checkbox" v-model="isAll"/>-->
      <input type="checkbox" :checked="isAll" @change="checkAll"/>
    </label>
    <span>
      <span>已完成{{doneTotal}}/ 全部{{total}}</span>
    </span>
    <button class="btn btn-danger" @click="clearAll">清楚已完成任务</button>
  </div>
</template>
<script>
  export default {
    name: 'MyFooter',
    props:['todos','checkAllDone', 'clearAllTodo'],
    computed:{
      doneTotal(){
        /* const x = this.todos.reduce((pre, current)=>{
          console.log('@', pre, current);
          return pre + (current.done ? 1 : 0)
        }, 0) */
        return this.todos.reduce((pre,current)=>pre + (current.done ? 1 : 0), 0)
      },
      total(){
        return this.todos.length
      },
      isAll(){
        this.doneTotal === this.total && this.total > 0
      }
    },
    methods: {
      checkAll(event){
        this.checkAllDone(event.target.checked)
      },
      clearAll(){
        this.clearAllTodo()
      }
    },
  }
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>
总结:
一、组件编码流程
1. 拆分静态组件:组件要按照功能点拆分, 命名不要与html元素冲突
2. 实现动态组件:考虑好数据的存放位置。数据是一个组件在用, 还是一些组件在用:
(1)一个组件在用:放在组件自身即可;
(2)一些组件在用: 放在它们共同的父组件上(<span style="color:red">状态提升</span>)
(3)实现交互:从绑定事件开始
二、props适用于:
(1)父组件 ==>子组件 通信
(2)子组件 ==>父组件 通信(要求父先给子一个函数)
三、使用v-model时要切记: v-model 绑定的值不能是props传递过来的值, 因为props是不可以修改的
四、props传过来的若是对象类型的值, 修改对象中的属性时Vue不会报错, 但不推荐这样做;
二.vue-cli
webStorage :
1. 存储内容大小一般支持5MB左右(不同的浏览器可能还不一样)
2. 浏览器端通过window.sessionStorage 和 window.localStorage属性来实现本地存储机制
3. 相关API:
xxxStorage.setItem('key', value); 该方法接受一个键和值作为参数, 会把键值对添加到存储中, 如果键名存在, 则更新对应的值
xxxStorage.getItem('key'); 该方法接受一个键名作为参数, 返回键名对应的值
xxxStorage.removeItem('key'); 该方法接受一个键名作为参数, 并把该键名从存储中删除
4. 备注:
(1)SessionStorage存储的内容会随着浏览器窗口关闭而消失
(2)LocalStorage存储的内容,需要手动清除才会消失
(3)xxxxStorage.getItem(xxx) 如果xxxx对应的value获取不到, 那么getItem的返回值是null
(4)JSON.parse(null) 的结果依然是null
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>localStorage</title>
</head>
<body>
  <h2></h2>
  <button onclick="saveData()">点我保存一个数据</button>
  <button onclick="readData()">点我读取一个数据</button>
  <button onclick="deleteData()">点我删除一个数据</button>
  <button onclick="clearData()">点我清空一个数据</button>
</body>
<script>
  let p = {name:'张三', age:18}
  function saveData(){
    localStorage.setItem('msg', 'hello');
    localStorage.setItem('meg2', 18);
    localStorage.setItem('person', JSON.stringify(p));
  }
  function readData(){
    localStorage.getItem('msg');
    localStorage.getItem('meg2');
    const result = localStorage.getItem('person');
    console.log('person', JSON.parse(result));
    
  }
  function deleteData(){
    localStorage.removeItem('msg');
  }
  function clearData(){
    localStorage.clear();
  }
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>sessionStorage(浏览器关闭, sessionStorage就不存在)</title>
</head>
<body>
  <h2></h2>
  <button onclick="saveData()">点我保存一个数据</button>
  <button onclick="readData()">点我读取一个数据</button>
  <button onclick="deleteData()">点我删除一个数据</button>
  <button onclick="clearData()">点我清空一个数据</button>
</body>
<script>
  let p = {name:'张三', age:18}
  function saveData(){
    sessionStorage.setItem('msg', 'hello');
    sessionStorage.setItem('meg2', 18);
    sessionStorage.setItem('person', JSON.stringify(p));
  }
  function readData(){
    sessionStorage.getItem('msg');
    sessionStorage.getItem('meg2');
    const result = sessionStorage.getItem('person');
    console.log('person', JSON.parse(result));
    
  }
  function deleteData(){
    sessionStorage.removeItem('msg');
  }
  function clearData(){
    sessionStorage.clear();
  }
</script>
</html>
组件的自定义事件
1. 一种组件间通信的方式, 适用于:子组件 ===>父组件
2. 使用场景: A是父组件, B是子组件, B想给A传数据, 那么就要在A中给B绑定自定义事件(事件的回调在A中)
3. 绑定自定义事件:
(1)第一种方式, 在父组件中:<Demo @testA = "test"> 或 <Demo v-on:testA="test">
(2)第二种方式, 在父组件中:
<Demo ref="demo">
...
mounted(){
this.$refs.xxx.$on('testA', this.test)
}
(3)若想让自定义事件只能触发一次, 可以使用once修饰符, 或$once方法
4. 触发自定义事件:this.$emit('testA', 数据 )
5. 解绑自定义事件: this.$off('testA')
6. 组件上也可以绑定原生DOM事件, 需要使用native修饰符
7. 注意: 通过this.$refs.xxx.$on('testA', 回调)绑定自定义事件时, 回调要么配置在methods中, 要么用箭头函数, 否则this指向会出问题
    
P80
三.vue-router
四.vuex
五.element-ui
六 .vue3
VsCode中的vue插件:
1. Vue 3 Snippets :代码自动提示

2. Vetur 插件
3. Markdown Preview Enhanced
VsCode中适配markdown的一款插件:适合记笔记
4. Typora 软件记笔记
5.引入UUId的精简版---> nanoid
命令: npm i nanoid
import {nanoid} from 'nanoid'
const todoObj = {id: nanoid()}
Vue总结:
https://blog.csdn.net/weixin_44972008/category_10622253.html
别人总结的:
https://blog.csdn.net/hangao233/article/details/123990192?spm=1001.2014.3001.5502
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号