← Back to all posts

Vue2

Vue核心

搭建Vue环境

先在此处附上Vue的安装教程地址:https://v2.cn.vuejs.org/v2/guide/installation.html

这里有两个版本,一个开发版本,一个生产版本

开发版本是在开发时使用的,当出现问题的时候会在控制台报警告

生产版本是在项目上线时使用的,不会有警告,而且体积更小

image-20230919083405727

在这里,我使用的是开发版本,学习一般使用开发版

将开发版本下载后保存到本地,使用方法与jquery是一致的,使用来script引入

直接用script引入

创建一个文件夹,在里面创建一个html文件

然后直接引入vue文件即可

代码 (17 行)
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>Document</title>
    <script src="../vue.js"></script>
</head>

<body>

</body>

</html>

运行文件

消除开发环境提示

在console里查看

image-20230919084602099

第一个是说下载vue的开发者工具来达到一个更好的开发者体验

第二个是说你正在运行开发环境,请你确信在生产环境不要这样做

两个小提示,但是不影响,后面再解决

如果成功引入了Vue,在console上写入Vue会输出其对应的函数

image-20230919084937292

解决第一个提示的方法是下载一个vue的开发者工具即可

vue开发者工具的github链接如下:https://github.com/vuejs/devtools#vue-devtools

找到下面这个地方,点击进入谷歌商店下载

image-20230919085712128

image-20230919085756365

第二个提示需要在代码中对其进行配置

vue
<script>
        Vue.config.productionTip = false   // 阻止vue在启动时生成生产提示
</script>

模板语法

Vue模板语法有2大类:

1. 插值语法:

​ 功能:用于解析标签体内容

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

2. 指令语法:

​ 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件等)

​ 举例:v-bind:hreft="xxx"或简写为:href="xxx",xxx同样要写js表达式。且可以直接读取到data中的所有属性

​ 备注:Vue中有很多的指令,且形式都是:v-?????

插值语法

代码 (29 行)
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>Document</title>

</head>

<body>
    <div id="box">
        {{test}}
    </div>
    <script src="../vue.js"></script>
    <script>
        Vue.config.productionTip = false;
        var vm = new Vue({
            el: '#box',
            data: {
                test: 'haha'
            }
        })
    </script>
</body>

</html>

指令语法

v-bind

单向绑定数据

代码 (16 行)
html
<body>
    <div id="box">
        <a v-bind:href="url">跳转到百度</a>
    </div>
    <script src="../vue.js"></script>
    <script>
        Vue.config.productionTip = false;
        var vm = new Vue({
            el: '#box',
            data: {
                url: 'http://www.baidu.com'
            }
        })
    </script>
</body>

可以简写为`跳转到百度`

代码 (16 行)
html
<body>
    <div id="box">
        <a :href="url">跳转到百度</a>
    </div>
    <script src="../vue.js"></script>
    <script>
        Vue.config.productionTip = false;
        var vm = new Vue({
            el: '#box',
            data: {
                url: 'http://www.baidu.com'
            }
        })
    </script>
</body>

数据绑定

v-bind:单向数据绑定

v-model:双向数据绑定

Vue中有2种数据绑定的方式:

1. 单向绑定(v-bind):数据只能从data流向页面

2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data

备注:

1. 双向绑定一般都应用在表单类元素上(如:input、select等)

