- 当我们把vue引入的时候,实际上只引入了一个对象,就是 Vue 对象

    - 好比引入 jQuery 的时候,实际上只引入了'$'或者'jQuery'对象

- 删除Vue的提示信息

    Vue.config.productionTip = false;

hello 示例

<head>
    ......
    <script src="js/v2.6.10/vue.js"></script> <!--引入vue-->
</head>
<body>
    <div id="container">
        {{name}}
    </div>
    
    <script type="text/javascript">
        var app = new Vue({
            el:'#container',
            data:{
                name:'Jim Green'
            },
        })
    </script>
</body>
- 想让vue工作,就必须创建一个vue实例,且要传入一个配置对象
- container容器里面的代码依然符合html规范,只不过混入了一些特殊的vue语法
- container容器里面的代码被称为'vue模板'

分析'Hello'示例

  • Vue对象和容器之间的关系 只能是'一对一关系'(一个Vue实例不会关照多个容器)
<html>
    <head>
        <script src="js/v2.6.10/vue.js"></script>
    </head>
    <body>
        <div class="container">
            {{name}} <!--有效果-->
        </div>
        
        <div class="container">
            {{name}} <!--不会生效-->
        </div>
        
        <script type="text/javascript">
            var app = new Vue({
                el:'.container',
                data:{
                    name:'Jim Green'
                },
            })
        </script>
    </body>
</html>
  • 区分"js表达式"和'js代码(语句)'
- 表达式: 一个表达式会产生一个值,可以放在任何需要该值的地方

    - 简单讲,就是有返回值

    - 例如

        - a
        - a + b
        - demo(1)
        - x === y ? 'a' : 'b'

- js代码(语句)

    - if(){...}
    - for(){...}
  • vue-devtools的使用: Root 指的就是管理这个容器的Vue实例(上手体验一下)

  • 小结


    - 真实开发中,只有一个Vue实例,并且会配合着'组件'(就是'小弟',帮助实例干活)一起使用

    - {{xxx}}, xxx中要写"js表达式",且xxx可以自动读取到data中的所有属性

    - 一旦 data 中的数据发生改变,那么模板中用到该数据的地方也会自动更新

模板语法

  • 插值语法: {

    • 功能:解析标签体内容

    • 写法: {{xxx}},xxx 是js表达式,且可以直接读到data中的所有属性

  • 指令语法: v-xxx

    • 功能: 用于解析标签(标签属性/标签体内容/绑定事件)

    • 举例: v-bind:href='xxx',xxx同样要写js表达式,也可以直接读到data中的所有属性

<body>
    <div id="container">
        {{name}} <br> <!--插值语法-->
        <a v-bind:href="url">百度链接</a> <!--指令语法-->
        <a :href="new_url" :x="hello">新浪链接</a> <!--这里可以简写(仅限于v-bind)-->
        <div :msg=company.name :address=company.address></div> <!--把数据绑定在一个对象上面-->
    </div>
    
    
    <script type="text/javascript">
        var app = new Vue({
            el:'#container',
            data:{
                name:'Jim Green',
                url:'http://www.baidu.com',
                new_url:'http://www.sina.com',
                hello:'world',
                company:{
                    name:'aclas',
                    address:'yyyyy'
                }
            },
        })
    </script>
</body>

数据绑定

  • 单向绑定

    • 使用 v-bind: 数据只能从data流向页面
  • 双向绑定

    • 使用 v-model: data和页面数据之间,可以互相影响

    • 局限性:只能应用在 表单类元素/输入类元素上面

  • 基础demo

......
<body>
    <div id="container">
        <form action="" method="">                   <!--单向绑定-->
            用户名: <input type="text" name="" id="" :value="name" /><br>
                                                    <!--双向绑定-->
            <!--<......简写(默认就是value,所以value可以不用写): v-model="email" />-->
            邮箱: <input type="email" name="" id="" v-model:value="email" />
        </form>

        <!--错误写法:这个属性不会生效,只能应用在表单类元素上面-->
        <h2 v-model:x="email">Hello</h2>
    </div>
    
    
    <script type="text/javascript">
        var app = new Vue({
            el:'#container',
            data:{
                name:'username',
                email:'agassics@sina.com'
            },
        })
    </script>
</body>
......

vue 实例大概可以看成由三部分组成


- "$"开头的属性,例如 $attrs,$children...: 是给'开发者'调用的

- "_"开头的属性,例如 _c,_data...: 是vue的底层代码,一般无需理会

- "Prototype": 原型,和js对象一样

vue 实例配置对象中,'el'和'data'的两种写法

  • 'el'的另外一种写法:以下写法,一模一样的效果
......
<body>
    <div id="container">
        Title: {{name}}
    </div>
    
    
    <script type="text/javascript">
        var vm = new Vue({
            // el:'#container',
            data:{
                name:'Jim Green',
            },
        })
        // 原型里面,有$mount()这个函数
        vm.$mount('#container')
    </script>
</body>

- 上述示例小改一下,使用定时器,效果一开始是双括号,然后才变成data定义的值

......
<body>
    <div id="container">
        Title: {{name}}
    </div>
    
    
    <script type="text/javascript">
        var vm = new Vue({
            // el:'#container',
            data:{
                name:'Jim Green',
            },
        })
        setTimeout(()=>{ // 新增定时器
            vm.$mount('#container')
        },1000)
    </script>
</body>

  • data的第二种写法(函数式): 写成函数的形式,然后返回一个配置对象

    • 第一种写法也可以称为'对象式'

    • 细节问题:函数一定要写成 function 的形式(this指向Vue实例)
      若写成'箭头函数'形式,this指向window对象

    • 用到'组件'的时候,data一定要写'函数式'(function形式)

<body>
    <div id="container">
        Title: {{name}}
    </div>
    
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            // data:{
            //  name:'Jim Green'
            // }
            data:function(){ // 该函数必须返回一个'配置对象'
                console.log(this) // Vue实例
                return {
                    name:'Jim Green'
                }
            }
        })
        
    </script>
