目录
[✅ 示例:](#✅ 示例:)
计算属性-methods实现:在插值模块里,实现函数的调用功能
[✅ 特点:](#✅ 特点:)
[⚠️ 与 methods 的区别:](#⚠️ 与 methods 的区别:)
[深度监视-deep:true 简写:](#深度监视-deep:true 简写:)
[监视属性- watch实现:](#监视属性- watch实现:)
[📊 主要区别:](#📊 主要区别:)
[:class 和 原生 JavaScript 的 classList ,v-bind --> :class](#:class 和 原生 JavaScript 的 classList ,v-bind --> :class)
[@click 和 原生 JavaScript 的 addEventListener, v-on --> @click](#@click 和 原生 JavaScript 的 addEventListener, v-on --> @click)
[四、条件渲染(v-if / v-show)](#四、条件渲染(v-if / v-show))
[✅ 区别总结:](#✅ 区别总结:)
面试题:react、vue中的key有什么作用?(key的内部原理)
[列表的过滤 - filter](#列表的过滤 - filter)
[列表排序 - computed实现:](#列表排序 - computed实现:)
[💡 Vue监视数据的原理:](#💡 Vue监视数据的原理:)
[总结不易~ 本章节对我有很大收获,希望对你也是!!!!](#总结不易~ 本章节对我有很大收获,希望对你也是!!!!)
在 Vue 的开发过程中,我们经常会遇到以下几种情况:
-
页面中某些值依赖其他数据计算得出
-
需要根据用户操作或数据变化自动响应处理逻辑
-
某些 DOM 元素是否显示要依赖条件判断
-
页面需要渲染一个数组列表并支持交互
这些需求其实都可以归结为几个 Vue 的"基础核心能力":计算属性、监视属性、条件渲染、列表渲染等。本文将从实战角度出发,带你理解这些特性背后的逻辑与使用场景。
这里给大家推荐一个Vue自动补全插件,还是很好用的:
一、计算属性(computed)
在模板中拼接字符串、进行运算是常见需求,但如果逻辑复杂且会多次使用,直接写在模板中会导致代码臃肿且难以维护。
Vue 提供了 computed
计算属性来解决这个问题。
✅ 示例:
我们先来实现插值案例 好与 计算属性进行对比:
html
<div class="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
姓名:<span>{{firstName}}-{{lastName}}</span>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
firstName: "张",
lastName: "三"
}
})
</script>
计算属性-methods实现:在插值模块里,实现函数的调用功能
html
<body>
<div class="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
<!-- 姓名:<span>{{firstName}}-{{lastName}}</span> -->
姓名:<span>{{fullName()}}</span>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
firstName: "张",
lastName: "三"
},
methods: {
fullName() {
// return '小猪佩奇'
console.log(this.firstName)
// 这里的this就是代表着vm 那么就可以直接访问data里面的属性值
return this.firstName + '-' + this.lastName
}
}
})
</script>
计算属性-computed的实现:
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
- (1).初次读取时会执行一次。
- (2).当依赖的数据发生改变时会被再次调用。
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便 。
5 .备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
html
<div class="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
<!-- 姓名:<span>{{firstName}}-{{lastName}}</span> -->
姓名:<span>{{fullName}}</span><br>
<!-- // 这里会将fullName存入缓存,这样不用每次都要对对fullName函数进行调用 -->
姓名:<span>{{fullName}}</span><br>
姓名:<span>{{fullName}}</span><br>
姓名:<span>{{fullName}}</span><br>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
// data里面写的都是属性
firstName: "张",
lastName: "三"
},
methods: {
demo() {
console.log('我是一个函数')
}
},
computed: { // 计算属性
// 这里计算属性 fullName 你不能看作是一个函数 而是要把它当作是data里面的属性来一样看待
// 当你访问fullName 的时候 就会自动调用get() 来获取返回值, 这个返回值的命名就是你创建的fullName 存入vm中
fullName: {
// get 有什么作用?
// 当有人读取fullName时, get就会被调用 返回值就作为fullName的值
//get 什么时候会被调用? 1. 初次读取fullName时。 2. 所依赖的数据发生改变的时候
get() {
console.log('get被调用了')
// console.log(this) // 此处的this是vm 系统自动将当前位置的this 指向vm
return this.firstName + '-' + this.lastName
},
// set什么时候调用? 当fullName被修改的时候
set(value) {
console.log('set', value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
计算属性-简写:
javascript
computed: { // 计算属性
// 简写 只有考虑读取不考虑修改的时候才能 用简写形式
// 一定要注意,表面上 fullName是一个函数 而实际上 是执行完这个函数后 往vm上放了一个叫fullName属性的值
// 那么以后只要记住一句 我们在上面配置的属性是 data里面的数据 还是methods里面的方法 还是computed的计算属性
fullName() {
return this.firstName + '-' + this.lastName
}
}
✅ 特点:
-
具有缓存能力:只在相关依赖变化时重新计算
-
更适合用于基于现有数据的"衍生数据"
⚠️ 与 methods 的区别:
特性 | computed | methods |
---|---|---|
缓存 | ✅ 会缓存 | ❌ 每次都重新执行 |
使用场景 | 衍生状态 | 触发行为、事件 |
二、监视属性(watch)
在 Vue 中,watch
用来"观察"某个响应式数据的变化,并在变化时执行指定的回调函数,常用于异步操作、手动监听等场景。
✅ 示例:
监视属性-watch:hanlder是真正的处理函数,必须要写!否则 Vue 不知道回调函数是谁。
这里watch监听的是isHot,所以在isHot发生改变的时候,就会调用handler处理函数!
handler(newValue ,oldValue ) 传入的参数就是新值 和 旧值
immediate: true, // 初始化的时候让handler调用一下
html
<div class="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
// 第一种写法
watch: {
isHot: {
immediate: true, // 初始化的时候让handler调用一下
// handler 什么时候调用? 当isHot发生改变时
handler(newValue, oldValue) {
console.log('isHost被修改了', newValue, oldValue)
}
}
}
})
// 第二种写法
vm.$watch('isHot', {
immediate: true, // 初始化的时候让handler调用一下
// handler 什么时候调用? 当isHot发生改变时
handler(newValue, oldValue) {
console.log('isHost被修改了', newValue, oldValue)
}
})
</script>
🌟结论:
只要
isHot
发生改变,计算属性info
会立刻重新计算(自动触发) ,watch
的监听器也会立刻触发 (前提是你监听了它)。这个过程是响应式系统 自动完成的,你不用手动调用
监视属性-深度监视:
深度监视:
- Vue中的watch默认不监测对象内部值的改变(一层)。
- 配置deep:true可以监测对象内部值改变(多层)。
备注:
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
- 使用watch时根据数据的具体结构,决定是否采用深度监视。
当我们对vm._data里面的对象里面的属性进行监视的时候,可以采用一个一个单独监视:
javascript
const vm = new Vue({
el: '.root',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
watch: {
// 这是在只有监视a一个属性下 如果我们要监视100个呢 不能写100个numbers.吧
'numbers.a': {
handler(newValue, oldValue) {
console.log('a改变了', newValue, oldValue)
}
}
})
但是我们不可能对一个对象里面的一百个属性 都单独写一个监视函数吧,所以这里就引入了一个深度监视的概念- deep:true,当该对象里面的任何一个属性发生变化时,该Vue都能进行监测到,因为深度监视是递归式发生的~
html
<script>
const vm = new Vue({
el: '.root',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
watch: {
// 监视numbers里面的所有属性 深度监视
numbers: {
// deep: true 开启深度监视 能够监视numbers 里面的所有属性的变化,
// 否则numbers里面的value就相当于是一个地址,只要地址没有发生变化,这个numbers就不会变化,handler就会监视失败
deep: true,
handler() {
console.log('numbers改变了')
}
}
}
})
</script>
深度监视-deep:true 简写:
简写 的代价就是不能配置其他属性,所以要配置任何一项属性的时候,都还是要老实些handler处理函数
javascript
// 正常写法
vm.$watch('isHot', {
immediate: true,
deep: true,
handler(newValue, oldValue) {
console.log('监视的是isHot', newValue, oldValue)
}
})
// 简写 的代价就是不能配置其他属性
vm.$watch('isHot', function (newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
})
监视属性- watch实现:
computed和watch之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
html
<div class="root">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
<!-- 姓名:<span>{{firstName}}-{{lastName}}</span> -->
姓名:<span>{{fullName}}</span><br>
<!-- // 这里会将fullName存入缓存,这样不用每次都要对对fullName函数进行调用 -->
姓名:<span>{{fullName}}</span><br>
姓名:<span>{{fullName}}</span><br>
姓名:<span>{{fullName}}</span><br>
<span>{{a}}</span>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
// data里面写的都是属性
firstName: "张",
lastName: "三",
fullName: '张-三',
x: 1,
y: 2
},
computed: { // 计算属性
a() {
return this.x + this.y
},
// 简写
fullName() {
return this.firstName + '-' + this.lastName
}
},
watch: {
firstName(val) {
// setTimeout(function() {
// 这里要写成箭头函数!!!
// 原因就是如果写成function 这个function就不是vue所管理的 而是浏览器引擎管理的
// 那么就是调用的setTimeout的this,这个this指向windows
// 但是如果写成箭头函数 就会跳过setTimeout 指向firstName ,这个firstName是vm里面的普通函数 firstName里面的this又是vm
// 所以此时的this就是指向vm的
setTimeout(() => {
console.log(this)
this.fullName = val + '-' + this.lastName
}, 1000)
},
// firstName(newValue, oldValue) {
// this.fullName = newValue + '-' + this.lastName
// },
lastName(newValue) {
this.fullName = this.firstName + newValue
}
}
})
</script>
📊 主要区别:
特性 | 计算属性 (computed ) |
监视属性 (watch ) |
---|---|---|
功能 | 用于派生 (计算)新值,依赖于响应式数据。 | 用于监听数据变化,并在数据变化时执行某些操作。 |
返回值 | 返回计算的结果,通常是一个新值。 | 没有返回值,通常用于执行副作用(如异步操作、数据更新等)。 |
依赖 | 会缓存计算结果,只有相关依赖数据变化时才会重新计算。 | 每次监听的数据发生变化时都会触发回调,不会进行缓存。 |
用例 | 用于简单的、派生的值(例如根据已有数据计算出一个新值)。 | 用于处理副作用(例如异步请求、手动 DOM 操作、或数据变更后的逻辑)。 |
适用场景 | 当你希望在模板中展示基于已有数据计算的内容时。 | 当你需要在数据变化时执行一些复杂的操作时,如请求API、改变其他数据等。 |
记住 ,如果你只是单纯地需要计算一个新值,选择 计算属性 ;如果你需要做复杂的操作或副作用,选择 监视属性。
三、绑定条件与属性(v-bind)
:class
和 原生 JavaScript 的classList ,v-bind --> :class
:class
是 Vue 中用于动态绑定 class
类名的指令,它相当于原生 JavaScript 中使用 classList
来动态添加、移除类名。
@click
和 原生 JavaScript 的addEventListener, v-on --> @click
@click
是 Vue 中的事件监听器简写,它相当于原生 JavaScript 中使用 addEventListener
来监听 click
事件。
html
<div class="root">
<!-- 绑定class样式 :class 字符串写法 适用于: 样式的类名不确定 需要动态绑定-->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br>
<!-- 绑定class样式 :class 数组写法 适用于: 样式的个数不确定、名字也不确定-->
<div class="basic" :class="classArr">{{name}}</div> <br>
<!-- 绑定class样式 :class 对象写法 适用于: 样式的个数确定、名字也确定 但要动态决定用不用-->
<div class="basic" :class="classObj">{{name}}</div> <br>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
name: "我是哈哈",
mood: 'normal',
// 数组形式
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
// 对象形式
classObj: {
atguigu1: false,
atguigu2: false
},
styleObj: {
fontSize: '40px',
color: 'red',
backgroundColor: 'orange'
}
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
let index = Math.floor(Math.random() * 3)
}
},
})
</script>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
.atguigu1 {
background-color: yellowgreen;
}
.atguigu2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.atguigu3 {
border-radius: 20px;
}
</style>
四、条件渲染(v-if / v-show)
Vue 提供了两种方式来控制元素的显示与隐藏:v-if
和 v-show
。
条件渲染:
- v-if
- 写法:
- (1).v-if="表达式"
- (2).v-else-if="表达式"
- (3).v-else="表达式"
- 适用于:切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。
- 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被"打断"。
- v-show
- 写法:v-show="表达式"
- 适用于:切换频率较高的场景。
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
- 备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
✅ 示例:
html
<div class="root">
<!-- 使用v-show做条件渲染 隐藏和显示 如果变换的频率比较快 就推荐使用 -->
<h2 v-show="a">欢迎来到{{name}}学习</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我助力n+1</button>
<div v-show="n === 1">Angular</div>
<div v-show="n === 2">React</div>
<div v-show="n === 3">Vue</div>
<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>哈哈</div>
<!--template 只能与 v-if 配合使用 -->
<!-- template 能够完全脱掉这一层的标签 从而不破坏内层的结构 -->
<template v-if="n === 1">
<div>hha</div>
<div>hha</div>
<div>hha</div>
</template>
<!-- 使用v-if做条件渲染 直接删除结构 不是隐藏了 -->
<h2 v-if="a">欢迎来到{{name}}学习</h2>
</div>
<script>
const vm = new Vue({
el: '.root',
data: {
name: "武汉传媒学院",
a: true,
n: 0
}
})
</script>
✅ 区别总结:
特性 | v-if | v-show |
---|---|---|
控制方式 | 动态添加/移除 DOM | 控制 display: none |
首次渲染性能 | 较慢 | 较快 |
切换频率 | 建议用于不频繁切换 | 频繁切换更适合 |
五、列表渲染(v-for)
当你需要展示一个数组时,v-for
是你必须掌握的工具。
v-for指令:
- 用于展示列表数据
- 语法:v-for="(item, index) in xxx" :key="yyy"
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
✅ 基础写法:
html
<div id="root">
<h2>人员列表(遍历数组 用的最多)</h2>
<ul>
<li v-for="p in persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
<li v-for="(p,index) in persons" :key="index">
<!-- // p 结构 , index 是当前p元素的索引值 -->
{{p}}----{{index}}
</li>
</ul>
<h2>欢迎来到{{name}}学习</h2>
<!-- 遍历对象 跟遍历数组不一样 第一个参数是value值 第二个参数是key属性名 -->
<h2>汽车信息</h2>
<ul>
<li v-for="(value, key) in car" :key="key">
{{key}} - {{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>遍历字符串(用的少)</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>遍历指定次数(用的少)</h2>
<ul>
<li v-for="(num,index) in 5" :key="index">
{{index}}-{{num}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
// 遍历数组类型数据
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
],
// 遍历对象数据类型
car: {
name: '奥迪A8',
price: '80万',
color: '黑色',
},
// 遍历字符串
str: "abcdefg"
}
})
</script>
key的原理:

面试题:react、vue中的key有什么作用?(key的内部原理)
- 虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
-
用index作为key可能会引发的问题:
-
若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
-
如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
-
开发中如何选择key?:
-
最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
-
如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
html
<div id="root">
<h2>人员列表(遍历数组 用的最多)</h2>
<button @click.once="add">点击添加</button>
<ul>
<li v-for="p in persons" :key="p.id">
{{p.name}}-{{p.age}} <input type="text">
</li>
</ul>
<h2>欢迎来到{{name}}学习</h2>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
// 遍历数组类型数据
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
],
},
methods: {
add() {
const p = { id: '004', name: '老牛', age: 30 }
this.persons.unshift(p)
}
},
})
</script>
html
<ul>
<li v-for="(p, index) in persons" :key="index">
{{p.name}}-{{p.age}} <input type="text">
</li>
</ul>
<ul>
<!-- 不写 默认就是就是key 为 index -->
<li v-for="p in persons">
{{p.name}}-{{p.age}} <input type="text">
</li>
</ul>
- 这两种都是以index索引来进行标记当前li 的序号
- 这样就会导致如果往数组首位置插入一个元素就会导致当前li的序号被改变
- 从而让vue内部进行虚拟内存对比算法的时候 比较不到相同的li序号下标,会导致重写再次生成一份p.name 而input会在原位置不变
- 所以 在数组有id标记的时候 还是要用id来标记当前的:key 这样就不会让当前的li序号随着数组的索引改变而改变
列表的过滤 - filter
✅ 场景一:filter 传自定义参数(推荐)
🧠 示例:过滤掉某个指定的值
javascript
filterHobby(target) {
const arr = this.student.hobby.filter(item => item !== target);
console.log(arr);
}
使用:
javascript
this.filterHobby('抽烟');
✅ 场景二:写成独立函数,传多个参数
javascript
function excludeTarget(item, target1, target2) {
return item !== target1 && item !== target2;
}
filterHobby(target1, target2) {
const arr = this.student.hobby.filter(item => excludeTarget(item, target1, target2));
console.log(arr);
}

