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

跟着尚硅谷学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.dtcms.com/a/320574.html

相关文章:

  • 【华为机试】55. 跳跃游戏
  • LeetCode有效三角形的个数
  • 借助Rclone快速从阿里云OSS迁移到AWS S3
  • 基于UDP的代理协议的Tuic怎么样?
  • 破解 Django N+1 查询困境:使用 select_related 与 prefetch_related 实践指南
  • 五十六、【Linux系统nginx服务】nginx虚拟主机实现
  • [linux] Linux:一条指令更新DDNS
  • GPT-OSS重磅开源:当OpenAI重拾“开放”初心
  • 面试题:bable,plugin,loader,还有在打包过程中.vue/.react文件是如何转化为.js文件的
  • linux mysql 8.X主从复制
  • 聚焦2025世界机器人大会:全尺寸人形交互陪伴机器人GR-3有哪些亮点值得关注?
  • React 原生部落的生存现状:观察“Hooks 猎人“如何用useEffect设陷阱反被依赖项追杀
  • vscode EIDE 无法编译,提示 “文件名、目录名或卷标语法不正确;
  • 论文精读(二)| 开源软件漏洞感知技术综述
  • spring.cache.type=SIMPLE详解
  • 2025年国内iPaaS平台精选
  • Docker搭建Jenkins实现自动部署:快速高效的持续集成之道!
  • 汉明码:从原理到实现的深度解析
  • 【软考中级网络工程师】知识点之路由器配置全解析
  • 微信小程序miniprogram-ci 模块实现微信小程序的自动上传功能
  • 分布式膛压应变测量系统
  • CSS BFC
  • Linux初级阶段性练习
  • Chrome与Firefox浏览器安全运维配置命令大全:从攻防到优化的专业实践
  • 内网穿透原理与部署实战指南:从理论到企业级应用
  • React Hooks 完全指南:从概念到内置 Hooks 全解析
  • C++-->stl: list的使用
  • 为什么输入 URL 后会显示页面?HTTP 协议的 “幕后操作”
  • docker缓存目录转移设置和生效过程
  • 稿定科技:多云架构下的 AI 存储挑战与 JuiceFS 实践