</body>

- 箭头函数this测试

......
<script type="text/javascript">
    var vm = new Vue({
        el:'#container',
        data:()=>{
            console.log(this); // this指向window对象
            return {
                name:'Jim Green'
            }
        }
    })
    
</script>

MVVM 模型

  • M:模型(Model),对应 data 中的数据

  • V:视图(View),模板

  • VM:视图模型(ViewModel),Vue实例对象

  • 简单理解: Vue实例 就是后端js逻辑和前端模板之间的桥梁,Vue实例就是'中间商'

data属性解析

  • data中的所有属性,变量vm都有

  • 而 vm 身上所有的属性,以及Vue原型上的所有属性,在Vue模板中,都可以直接使用

......
<body>
    <div id="container">
        Title: {{name}}
        Address: {{address}} // data里面的
        Other1: {{$children}} 
        Other2: {{_c}} // vm 里面的
        Other3: {{$emit}} // 原型里面的
    </div>
    
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:function(){
                return {
                    name:'Jim Green',
                    address:'Xiamen'
                }
            }
        })
        
    </script>
</body>
......

Object.defineProperty: 给对象增加属性

......
<script type="text/javascript">
    var person = {
        name:'JimGreen',
        age:18
    }
    Object.defineProperty(person,'sex',{ // 给 person 新增 sex 属性值
        value:'male'
    })
</script>
......

- 返回结果

    - name,age(颜色标识一致)
      sex(颜色标识和上面不一样): 'sex' 属性不可被'枚举'(就是不可被'遍历')

        - console.log(Object.keys(person)) 返回结果 ['name','age']

        - 如何让 sex 参与'枚举'呢?配置对象添加一个参数即可

            Object.defineProperty(person,'sex',{
                value:'male',
                enumerable:true // 添加之处
            })

            - console.log(Object.keys(person)) 返回结果 ['name','age','sex']

            - 此时,sex 属性是无法被修改的,测试如下:

                >person.sex = 'female'
                >'female'
                >person
                >{name: 'JimGreen', age: 18, sex: 'male'} // 无法被修改

            - 同样的解决方式,配置一个参数即可

                Object.defineProperty(person,'sex',{
                    value:'male',
                    enumerable:true,
                    writable:true, // 新增
                })

            - 此时,sex 属性是无法被删除的,测试如下:

                >delete person.sex
                >false

            - 同样的解决方式,配置一个参数即可

                Object.defineProperty(person,'sex',{
                    value:'male',
                    enumerable:true,
                    writable:true,
                    configurable:true // 新增
                })

                >delete person.sex
                >true

    - 小结:

        - enumerable:true, 是否可以被枚举,默认 false
        - writable:true, 是否可以被修改,默认 false
        - configurable:true, 是否可以被删除,默认 false

  • 双向绑定对象属性: get/set
<body>
    <div id="container">
        1111
    </div>
    
    <script type="text/javascript">
        var num = 18
        var person = {
                name:'JimGreen',
                sex:'male',
            }
        // 把 num值 赋予 person.age 属性值
        Object.defineProperty(person,'age',{
            get:function(){ // 可以简写成 get(){......}
                return num
            }
            }

        })
    </script>
</body>

- 现在,无论如何修改 num值, person.age值也会跟着变

- 小结: 当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值

- set 方法: 当修改了person的age属性时,set函数会被调用,且会受到修改的值
<script type="text/javascript">
    var num = 18
    var person = {
            name:'JimGreen',
            sex:'male',
        }
    Object.defineProperty(person,'age',{
        get(){
            return num
        },
        set(value){
            num=value;
        }
    })
</script>

    // 测试
    >person.age = 22
    >22
    >num // 跟着变
    >22

数据代理

  • 定义: 通过一个对象,代理 对另外一个对象的属性操作(读/写)

  • demo演示

......
<script type="text/javascript">
    var num1 = {
        x:100
    }
    var num2 = {
            y:200
        }
    Object.defineProperty(num2,'x',{
        get(){
            return num1.x
        },
        set(value){
            num1.x=value
        }
    })
</script>
......
  • Vue实例中的'_data',就是js中,Vue的data
