当前位置: 首页 > ds >正文

跟着尚硅谷学vue-day7

列表,vue2的监测机制,动态新增深层监测,数据劫持

根据输入框的值,自动更新列表

案例1:根据输入框的值,自动更新列表

1.首先得到输入框的值。利用v-model进行绑定数据

2.当输入框发生变化的时候,采用watch或者computed进行监测。根据需求过滤列表

3.判断一个字符串当中是否包含字符,可以用indexOf来判断。

<body><div id="root"><input type="text" placeholder="请输入名字" v-model="keyword"/><ul><li v-for="(p,index) in personArr" :key="index" >{{p.name}}-{{p.age}}--{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{keyword:'',personArr:[{id:'001',name:'周小红',age:20,sex:'女'},{id:'002',name:'小红',age:30,sex:'女'},{id:'003',name:'刘小绿',age:25,sex:'女'},{id:'004',name:'小绿',age:25,sex:'女'}]},watch:{keyword(val){this.personArr=this.personArr.filter((p)=>{return p.name.indexOf(val) !== -1})}}})</script>
</body>

会出现越搜索越少的这种情况,原因就是上一次的搜索结果会影响原数组,导致原数组的数据越来越少

利用监视器属性实现,首先,需要新增一个filPerson空数组,用来存放搜索结果,接着,修改keyword的handler,将搜索结果赋值给filPerson,遍历filPerson,呈现给用户。

 <div id="root"><input type="text" placeholder="请输入名字" v-model="keyword"/><ul><li v-for="(p,index) in filPerson" :key="index" >{{p.name}}-{{p.age}}--{{p.sex}}</li></ul>
</div>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{keyword:'',personArr:[{id:'001',name:'周小红',age:20,sex:'女'},{id:'002',name:'小红',age:30,sex:'女'},{id:'003',name:'刘小绿',age:25,sex:'女'},{id:'004',name:'小绿',age:25,sex:'女'}],filPerson:[]},watch:{keyword(val){this.filPerson=this.personArr.filter((p)=>{return p.name.indexOf(val) !== -1})}}})
</script>

但是这样做会有一个问题,就是进入页面,首次出现的就是一个空白数组,所以,可以用watch的immediate来实现。而且,当搜索框中没有数据的时候,即是''的时候,filter过滤结果会是全数组。因为'abc'.indexOf('')=0,即每个字符串的首位都是空。

 watch:{keyword:{immediate:true,handler(val){this.filPerson=this.personArr.filter((p)=>{return p.name.indexOf(val) !== -1})}}
}