2. v-model:value 可以简写为v-model,因为v-model默认收集的就是value值

  • el有两种写法
  • 1. new Vue时配置el属性

    vue
    		new Vue({
    		            // el: '#box', 第一种写法
    		        })

    2. 先创建Vue实例,随后再通过vm.$mount('#root')指定el的值

    `vm.$mount('#box')`

  • data有2种写法
  • 1. 对象式

    html
    		new Vue({
    		            // el: '#box', 第一种写法
    		        })

    2. 函数式

    html
    		// data的第二种写法:函数式
    		            data() {
    		                return {
    		                    name: 'zhangsan'
    		                }
    		            }

  • 一个重要的原则
  • 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
  • v-bind的简写方式::xxx=""

    v-model的简写方式:v-model=""

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

    单向数据绑定

    下面这段代码是对其进行了一个单向数据绑定,可以在页面中进行测试

    代码 (16 行)
    html
    <body>
        <div id="box">
            单向数据绑定:<input type="text" :value="name">
        </div>
        <script src="../vue.js"></script>
        <script>
            Vue.config.productionTip = false;
            var vm = new Vue({
                el: '#box',
                data: {
                    name: 'zhangsan'
                }
            })
        </script>
    </body>

    之前在网上下载的Vue开发者工具此时就可以使用了

    打开页面后单击F12,打开这个页面,在最后一栏找到Vue

    image-20230919161313748

    在name:'zhangsan'的位置进行修改,单向数据绑定处的数据也会发生改变,而在输入框中修改,并不会改变name:'zhangsan'的内容

    双向数据绑定

    v-model:双向数据绑定

    代码 (17 行)
    html
    <body>
        <div id="box">
            单向数据绑定:<input type="text" :value="name"><br>
            双向数据绑定:<input type="text" v-model:value="name">
        </div>
        <script src="../vue.js"></script>
        <script>
            Vue.config.productionTip = false;
            var vm = new Vue({
                el: '#box',
                data: {
                    name: 'zhangsan'
                }
            })
        </script>
    </body>

    被绑定了双向后,在测试工具中输入任意的数据,输入框中的内容都会发生改变,或者在输入框中输入内容,也会互相的改变

    当输入框中的内容改变时,会带起一系列的连锁反应

    image-20230919161959667

    注意:v-model元素只能应用在表单类元素上(输入类元素),在其他标签类元素上使用会报错

    el与data的两种写法

    对象式

    代码 (18 行)
    html
    <body>
        <div id="box">
            单向数据绑定:<input type="text" :value="name"><br>
            双向数据绑定:<input type="text" v-model="name">
        </div>
        <script src="../vue.js"></script>
        <script>
            Vue.config.productionTip = false;
            var vm = new Vue({
                // el: '#box', 第一种写法
                data: {
                    name: 'zhangsan'
                }
            })
            vm.$mount('#box')	// 第二种写法
        </script>
    </body>

    相较之下,第二种更灵活,使用的时候两种都可以

    代码 (26 行)
    html
    <body>
        <div id="box">
            单向数据绑定:<input type="text" :value="name"><br>
            双向数据绑定:<input type="text" v-model="name">
        </div>
        <script src="../vue.js"></script>
        <script>
            Vue.config.productionTip = false;
            var vm = new Vue({
                // el: '#box', 第一种写法
                // data: {
                //     name: 'zhangsan'
                // }
    
                // data的第二种写法:函数式
                data: function () {
                    return {
                        name: 'zhangsan'
                    }
                }
            })
            vm.$mount('#box')
    
        </script>
    </body>

    data可以简写为下面的方式

    vue
     // data的第二种写法:函数式
                data() {
                    return {
                        name: 'zhangsan'
                    }
                }

    MVVM

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

    V:视图(View), 模板

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

    image-20230920124821781

    Data Bindings:数据绑定,将Model中的数据绑定到View中

    DOM Listeners:页面模型监听器

    总结:

  • data中所有的属性,最后都出现了Vue对象上
  • vm身上所有的属性及Vue原型上所有属性,在Vue模板中都可以直接使用
  • 数据代理

    Object.defineProperty方法

    `Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})`

    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男'
            }
            
            Object.defineProperty(person, 'age', {
                value: 18
            })
            console.log(person)
        </script>
    </body>

    为person对象添加了一个age的属性,值为18

    image-20230920132903453

    这样添加与普通的添加方式有什么区别呢,这样添加的属性是不参与遍历的

    正常情况下是参与遍历的

    代码 (16 行)
    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男',
                age: 18
            }
            // Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})
            // Object.defineProperty(person, 'age', {
            //     value: 18
            // })
            console.log(Object.keys(person))
        </script>
    </body>

    image-20230920133350604

    但是使用该方法进行遍历就遍历不到了

    代码 (16 行)
    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男',
                // age: 18
            }
            // Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})
            Object.defineProperty(person, 'age', {
                value: 18
            })
            console.log(Object.keys(person))
        </script>
    </body>

    image-20230920133321585

    这种情况也叫不可枚举,如果需要遍历该怎么办呢

    可以在代码中加入

    `enumerable: true`:控制属性是否可以被枚举,默认是false

    代码 (16 行)
    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男',
            }
            // Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})
            Object.defineProperty(person, 'age', {
                value: 18,
                enumerable: true
            })
            console.log(Object.keys(person))
        </script>
    </body>

    再次运行,就可以遍历到了

    如果你试图修改`Object.defineProperty`添加的方法,可以修改,但不会修改成功

    如果需要被修改怎么办呢

    在代码中加入`writable: true`,控制属性是否可以被修改,默认是false

    代码 (17 行)
    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男',
            }
            // Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})
            Object.defineProperty(person, 'age', {
                value: 18,
                enumerable: true,
                writable: true
            })
            console.log(Object.keys(person))
        </script>
    </body>

    数据需要被删除,可以加入`configurable: true`,控制属性是否可以被删除,默认是false

    getter和setter

    下面的代码提供了`Object.defineProperty`的get和set示例

    代码 (23 行)
    html
    <body>
        <script src="../vue.js"></script>
        <script>
            let person = {
                name: '张三',
                sex: '男',
    
            }
            let number = 19
            // Object.defineProperty(添加属性的对象名,添加的属性名,{value:添加的值})
            Object.defineProperty(person, 'age', {
                // 当有人读取person的age属性时,get函数就会被调用,且返回number的值
                get() {
                    return number
                },
                // 当有人修改person的age属性时,set函数就会被调用,且会收到修改的具体值value
                set(value) {
                    number = value;
                }
            })
        </script>
    </body>

    image-20230920135946414

    Vue中的数据代理

    数据代理就是让_data中的数据在Vue身上也有一份,不管是getter还是setter都可以直接通过属性名来获取使用,而不是通过 _data.属性名来使用,这样会非常的冗余,在getter时,数据代理会getter到 _data的身上,而setter时,会setter到 _data的身上,从而达到一个连锁的效果,简化了开发

    数据代理图示:

    image-20230921125656317

    Vue先将data中的数据存放到_data中,然后再通过`Object.defineProperty`方法将数据放到vm身上,这样就起到一个数据代理的效果

    代码 (17 行)
    html
    <body>
        <div id="box">
            <h1>{{name}}</h1>
            <h1>{{age}}</h1>
        </div>
        <script src="../vue.js"></script>
        <script>
            new Vue({
                el: '#box',
                data: {
                    name: 'zhangsan',
                    age: 66
                }
            })
        </script>
    </body>

    以上面这段示例代码为例,Vue中的数据代理其实也是同理的

    当有人访问页面中的name或age时,它会利用getter去访问data中的name或age

    当有人修改页面中的name或age时,它会利用setter去修改data中的name或age

    此时就不是直接访问,而是以一种代理的形式,通过访问Vue上的getter或setter方法来达到修改data上的数据

    这里对数据代理的两种形式做一个验证

    修改data中的属性后,查看其中vue中的data是否发生变化

    image-20230921091925137

    发现此时这里的name确实是发生改变了,这说明数据改变后,依然是从data中来获取的

    对setter进行一个验证

    修改对应的属性,然后获取其data中的属性是否发生了改变

    image-20230921093045894

    这里我们发现,修改了它的属性后,data中的属性也发生了改变,这说明setter方法是存在数据代理的,设置完的属性会放到data中

    总结:

    1. Vue中的数据代理:

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

    2. Vue中数据代理的好处:

    ​ 更加方便的操作data中的数据

    3. 基本原理

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

    ​ 通过每一个添加到vm上的属性,都指定一个getter/setter

    ​ 在getter/setter内部去操作(读/写)data中对应的属性

    事件处理

    事件的基本使用

    事件的基本使用:

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

    2. 事件的回调需要配置在methods对象中,最终会在vm上

    3. methods配置的函数,不要用箭头函数,否则this就不是vm了

    4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象

    5. @click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参

    v-on:click

    简写形式为:@click

    当被点击时

    代码 (20 行)
    html
    <body>
        <div id="box">
            <button v-on:click="show">点我</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show() {
                        alert("show")
                    }
                },
            })
        </script>
    </body>

    如果需要接收参数,可以这样写

    代码 (20 行)
    html
    <body>
        <div id="box">
            <button v-on:click="show(66)">点我66</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(number) {
                        alert(number)
                    }
                },
            })
        </script>
    </body>

    但是这样写event就无法使用了

    可以通过在传参位置加入占位符`$event`的形式传递event参数

    代码 (21 行)
    html
    <body>
        <div id="box">
            <button v-on:click="show($event,66)">点我66</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(event, number) {
                        console.log(event)
                        alert(number)
                    }
                },
            })
        </script>
    </body>

    事件修饰符

    正常的运行下面这段代码,会在代码运行后依然将网页跳转到了百度

    代码 (20 行)
    html
    <body>
        <div id="box">
            <a href="http://www.baidu.com" @click="jump">点击跳走</a>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    jump() {
                        alert("跳走喽")
                    }
                },
            })
        </script>
    </body>

    跳转是a标签的默认行为,我们有什么好的办法来阻止这个默认行为呢

    可以通过`e.preventDefault`方法对这个默认行为进行阻止

    html
    methods: {
    
            jump(e) {
    
              e.preventDefault();
    
              alert("跳走喽")
    
            }
    
          },

    也可以通过Vue中的事件修饰符

    1. prevent:阻止默认事件(常用)

    2. stop:阻止事件冒泡(常用)

    3. once:事件只触发一次(常用)

    4. capture:使用事件的捕获模式

    5. self:只有event.target是当前操作的元素时才触发事件

    6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕

    演示一下常用的三种,其余不做演示

    prevent:

    效果和`e.preventDefault();`是一样的

    代码 (21 行)
    html
    <body>
        <div id="box">
            <a href="http://www.baidu.com" @click.prevent="jump">点击跳走</a>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    jump(e) {
                        // e.preventDefault();
                        alert("跳走喽")
                    }
                },
            })
        </script>
    </body>

    stop:

    冒泡是从当没有返回值true或false之类时,会从里向外(向它的父元素执行其父元素的方法直到结束)

    冒泡情况演示

    代码 (22 行)
    html
    <body>
        <div id="box">
            <div @click="clickOnButton">
                <button @click="clickOnButton">点我老铁666</button>
            </div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    clickOnButton() {
                        alert("冒泡效果")
                    }
                },
            })
        </script>
    </body>

    冒泡解决方案:

    代码 (24 行)
    html
    <body>
        <div id="box">
            <div @click="clickOnButton">
                <button @click="clickOnButton">点我老铁666</button>
            </div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    clickOnButton(e) {
                        e.stopPropagation();
                        alert("冒泡效果")
    
                    }
                },
            })
        </script>
    </body>

    冒泡解决方案Vue版:

    代码 (25 行)
    html
    
    <body>
        <div id="box">
            <div @click="clickOnButton">
                <button @click.stop="clickOnButton">点我老铁666</button>
            </div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    clickOnButton(e) {
                        // e.stopPropagation();
                        alert("冒泡效果")
    
                    }
                },
            })
        </script>
    </body>

    once:

    事件只触发一次,字面意思,挺好理解的

    代码 (22 行)
    html
    <body>
        <div id="box">
            <button @click.once="clickOnButton">点我老铁666</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    clickOnButton(e) {
                        // e.stopPropagation();
                        alert("once")
    
                    }
                },
            })
        </script>
    </body>

    键盘事件

    @keyup:当键盘弹起时

    @keydown:当键盘按下时

    下面这段代码是当键盘弹起时,如果按下的是回车就打印内容在控制台上

    代码 (21 行)
    html
    <body>
        <div id="box">
            <input type="text" placeholder="按下回车提示输入" @keyup="show">
        </div>
        <script src="../vue.js"></script>
        <script>
            new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(e) {
                        if (e.keyCode !== 13) return;
                        console.log(e.target.value)
                    }
                },
            })
        </script>
    </body>

    这里的判断keyCode也可以置换为vue中的别名

    代码 (21 行)
    html
    <body>
        <div id="box">
            <input type="text" placeholder="按下回车提示输入" @keyup.enter="show">
        </div>
        <script src="../vue.js"></script>
        <script>
            new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(e) {
                        // if (e.keyCode !== 13) return;
                        console.log(e.target.value)
                    }
                },
            })
        </script>
    </body>

    vue常见按键别名

    回车 => enter

    删除 => delete(捕获"删除"和"退格"键)

    退出 => esc

    空格 => space

    换行 => tab

    上 => up

    下 => down

    左 => left

    右 => right

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

    短横线命名是什么意思呢?

    就是说,如果某个按键未提供别名,可以使用原始的key去绑定,比如CapsLock,如果想要使用的话,需要转为caps-lock,记得要在两个单词之间加入短横线-

    示例代码

    代码 (21 行)
    html
    <body>
        <div id="box">
            <input type="text" placeholder="按下回车提示输入" @keyup.caps-lock="show">
        </div>
        <script src="../vue.js"></script>
        <script>
            new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(e) {
                        // if (e.keyCode !== 13) return;
                        console.log(e.target.value)
                    }
                },
            })
        </script>
    </body>

    特殊按键tab:按了之后会离开焦点,这种不适合在keyup上使用,因为在键盘弹起后触发时,焦点已经离开了,方法可能就无法触发了(必须配合keydown使用)

    系统修饰键(用法特殊):ctrl、alt、shift、meta(win)

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

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

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

    ``

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

    `Vue.config.keyCodes.huiche = 13`

    代码 (22 行)
    html
    <body>
        <div id="box">
            <input type="text" placeholder="按下回车提示输入" @keyup.huiche="show">
        </div>
        <script src="../vue.js"></script>
        <script>
            Vue.config.keyCodes.huiche = 13
            new Vue({
                el: '#box',
                data: {
    
                },
                methods: {
                    show(e) {
                        // if (e.keyCode !== 13) return;
                        console.log(e.target.value)
                    }
                },
            })
        </script>
    </body>

    事件总结

  • 事件修饰符可以连着写,例如@click.prevent.stop="xxx",效果是停止默认事件并阻止冒泡,但它是有先后顺序的,也就是说,先停止默认事件,再阻止冒泡,谁写在前面谁先
  • 而键盘事件也可以连着写,例如,`@keyup.ctrl.y`,连着写之后的效果就是按下特定的这两位才会触发效果,其他的没效果
  • 计算属性

    姓名案例

    插值语法实现

    代码 (18 行)
    html
    <body>
        <div id="box">
            姓: <input type="text" v-model=firstName><br>
            名: <input type="text" v-model=lastName><br>
            姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三'
                }
            })
        </script>
    </body>

    如果我们想为名称加一些新的效果,但是会有很多,这样就会很麻烦,如果我们把它整理成一个methods,看起来就不会特别的冗余了

    代码 (23 行)
    html
    <body>
        <div id="box">
            姓: <input type="text" v-model=firstName><br>
            名: <input type="text" v-model=lastName><br>
            姓名: <span>{{name()}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三'
                },
                methods: {
                    name() {
                        return this.firstName + "-" + this.lastName
                    }
                },
            })
        </script>
    </body>

    计算属性

    1. 定义:要用的属性不存在,要通过已有属性(vue中的才叫属性)计算得来

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

    3. get函数什么时候执行?

  • 初次读取时会执行一次
  • 当依赖的数据发生改变时会被再次调用
  • 4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

    5. 备注:

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

    2. 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变,否则无效

    使用计算属性编写之前的姓名案例

    这里的get和`Object.defineProperty`是一样的

    当有人读取fullName时,get就会被调用,且返回值就作为fullName的值

    get什么时候调用

  • 初次读取fullName时
  • 所依赖的数据发生改变时
  • 代码 (26 行)
    html
    <body>
        <div id="box">
            姓: <input type="text" v-model=firstName><br>
            名: <input type="text" v-model=lastName><br>
            姓名: <span>{{fullName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三'
                },
                computed: {
                    fullName: {
                        get() {
                            console.log(this)
                            return this.firstName + "-" + this.lastName
                        }
                    }
                }
            })
        </script>
    </body>

    且get是有缓存的,而methods是没有缓存的,性能上computed更好一些

    有get肯定就有set,和get的道理其实也是一样的

    代码 (38 行)
    html
    <body>
        <div id="box">
            姓: <input type="text" v-model=firstName><br>
            名: <input type="text" v-model=lastName><br>
            姓名: <span>{{fullName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三'
                },
                computed: {
                    fullName: {
                        // set方法的调用时机是当fullName被改变时调用
                        // 而fullName是依靠firstName和lastName来改变的
                        // 也就是当输入框中的两个东西被改变后,get就调用了
                        get() {
                            console.log(this)
                            return this.firstName + "-" + this.lastName
                        },
                        set(value) {
                            // set会传递一个参数
                            // 这个参数就是值了
                            console.log(value)
                            // 得到了值,需要通过中间的-来进行获取姓和名
                            const arr = value.split('-');
                            this.firstName = arr[0]
                            this.lastName = arr[1]
                        }
                    }
                }
            })
        </script>
    </body>

    计算属性简写

    简写的形式只有在只读不改的时候才能使用

    代码 (24 行)
    html
    <body>
        <div id="box">
            姓: <input type="text" v-model=firstName><br>
            名: <input type="text" v-model=lastName><br>
            姓名: <span>{{fullName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三'
                },
                computed: {
                    fullName() {
                        // 简写
                        return this.firstName + "-" + this.lastName;
                    }
                }
            })
        </script>
    </body>

    监视属性

    天气案例

    先做一个天气的小案例,点击按钮后变换天气

    这里使用了计算属性来进行操作

    代码 (25 行)
    html
    <body>
        <div id="box">
            <h1>今天天气很{{info}}</h1>
            <button @click="change">改变天气</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isHot: true
                },
                computed: {
                    info() {
                        return this.isHot ? '炎热' : '寒冷'
                    }
                }, methods: {
                    change() {
                        this.isHot = !this.isHot
                    }
                },
            })
        </script>
    </body>

    但是在这里有一个小坑

    如果我们将代码中的这一行改变了

    改变为`

    今天天气很一般

    `

    此时页面上就不会用到isHot和info了,如果我们此时点击一下按钮,页面是肯定不会发生变化的,但是你打开开发者工具一看

    image-20230922140923321

    开发者工具中却是true和炎热,我们去控制台上看一下

    image-20230922140756964

    控制台上就发生了变化了

    这是为什么呢,原因其实是vue的开发者工具认为你页面没有使用到该数据,就没有为你更新了,这个是官方的一个bug,不影响

    这里写的代码其实很多,只是为了实现一个切换功能,那还有什么办法能更简单吗

    代码 (23 行)
    html
    <body>
        <div id="box">
            <h1>今天天气很{{info}}</h1>
            <button @click="isHot = !isHot">改变天气</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isHot: true
                },
                computed: {
                    info() {
                        return this.isHot ? '炎热' : '寒冷'
                    }
                }, methods: {
    
                },
            })
        </script>
    </body>

    一些简单的代码其实可以直接使用@click来写

    如果你不仅想做这个操作,还想做别的操作呢,有两种方式

    1. 直接在methods中写

    2. `@click="isHot = !isHot";你想做的操作`

    比如:

    代码 (24 行)
    html
    <body>
        <div id="box">
            <h1>今天天气很{{info}}</h1>
            <button @click="isHot = !isHot;x++">改变天气{{x}}</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isHot: true,
                    x: 0
                },
                computed: {
                    info() {
                        return this.isHot ? '炎热' : '寒冷'
                    }
                }, methods: {
    
                },
            })
        </script>
    </body>

    监视属性watch

    1. 当被监视的属性变化时,回调函数自动调用,进行相关操作

    2. 监视的属性必须存在,才能进行监视

    3. 监视的两种写法:

    1. new Vue时传入watch配置

    2. 通过vm.$watch监视

    代码 (29 行)
    html
    <body>
        <div id="box">
            <h1>今天天气很{{info}}</h1>
            <button @click="isHot = !isHot">改变天气</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isHot: true,
                },
                computed: {
                    info() {
                        return this.isHot ? '炎热' : '寒冷'
                    }
                }, watch: {
                    info: {
                        immediate: true,     // 初始化时让handler调用一下
                        // handler什么时候调用,当isHot发生改变时
                        handler(newValue, oldValue) {
                            console.log("新的值为", newValue, "旧的值为", oldValue)
                        }
                    }
                }
            })
        </script>
    </body>

    监视属性是watch,是一个对象的形式,里面可以放置多个对象,每个对象里面有配置属性和handler等

    handler中有两个参数可以接收,第一个是newValue,第二个是oldValue

    除了上面这种监视属性的写法,还有另一种监视属性的写法

    代码 (32 行)
    html
    <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isHot: true,
                },
                computed: {
                    info() {
                        return this.isHot ? '炎热' : '寒冷'
                    }
                },
                // watch: {
                //     info: {
                //         immediate: true,     // 初始化时让handler调用一下
                //         // handler什么时候调用,当isHot发生改变时
                //         handler(newValue, oldValue) {
                //             console.log("新的值为", newValue, "旧的值为", oldValue)
                //         }
                //     }
                // }
            })
            vm.$watch('info',
                {
                    immediate: true,     // 初始化时让handler调用一下
                    // handler什么时候调用,当isHot发生改变时
                    handler(newValue, oldValue) {
                        console.log("新的值为", newValue, "旧的值为", oldValue)
                    }
                }
            )
        </script>

    深度监视

    1. Vue中的watch默认不监测对象内部值的改变(一层)

    2. 配置`deep:true`可以监测对象内部值的改变(多层)

    备注:

  • Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
  • 使用watch时根据数据的具体结构,决定是否采用深度监视,采用深度监视会有效率的问题
  • 监视多级结构中某个属性的变化

    当多级结构中的a发生变化时,可以获取得到它

    代码 (24 行)
    html
    <body>
        <div id="box">
            <h1>a的值是:{{numbers.a}}</h1>
            <button @click="numbers.a++">点我让a++</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    numbers: {
                        a: 1,
                        b: 2
                    }
                },
                watch: {
                    'numbers.a'() {
                        console.log(this.numbers.a)
                    }
                }
            })
        </script>
    </body>

    检测整体结构变化

    代码 (27 行)
    html
    <body>
        <div id="box">
            <h1>a的值是:{{numbers.a}}</h1>
            <button @click="numbers.a++">点我让a++</button>
            <button @click="numbers.b++">点我让b++</button>
            <!-- 当numbers整体发生改变时,才能检测到 -->
            <button @click="numbers = {a:66,b:99}">点我让numbers改变</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    numbers: {
                        a: 1,
                        b: 2
                    }
                },
                watch: {
                    'numbers'() {
                        console.log(this.numbers)
                    }
                }
            })
        </script>
    </body>

    检测整体结构的任意一个值的变化

    为watch中的属性配置deep即可检测值的变化

    代码 (30 行)
    html
    <body>
        <div id="box">
            <h1>a的值是:{{numbers.a}}</h1>
            <button @click="numbers.a++">点我让a++</button>
            <button @click="numbers.b++">点我让b++</button>
            <!-- 当numbers整体发生改变时,才能检测到 -->
            <button @click="numbers = {a:66,b:99}">点我让numbers改变</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    numbers: {
                        a: 1,
                        b: 2
                    }
                },
                watch: {
                    numbers: {
                        deep: true,
                        handler() {
                            console.log(this.numbers)
                        }
                    }
                }
            })
        </script>
    </body>

    监视的简写形式

    监视的简写形式只有在仅handler的情况下才可以使用

    代码 (29 行)
    html
    <body>
        <div id="box">
            <h1>numbers的值是:{{numbers}}</h1>
            <button @click="numbers++">点我让numbers++</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    numbers: 1
                },
                watch: {
                    // 正常写法
                    // numbers: {
                    //     deep: true,
                    //     handler() {
                    //         console.log(this.numbers)
                    //     }
                    // }
                    // 简写
                    numbers(newValue, oldValue) {
                        console.log(newValue, oldValue)
                    }
                }
            })
        </script>
    </body>

    但是监视有两种写法,它还有一种vm.$watch的写法,也可以简写

    代码 (32 行)
    html
    <body>
        <div id="box">
            <h1>numbers的值是:{{numbers}}</h1>
            <button @click="numbers++">点我让numbers++</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    numbers: 1
                },
                // watch: {
                //     // 正常写法
                //     // numbers: {
                //     //     deep: true,
                //     //     handler() {
                //     //         console.log(this.numbers)
                //     //     }
                //     // }
                //     // 简写
                //     numbers(newValue, oldValue) {
                //         console.log(newValue, oldValue)
                //     }
                // }
            })
            vm.$watch('numbers', function (newValue, oldValue) {
                console.log(newValue, oldValue)
            })
        </script>
    </body>

    watch对比computed

    computed和watch之间的区别:

    1. computed能完成的功能,watch都可以完成

    2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作(定时器)

    两个重要的小原则:

    1. 被Vue所管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象

    2. 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,因为在JavaScript中,箭头函数并不会创建自己的this上下文,而是继承其所在代码块的this。因此,在Vue组件的方法中,如果我们使用了箭头函数,那么this才会指向Vue实例。

    watch写法的姓名案例

    代码 (27 行)
    html
    <body>
        <div id="box">
            姓:<input type="text" v-model="firstName">
            名:<input type="text" v-model="lastName">
            <br><span>姓名:{{fullName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三',
                    fullName: '张三'
                },
                watch: {
                    firstName(newValue) {
                        this.fullName = newValue + this.lastName;
                    },
                    lastName(newValue) {
                        this.fullName = this.firstName + newValue;
                    }
                }
            })
        </script>
    </body>

    computed写法的姓名案例

    代码 (23 行)
    html
    <body>
        <div id="box">
            姓:<input type="text" v-model="firstName">
            名:<input type="text" v-model="lastName">
            <br><span>姓名:{{fullName}}</span>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    firstName: '张',
                    lastName: '三',
                },
                computed: {
                    fullName() {
                        return this.firstName + this.lastName;
                    }
                }
            })
        </script>
    </body>

    绑定样式

    绑定class样式

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

    可以将样式修改为指定的内容

    代码 (21 行)
    html
    <body>
        <div id="box">
            <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">test</div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    mood: 'normal'
                },
                methods: {
                    changeMood() {
                        this.mood = 'happy'
                    }
                },
            })
        </script>
    </body>

    也可以通过布尔值进行判断之类的情况

    当isActive为true时,active才会作为类生效

    html
    <body>
        <div id="box">
            <div :class="{active:isActive}">test</div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isActive: false
                }
            })
        </script>
    </body>

    绑定计算属性作为class的判断

    代码 (24 行)
    html
    <body>
        <div id="box">
            <div :class="classObject">test</div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    isActive: true,
                    error: null
                },
                computed: {
                    classObject() {
                        return {
                            active: this.isActive && !this.error,
                            'text-danger': this.error && this.error.type === 'fatal'
                        }
                    }
                }
            })
        </script>
    </body>

    这段代码的释义是使用了一个计算属性

    计算属性的含义是classObject,当满足isActive为true并且error不为空的条件时,返回active;当error存在并且error的类型为fatal时返回text-danger

    数组语法

    我们可以把一个数组传给v-bind:class,来应用一个class列表的情况

    代码 (16 行)
    html
    <body>
        <div id="box">
            <div :class="[active,active2]">test</div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    active: 'activeClass',
                    active2: 'isYes'
                },
            })
        </script>
    </body>

    最终它会渲染为`class='activeClass isYes'`

    如果想根据条件来切换数组语法中的内容,也是可以的

    html
    <div :class="[isActive ? active : '',active2]">test</div>

    可以通过三元表达式来对其进行修改

    如果觉得三元表达式不够友好,可以采取对象语法的方式

    html
    <div :class="[{active:isActive},active2]">test</div>

    绑定内联样式

    对象语法

    代码 (16 行)
    html
    <body>
        <div id="box">
            <!-- 使用对象语法 -->
            <div :style="{fontSize:size}">hahahha</div>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    size: '36px'
                },
            })
        </script>
    </body>

    或者将样式绑定到一个对象中,直接使用对象

    html
    <div :style="styleData">hahahha</div>

    js
    data: {
                    styleData: {
                        backgroundColor: 'red',
                        fontSize: '30px'
                    }
                }

    数组语法

    将多个样式对象绑定到一个数组上

    html
    <div :style="[styleData,a,b,c]">hahahha</div>

    多重值

    从vue2.3.0开始可以为style绑定中的property提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

    html
    <div :style="{display:['-webkit-box,'-ms-flexbox','flex']}"></div>

    `'-webkit-box,'-ms-flexbox','flex'`是不同浏览器支持的类型前缀,这样写只会渲染数组中最后一个被浏览器支持的值,如果浏览器支持不带浏览器前缀的flexbox,那么就只会渲染`display:flex`

    条件渲染

    v-if、v-else

    `v-if` 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染。

    `v-else`的效果是当if不生效时,else生效

    `v-else` 元素必须紧跟在带 `v-if` 或者 `v-else-if` 的元素的后面,否则它将不会被识别。

    代码 (22 行)
    html
    <body>
        <div id="box">
            <div v-if="flag">66</div>
            <div v-else="flag">77</div>
            <button @click="onClick">点击后改变</button>
        </div>
        <script src="../vue.js"></script>
        <script>
            const vm = new Vue({
                el: '#box',
                data: {
                    flag: false
                },
                methods: {
                    onClick() {
                        this.flag = !this.flag
                    }
                },
            })
        </script>
    </body>

    在`