......
<body>
    <div id="container">
        {{name}}--{{age}}
    </div>
    
    <script type="text/javascript">
        // 定义一个对象
        var data = {
            name:'JimGreen',
            age:18
        }
        var vm = new Vue({
            el:'#container',
            data:data // 赋值
        })
    </script>
</body>

// 控制台验证
>vm._data === data
>true

  • 小结:
- Vue中的数据代理:

    - 通过vm对象来代理data对象中属性的操作(读/写)

    - 好处: 更加方便的操作data中的数据

    - 原理:

        - 通过 Object.defineProperty()把data对象中所有的属性添加到vm上

        - 为每一个添加到vm上的属性,都指定一个 gettter/setter

        - 在 gettter/setter 内部去操作data中对应的属性

绑定事件

  • 点击事件示例
......
<body>
    <div id="container">
        {{name}}--{{age}}
                                <!--绑定点击事件-->
        <button type="button" v-on:click="showMsg">点我提示</button>
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                showMsg(){ // 执行事件逻辑
                    alert('Hello!')
                }
            }
        })
    </script>
</body>
......
  • 自动传参: event 参数

    • 作用: 存储本次事件的全部信息,通过它可以获取事件的各种信息
<body>
    <div id="container">
        {{name}}--{{age}}
                             <!--绑定点击事件-->
        <button type="button" v-on:click="showMsg">点我提示</button>
                                        <!--简写形式-->
        <!--<button type="button" @click="showMsg">点我提示</button>-->
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                showMsg(event){ // 这个是js自动帮我们传过来的
                    // alert('Hello!')
                    // 执行结果: '点我提示'
                    console.log(event.target.innerText)
                }
            }
        })
    </script>
</body>
  • methods 里面的方法,this指向问题

    • 结论:methods里面,不要写成箭头函数的形式,因为this指向问题
......
methods:{
    showMsg(event){
        console.log(this) // 结果: Vue实例
    }
}
......
methods:{
    showMsg:(event)=>{
        console.log(this) // 结果: 指向window对象
    }
}
  • 传参的写法
......
<div id="container">
    {{name}}--{{age}}
                                <!--关注之处-->
    <button type="button" @click="showMsg(666)">点我提示</button>
</div>
......
<script type="text/javascript">
    var vm = new Vue({
        ......
        methods:{
            // 形参传进来
            showMsg:(number)=>{
                console.log(number) // 结果: 666
            }
        }
    })
</script>

- 此时,有一个问题: event 参数丢失

......
methods:{
    showMsg:(number,a,b,c)=>{
        console.log(number,a,b,c) // 结果: 666 undefined undefined undefined
    }
}

- 解决办法: 使用 Vue 提供的形参'$event'(不要去修改它的名称)
......
                                    <!--传入 $event-->
<button type="button" @click="showMsg(666,$event)">点我提示</button>
......
methods:{
    showMsg:(number,a,b,c)=>{
        console.log(number,a,b,c) // 结果: 666 event对象 undefined undefined
    }
}

  • 小结:

    • 使用'v-on:xxx' 或 '@xxx'绑定事件,其中xxx是事件名

    • @click="showMsg"和@click="showMsg($event)"效果一致,但是后者支持传参

事件修饰符

<body>
    <div id="container">
        <h4>测试</h4>
        <a href="http://www.baidu.com" @click="showMsg">点我提示信息</a>
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                showMsg(){
                    alert('Hello!')
                }
            }
        })
    </script>
......
  • 现在,阻止页面被带走,可以这么写
......
methods:{
    showMsg(event){
        event.preventDefault() // 阻止页面被带走
        alert('Hello!')
    }
}
......
                            <!--关注之处,添加修饰符-->
a href="http://www.baidu.com" @click.prevent="showMsg">点我提示信息</a>
methods:{
    showMsg(event){
        // event.preventDefault() 这句不用了
        alert('Hello!')
    }
}
  • 常用修饰符
- prevent: 阻止默认事件

- stop: 阻止事件冒泡

- once: 限制事件只能触发一次


- capture: 使用事件的"捕获模式"
- self: 只有 event.target 是当前操作的元素,才触发事件
  • 冒泡事件解析
<body>
    <div id="container">
       ......
        <!--容器和按钮都绑定点击事件,为了方便,绑定同一个点击事件-->
        <div class="box" @click="test">
            <button type="button" @click="test">测试</button>
        </div>
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            ......
            methods:{
                ......
                test(){
                    alert('hello!!!')
                }
            }
        })
    </script>
</body>

- 结果: 点击按钮以后,弹窗两次,因为事件向上冒泡了,导致容器里面的点击事件也被触发

- 解决办法

......
ethods:{
    ......
    test(event){
        event.stopPropagation() // 阻止事件冒泡
        alert('hello!!!')
    }
}
............
                        <!--使用修饰符,可以添加prevent修饰符: @click.stop.prevent="test"-->
<button type="button" @click.stop="test">测试</button>

  • once: 事件只能执行一次
......
<div>                     <!--添加once修饰符-->
    <button type="button" @click.once="test1">测试1</button>
