【Vue】 核心特性实战解析:computed、watch、条件渲染与列表渲染

目录

一、计算属性(computed)

✅ 示例:

计算属性-methods实现:在插值模块里,实现函数的调用功能

计算属性-computed的实现:

计算属性-简写:

✅ 特点:

⚠️ 与 methods 的区别:

二、监视属性(watch)

🌟结论:

监视属性-深度监视:

深度监视-deep:true 简写: 

监视属性- watch实现:

📊 主要区别:

三、绑定条件与属性(v-bind)

:class 和 原生 JavaScript 的 classList ,v-bind --> :class

@click 和 原生 JavaScript 的 addEventListener, v-on --> @click

四、条件渲染(v-if / v-show)

✅ 区别总结:

五、列表渲染(v-for)

key的原理:

面试题:react、vue中的key有什么作用?(key的内部原理)

列表的过滤 - filter

列表排序 - computed实现:

模拟一个数据监测:

Vue.set的使用:

Vue监测数据改变的原理:

数据劫持:

💡 Vue监视数据的原理: 

练习:答案在此,写完可以自行对照一下~

​编辑

总结不易~ 本章节对我有很大收获,希望对你也是!!!!


在 Vue 的开发过程中,我们经常会遇到以下几种情况:

  • 页面中某些值依赖其他数据计算得出

  • 需要根据用户操作或数据变化自动响应处理逻辑

  • 某些 DOM 元素是否显示要依赖条件判断

  • 页面需要渲染一个数组列表并支持交互

这些需求其实都可以归结为几个 Vue 的“基础核心能力”:计算属性、监视属性、条件渲染、列表渲染等。本文将从实战角度出发,带你理解这些特性背后的逻辑与使用场景。


这里给大家推荐一个Vue自动补全插件,还是很好用的:

一、计算属性(computed)

在模板中拼接字符串、进行运算是常见需求,但如果逻辑复杂且会多次使用,直接写在模板中会导致代码臃肿且难以维护。

Vue 提供了 computed 计算属性来解决这个问题。

✅ 示例:

我们先来实现插值案例 好与 计算属性进行对比:

  <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实现:在插值模块里,实现函数的调用功能

<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的实现:

  1. 定义:要用的属性不存在,要通过已有属性计算得来
  2. 原理:底层借助了Objcet.defineproperty方法提供的getter和setter
  3. get函数什么时候执行?
  • (1).初次读取时会执行一次。
  • (2).当依赖的数据发生改变时会被再次调用。

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

  • 计算属性最终会出现在vm上,直接读取使用即可。
  • 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
  <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 指向vmreturn 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>

计算属性-简写:

    computed: { // 计算属性// 简写 只有考虑读取不考虑修改的时候才能 用简写形式// 一定要注意,表面上 fullName是一个函数 而实际上 是执行完这个函数后 往vm上放了一个叫fullName属性的值// 那么以后只要记住一句 我们在上面配置的属性是 data里面的数据 还是methods里面的方法 还是computed的计算属性fullName() {return this.firstName + '-' + this.lastName}}

✅ 特点:

  • 具有缓存能力:只在相关依赖变化时重新计算

  • 更适合用于基于现有数据的“衍生数据”

⚠️ 与 methods 的区别:

特性computedmethods
缓存✅ 会缓存❌ 每次都重新执行
使用场景衍生状态触发行为、事件

二、监视属性(watch)

在 Vue 中,watch 用来“观察”某个响应式数据的变化,并在变化时执行指定的回调函数,常用于异步操作、手动监听等场景。

✅ 示例:

监视属性-watch:hanlder是真正的处理函数,必须要写!否则 Vue 不知道回调函数是谁。

这里watch监听的是isHot,所以在isHot发生改变的时候,就会调用handler处理函数!

handler(newValue, oldValue) 传入的参数就是新值旧值

immediate: true, // 初始化的时候让handler调用一下

  <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 的监听器也会立刻触发(前提是你监听了它)。这个过程是响应式系统自动完成的,你不用手动调用

监视属性-深度监视:

深度监视:

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

 备注:

  1. Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
  2. 使用watch时根据数据的具体结构,决定是否采用深度监视。

当我们对vm._data里面的对象里面的属性进行监视的时候,可以采用一个一个单独监视:

  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都能进行监测到,因为深度监视是递归式发生的~

<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处理函数

  // 正常写法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之间的区别:

  1. computed能完成的功能,watch都可以完成。
  2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则:

  1. 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
  2. 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

  <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 事件。

<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-ifv-show

 条件渲染:

  1. v-if
  • 写法:
  • (1).v-if="表达式"
  • (2).v-else-if="表达式"
  • (3).v-else="表达式"
  • 适用于:切换频率较低的场景。
  • 特点:不展示的DOM元素直接被移除。
  • 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
  1. v-show
  • 写法:v-show="表达式"
  • 适用于:切换频率较高的场景。
  • 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉      
  1. 备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

✅ 示例:

  <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-ifv-show
控制方式动态添加/移除 DOM控制 display: none
首次渲染性能较慢较快
切换频率建议用于不频繁切换频繁切换更适合

五、列表渲染(v-for)

当你需要展示一个数组时,v-for 是你必须掌握的工具。

v-for指令:

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

✅ 基础写法:

  <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的内部原理)

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是没有问题的。
  <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>

    <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 传自定义参数(推荐)

