FE_Vue学习笔记-模板语法 & 数据绑定 & 数据代理 & 事件 & 计算属性 & 监视属性

尝试 Vue.js 最简单的方法是使用 Hello World 例子,你可以在浏览器新标签页中打开它,跟着例子学习一些基础用法,或者你也可以创建一个 .html 文件,然后通过如下方式引入 Vue:

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

1 引入Hello案例- 声明式渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。下面一个demo讲解:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="root">
    <h1>Hello - {{ message }}</h1>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let app = new Vue({
        el: '#root', // el用于指定当前Vue实例为哪个容器服务。
        data: {
            message: 'zhaoshuai@inspur.com'
        }
    })
</script>

</body>
</html>

在这里插入图片描述
我们已经成功创建了第一个 Vue 应用!看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.message 的值,你将看到上例相应地更新。(如上)

注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) ,然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。

除了文本插值,我们还可以像这样来绑定元素 attribute:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app-2">
  <span v-bind:title="bindMessage">
      <h1>鼠标悬停几秒钟查看此处动态绑定的提示信息!</h1>
  </span>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    let app2 = new Vue({
        el: '#app-2',
        data: {
            bindMessage: '页面加载于 ' + new Date().toLocaleString()
        }
    })
</script>
</body>
</html>

在这里插入图片描述
这里我们遇到了一点新东西。你看到的 v-bind attribute 被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute。可能你已经猜到了,它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 title attribute 和 Vue 实例的 message property 保持一致”。

property是DOM中的属性,是JavaScript里的对象;
attribute是HTML标签上的特性,它的值只能够是字符串;

如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = ‘新消息’,就会再一次看到这个绑定了 title attribute 的 HTML 已经进行了更新。注意:

  1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
  2. root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
  3. root容器里的代码被称为【Vue模板】;
  4. Vue实例和容器是一一对应的;
  5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
  6. {{ xxx} }中的 xxx 要写js表达式,且xxx可以自动读取到data中的所有属性;
  7. 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;

2 模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

2.1 插值
文本

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

<span>Message: {{ msg }}</span>

Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。

2.2 指令

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:

<a v-bind:href="url">...</a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-3">
    <a v-bind:href="url">百度一下</a>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    let app3 = new Vue({
        el: "#app-3",
        data: {
            url: 'https://www.baidu.com/'
        }
    })
</script>
</body>
</html>

在这里插入图片描述

<div id="app-3">
    <a :href="url">百度一下</a>
</div>
2.3 Vue模板语法-插值语法 & 指令语法
1.插值语法:
	功能:用于解析标签体内容。
	写法:{{xxx}},xxx是JS表达式,且可以直接读取到data中所有属性。
	起始标签和结束标签中间夹的内容就是标签体
	<div id="v-bind-href">
       <a v-bind:href="url">{{url === 'https://www.baidu.com' ? 'hello-baidu' : 'hello-ohters'}}  </a>
    </div>
2.指令语法:
	功能:用于解析标签(包括:标签属性、标签体内容、绑定事件......)。
	举例:v-bind:href="xxx" 或写为 :href="xxx" ,xxx同样为JS表达式。
	Vue中有很多指令,且形式都是 v-??? ,此处的v-bind只是例子

3 数据绑定 [vue 页面的流动] v-bind & v-model

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
<!--    单向绑定(v-bind):数据只能从data流向页面-->
    单项数据绑定: <input type="text" v-bind:value="name"> 
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    new Vue({
        el: '#app-1',
        data: {
            name: 'zhaoshuai-lc@inpsur.com'
        }
    })
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
(1)双向绑定一般都应用在表单类元素上(都有value值)(如input、select等)。
(2)v-model:value 可以简写为 v-model,因为 v-model默认收集的就是value的值。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
    <!--    单向绑定(v-bind):数据只能从data流向页面-->
    单项数据绑定: <input type="text" v-bind:value="name"><br/>
    <!--    双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data-->
    双向数据绑定:<input type="text" v-model:value="name"><br/>
<!-- 备注:
    (1)双向绑定一般都应用在表单类元素上(都有value值)(如input、select等)。
    (2)v-model:value 可以简写为 v-model,因为 v-model默认收集的就是value的值。-->
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    new Vue({
        el: '#app-1',
        data: {
            name: 'zhaoshuai-lc@inpsur.com'
        }
    })
</script>
</body>
</html>

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

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

4 el [vue实例页面容器的关联] && data [vue 数据]

data与el的两种写法

1.el有2种写法
(1)new Vue时候配置el属性。
(2)先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
 2.data有2种写法
(1)对象式
(2)函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
3.一个重要原则:
 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