</div>
......
methods:{
    ......
    test1(){
        alert('hhh')
    }
}
  • self: 只有 event.target 是当前操作的元素,才触发事件,把之前的示例小改一下
<!--绑定同一个点击事件-->
<div class="box" @click="test">
    <button type="button" @click="test">测试</button>
</div>
......
test(event){
    console.log(event.target) // <button type="button">测试</button> 输出两遍
}
......

- 可以看出,冒泡的时候, event.target始终指向内层的按钮
  若在外层容器使用 self 修饰符,由于 event.target 不属于容器自身,所以点击事件会失效
  从而也达到'阻止冒泡'的效果

......
                 <!--添加self修饰符,阻止冒泡-->
<div class="box" @click.self="test">
    <button type="button" @click="test">测试</button>
</div>

键盘事件

  • 引入demo: 给"input框"绑定键盘事件,获取用户输入的值
<body>
    <div id="container">
                                                <!--绑定keyup-->
        <input type="text" @keyup="showMsg" />
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                showMsg(event){
                    console.log(event.target.value) // 输出input框的值
                },
            }
        })
    </script>
</body>
  • 把上述demo小改一下,当用户按下'回车'的时候,才显示'input框'的值
......
methods:{
    showMsg(event){
        // 回车键的数字编码是 13,当不是它的时候,就返回空值,满足需求
        if(event.keyCode !== 13) return 
        console.log(event.target.value)
    },
}
......
- 上述示例还可以这么写

    - 把 if 判断注释掉,Vue模板里面,修改一句('.enter'): <input type="text" @keyup.enter="showMsg" />

- 还有使用数字编码(不推荐这种写法)

    <input type="text" name="" id="" value="" @keydown.13="showMsg" />
  • 小结:
- vue 中常用的按键别名

    - 回车: enter

    - 删除: delete(捕获'删除键'和'退格键')

    - 退出: esc

    - 空格: space

    - 换行: tab

    - 上: up

    - 下: down

    - 左: left

    - 右: right

- vue 未提供别名的按键,可以使用按键原始的key值去绑定,但要注意转为"kebab-case"(短横线命名)

- 系统修饰键(用法特殊): ctrl,alt,shift,meta(window键)

    - 配合keyup使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发

    - 配合keydown使用: 正常触发事件

- 也可以使用keyCode去指定具体的按键(不推荐,因为数字编码不是通用的...)

- Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名

  • js提供查看 按键和按键码的API
......
<body>
    <div id="container">
                                                    <!--绑定事件-->
        <input type="text" name="" id="" value="" @keyup="showMsg" />
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                showMsg(event){
                    console.log(event.key,event.keyCode) // Enter 13
                },
            }
        })
    </script>
</body>
......
  • Caps Lock 按键 demo 演示: 只有按下这个键,才显示input框的值
......
<div id="container">
                                                <!---注意Vue提供的这种格式->
    <input type="text" name="" id="" value="" @keyup.caps-lock="showMsg" />
</div>

<script type="text/javascript">
    var vm = new Vue({
        el:'#container',
        data:{
            name:'JimGreen',
            age:18
        },
        methods:{
            showMsg(event){
                console.log(event.target.value)
            },
        }
    })
</script>
  • Tab 按键 demo 演示: 只有按下这个键,才显示input框的值
......
        <!--无效果,因为按下的瞬间,已经失去焦点,keyup事件不会被触发-->            
<input type="text" name="" id="" value="" @keyup.tab="showMsg" />
......

- 解决办法: 使用 keydown事件即可
......
<input type="text" name="" id="" value="" @keydown.tab="showMsg" />
......
  • 自定义按键的'别名'
......
<body>
    <div id="container">                            <!--应用-->
        <input type="text" name="" id="" value="" @keydown.huiche="showMsg" />
    </div>
    
    <script type="text/javascript">
        Vue.config.keyCodes.huiche = 13; // 加这句
        .......
    </script>
</body>
  • 自定义'快捷键'
<body>
    <div id="container">                            <!--ctrl+y才生效-->
        <input type="text" name="" id="" value="" @keydown.ctrl.y="showMsg" />
    </div>
    
    <script type="text/javascript">
       ......
            methods:{
                showMsg(event){
                    console.log(event.target.value)
                },
            }
        ......
    </script>
</body>

计算属性

  • 引入场景: 姓名,年龄案例之插值语法
......
<body>
    <div id="container">
        姓名: <input type="text" name="" id="" v-model="name" /> <br>
        年龄: <input type="text" name="" id="" v-model="age" /> <br>
        小结: <span>{{name + '--' +age}}</span>
    </div>
    
    <script type="text/javascript">
        // Vue.config.keyCodes.huiche = 13;
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{
                
            }
        })
    </script>
</body>
......
  • 上述示例可以小改成这样
......
<div id="container">
    ......
    小结: <span>{{name}} -- {{age}}</span> <!--改进之处-->
</div>
  • 改造上述示例,"小结"部分改成'只保留前三个字符'
<div id="container">
    ......
    小结: <span>{{name.slice(0,3)}} -- {{age}}</span> <!--修改之处-->