🧠 示例:过滤掉某个指定的值

filterHobby(target) {const arr = this.student.hobby.filter(item => item !== target);console.log(arr);
}

使用:

this.filterHobby('抽烟');

✅ 场景二:写成独立函数,传多个参数

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实现折叠代码

  <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>
  // 用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实现:

 

  <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)

模拟一个数据监测:

    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的使用:

 

 

 

 

  <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是不会响应的。

总结一下对于数组 和 对象的各种修改的场景:

数组:修改数组内容

  // 用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)}}})

 

对象:添加性别属性 和 修改性别属性

  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监视数据的原理: 

<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>
  1. Vue会监视data中所有层次的数据。

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

    • 通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。

    • 注意:

      • (1) 对象中后追加的属性,Vue 默认不做响应式处理。

      • (2) 如需给后添加的属性做响应式,请使用以下 API:

        • Vue.set(target, propertyName/index, value)vm.$set(target, propertyName/index, value)

  3. 如何监测数组中的数据?

    • 通过 包裹数组更新元素的方法 实现,具体做了两件事:

      • (1) 调用原生对应的方法对数组进行更新。

      • (2) 重新解析模板,进而更新页面。

  4. 在 Vue 修改数组中的某个元素一定要用如下方法:

    • 使用以下 API:push()pop()shift()unshift()splice()sort()reverse()

    • 或者使用:Vue.set()vm.$set()

💡 特别注意

  • Vue.set()vm.$set() 不能vmvm 的根数据对象添加属性!!!

  • 不能应用在 vmvm._data 上。

练习:答案在此,写完可以自行对照一下~

https://gitee.com/liu-yihao-hhh/i-love---vue/blob/master/vue-02/05.%E5%88%97%E8%A1%A8%E6%B8%B2%E6%9F%93/10.%E6%80%BB%E7%BB%93Vue%E6%95%B0%E6%8D%AE%E6%80%BB%E7%BB%93.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>

总结不易~ 本章节对我有很大收获,希望对你也是!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/900228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树 递归

本篇基于b站灵茶山艾府的课上例题与课后作业。 104. 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&…

与 AI 共舞:解锁自我提升的无限可能

与 AI 共舞&#xff1a;解锁自我提升的无限可能 在数字化浪潮的汹涌冲击下&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度重塑着世界的每一个角落。从日常生活的点滴便利到复杂工作的高效推进&#xff0c;AI 的力量无处不在。然而&#xff0c;面对 AI 的强…

【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析

【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析 简述一、引言1.1 研究背景1.2 研究目的与意义1.3 国内外研究现状1.4 研究方法与创新点二、局域网网络安全基础理论2.1 局域网概述2.1.1 局域网的定义与特点2.1.2 局域网的常见拓扑结构2.2 网络安全基本概念2.2.1 网络…

MoE Align Sort在医院AI医疗领域的前景分析(代码版)