4.1 el 两种写法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
    <div>你好,{{ name }}</div>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    let vm = new Vue({
        // el: '#app-1', // 第一种
        data: {
            name: 'zhaoshuai-lc@inspur.com'
        }
    })
    console.info(vm)
    // 2秒之后再指定值
    setTimeout(() => {
        vm.$mount('#app-1') // 第二种
    }, 2000)

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

在这里插入图片描述

4.2 data 的两种写法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!--准备好一个容器-->
<div id="app-1">
    <div>你好,{{ name }}</div>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。
    let vm = new Vue({
        el: '#app-1',
        // 对象的第一种写法 对象式
        /*        data: {
                    name: 'zhaoshuai-lc@inspur.com'
                }*/
        // 对象的第二种写法 函数式
        // 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了,而是window。
        // data: function () { 替换为
        data() {
            console.info('@@@', this) // 此处的this是Vue实例对象
            return {
                name: 'zhaoshuai-lc@inspur.com'
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

5 M[模型] - V[页面视图] - VM[VM实例对象]

虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。
在这里插入图片描述

原型上有的东西,插值中都可以随便用,如“$options”、“$emit”、“_c”等。
也就是说,并不是data里面有的,DOM才可以直接用----
而是data中的东西,最终在实例里面(VM中),所以都能在模板(V)中直接使用。

<body>
<!--准备好一个容器-->
<div id="v-bind-date">
    单项数据流动: <input type="text" v-model="url">
    <h1>{{this.$createElement.length}}</h1>
</div>
<script>
    Vue.config.productionTip = false
    let app = new Vue({
        el: '#v-bind-date', // el用于指定当前Vue实例为哪个容器服务。
        data: {
            url: 'https://www.baidu.com'

        }
    })
</script>
</body>

MVVM模型:
1.M:模型(Model):对应data中的数据
2.V:视图(View):模板
3.VM:视图模型(ViewModel):Vue实例对象
观察发现:
1.data中所有的属性,最后都出现在VM身上。
2.VM身上的所有属性即Vue原型上所有属性,在Vue模板中都可以直接使用。

6 JS 相关知识 - 为对象添加一个属性 - Object.defineProperty

<script>
    let person = {
        name: 'zhaoshuai-lc',
        sex: 'male'
    }
    Object.defineProperty(person, 'age', {
        value: 18
    })

    console.log(person)
    for (let personKey in person) {
        console.log(person[personKey])
    }
</script>

在这里插入图片描述
通过遍历对象可以发现 - age 是不可以被枚举的
在这里插入图片描述

6.1 控制属性是否可以枚举 enumerable:true
    Object.defineProperty(person, 'age', {
        value: 18,
        enumerable:true, //控制属性是否可以枚举,默认值为false
    })

在这里插入图片描述
可以被枚举,但是不可以被修改
在这里插入图片描述

6.2 控制属性是否可以被修改 writable:true && configurable:true - 控制属性是否可以被删除
    Object.defineProperty(person, 'age', {
        value: 18,
        enumerable:true, //控制属性是否可以枚举,默认值为false
        writable:true, //控制属性是否可以被修改,默认值为false
        configurable:true //控制属性是否可以被删除,默认值为false
    })
6.3 对象属性存在的 - get() set()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    let number = 19
    let person = {
        name: 'zhaoshuai-lc',
        sex: 'male'
    }
    Object.defineProperty(person, 'age', {
        // 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
        get() {
            console.log('有人读取age属性了')
            return number
        },

        // 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
        set(value){
            console.log('有人修改了age属性,且值是',value)
            number = value
        }
    })
    console.info("person", person)
    console.info("number", number)
</script>
</body>
</html>

在这里插入图片描述

7 引出理解数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作 (读/写)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    let obj1 = {
        x: 100
    }
    let obj2 = {
        y: 200
    }
    Object.defineProperty(obj2, 'x', {
        get() {
            return obj1.x
        },
        set(value) {
            obj1.x = value
        }
    })

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

在这里插入图片描述

8 Vue中的数据代理

将鼠标放到实例中的数据上,可以看到也提示了“Invoke property getter”。也就是说当有人访问name的时候,getter就在工作。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
vm._data === data 为ture。也就是说_data完全来自于data
在这里插入图片描述
创建了实例对象vm——Vue收集data数据——拿到vm后,Vue往vm的_data上添加name、address(通过getter)

在这里插入图片描述
也就是说,Vue通过数据代理,把_data中的数据放到vm上。目的就是为了编码更方便。
_data中的东西不是数据代理,而是做了一个数据劫持。相当于是把data里面的东西做了修改/升级,以便更好地完成响应式操作。

1.Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:更加方便操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。

9 事件处理 - v-on:click / @click

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app-1">
    <button v-on:click="showInfo">{{ name }}</button>
</div>

<script type="text/javascript">
    new Vue({
        el: '#app-1',
        data() {
            return {
                name: '点我提示信息'
            }
        },
        methods: {
            showInfo() {
                alert('Hello-zhaoshuai-lc@inspur.com')
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件处理</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root">
    <div>{{ name }}</div>
    <!-- <button v-on:click="showInfo">点我提示信息</button> -->
    <button @click="showInfo1">点我提示信息1(不传参)</button>
    <button @click="showInfo2(66,$event)">点我提示信息2(传参)</button>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#root',
        data: {
            name: 'zhaoshuai-lc'
        },
        methods: {
            showInfo1(event) {
                console.log(event.target.innerText)
                console.log(this) //此处的this指的是vm
                alert('hello-zhaoshuai-lc@inspur.com')
            },
            showInfo2(number, event) {
                console.log(event.target.innerText)
                alert('HELLO-zhaoshuai-lc@inspur.com')
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
事件的基本使用:

1.使用 v-on:xxx 或者 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了(指向window);
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或者组件实例对象;
5. @click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参。

10 事件修饰符

10.1 @click.prevent
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件处理</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
    <style>
        * {
            margin: 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="event">
    <!-- 阻止默认事件(常用) -->
    <a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>

</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vmEvent = new Vue({
        el: '#event',
        data: {},
        methods: {
            showInfo() {
                alert("hello-zhaoshuai-lc@inspur.com")
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

10.2 @click.stop
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件处理</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
    <style>
        * {
            margin: 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="event">
    <!-- 阻止默认事件(常用) -->
    <a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>

    <!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
    <div class="demo1" @click="showInfo_2">
        <button @click.stop="showInfo_1">点我提示信息</button>
    </div>

</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vmEvent = new Vue({
        el: '#event',
        data: {},
        methods: {
            showInfo_1() {
                alert("hello-zhaoshuai-lc@inspur.com")
            },

            showInfo_2() {
                alert("hello-2-zhaoshuai-lc@inspur.com")
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

<!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
    <div class="demo1" @click="showInfo_2">
        <button @click.stop="showInfo_1">点我提示信息</button>
    </div>

这是往外冒泡的点击事件:
在这里插入图片描述

10.3 @click.once
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件处理</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
    <style>
        * {
            margin: 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="event">
    <!-- 阻止默认事件(常用) -->
    <a href="https://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>

    <!-- 阻止事件冒泡(常用) 冒泡指的是从里到外 -->
    <div class="demo1" @click="showInfo_2">
        <button @click.stop="showInfo_1">点我提示信息</button>
    </div>

    <!-- 事件只触发一次(常用) -->
    <button @click.once="showInfo_3">点我提示信息</button>

</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vmEvent = new Vue({
        el: '#event',
        data: {},
        methods: {
            showInfo_1() {
                alert("hello-zhaoshuai-lc@inspur.com")
            },

            showInfo_2() {
                alert("hello-2-zhaoshuai-lc@inspur.com")
            },

            showInfo_3() {
                alert("hello-3-zhaoshuai-lc@inspur.com")
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

<!-- 事件只触发一次(常用) -->
    <button @click.once="showInfo_3">点我提示信息</button>

事件只会触发一次。

10.4 事件的捕获模式-@click.capture
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件处理</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
    <style>
        * {
            margin: 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="event">
   
    <!-- 使用事件的捕获模式 -->
    <div class="box1" @click="showMsg(1)">
        div1
        <div class="box2" @click="showMsg(2)">
            div2
        </div>
    </div>

</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vmEvent = new Vue({
        el: '#event',
        data: {},
        methods: {
            showMsg(msg) {
                console.log(msg)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
冒泡的形式,从里到外调用(如上)

<!-- 使用事件的捕获模式 -->
    <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_2">
        <button @click="showInfo_1">点我提示信息</button>
    </div>
10.5 事件的默认行为立即执行,无需等待事件回调执行完毕

在这里插入图片描述

 <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<!--    滚轮事件-->
<!--    <ul @wheel.passive="demo" class="list">-->
<!--    拖动滚动条事件-->
    <ul @scroll.passive="demo" class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>

Vue中的事件修饰符:

1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕。

11 键盘事件

11.1 @keyup.enter 按下回车提示信息
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="key-event">
    <input type="text" placeholder="按下回车提示信息" @keyup.enter="showInfo">
</div>
<script type="text/javascript">
    new Vue({
        el: '#key-event',
        data: {},
        methods: {
            showInfo(e) {
                console.log(e.target.value)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

11.2 Vue中常用的按键别名
回车 => enter
删除 => delete(捕获“删除”和“退格”键)
推出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right

打印其他键盘按键的别名以及code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="key-event">
    <input type="text" placeholder="打印其他键盘按键的别名以及code" @keyup="showInfo">
</div>
<script type="text/javascript">
    new Vue({
        el: '#key-event',
        data: {},
        methods: {
            showInfo(e) {
                console.log(e.key, e.keyCode)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
特别注意两个单词的 如 CapsLock:

<input type="text" placeholder="按下CapsLock提示信息" @keyup.caps-lock="showInfo">

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

配合keyup使用:按下修饰键的同时,再按下其它键,随后释放其它键,事件才被触发。
配合keydown使用:正常触发

修饰符可以连续写,如@click.prevent.stop,先阻止默认事件,后阻止冒泡
系统修饰键可以连续写,如@keyup.ctrl.y,也就是按下Ctrl+y事件

12 姓名案例引出计算属性

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。

复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{firstName.slice(0,3)}}-{{secondName}} </span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc'
            }
        },
        methods: {}
    })
</script>
</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName() }}</span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc@inspur.com'
            }
        },
        methods: {
            fullName() {
                return this.firstName + '-' + this.secondName
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

13 计算属性

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

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

3.get函数什么时候执行?
1)初次读取时会执行一次。
2)当依赖的数据发生改变时会被再次调用。

4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。

5.备注:
1)计算属性最终会出现在vm上,直接读取使用即可。
2)如果计算属性要被修改,那必须直接写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="name-calculate">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
        el: '#name-calculate',
        data: {
            firstName: 'zhao',
            secondName: 'shuai-lc@inspur.com'
        },
        methods: {},
        computed: {
            fullName: {
                // 当有人读取fullName的时候调用get()
                // 1)初次读取时会执行一次。
                // 2)当依赖的数据发生改变时会被再次调用。
                get() {
                    return this.firstName + ' - ' + this.secondName
                },
                // 当有人修改fullName时候调用set()
                set(value) {
                    const list = value.split('-');
                    this.firstName = list[0]
                    this.secondName = list[1]
                }
            }
        }
    })

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

在这里插入图片描述
get有什么作用?
当有人读取了fullName时,get就会被调用,且返回值就作为fullName的值
get什么时候被调用?1.初次读取fullName时。2.所依赖的数据发生变化时。

1)vm._data里面是没有计算属性的。
在这里插入图片描述

2)计算属性中get函数的this,Vue已经维护好了,并把getter中的this指向调成了vm。
3)书写多次fullName但是getter只用了一次,说明Vue有缓存。如果用上集methods方法,则书写几次方法就调用几次,显然有弊端。

14 计算属性简写

确定了只读,不可以修改,才能使用简写方式。

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span> <br/><br/>
</div>

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

    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            // 简写
            fullName: function () {
                return this.firstName + '-' + this.lastName
            }
        }
    })
</script>
</body>
</html>

14 天气案例引入监视属性

在Vue开发者工具中,如果在页面中没用到该值,即使数据已经发生改变,Vue也不会更新数据。

14.1 methods && computed
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h1>今天天气很 - {{ info }}</h1>
    <!--    <button @click="changeWeather()">切换天气</button>-->
    <button @click="judgeHot = !judgeHot">切换天气</button>
</div>

<script type="text/javascript">
    new Vue({
        el: '#weather',
        data: {
            judgeHot: true
        },
        computed: {
            info() {
                return this.judgeHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.judgeHot = !this.judgeHot
            }
        }

    })
</script>

</body>
</html>

在这里插入图片描述

15 监视属性

监视属性watch:

1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!
3.监视的两种写法:
1)new Vue时传入watch配置
2)通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h1>今天天气很 - {{ info }}</h1>
    <button @click="changeWeather()">切换天气</button>
</div>

<script type="text/javascript">
    new Vue({
        el: '#weather',
        data: {
            judgeHot: true,
        },
        computed: {
            info() {
                return this.judgeHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.judgeHot = !this.judgeHot
            }
        },
        watch: {
            judgeHot: {
                // immediate:true, //初始化时让hander()调用一下, 默认为false
                immediate: false,
                // handler什么时候调用?当judgeHot发生改变时
                handler(newValue, oldValue) {
                    console.log('judgeHot 被修改了 ...', newValue, oldValue)
                }
            },

            info: {
                handler(newValue, oldValue) {
                    console.log('info 被修改了 ...', newValue, oldValue)
                }
            }
        }
    })
</script>

</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h1>今天天气很 - {{ info }}</h1>
    <button @click="changeWeather()">切换天气</button>
</div>

<script type="text/javascript">
    let vm = new Vue({
        el: '#weather',
        data: {
            judgeHot: true,
        },
        computed: {
            info() {
                return this.judgeHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.judgeHot = !this.judgeHot
            }
        }
    })
    vm.$watch('judgeHot', {
        handler(newValue, oldValue) {
            console.log('judgeHot 被修改了 ...', newValue, oldValue)
        }
    })
</script>

</body>
</html>

16 深度监视

深度监视:

1.Vue中的watch默认不监测对象内部值得改变(一层)。
2.配置deep:true可以监测对象内部值改变(多层)。

备注:
1.Vue自身可以监测对象内部值得改变,但Vue提供得watch默认不可以!
2.使用watch时根据数据得具体结构,决定是否采用深度监视。
默认不开启是为了提升效率
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h3>a的值是:{{ numbers.a }}</h3>
    <button @click="autoIncrement()">点我让a+1</button>
</div>

<script type="text/javascript">
    let vm = new Vue({
        el: '#weather',
        data: {
            numbers: {
                a: 1,
                b: 2
            }
        },
        computed: {},
        methods: {
            autoIncrement() {
                return this.numbers.a++
            }
        },
        watch: {
            // 监视多级结构中某个属性的变化
            'numbers.a': {
                handler() {
                    console.log('a has changed ...')
                }
            }
        }
    })

</script>

</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h3>a的值是:{{ numbers.a }}</h3>
    <button @click="autoIncrementA()">点我让a+1</button>
    <h3>b的值是:{{ numbers.b }}</h3>
    <button @click="autoIncrementB()">点我让b+1</button>
    <br/><br/>
</div>

<script type="text/javascript">
    let vm = new Vue({
        el: '#weather',
        data: {
            numbers: {
                a: 1,
                b: 2
            }
        },
        computed: {},
        methods: {
            autoIncrementA() {
                return this.numbers.a++
            },
            autoIncrementB() {
                return this.numbers.b++
            }
        },
        watch: {
            // 监视多级结构中所有属性的变化
            numbers: {
                deep: true,
                handler() {
                    console.log('numbers has changed ...')
                }
            }
        }
    })

</script>

</body>
</html>

在这里插入图片描述

17 监视的简写形式

简写的前提是,如果不需要immediate、deep等的配置项,即配置项中只有handler的时候才可以简写。

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="root111">
    <h2>今天天气很{{ info }}</h2>
    <button @click="changeWeather">切换天气</button>
</div>

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

    const vm = new Vue({
        el: '#root111',
        data: {
            isHot: true
        },
        computed: {
            info() {
                return this.isHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot
            }
        },
        watch: {
            /*isHot:{
                handler(newValue,oldValue){
                    console.log('isHot被修改了',newValue,oldValue)
                }
            }*/

            isHot(newValue, oldValue) {
                console.log('isHot被修改了', newValue, oldValue)
            }
        }
    })

    vm.$watch('isHot', {
        handler(newValue, oldValue) {
            console.log('isHot 被修改了 ...', newValue, oldValue)
        }
    })
    //vm.$watch简写
    vm.$watch('isHot', function (newValue, oldValue) {
        console.log('isHot被修改了', newValue, oldValue)
    })
</script>
</body>
</html>

18 watch对比computed

两者都能实现的时候,选用computed比较简单,需要异步计算等比较复杂实现的时候用watch。

computed和watch之间的区别:

1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        watch: {
            firstName(newValue, oldValue) {
                this.fullName = newValue + '#' + this.lastName
            },
            lastName(newValue, oldValue) {
                this.fullName = this.firstName + '#' + newValue
            }
        }
    })
</script>
</body>
</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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        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
            }
        }
    })
</script>
</body>
</html>

19 条件渲染 v-show v-if

使用template可以使其里面的内容在html的结构中不变。
条件渲染:
1.v-if
写法:
(1)v-if=“表达式”
(2)v-else-if=“表达式”
(3)v-else
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。

注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”。

2.v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
3.备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="conditional-rendering">
    <!-- 使用v-show做条件渲染 切换频率高-->
    <!--    <h1 v-show="judge">hello {{ name }}</h1>-->

    <!-- 使用v-if做条件渲染 切换频率低-->
    <!--    <h1 v-if="judge">hello {{ name }}</h1>-->

    <h2>当前的n值是:{{ n }}</h2>
    <button @click="n++">点我n+1</button>
    <!-- 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>other</div>

    <!-- v-if与template结合 -->
    <template v-if="n === 1">
        <h2>你好</h2>
        <h2>尚硅谷</h2>
        <h2>北京</h2>
    </template>


</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
        el: '#conditional-rendering',
        data: {
            name: 'zhaoshuai-lc@inspur.com',
            judge: false,
            n: 1
        }
    })
</script>
</body>
</html>

20 列表渲染

v-for指令:
1.用于展示列表数据
2.语法:v-for=“(item,index) in xxx” :key=“yyy”
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <ul>
        <li v-for="item in personList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>
</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            personList: [
                {id: 1001, name: 'zhaoshuai-la', age: 101},
                {id: 1002, name: 'zhaoshuai-lb', age: 102},
                {id: 1003, name: 'zhaoshuai-lc', age: 103}
            ]
        }
    })
</script>
</body>
</html>

在这里插入图片描述

    <ul>
        <li v-for="(a, b) in personList">
            {{ a }} - {{ b }}
        </li>
    </ul>

在这里插入图片描述
上面的key可以做一个替换:

    <ul>
        <li v-for="(item, index) in personList" :key="index">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

in 也可以替换为 of:

    <ul>
        <li v-for="(item, index) of personList" :key="index">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

除了数组还可以遍历对象:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <ul>
        <li v-for="(key, value) of car" :key="key">
            {{ key }} - {{ value }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            car: {
                name: '奥迪A8',
                price: '70万',
                color: '黑色'
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
测试遍历字符串:

  <ul>
      <li v-for="(char, index) of str" :key="index">
          {{ index }} - {{ char }}
      </li>
  </ul>
    
str: 'hello'

在这里插入图片描述

21 key作用与原理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <button @click.once="add">添加一个 zhaoshuai-ld</button>
    <ul>
        <li v-for="(item, index) of personList" :key="index">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            personList: [
                {id: 1001, name: 'zhaoshuai-la', age: 101},
                {id: 1002, name: 'zhaoshuai-lb', age: 102},
                {id: 1003, name: 'zhaoshuai-lc', age: 103}
            ]
        },
        methods: {
            add() {
                let person = {id: 1004, name: 'zhaoshuai-ld', age: 104}
                this.personList.unshift(person)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

在这里插入图片描述
看似没有问题,其实问题很大,如下:

遍历列表时key的作用(index作为key)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <button @click.once="add">添加一个 zhaoshuai-ld</button>
    <ul>
        <li v-for="(item, index) of personList" :key="index">
            {{ item.name }} - {{ item.age }}
            <input type="text">
        </li>
    </ul>

</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            personList: [
                {id: 1001, name: 'zhaoshuai-la', age: 101},
                {id: 1002, name: 'zhaoshuai-lb', age: 102},
                {id: 1003, name: 'zhaoshuai-lc', age: 103}
            ]
        },
        methods: {
            add() {
                let person = {id: 1004, name: 'zhaoshuai-ld', age: 104}
                this.personList.unshift(person)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

遍历列表时key的作用(id作为key)

    <ul>
        <li v-for="(item, index) of personList" :key="item.id">
            {{ item.name }} - {{ item.age }}
            <input type="text">
        </li>
    </ul>

在这里插入图片描述
在这里插入图片描述
面试题:react、Vue中的key有什么作用?(key的内部原理)
1.虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:

 若虚拟DOM中内容没变,直接使用之前的真实DOM
 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

(2)旧虚拟DOM中未找到与新虚拟DOM相同的key:

创建新的真实DOM,随后渲染到页面

3.用index作为key可能会引发的问题:

(1)若对数据进行逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。

4.开发中如何选择key?

(1)最好使用每条数据的唯一标识作为key,比如说id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表中用于展示,使用index作为key是没有问题的。

22 列表过滤

用watch实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <input type="text" placeholder="请输入名字" v-model="keyWorld">
    <ul>
        <li v-for="(item, index) of filterPersonList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>
</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            keyWorld: '',
            personList: [
                {id: '001', name: '马冬梅', age: 19, sex: '女'},
                {id: '002', name: '周冬雨', age: 20, sex: '女'},
                {id: '003', name: '周杰伦', age: 21, sex: '男'},
                {id: '004', name: '温兆伦', age: 22, sex: '男'}
            ],
            filterPersonList: []
        },
        watch: {
            keyWorld: {
                immediate: true,
                handler(newValue, oldValue) {
                    this.filterPersonList = this.personList.filter((item) => {
                        return item.name.indexOf(newValue) !== -1
                    })
                }
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
用computed实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <input type="text" placeholder="请输入名字" v-model="keyWorld">
    <ul>
        <li v-for="(item, index) of filterPersonList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            keyWorld: '',
            personList: [
                {id: '001', name: '马冬梅', age: 19, sex: '女'},
                {id: '002', name: '周冬雨', age: 20, sex: '女'},
                {id: '003', name: '周杰伦', age: 21, sex: '男'},
                {id: '004', name: '温兆伦', age: 22, sex: '男'}
            ]
        },
        computed: {
            filterPersonList() {
                return this.personList.filter((item) => {
                    return item.name.indexOf(this.keyWorld) !== -1
                })
            }
        }
    })
</script>
</body>
</html>

23 列表排序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <input type="text" placeholder="请输入名字" v-model="keyWorld">
    <button @click="sortType = 2">升序</button>
    <button @click="sortType = 1">降序</button>
    <button @click="sortType = 0">原序</button>
    <ul>
        <li v-for="(item, index) of filterPersonList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    new Vue({
        el: '#v-for',
        data: {
            keyWorld: '',
            sortType: 0, // 0-原顺序 1-降序 2-升序
            personList: [
                {id: '001', name: '马冬梅', age: 19, sex: '女'},
                {id: '002', name: '周冬雨', age: 24, sex: '女'},
                {id: '003', name: '周杰伦', age: 55, sex: '男'},
                {id: '004', name: '温兆伦', age: 12, sex: '男'}
            ]
        },
        computed: {
            filterPersonList() {
                let filterList = this.personList.filter((item) => {
                    return item.name.indexOf(this.keyWorld) !== -1
                })
                // 判断是否需要排序
                if (this.sortType) {
                    filterList.sort((a, b) => {
                        return this.sortType === 1 ? b.age - a.age : a.age - b.age
                    })
                }
                return filterList
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

24 更新时的一个问题-this.personList[0] = { 更新值 }不起作用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <button @click="updateMei">更新马冬梅的信息</button>
    <ul>
        <li v-for="(item, index) of personList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    let vm = new Vue({
        el: '#v-for',
        data: {
            personList: [
                {id: '001', name: '马冬梅', age: 19, sex: '女'},
                {id: '002', name: '周冬雨', age: 24, sex: '女'},
                {id: '003', name: '周杰伦', age: 55, sex: '男'},
                {id: '004', name: '温兆伦', age: 12, sex: '男'}
            ]
        },
        methods: {
            updateMei() {
                /*this.personList[0].name = '马保国'
                this.personList[0].age = 50
                this.personList[0].sex = '男'*/
                // ok 如上是起作用的

                this.personList[0] = {id: '001', name: '马保国', age: 50, sex: '男'}
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

25 通过以上问题 - 我们引出 Vue监测数据的原理_对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-school">
    学校名称:{{ name }} <br/>
    学校地址:{{ address }}
</div>
<script type="text/javascript">
    let vm = new Vue({
        el: '#v-school',
        data: {
            name: '北京大学',
            address: '北京'
        }
    })
</script>
</body>
</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>
<script type="text/javascript">

    let data = {
        name: '尚硅谷',
        address: '北京',
    }

    // 创建一个监视的实例对象,用于监视data中属性的变化
    const obs = new Observer(data)
    console.log(obs)

    // 准备一个vm实例对象
    let vm = {}
    vm._data = data = obs

    function Observer(obj) {
        // 汇总对象中所有的属性
        const keys = Object.keys(obj)
        // 遍历
        keys.forEach((k) => {
            Object.defineProperty(this, k, {
                get() {
                    return obj[k]
                },
                set(val) {
                    console.log('${k}被改了,我要去解析模板,生成虚拟DOM...我要开始忙了')
                    obj[k] = val
                }
            })
        })
    }

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

在这里插入图片描述
本次书写的代码为简化版,Vue更完善的点有:

如果要修改_data中的name,完整写法为 vm._data.name = ‘atguigu’ ,还可以直接简写为 vm.name = ‘atguigu’,因为Vue做了数据代理。

当对象中还有对象时,Vue也能做到有为其服务的getter和setter。Vue里面写了递归,有多少层就能写到多少层,直到那个东西不再是对象为止,对象在数组中也是如此。

Vue监测数据的原理,就是靠setter。

只要修改数据,Vue就会重新解析模板,生成虚拟DOM。

26 Vue.set()方法

(1)假设数组a中不存在对象b,Vue中访问a.b不会报错,只是不显示(Vue中默认undefined不显示),Vue中访问b会报错。

(2)Vue.set 只能给data里的某个对象追加属性,不能直接给data追加属性。

(3)向响应式对象中添加一个property,并确保这个新property同样是响应式的,且触发视图更新。
在这里插入图片描述

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-set">
    <h1>学校信息</h1>
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <hr/>
    <h1>学生信息</h1>
    <button @click="addSex">添加一个性别属性,默认值是男</button>
    <h2>姓名:{{ student.name }}</h2>
    <h2>性别:{{ 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>

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

    let vm = new Vue({
        el: '#v-set',
        data: {
            name: '北京大学',
            address: '北京',
            student: {
                name: 'tom',
                age: {
                    rAge: 40,
                    sAge: 29
                },
                friends: [
                    {name: 'jerry', age: 35},
                    {name: 'tony', age: 36}
                ]
            }
        },
        methods: {
            addSex() {
                // Vue.set(this.student,'sex','男')
                this.$set(this.student, 'sex', '男')
            }
        },
    })
</script>
</body>
</html>

27 Vue监测数据的原理_数组

原生Javascript数组使用的方法,例如push,就是从Array原型中找到的。可用 arr.push === Array.prototype.push 验证。

而Vue中的push却不等于 Array.prototype.push ,因为Vue中的push是经过包装的。

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="ddd">
    <h1>学生信息</h1>
    <h2>爱好:</h2>
    <ul>
        <li v-for="(item, index) in student.hobby" :key="index">
            {{ item }}
        </li>
    </ul>
</div>

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

    const vm = new Vue({
        el: '#ddd',
        data: {
            student: {
                name: 'tom',
                age: 18,
                hobby: ['抽烟', '喝酒', '烫头'],
                friends: [
                    {name: 'jerry', age: 35},
                    {name: 'tony', age: 36}
                ]
            }
        },
        methods: {
        },
    })
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
针对于上面的数组,有了解决方案:如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="v-for">
    <button @click="updateMei">更新马冬梅的信息</button>
    <ul>
        <li v-for="(item, index) of personList" :key="item.id">
            {{ item.name }} - {{ item.age }}
        </li>
    </ul>

</div>
<script type="text/javascript">
    let vm = new Vue({
        el: '#v-for',
        data: {
            personList: [
                {id: '001', name: '马冬梅', age: 19, sex: '女'},
                {id: '002', name: '周冬雨', age: 24, sex: '女'},
                {id: '003', name: '周杰伦', age: 55, sex: '男'},
                {id: '004', name: '温兆伦', age: 12, sex: '男'}
            ]
        },
        methods: {
            updateMei() {
                /*this.personList[0].name = '马保国'
                this.personList[0].age = 50
                this.personList[0].sex = '男'*/
                // ok 如上是起作用的

                // this.personList[0] = {id: '001', name: '马保国', age: 50, sex: '男'} 不起作用
                this.personList.splice(0,1, {id: '001', name: '马保国', age: 50, sex: '男'})
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

28 总结Vue监视数据

<!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></title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="ddd">
    <h1>学生信息</h1>

    <button @click="student.age++">年龄+1岁</button>
    <br/>
    <button @click="addSex">添加一个性别属性,默认值:男</button>
    <br/>
    <button @click="student.sex = '未知' ">修改性别</button>
    <br/>
    <button @click="addFriend">在列表首位添加一个朋友</button>
    <br/>
    <button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button>
    <br/>
    <button @click="addHobby">添加一个爱好</button>
    <br/>
    <button @click="updateHobby">修改第一个爱好为:开车</button>
    <br/>
    <button @click="removeSmoke">过滤掉爱好中的抽烟</button>
    <br/>

    <h2>姓名:{{ student.name }}</h2>

    <h2 v-if="student.sex">性别:{{ student.sex }}</h2>

    <h2>年龄:{{ student.age }}</h2>

    <h2>爱好:</h2>
    <ul>
        <li v-for="(item, index) in student.hobby" :key="index">
            {{ item }}
        </li>
    </ul>

    <h2>朋友们:</h2>
    <ul>
        <li v-for="(item, index) in student.friends" :key="index">
            {{ item.name }}--{{ item.age }}
        </li>
    </ul>
</div>

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

    const vm = new Vue({
        el: '#ddd',
        data: {
            student: {
                name: 'tom',
                age: 18,
                hobby: ['抽烟', '喝酒', '烫头'],
                friends: [
                    {name: 'jerry', age: 35},
                    {name: 'tony', age: 36}
                ]
            }
        },
        methods: {
            addSex() {
                // Vue.set(this.student,'sex','男')
                this.$set(this.student, 'sex', '男')
            },
            addFriend() {
                this.student.friends.unshift({name: 'jack', age: 70})
            },
            updateFirstFriendName() {
                this.student.friends[0].name = '张三'
                this.student.friends[0].age = 5
            },
            addHobby() {
                this.student.hobby.push('学习')
            },
            updateHobby() {
                // this.student.hobby.splice(0,1,'开车')
                // Vue.set(this.student.hobby,0,'开车')
                this.$set(this.student.hobby, 0, '开车')
            },
            removeSmoke() {
                this.student.hobby = this.student.hobby.filter((h) => {
                    return h !== '抽烟'
                })
            }

        },
    })
</script>
</body>
</html>

Vue监视数据的原理:

1.Vue会监视data中所有层次的数据。

2.如何监测对象中的数据?

 通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1)对象中后追加的属性,Vue默认不做响应式处理。
(2)如需给后添加的属性做响应式,请使用如下API:
  vm.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的根数据对象添加属性!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值