</div>
......

- 虽然实现了需求,但是这种写法不够简洁,假设有更多逻辑的话,代码就更复杂

  • 把上述复杂需求,使用方法来实现
......
<body>
    <div id="container">
        ......
                    <!--这里一定要加括号,否则就是返回'函数对象'-->
        小结: <span>{{showMsg()}}</span>
    </div>
    
    <script type="text/javascript">
    
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            methods:{ // 修改之处
                showMsg(){
                    return this.name + '--' + this.age
                }
            }
        })
    </script>
</body>
......
  • 如果嫌弃使用方法的方式,代码还是不够简洁(多加了一对括号),就可以使用'计算属性'来实现
......
<body>
    <div......
                <!--是'属性',加括号会报错-->
        小结: <span>{{showMsg}}</span>
    </div>
    
    <script type="text/javascript">
        var vm = new Vue({
            el:'#container',
            data:{
                name:'JimGreen',
                age:18
            },
            computed:{ // 属性,配置成对象,对象里面配置key,返回get()方法即可
                showMsg:{
                    get(){
                        // 这里的this被Vue作了优化,指向'Vue实例'
                        return this.name + '--' + this.age
                    }
                }
            }
        })
    </script>
</body>
......

- get()的作用: 当有人读取了 showMsg 时,get就会被调用,且返回值就作为 showMsg 的值
  • 小结: get()什么时候被调用
- 初次调用 showMsg 的时候

- 所依赖的数据发生变化,get()会再次被调用,从而实现数据的更新

- 注意事项: 为什么不写在methods?原因是 methods太过活跃

	- 每调用一次 methods,里面的逻辑就执行一次

	- 而 computed 不一样,有'缓存',不会这么活跃,从而节省性能开销
  • set()介绍

    • 什么时候被调用: 当 showMsg 的值被修改的时候
......
<script>
	var vm = new Vue({
		el:'#container',
		data:{
			name:'Jim Green',
			age:18
		},
		computed: {
			showMsg:{
				get(){
					return this.name + '--' + this.age
				},
				set(value){ // 关注之处
					console.log('set被调用了',value)
				}
			}
		},
	})
	Vue.config.productionTip = false;
</script>
......执行结果:

>vm.showMsg = 'king' // 修改值,触发了set()
index.html:30 set被调用了 king


  • 当我们修改 showMsg 的值时,也修改 name 和 age,从而实现页面数据实时刷新
......
computed: {
	showMsg:{
		get(){
			......
		},
		set(value){ // value 就是 showMsg 的值
			var arr = value.split('--')
			this.name=arr[0]
			this.age=arr[1]
		}
	}
},


- 计算属性小结

  • 定义: 要用的属性不存在,要通过已有属性计算得来

  • 原理: 底层借助了 Object.defineproperty 方法提供的 getter 和 setter

  • 优势: 相比 methods,由于有了'缓存机制',效率更高

  • 注意事项:

    • "计算属性"最终会出现在vm上,直接读取使用即可

    • 如果'计算属性'要被修改,那必须写set()去实现,且set中要引起计算时依赖的数据发生变化

  • 计算属性的简写形式

    • 前提条件:只读取数据,而不修改数据

    • 即只实现get,而放弃set

......
computed: {
	// showMsg:{
	// 	get(){
	// 		return this.name + '--' + this.age
	// 	},
	// 	set(value){
	// 		var arr = value.split('--')
	// 		this.name=arr[0]
	// 		this.age=arr[1]
	// 	}
	// }
	showMsg(){ // 写成函数的形式即可
		return this.name + '--' + this.age
	}
},

监视属性

  • 引入场景:天气预报案例分析
......
<body>
	<div id="container">
		<!--三元运算符解决问题,只不过此时是写死的-->
		<h2>今天天气很{{isHot ? '炎热' : '凉爽'}}</h2>
	</div>
	<script>
		var vm = new Vue({
			el:'#container',
			data:{
				isHot:true
			},
		})
		Vue.config.productionTip = false;
	</script>
</body>
  • 把上述示例修改成用'方法'/'属性'实现
......
<div id="container">
				<!--计算属性-->
	<h2>今天天气很{{info}}</h2>
			<!--绑定点击事件-->
	<button @click="changeWeather">改变天气</button>