MoE Align & Sort技术通过优化混合专家模型(MoE)的路由与计算流程,在医疗数据处理、模型推理效率及多模态任务协同中展现出显著优势,其技术价值与应用意义从以下三方面展开分析: 一、方向分析 1、提升医疗数据处理效率 在医疗场景中,多模态数据(如医学影像、文本…

[ctfshow web入门] web4

前置知识 robots.txt是机器人协议&#xff0c;在使用爬虫爬取网站内容时应该遵循的协议。协议并不能阻止爬虫爬取&#xff0c;更像是一种道德规范。 假设robots.txt中写道 Disallow: /admind.php&#xff0c;那我就暴露了自己的后台&#xff0c;这属于信息泄漏&#xff0c;攻击…

innodb如何实现mvcc的

InnoDB 实现 MVCC&#xff08;多版本并发控制&#xff09;的机制主要依赖于 Undo Log&#xff08;回滚日志&#xff09;、Read View&#xff08;读视图&#xff09; 和 隐藏的事务字段。以下是具体实现步骤和原理&#xff1a; 1. 核心数据结构 InnoDB 的每一行数据&#xff08…

coding ability 展开第九幕(位运算——进阶篇)超详细!!!!

文章目录 前言丢失的数字两整数之和只出现一次的数字II消失的两个数字总结 前言 上一篇博客&#xff0c;我们已经把位运算的基础知识&#xff0c;以及基本运算都掌握啦 上次的习题还是让人意犹未尽&#xff0c;今天我们来尝试一下难一点的题目 位运算熟练起来真的让人觉得做题是…

【数据结构篇】算法征途:穿越时间复杂度与空间复杂度的迷雾森林

文章目录 【数据结构篇】算法征途&#xff1a;穿越时间复杂度与空间复杂度的迷雾森林 一、 什么是算法1. 算法的定义1.1 算法的五个特征1.2 好算法的特质 2. 时间复杂度3. 空间复杂度 【数据结构篇】算法征途&#xff1a;穿越时间复杂度与空间复杂度的迷雾森林 &#x1f4ac;欢…

Logo语言的系统监控

Logo语言的系统监控 引言 在信息技术飞速发展的时代&#xff0c;系统监控成为了确保计算机系统和网络平稳运行的重要手段。系统监控不仅可以实时跟踪系统的性能、资源使用情况和安全风险等&#xff0c;还能够在出现问题时及时发出警报&#xff0c;从而避免潜在的故障和损失。…

STP学习

{所有内容均来自于西安欧鹏的陈俊老师} STP生成树 当二层交换机意外成环路的时候会发生&#xff1a; 1.广播风暴&#xff1a;当广播帧进入环路时&#xff0c;会被不断复制并传输&#xff0c;导致网络中的广播流量急剧增加&#xff0c;消耗大量的网络带宽&#xff0c;降低网络…

使用RKNN进行yolo11-cls部署

文章目录 概要制作数据集模型训练onnx导出rknn导出概要 YOLO(You Only Look Once)是一系列高效的目标检测算法,其核心思想是将目标检测任务转化为一个回归问题,通过单个神经网络直接在图像上预测边界框和类别概率。当将其用于分类任务时,会去除目标检测相关的边界框预测部…

【MySQL】01.MySQL环境安装

注意&#xff1a;在MYSQL的安装与卸载中&#xff0c;需要使用root用户进行。 一、卸载不必要的环境 • 查看是否有运行的服务 [rootVM-24-10-centos etc]# ps axj |grep mysql1 22030 22029 22029 ? -1 Sl 27 0:00 /usr/sbin/mysqld --daemonize --pid-fi…

程序化广告行业(59/89):广告验证与反作弊实战技巧

程序化广告行业&#xff08;59/89&#xff09;&#xff1a;广告验证与反作弊实战技巧 大家好&#xff01;在程序化广告领域&#xff0c;想要做好投放&#xff0c;除了了解基本的架构和原理&#xff0c;还得掌握一些关键的技能&#xff0c;比如广告验证和反作弊。今天就和大家一…

矢量瓦片切片工具

1.geoserver 可以生成geojson mvt(pbf) tojson 三种格式矢量瓦片 2.mapbox的tippecanoe 可以生成pbf矢量瓦片&#xff0c;文件夹形式和mbtiles两种 3.TileStache python工具&#xff0c;可以生成geojson瓦片 4.PostGis mapbox插件可以生成pbf瓦片&#xff0c;据说是动态切片…

Windows 系统 Git 2.15.0 (64位) 下载与安装教程

1. 下载 Git 2.15.0 (64位) 安装包 下载地址&#xff1a;https://pan.quark.cn/s/f817ab9285dc 2. 运行安装程序 双击下载的 Git-2.15.0-64-bit.exe。 如果系统提示安全警告&#xff0c;选择 “运行”&#xff08;确认来源可信&#xff09;。 3. 安装向导设置 按以下步骤配…

MCP服务器:AI与外部工具交互的桥梁——Python和代理AI工具集成指南

&#x1f9e0; 向所有学习者致敬&#xff01; “学习不是装满一桶水&#xff0c;而是点燃一把火。” —— 叶芝 我的博客主页&#xff1a; https://lizheng.blog.csdn.net &#x1f310; 欢迎点击加入AI人工智能社区&#xff01; &#x1f680; 让我们一起努力&#xff0c;共创…

AIGC8——大模型生态与开源协作:技术竞逐与普惠化浪潮

引言&#xff1a;大模型发展的分水岭时刻 2024年成为AI大模型发展的关键转折点&#xff1a;OpenAI的GPT-4o实现多模态实时交互&#xff0c;中国DeepSeek-MoE-16b模型以1/8成本达到同类90%性能&#xff0c;而开源社区如Mistral、LLama 3持续降低技术门槛。这场"闭源商业巨…

Muduo网络库实现 [十五] - HttpContext模块

目录 设计思路 类的设计 解码过程 模块的实现 私有接口 请求函数 解析函数 公有接口 疑惑点 设计思路 记录每一次请求处理的进度&#xff0c;便于下一次处理。 上下文模块是Http协议模块中最重要的一个模块&#xff0c;他需要记录每一次请求处理的进度&#xff0c;需…

解决GraalVM Native Maven Plugin错误:JAVA_HOME未指向GraalVM Distribution

目录 问题描述解决方案为什么需要这样配置&#xff1f; 问题描述 在你的项目中&#xff0c;如果你遇到了以下错误信息&#xff1a; [ERROR] Failed to execute goal org.graalvm.buildtools:native-maven-plugin:0.10.5:test (native-test) on project DIctSystemInJavaUsing…

java 代码错误分析

错误代码 class Test {private static String name; // 声明一个私有静态变量 namename "World"; // 静态初始化块&#xff0c;给 name 赋值为 "World"System.out.print(name); // 打印 name 的值public static void main(String[] args) {System.out.p…