#region 和 #endregion实现折叠代码
html
<div id="root">
<h2>人员列表(遍历数组 用的最多)</h2>
<!-- placeholder 显示提示文本 -->
<input type="text" placeholder="请输入搜索的名字" v-model="keyWord">
<ul>
<li v-for="p in filPerons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
<h2>欢迎来到{{name}}学习</h2>
</div>
<script>
// 用watch实现
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
keyWord: "",
// 遍历数组类型数据
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
],
filPerons: []
},
watch: {
keyWord: {
immediate: true,
handler(val) {
// filter 方法会遍历原数组中的每一个元素,
// 并根据提供的回调函数来判断每个元素是否满足条件,
// 如果满足条件,就将该元素加入到返回的新数组中。
this.filPersons = this.persons.filter((p) => {
// 这里构造新数组 不会使用这里的返回值, 这里的返回值就只相当于回调函数 俩判断是否可以添加到新数组中
return p.name.indexOf(val) !== -1
})
// indexOf 返回指定元素在字符串或数组中首次出现的位置(索引)。
// 如果没有找到该元素,则返回 -1。
}
}
}
})
</script>
javascript
// 用computed实现
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
keyWord: '',
// 遍历数组类型数据
persons: [
{ 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: {
// 表面上是一个函数 但实际上就是 返回一个数组后 放入data数据内的 然后该数组就由filPersons来命名
filPerons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
列表排序 - computed实现:

javascript
<div id="root">
<h2>人员列表(遍历数组 用的最多)</h2>
<!-- placeholder 显示提示文本 -->
<input type="text" placeholder="请输入搜索的名字" v-model="keyWord">
<button @click="sortType = 2">年龄升序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 0">原顺序</button>
<ul>
<li v-for="p in filPerons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
<h2>欢迎来到{{name}}学习</h2>
</div>
// 用computed实现
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
keyWord: '',
sortType: 0, // 0原顺序 1降序 2升序 @click=""
// 遍历数组类型数据
persons: [
{ id: '001', name: '马冬梅', age: 30, sex: '女' },
{ id: '002', name: '周冬雨', age: 31, sex: '女' },
{ id: '003', name: '周杰伦', age: 18, sex: '男' },
{ id: '004', name: '温兆伦', age: 19, sex: '男' }
],
},
computed: {
filPerons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
// 这里只是将arr进行排序 那么 每次改变的都是只是arr数组 判断一下是否需要排序 vocal 好聪明
if (this.sortType) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
let arr = [1, 3, 2, 6, 4, 5]
arr.sort((a, b) => {
return a - b
})
console.log(arr)
模拟一个数据监测:
javascript
let data = {
name: '我是哈哈',
address: '武汉'
}
// 创建一个监视的实例对象 用于监视data中属性的变化
const obs = new Observer(data)
// Observer对象
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
}
})
})
}
// setInterval(() => {
// if (data.name !== '武汉') {
// console.log('name被我改了')
// }
// }, 100)
// 这里会进行死循环递归调用
// 每次一要访问data.name 就要调用get 然后又要访问data.name
// Object.defineProperties(data, 'name', {
// get() {
// return data.name
// },
// set(val) {
// data.name = val
// }
// })
Vue.set的使用:



html
<div id="root">
<button @click="addLeader">点我给call校长出来</button>
<h1>学校信息</h1>
<h2>学习的名称:{{name}}</h2>
<h2>学习的地址:{{address}}</h2>
<h2 v-if="school.leader">校长是{{school.leader}}</h2>
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个学生信息 点我让学生变性</button>
<h2>学生姓名:{{student.name}}</h2>
<h2 v-if="student.sex">学生性别:{{student.sex}}</h2>
<h2>学生年龄:{{student.age.rAge}},对外{{student.age.sAge}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}} --- {{f.age}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: "武汉传媒学院",
address: '武汉',
student: {
name: 'tom',
age: {
rAge: 40,
sAge: 29,
},
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
],
},
school: {
}
},
methods: {
addSex() {
// Vue.set(target(要添加的目标位置), key(属性名), val(属性值))
// Vue.set(this.student, 'sex', "男")
// 不过set有局限性 只能给data里面的某一个对象进行追加属性 而不能直接往data里面进行追加属性
this.$set(this.student, 'sex', "男")
},
addLeader() {
this.$set(this.school, 'leader', "小帅")
}
}
})
</script>
Vue监测数据改变的原理:

那么也就是说,对于vm里面的数组和对象进行修改的操作是不同的:
对于数组:

我们直接拿着数组的下标进行修改,会发现Vue根本就不会响应!
是因为对于作者而已,Vue内对数组下标直接访问是没有另外进行Vue的封装的,观察上面对数组进行修改的七个函数,才会让Vue对数组的修改进行响应!!!



也就是说,对于Vue内部对 对象和数组的修改响应各有千秋,而对数组的修改却只能操作函数来进行修改,不然Vue是不会响应的。
总结一下对于数组 和 对象的各种修改的场景:
数组:修改数组内容
javascript
// 用computed实现
const vm = new Vue({
el: '#root',
data: {
name: '武汉传媒学院',
// 遍历数组类型数据
persons: [
{ id: '001', name: '马冬梅', age: 30, sex: '女' },
{ id: '002', name: '周冬雨', age: 31, sex: '女' },
{ id: '003', name: '周杰伦', age: 18, sex: '男' },
{ id: '004', name: '温兆伦', age: 19, sex: '男' }
],
},
methods: {
updateMei() {
// this.persons[0].name = '马老师'
// this.persons[0].age = '50'
// this.persons[0].sex = '男'
// 这就说明了为什么这样修改 界面不响应的原因
// this.persons[0] = { id: '001', name: '马老师', age: 50, sex: '男' }
const p = { id: '001', name: '马老师', age: 50, sex: '男' }
this.persons.splice(0, 1, p)
}
}
})
对象:添加性别属性 和 修改性别属性
javascript
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
// sex: '男',
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex() {
this.$set(this.student, 'sex', '男')
},
changeSex() {
this.$set(this.student, 'sex', '女' === this.student.sex ? '男' : '女')
},
},
})
数据劫持:

💡 Vue监视数据的原理:
html
<body>
<div id="root">
<button @click="addLeader">点我给call校长出来</button>
<h1>学校信息</h1>
<h2>学习的名称:{{name}}</h2>
<h2>学习的地址:{{address}}</h2>
<h2 v-if="school.leader">校长是{{school.leader}}</h2>
<hr>
<h1>学生信息</h1>
<button @click="addSex">添加一个学生信息 点我让学生变性</button>
<h2>学生姓名:{{student.name}}</h2>
<h2 v-if="student.sex">学生性别:{{student.sex}}</h2>
<h2>学生年龄:{{student.age.rAge}},对外{{student.age.sAge}}</h2>
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}} --- {{index}}
</li>
</ul>
<h2>朋友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}} --- {{f.age}}
</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
name: "武汉传媒学院",
address: '武汉',
student: {
name: 'tom',
age: {
rAge: 40,
sAge: 29,
},
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
],
hobby: ["喝酒", "抽烟", "烫头"]
// Vue内对数组进行修改
// vm.student.hobby.push('打游戏')
},
school: {
}
},
methods: {
addSex() {
// Vue.set(target(要添加的目标位置), key(属性名), val(属性值))
// Vue.set(this.student, 'sex', "男")
// 不过set有局限性 只能给data里面的某一个对象进行追加属性 而不能直接往data里面进行追加属性
this.$set(this.student, 'sex', "男")
},
addLeader() {
this.$set(this.school, 'leader', "小帅")
}
}
})
</script>
-
Vue会监视data中所有层次的数据。
-
如何监测对象中的数据?
-
通过 setter 实现监视,且要在
new Vue
时就传入要监测的数据。 -
注意:
-
(1) 对象中后追加的属性,Vue 默认不做响应式处理。
-
(2) 如需给后添加的属性做响应式,请使用以下 API:
Vue.set(target, propertyName/index, value)
或vm.$set(target, propertyName/index, value)
-
-
-
如何监测数组中的数据?
-
通过 包裹数组更新元素的方法 实现,具体做了两件事:
-
(1) 调用原生对应的方法对数组进行更新。
-
(2) 重新解析模板,进而更新页面。
-
-
-
在 Vue 修改数组中的某个元素一定要用如下方法:
-
使用以下 API:
push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
-
或者使用:
Vue.set()
或vm.$set()
-
💡 特别注意:
-
Vue.set()
和vm.$set()
不能 给vm
或vm
的根数据对象添加属性!!! -
不能应用在
vm
和vm._data
上。
练习:答案在此,写完可以自行对照一下~
html
<div id="root">
<h1>学生信息</h1>
<button>年龄+1岁</button> <br />
<button>添加性别属性,默认值:男</button> <br />
<button>修改性别</button> <br />
<button>在列表首位添加一个朋友</button> <br />
<button>修改第一个朋友的名字为:张三</button> <br />
<button>添加一个爱好</button> <br />
<button>修改第一个爱好为:开车</button> <br />
<button>过滤掉爱好中的抽烟</button> <br />
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
})
</script>