</div>
<script>
	var vm = new Vue({
		el:'#container',
		data:{
			isHot:true
		},
		computed: {
			info() {
				// data的值一旦改变,计算属性跟着变
				return this.isHot?'炎热':'寒冷'
			}
		},
		methods: {
			changeWeather() {
				// 修改 data的值
				this.isHot = !this.isHot
			}
		},
	......
  • @click='methods'的简写形式

    • 当代码逻辑很简单的时候,可以考虑这种写法
......
<div id="container">
	<h2>今天天气很{{info}}</h2>
			<!--把简单代码逻辑写在这里-->
	<button @click="isHot = !isHot">改变天气</button>
</div>
<script>
	var vm = new Vue({
		el:'#container',
		data:{
			isHot:true
		},
		computed: {
			info() {
				return this.isHot?'炎热':'寒冷'
			}
		},
		// 注释掉 methods
  • 简写的时候,注意以下示例
......
			<!--window对象不可以省略-->
<button @click="window.alert(111)">改变天气</button>
......
var vm = new Vue({
	el:'#container',
	data:{
		isHot:true,
		// window:window 这种写法也可以
		window // 简写形式
	},

监视属性

  • 引入场景
......
<body>
	<div id="container">
		<h2>今天天气很{{info}}</h2>
		<button @click="isHot = !isHot">改变天气</button>
	</div>
	<script>
		var vm = new Vue({
			el:'#container',
			data:{
				isHot:true,
			},
			computed: {
				info() {
					return this.isHot?'炎热':'寒冷'
				}
			},
			watch:{
				isHot:{ // 配置哪个data被监视
					handler(){ // handler什么时候被调用?当isHot发生改变的时候
						console.log('isHot被修改了!')
					}
				}
			}
			
		})
		Vue.config.productionTip = false;
	</script>
</body>
  • handler(newValue,oldValue)的真正用法是这样
......
watch:{
	isHot:{
		// 可以获取到 新的值 以及 以前的值
		handler(newValue,oldValue){
			console.log('isHot被修改了!',newValue,oldValue)
		}
	}
}
......

- 这里还可以增加一个配置项'immediate',初始化时,让hander()调用一次

......
watch:{
		isHot:{
			immediate:true, // 即时 isHot 的值没有被修改,handler也会调用一次
			handler(newValue,oldValue){
										// oldValue 为 undefined
				console.log('isHot被修改了!',newValue,oldValue)
			}
		}
	}
  • 监视属性的另外一种写法
......
<script>
	var vm = new Vue({
		el:'#container',
		data:{
			isHot:true,
		},
		computed: {
			info() {
				return this.isHot?'炎热':'寒冷'
			}
		},
		
	})
	// vm.$watch 传入两个参数,"被监视的属性"和'配置项'
	vm.$watch('isHot',{
		immediate:true,
		handler(newValue,oldValue){
			console.log('isHot被修改了!',newValue,oldValue)
		}
	})
	Vue.config.productionTip = false;
</script>
  • 小结
- 当被监视的属性发生变化时,回调函数(handler)自动被调用,执行函数体的逻辑

- 被监视的属性必须是存在的,才能进行监视

- 监视的两种写法:

	- new Vue时传入watch配置

	- 通过vm.$watch监视

深度监视--引入场景: 修改a的值

......
<body>
	<div id="container">
		<h2>今天天气很{{info}}</h2>
		<button @click="isHot = !isHot">改变天气</button>
		<hr>
		<!--注意语法-->
		<h2>a的值是{{numbers.a}}</h2>
		<button @click="numbers.a++">改变值</button>
	</div>
	<script>
		var vm = new Vue({
			el:'#container',
			data:{
				isHot:true,
				numbers:{
					a:1,
					b:2
				}
			},
		})
	
</body>
  • 此时,如何监视a值,可以这么写
......
watch:{
	// 监视多级结构中,某个属性的变化
	'numbers.a':{	// 这里不能写成 numbers.a:{...} 会报语法错误
		handler(newValue,oldValue){
			console.log('a被修改了!',newValue,oldValue)
		}
	}
}
  • 若b值也重复a值的结构,此时该如何监视numbers?要加一个配置
......
<div id="container">
	<h2>a的值是{{numbers.a}}</h2>
	<button @click="numbers.a++">改变值</button>
	<hr>
	<h2>b的值是{{numbers.b}}</h2>
	<button @click="numbers.b++">改变值</button>
</div>
......
<script>
			var vm = new Vue({
				el:'#container',
				data:{
					numbers:{
						a:1,
						b:2
					}
				},
				watch:{
					numbers:{
						deep:true, // 监视多级结构中,所有属性的变化
						handler(newValue,oldValue){
							console.log('numbers被修改了!',newValue,oldValue)
						}
					}
				}
				
			})
		</script>
  • 小结
- Vue中的watch默认不检测对象内部值的改变(一层可以)

- 配置 deep=true 可以监测对象内部值的改变(根据值的层级结构来决定是否配置此项)
  • watch 的简写形式

    • 前提条件,当只需要 handler() 而不需要其他配置项的时候,就可以简写
......
watch:{
	// isHot:{
	// 	immediate:true,
	// 	handler(newValue,oldValue){
	// 		console.log('isHot被修改了!',newValue,oldValue)
	// 	}
	// },
	// 简写形式
	isHot(newValue,oldValue){
		console.log('isHot被修改了!',newValue,oldValue)
	},
	
}
......

- 或者可以这么写

// vm.$watch('isHot',{
// 	immediate:true,
// 	handler(newValue,oldValue){
// 		console.log('isHot被修改了!',newValue,oldValue)
// 	}
// })
vm.$watch('isHot',function(newValue,oldValue){ // 传入一个function
	console.log('isHot被修改了!',newValue,oldValue)
})
				

计算属性 和 监视属性 的区别

  • 引入示例,先把之前的例子,使用 监视属性 实现一次
......
<body>
	<!--骨架-->
	<div id="container">
		姓: <input type="text" v-model="firstName"><br>
		名: <input type="text" v-model="lastName"><br>
		<span>全名: {{fullName}}</span>
	</div>
	<script>
		var vm = new Vue({
			el:'#container',

			data:{ // 绑定三个值
				firstName:'Jim',
				lastName:'Green',
				fullName:'Jim Green'
			},
			watch:{ // 监视并获取新值,给 fullName 重新赋值
				firstName(val){
					this.fullName = val + this.lastName
				},
				lastName(val){
					this.fullName = this.firstName + val
				}
			}
			
		})
	
		Vue.config.productionTip = false;
	</script>
</body>
  • 我们使用 计算属性 再实现一次
......
<body>
	<div id="container">
		姓: <input type="text" v-model="firstName"><br>
		名: <input type="text" v-model="lastName"><br>
		<span>全名: {{fullName}}</span>
	</div>
	<script>
		var vm = new Vue({
			el:'#container',
			data:{
				firstName:'Jim',
				lastName:'Green',
			},
			computed: {
				fullName() {
					return this.firstName + this.lastName
				}
			},
			
		})
	
		Vue.config.productionTip = false;
	</script>
</body>
......

- 可以看出,使用 计算属性 明显更为方便,然而,真实的情况的确如此么?

  • 现在有这样的需求,'全名'那边要等1秒以后再显示,使用 监视属性 可以这么写
......
watch:{
	firstName(val){
		// 调用定时器,且一定要使用箭头函数,写的成普通函数是不会赋值成功的
		setTimeout(()=>{
			console.log(this) // Vue实例
			this.fullName = val + this.lastName
		},1000)
		// setTimeout(function(){
		// 	console.log(this) // 指向 window对象,显然赋值代码无法成功
		//	this.fullName = val + this.lastName
		// },1000)
	},
	lastName(val){
		this.fullName = this.firstName + val
	}
}
  • 但是,计算属性就无法实现
......
computed: { // 关注返回值,定时器的返回值是 number,显然是不满足需求的
	fullName() {
		console.log('fullName 被调用了')
		setTimeout(()=>{
			 return this.firstName + this.lastName
		},1000)
		return 800
	}
},
......
  • 计算属性 和 监视属性 区别小结
- computed 能完成的功能, watch 也可以完成

- watch 能完成的功能, computed 不一定能完成,例如 watch 可以进行异步操作

- 注意事项

	- 被Vue管理的函数,最好写成普通函数的形式,this指向才是Vue实例

	- 不被Vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成箭头函数的形式,this指向才是Vue实例

使用 Vue 修改css样式

  • 引入场景:单击文字,改变文字颜色
......
<style type="text/css">
    .normal {
        color: yellow;
    }
    .sad {
        color: skyblue;
    }
</style>
......
<body>
    <div id="container" class="normal" @click="changeMood"> <!--给定默认样式并绑定点击事件-->
        {{name}}
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                name:'心情颜色'
            },
            methods:{
                changeMood(){
                    document.querySelector('#container').className = 'sad' // 单击改变文字颜色
                },
            }
            
        })
    </script>
</body>
  • 上述写法,使用原生js去实现,现在,我们使用 vue 的写法实现相同的功能
......
<body>                  <!--动态绑定class属性,给定默认值-->
    <div id="container" :class="mood" @click="changeMood">
        {{name}}
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                name:'心情颜色',
                mood:'normal' // 默认值
            },
            methods:{
                changeMood(){
                    this.mood = 'sad' // 更改默认值
                },
            }
            
        })
    </script>