计算属性是一上来就调用一下,然后依赖的属性发生变化的时候调用一下。所以当keyword发生变化时,filPerson会重新调用一次。

 computed:{//简写方式filPerson(){return this.personArr.filter((p)=>{return p.name.indexOf(this.keyword) !== -1})  }//keyword参与计算属性 }

对搜索出来的数组进行排序或者不用搜索,直接排序

案例升级:对搜索出来的数组进行排序或者不用搜索,直接排序

1.记录点击了哪一个按钮,通过给每个按钮绑定一个数字的方式来记录

2.利用arr.sort进行排序。

<body><div id="root"><input type="text" placeholder="请输入名字" v-model="keyword"/><button @click="keysort=2">年龄升序</button><button @click="keysort=1">年龄降序</button><button @click="keysort=0">原顺序</button><ul><li v-for="(p,index) in filPerson" :key="index" >{{p.name}}-{{p.age}}--{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{keyword:'',personArr:[{id:'001',name:'周小红',age:20,sex:'女'},{id:'002',name:'小红',age:30,sex:'女'},{id:'003',name:'刘小绿',age:25,sex:'女'},{id:'004',name:'小绿',age:25,sex:'女'}],keysort:0 },computed:{//简写方式filPerson(){const arr = this.personArr.filter((p)=>{return p.name.indexOf(this.keyword) !== -1})  if(this.keysort){arr.sort((a,b)=>{return this.keysort===1?a.age-b.age:b.age-a.age;})}return arr;}//keyword参与计算属性 }})</script>
</body>

更新时的问题,索引值作为key,如果有升序降序操作,并且每一条数据之后有输入框,那么就会造成输入框内容和数据不对版的情况发生,正确操作是一id作为key,但是为什么id作为key就不会有这种情况发生

<div id="root"><button @click="updatemei">更新周小红的信息</button><ul><li v-for="(p,index) in personArr" :key="p.id" >{{p.name}}-{{p.age}}--{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{personArr:[{id:'001',name:'周小红',age:20,sex:'女'},{id:'002',name:'小红',age:30,sex:'女'},{id:'003',name:'刘小绿',age:25,sex:'女'},{id:'004',name:'小绿',age:25,sex:'女'}]},methods: {updatemei(){this.personArr[0].name='周红'}},})</script>

更新列表问题

这种情况下,点击按钮,可以实现更新周小红的信息,但是如果将methods当中的updatemei修改成为

 methods: {updatemei(){this.personArr[0]= {id:'001',name:'周红',age:20,sex:'女'}}}

会发现点击按钮,点开vue控制台,root根节点数据会发生变化,但是页面不会更新

反之,如果先点开vue控制台,再点击按钮,root根节点数据不会发生变化,页面也不会更新。

vue到底如何监测数据改变的

Vue 监测数据变化的核心机制是通过数据劫持(Data Observation)实现的,其底层原理在 Vue 2 和 Vue 3 中有所不同。以下是详细的技术解析,结合 _data 的加工过程说明:

Vue 2 的监测机制:Object.defineProperty

1. _data 的加工过程

当你在组件中定义 data 时,Vue 会进行以下处理:

// 用户定义的 data
data() {return {message: 'Hello',user: { name: 'Alice' }};
}// Vue 内部处理后的 _data
vm._data = {message: 'Hello', // 被劫持的属性user: {           // 嵌套对象也会被递归劫持name: 'Alice'}
};

关键步骤

  1. 递归遍历 data:Vue 会递归遍历 data 的所有层级,将每个属性转换为响应式属性
  2. 使用 Object.defineProperty 劫持属性
    • 对每个属性定义 getter 和 setter
    • getter 用于依赖收集(记录哪些组件/Watcher 依赖该属性)。
    • setter 用于触发更新(当属性变化时通知所有依赖更新视图)。

2. _data 的响应式实现

function observe(data) {if (!data || typeof data !== 'object') return;Object.keys(data).forEach(key => {defineReactive(data, key, data[key]);});
}function defineReactive(obj, key, val) {const dep = new Dep(); // 依赖管理器// 递归处理嵌套对象observe(val);Object.defineProperty(obj, key, {get() {if (Dep.target) { // 当前正在计算的 Watcherdep.addSub(Dep.target); // 收集依赖}return val;},set(newVal) {if (newVal === val) return;val = newVal;observe(newVal); // 新值如果是对象,继续劫持dep.notify(); // 通知所有依赖更新}});
}

例:

const vm = new Vue({data: { message: 'Hello' }
});// 访问 message 时,触发 getter,收集依赖
console.log(vm.message); // Dep 记录当前 Watcher// 修改 message 时,触发 setter,通知更新
vm.message = 'World'; // 视图自动更新

3. 局限性

  • 无法检测新增/删除属性:需用 Vue.set() 或 vm.$set()
  • 数组索引赋值不响应:需用 push()pop() 等方法。

修改属性,就会有个自己加的提示(定时器)。

案例:修改name,就会有个自己加的提示。

 <script type="text/javascript">let data = {name:'尚硅谷',address:'北京'}let tmp = '尚硅谷'setInterval(() => {if(data.name !== tmp){tmp = data.nameconsole.log('尚硅谷被改了')}}, 100);</script>
 Object.defineProperty(data,'name',{get(){return data.name;},set(val){data.name = val}})
 Object.defineProperty(data,'name',{get(){return data.name;},set(val){data.name = val}})

get方法导致,data.name会多次调用get()方法。get又会返回data.name;

假如data只有一层对象方法,代码

const obs =  new observe(data);let vm = {};vm._data = data = obs;function observe(data){const keys = Object.keys(data);keys.forEach((key)=>{Object.defineProperty(this,key,{get(){return data[key]},set(val){console.log(`${key}被修改了,我要去解析模版,生成虚拟dom,我要开始忙了`)data[key]=val;}})})}

对于data当中有多层对象的,vue会一直往下找,一直往下找。值得说明的是Object.defineProperty(this,key,)没有利用data的key,用的是另一个对象的key。避免递归。

Vue.config.productionTip = false;const vm = new Vue({el:'#root',data:{name:'尚硅谷',address:'北京',student:{name:'tom',age:{rAge:20,sAge:21},friends:[{name:'jerry',age:13}]}}})

Vue 2 的响应式系统确实会对对象及其初始存在的嵌套属性递归地劫持(分配 getter/setter),但动态新增的深层属性或嵌套对象需要特殊处理才能保证响应式。

动态新增深层监测或嵌套对象

对于在student当中新增sex属性,可以利用Vue.set(vm._data.student,'sex','男')/vm.$set(vm.student,'sex','女')/vm.$set(vm._data.student,'sex','未知')可以添加新属性,添加的新属性可以有响应式

在代码当中的体现就是

methods:{addSex(){Vue.set(this.student,'sex','男')}}

在代码当中,this代表的是vm。Vue.set只能给data中的某一个对象添加属性,但是不能给data添加属性。

data下的student对象当中包含数组

因为如果对象当中包含数组,那么数组的元素没有对应的get,set,所以如果修改数组,页面不会触发响应式。但是可以用针对数组的一些方法,来触发响应式

所以周小红 this.personArr.splice(0,1,{id:'001',name:'周红',age:20,sex:'女'}),使用这个就可以奏效。

原因:

hobby被vue管理了,hobby用的push不等于array用的push

Vue.set也可以用数据代理来操作数组。

总结

<body><div id="root"><button @click="student.age.rAge++">年龄+1岁</button><!-- <button @click="Vue.set(student,'sex','男')">添加性别属性,默认为男</button>  -->错的因为vue不是全局<button @click="this.$set(student,'sex','男')">添加性别属性,默认为男</button> 错的不需要this<!-- 移除this:在模板中直接使用$set,不需要this --><button  @click="$set(student, 'sex', '男')">添加性别属性,默认为男</button> 对的<button @click="addFriend">在列表首位添加一个盆友</button><button @click="updatefirstFriend">修改第一个盆友的名字为张三</button><button @click="addhobby">添加一个爱好</button><button @click="updatehobby">修改第一个爱好为开车</button><ul><li v-for = "(f,index) in student.hobby" :key = "index">{{f}}</li></ul><h2>学校:{{name}}</h2><h2>地址:{{address}}</h2><hr/><button @click="addSex">点我添加性别,默认是男</button><h2>学生姓名:{{student.name}}</h2><h2>学生年龄:真实{{student.age.rAge}},对外真实{{student.age.sAge}}</h2><h2 v-if="student.sex">学生性别:{{student.sex}}</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;const vm = new Vue({el:'#root',data:{name:'尚硅谷',address:'北京',student:{name:'tom',age:{rAge:20,sAge:21},friends:[{name:'jerry',age:13}],hobby:['抽烟','喝酒','烫头','打麻将']}},methods:{addSex(){Vue.set(this.student,'sex','男')},addFriend(){this.student.friends.unshift({name:'jerry',age:12})},updatefirstFriend(){//     this.student.friends.splice(0,1,{//     name:'张三',age:12// })//奏效this.student.friends[0].name = '张三'//因为this.student.friends[0]这个对象的name有getset     },addhobby(){this.student.hobby.push('学习')},updatehobby(){// this.student.hobby.splice(0,1,'开车')//奏效this.$set(this.student.hobby,0,'开车')}}})// 如果想要什么就先想好,如果自己填充的话,在data,或者vm上添加新属性,vm.data.sex='男',这样子是错的。因为不会有响应式。</script>
</body>
</html>

Vue监视数据的原理:
1.vue会监视data中所有层次的数据。
2.如何监测对象中的数据?
通过setter实现监视,且要在newVue时就传入要监测的数据。

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

(2).如需给后添加的属性做响应式,请使用如下API:Vue.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的根数据对象 添加属性!!

filter,concat,不在包裹数组内,所以直接将结果赋值

数据劫持

数据劫持是Vue框架实现响应式数据绑定的核心技术机制,其核心原理是通过拦截对象属性的访问和修改操作,实现数据变化时视图的自动更新

http://www.xdnf.cn/news/17437.html

相关文章:

  • 【MongoDB学习笔记2】MongoDB的索引介绍
  • 宁商平台税务新政再升级:精准施策,共筑金融投资新生态
  • 塑料可回收物检测数据集-10,000 张图片 智能垃圾分类系统 环保回收自动化 智慧城市环卫管理 企业环保合规检测 教育环保宣传 供应链包装优化
  • UE5太空射击游戏入门(一):项目创建与飞船控制
  • 5.0.9 C# wpf通过WindowsFormsHost嵌入winform控件
  • 网络基础浅谈
  • 僵尸进程、孤儿进程、进程优先级、/proc 文件系统、CRC 与网络溢出问题处理(实战 + 原理)
  • Docker搭建Jenkins实现自动部署:快速高效的持续集成之道!
  • 进程管理、系统高负载、cpu超过800%等实战问题处理
  • Android 中解决 Button 按钮背景色设置无效的问题
  • Numpy科学计算与数据分析:Numpy广播机制入门与实践
  • conda pip uv与pixi
  • react的form.resetFields()
  • 论文阅读:User Behavior Simulation with Large Language Model-based Agents
  • 五十五、【Linux系统nginx服务】nginx安装、用户认证、https实现
  • MySQL 配置性能优化赛:核心策略与实战技巧
  • HTML5 Web Workers 深度剖析:助力网页性能飞速提升
  • dmctlcvt工具介绍数据文件路径变化后如何拉起数据库
  • 边缘算力×AI应用:如何在2025年实现爆发式增长
  • HTML已死,HTML万岁——重新思考DOM的底层设计理念
  • 用LaTeX优化FPGA开发:结合符号计算与Vivado工具链
  • 《C语言》函数练习题--1
  • 通过CNN、LSTM、CNN-LSTM及SSA-CNN-LSTM模型对数据进行预测,并进行全面的性能对比与可视化分析
  • 前端开发工具大全
  • day069-Jenkins基础使用与参数化构建
  • 【论文阅读】基于元模型的体系知识图谱构建
  • DataEase官方出品丨SQLBot:基于大模型和RAG的智能问数系统
  • C++高频知识点(十五)
  • 浅谈欧拉函数与素数筛法思想
  • Flink的运行模式