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 数据代理

回顾Object.defineproperty方法
<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 深度监视

深度监视
     1. Vue中的watch默认不监视对象内部值得改变(一层)
     2. 配置deep:true可以监测对象内部值改变(多层)
备注:
    1. Vue自身可以监测对象内部值得改变。但Vue提供的watch默认不可以;
     2. 使用watch时根据数据的具体结构。决定是否采用深度监视;
<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 绑定样式

 

绑定样式:
1. class样式
                    写法: class="xxx" , xxx可以是字符串、对象、数组;
                         字符串写法适用于:类名不确定,要动态获取;
                         对象写法适用于: 要绑定多个样式,个数不确定,名字也不确定
                         数组写法适用于: 要绑定多个样式,个数确定,名字也确定,但不确定用不用
2. style样式
                    :style="{fontSize: xxx}" 其中xxx是动态值
                    :style="[a,b]" 其中a、b是样式对象

 

<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_指令

    v-bind : 单向绑定解析表达式,可简写为 :xxx
            v-model : 双向数据绑定
            v-for : 遍历数组/对象/字符串
            v-on : 绑定事件监听,可简写为@
            v-if : 条件渲染(动态控制节点是否存在)
            v-else : 条件渲染(动态控制节点是否存在)
            v-show : 条件渲染(动态控制节点是否展示)

           v-text:指令:
                1. 作用: 向其所在的节点中渲染文本内容;
                2. 与插值语法的区别:v-text 会替换掉节点中的内容,{{xx}}则不会
<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_指令

v-html:指令:
                1. 作用:向指定节点中渲染包含html结构的内容;
                2. 与插值语法的区别:
                    (1)v-html会替换掉节点中的所有内容,{{xx}}则不会。
                    (2)v-html可以识别html结构;
                3. 严重注意:v-html有安全性问题!!!
                    (1)在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击;
                    (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_指令

v-once_指令:
                1. v-once所在节点在初次动态渲染后,就视为静态内容了
                2. 以后数据的改变不会引起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_指令

v-cloak:指令(没有值):
                1. 本质是一个特殊属性。Vue实例创建完毕并接管容器后,会删掉v-cloak属性
                2. 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
<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_指令

v-pre:指令:
                1. 作用:跳过其所在节点的编译过程
                2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
<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 引出生命周期

 生命周期:
                1. 别名:生命周期回调函数、生命周期函数、生命周期钩子
                2. 是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数
                3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
                4. 生命周期函数中的this指向是vm 或 组件实例对象
<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 生命周期_挂载流程

mounted
此时:
1. 页面中呈现的是经过Vue编译的DOM;
2. 对DOM的操作均有效,至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作

 

<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

此时:vm中所有的:data、methods、指令等等,都处于可用状态,马上要执行销毁过程,
一般在此阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作

 

<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

posted @ 2022-04-28 09:10  IT6889  阅读(200)  评论(0编辑  收藏  举报