</body>
  • 优化上述写法,每点击一次文字,文字颜色就随机生成
......
<style type="text/css">
    .normal {
        color: yellow;
    }
    .sad {
        color: skyblue;
    }
    .red {
        color: red;
    }
</style>
......
<body>
    <div id="container" :class="mood" @click="changeMood">
        {{name}}
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                name:'心情颜色',
                mood:'normal'
            },
            methods:{
                changeMood(){
                    var arr = ['normal','sad','red'];
                    var index = Math.floor(Math.random()*3); // 随机生成索引
                    this.mood = arr[index]; // 赋值
                },
            }
            
        })
    </script>
</body>
  • 小结

    • :class="mood"

    • '类名'的字符串写法,适用于'类名'不确定,需要动态指定

  • 如果想同时应用多个样式,可以把class值写成'数组'的形式

    • 适用场景: 类名不确定,类名的个数也不确定
......
<style type="text/css">
    .normal {
        color: yellow;
    }
    .sad {
        background-color: skyblue;
    }
    .red {
        border: 1px solid purple;
    }
</style>
......
<body>                  <!--值为数组-->
    <div id="container" :class="arr" @click="changeMood">
        {{name}}
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                name:'心情颜色',
                arr:['normal','sad','red'] // 把类名写在这边
            },
            methods:{
               ......
            }
            
        })
    </script>
</body>
  • 还可以把数组直接写在class值
......
<body>
    <div id="container" :class="[a,b,c]" @click="changeMood">
        {{name}}
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                name:'心情颜色',
                a:'normal',
                b:'sad',
                c:'red'
            },
            methods:{
                ......
            }
            
        })
    </script>
</body>
......
- 注意事项:如果class值不包裹在'[]'里面,那么此时只显示 class='normal'
    
    <div id="container" :class="a,b,c" @click="changeMood"> <!--只有 a样式 生效-->

  • class值的最后一种写法---对象式

    • 适用场景: 类名确定,类名个数也确定,自己决定用不用
......
                    <!--class值为一个'配置对象'-->
<div id="container" :class="classObj" @click="changeMood">
    {{name}}
</div>
......
var vm = new Vue({
    el:'#container',
    data:{
        name:'心情颜色',
        classObj:{
            normal:true, // 只适用前两种样式,最后一种不要
            sad:true,
            red:false
        }
    },
  • 使用vue,实现行内式的css样式
......
                    <!--动态绑定style属性-->
<div id="container" :style="styleObj" @click="changeMood">
    {{name}}
</div>
......
var vm = new Vue({
    el:'#container',
    data:{
        name:'心情颜色',
        styleObj:{
            fontSize:'60px' // 设置样式
        }
    },

-class值可以写成数组的形式

......
                    <!--数组成员为一个个对象-->
<div id="container" :style="[styleObj1,styleObj2]" @click="changeMood">
    {{name}}
</div>
......
data:{
        ......
        styleObj1:{
            fontSize:'40px'
        },
        styleObj2:{
            color:'orange'
        }
    },
  • 绑定样式小结
- class样式

    - 写法: class='xxx',xxx可以是字符串,对象,数组

    - 字符串写法适用于: 类名不确定,要动态获取

    - 对象写法适用于: 要绑定多个样式,名字不确定,个数也不确定

    - 数组写法适用于: 要绑定多个样式,名字确定,个数确定,但不确定用不用

- sytle样式

    - :style="{fontSize: xxx}"其中xxx是动态值

    - :sytle="[a,b]",其中 a,b是样式对象

条件渲染指令(v-show和v-if)

  • v-if:项目的切换频率低,推荐使用

  • v-show: 项目的切换频率高,推荐使用

......
<div id="container" v-show="false"> <!--不会显示name-->
    你好,{{name}}
</div>

<script type="text/javascript">
    
    var vm = new Vue({
        el:'#container',
        data:{
            name:'Jim Green',
        }
    })
</script>

- 通过调试可以发现,DOM节点还是存在的,只不过设置了display:none.
  • 把上述示例换成 v-if
......
<div id="container" v-if="false"> <!--不会显示name-->
    你好,{{name}}
</div>

<script type="text/javascript">
    
    var vm = new Vue({
        el:'#container',
        data:{
            name:'Jim Green',
        }
    })
</script>

- 通过调试可以发现,没有DOM节点了,项目本身被删得干干净净...
  • 小案例,依据number的值显示不同的内容
......
<body>
    <div id="container">
        {{number}}
        <button type="button" @click="number++">改变number值</button>
        <div v-if="number===1">111</div>
        <div v-if="number===2">222</div>
        <div v-if="number===3">333</div>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            el:'#container',
            data:{
                number:0
            }
        })
    </script>
</body>

- 小改demo,变成 v-if,v-else-if

......
<div id="container">
    {{number}}
    <button type="button" @click="number++">改变number值</button>
    <div v-if="number===1">111</div> <!--满足任何一个条件以后,都不会再向下判断-->
    <div v-else-if="number===2">222</div>
    <div v-else-if="number===3">333</div>
</div>

- 再次小改demo,变成 v-if,v-else-if,v-else

<div v-if="number===1">111</div>
<div v-else-if="number===2">222</div>
<div v-else-if="number===3">333</div>
<div v-else>无穷无尽...</div> <!--页面加载完就会显示,因为 number 不满足前面所有的条件-->

- 注意事项: v-if,v-else-if,v-else 的结构必须是连续的,若中途被打断,那么前面的判断会生效,后面的判断无效(后面白写)

<div id="container">
    {{number}}
    <button type="button" @click="number++">改变number值</button>
    <div v-if="number===1">111</div>
    <div v-else-if="number===2">222</div>
    <div>......</div> <!--打断结构,后面的判断无效-->
    <div v-else-if="number===3">333</div>
    <div v-else>无穷无尽...</div>
</div>
  • template 元素介绍:临时元素,不会破坏原有的结构

    • 注意事项:只能搭配 v-if 使用(搭配v-show不会生效)
    • 因为v-show是通过display来控制标签进行渲染的,但是template 标签在vue解析后是不会显示在页面上的 ,是虚拟Dom ,所以不可以,v-if是条件渲染,只要满足v-if后的条件就可以完成渲染
......
<!--<template v-show="false">--> <!--不会生效-->
<template v-if="false">
    <div>111</div>
    <div>222</div>
    <div>333</div>
</template>
  • 注意事项: 使用v-if元素可能无法获取到,而使用v-show元素一定可以获取到