【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的实现:
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- 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 的区别:
特性 | computed | methods |
---|---|---|
缓存 | ✅ 会缓存 | ❌ 每次都重新执行 |
使用场景 | 衍生状态 | 触发行为、事件 |
二、监视属性(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
的监听器也会立刻触发(前提是你监听了它)。这个过程是响应式系统自动完成的,你不用手动调用
监视属性-深度监视:
深度监视:
- Vue中的watch默认不监测对象内部值的改变(一层)。
- 配置deep:true可以监测对象内部值改变(多层)。
备注:
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
- 使用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之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被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-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一定可以获取到。
✅ 示例:
<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"
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
✅ 基础写法:
<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可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
4. 开发中如何选择key?:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用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>
-
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
上。
练习:答案在此,写完可以自行对照一下~
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>
总结不易~ 本章节对我有很大收获,希望对你也是!!!!
相关文章:
【Vue】 核心特性实战解析:computed、watch、条件渲染与列表渲染
目录 一、计算属性(computed) ✅ 示例: 计算属性-methods实现:在插值模块里,实现函数的调用功能 计算属性-computed的实现: 计算属性-简写: ✅ 特点: ⚠️ 与 methods 的区别…...
精品可编辑PPT | 基于湖仓一体构建数据中台架构大数据湖数据仓库一体化中台解决方案
本文介绍了基于湖仓一体构建数据中台架构的技术创新与实践。它详细阐述了数据湖、数据仓库和数据中台的概念,分析了三者的区别与协作关系,指出数据湖可存储大规模结构化和非结构化数据,数据仓库用于高效存储和快速查询以支持决策,…...
基于Python网络爬虫的智能音乐可视化系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 时代在飞速进步,每个行业都在努力发展现在先进技术,通过这些先进的技术来提高自己的水平和优势,智能音乐可视化系统当然不能排除在外。我本次开发的基于网络爬虫的智能音乐可视化系统是在实际应用和软件工程的开发原理之上,…...
基于STM32与应变片的协作机械臂力反馈控制系统设计与实现----2.2 机械臂控制系统硬件架构设计
2.2 机械臂控制系统硬件架构设计 一、总体架构拓扑 1.1 典型三级硬件架构 #mermaid-svg-MWmxD3zX6bu4iFCv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MWmxD3zX6bu4iFCv .error-icon{fill:#552222;}#mermaid-s…...
在线记事本——支持Markdown
项目地址 https://github.com/Anyuersuper/CloudNotebook 百度网盘 通过网盘分享的文件:CloudNotebook-master.zip 链接: https://pan.baidu.com/s/1kd2qNvm0eXc6_7oYDR769A?pwdyuer 提取码: yuer 📝 云笔记 (Cloud Notebook) 云笔记是一个简洁、安全…...
DDPM 做了什么
本博客主要侧重点在于HOW也就是DDPM怎么做的而不是WHY为什么要这样做 那么第一个问题DDPM做了一件什么事:这个算法通过逐渐向原图像添加噪声来破坏图像,然后再学习如何从噪声成恢复图像。 第二件事如何做到的:通过训练一个网络,…...
Redis数据结构之List
目录 1.概述2.常见操作2.1 LPUSH/RPUSH/LRANGE2.2 LPOP/RPOP2.3 LINDEX2.4 LLEN2.5 LREM2.6 LTRIM2.7 RPOPLPUSH2.8 LSET2.9 LINSERT 1.概述 List是简单的字符串列表,单key多个value,按照插入顺序排序。 支持添加一个元素到列表的头部(左边)或者尾部(右…...
L2-023 图着色问题 #DFS C++邻接矩阵存图
文章目录 题目解读输入格式输出格式 思路Ac CODE 参考 题目解读 给定一个无向图V,询问是否可以用K种颜色为V中每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色 输入格式 第一行给出V,E,K, 分别代表无向图的顶点,…...
架构下的按钮效果设置
以下是一个完整的跨QML/Qt Widgets的主题方案实现,包含对按钮阴影的统一管理: 一、项目结构 Project/ ├── core/ │ ├── thememanager.h │ └── thememanager.cpp ├── widgets/ │ ├── mainwindow.h │ ├── mainwindow.cpp …...
Unhandled exception: org.apache.poi.openxml4j.exceptions.InvalidFormatException
代码在main方法里面没有报错,在Controller里面就报错了。 原来Controller类里面少了行代码 import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 加上去就解决了。...
Vue2_Vue.js教程
目录 一、Vue.js安装 1、独立版本 2、CDN 方法 3、npm 方法 二、Vue Al编程助手 三、Vue.js目录结构 目录解析 四、Vue.js 起步 1.如何定义数据对象和方法并渲染进页面 五、Vue.js 模板语法 插值 文本_{{}} Html_v-html 指令 属性_v-bind (数据传输工具)指令 表…...
2025/4/2 心得
第一题 题目描述 给定1001个范围在[1,1000]的数字,保证只有1个数字重复出现2次,其余数字只出现1次。试用O(n)时间复杂度来求出出现2次的这个数字。 不允许用数组 输入格式 第一行:一个整数1001; 第二行:1001个用…...
Deep Reinforcement Learning for Robotics翻译解读
a. 机器人能力 1 单机器人能力(Single-robot competencies) 运动能力(Mobility) 行走(Locomotion)导航(Navigation) 操作能力(Manipulation) 静态操作&…...
【Linux】日志模块实现详解
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
AT_abc212_d [ABC212D] Querying Multiset
链接:AT_abc212_d [ABC212D] Querying Multiset - 洛谷 题目描述 高橋君は何も書かれていないたくさんのボールと 1 つの袋を持っています。 最初、袋は空で、高橋君は Q 回の操作を行います。 それぞれの操作は以下の 3 種類のうちのいずれかです。 操作 1 : ま…...
Android使用OpenGL和MediaCodec录制
目录 一,什么是opengl 二,什么是Android OpenGL ES 三, OpenGL 绘制流程 四, OpenGL坐标系 五, OpenGL 着色器 六, GLSL编程语言 七,使用MediaCodec录制在Opengl中渲染架构 八,代码实现 8.1 自定义渲染view继承GLSurfaceView 8.2 自定义渲染器TigerRender 8.3 创建编…...
Java 实现插入排序:[通俗易懂的排序算法系列之三]
引言 大家好!欢迎继续关注我的排序算法系列。今天,我们要学习的是另一种非常基础且重要的排序算法——插入排序 (Insertion Sort)。 插入排序的思路非常贴近我们日常整理扑克牌的方式,理解起来相对自然。虽然它在最坏情况下的效率不高&…...
HarmonyOS:WebView 控制及 H5 原生交互实现
一、效果展示 二、技术栈 技术栈: 编程语言:使用 TypeScript 进行开发,借助其类型系统提升代码的可读性与稳定性。 框架与库:基于鸿蒙系统相关框架(如kit.ArkWeb、hadss/hmrouter)…...
250405-VSCode编辑launch.json实现Debug调试Open-WebUI
A. 最终效果 根据__init__.py配置launch.json 根据中utils/chat.py中form_data的messages [{role: user, content: 唐老鸭}],可以找到用户输入,进而通过关键词或模型调用的方式,对敏感问题进行特殊处理。 B. 文件配置 launch.json // { // /…...
SQL Server 数据库实验报告
1.1 实验题目:索引和数据完整性的使用 1.2 实验目的: (1)掌握SQL Server的资源管理器界面应用; (2)掌握索引的使用; (3)掌握数据完整性的…...
【寻找Linux的奥秘】第三章:基础开发工具(上)
请君浏览 前言1. 软件包管理器1.1 linux中安装软件1.2 yum的具体操作1.2.1 查找软件包1.2.2 安装软件1.2.3 卸载软件 1.3 小结 2. 编辑器vim2.1 vim的基本概念和操作2.2 命令模式的命令集光标定位其他命令模式切换(常用的) 2.3 末⾏模式的命令集2.4 小结…...
Photoshop 2025 Mac中文Ps图像编辑
Photoshop 2025 Mac中文Ps图像编辑 文章目录 Photoshop 2025 Mac中文Ps图像编辑一、介绍二、效果三、下载 一、介绍 Adobe Photoshop 2025 Mac版集成了多种强大的图像编辑、处理和创作功能。①强化了Adobe Sensei AI的应用,通过智能抠图、自动修复、图像生成等功能…...
#SVA语法滴水穿石# (004)关于 ended 和 triggered 用法
在 SystemVerilog 断言(SVA, SystemVerilog Assertions)中,ended 是一个用于 序列(sequence) 的关键字,它表示某个序列(sequence)在特定时间点已经成功匹配(即“结束”)。 ended 主要用于 同步不同序列的时间关系,尤其是在多序列组合或属性(property)中需要对齐时…...
16.1Linux自带的LED灯驱动实验(知识)_csdn
前面我们都是自己编写 LED 灯驱动,其实像 LED 灯这样非常基础的设备驱动, Linux 内核已经集成了。 Linux 内核的 LED 灯驱动采用 platform 框架,因此我们只需要按照要求在设备树文件中添加相应的 LED 节点即可,本章我们就来学习如…...
普通类、抽象类和接口的区别
1. 普通类 (Concrete Class) 定义:完整的类,可以直接实例化 特点: 可以包含属性、普通方法(有具体实现)和构造方法 可以被直接实例化创建对象 可以被继承(除非用final修饰) 示例࿱…...
使用 Elastic 实现端到端的大语言模型(LLM)可观测性:洞察生成式 AI 应用这个不透明的世界
作者:来自 Elastic Daniela Tzvetkova 及 Bahubali Shetti 在快速发展的人工智能领域,大语言模型(Large Language Models - LLMs)已成为创新的灯塔,为各行各业带来了前所未有的能力。从生成类人文本、翻译语言到提供个…...
15.2linux设备树下的platform驱动编写(程序)_csdn
我尽量讲的更详细,为了关注我的粉丝!!! 修改设备树文件: 这个我们在上一章已经写过了,但是还是带着大家来重写一遍! 1.打开pinctrl-stm32.c 这个文件: strict 成员变量默认为 true&…...
Java的Selenium的特殊元素操作与定位之window切换
当你要操作另外一个窗口页面的元素时,一定要注意先切换窗口 切换方式:传入要操作窗口的name或者句柄handle driver.switchTo.window(nameOrHandle); 如何获取到窗口的句柄 driver.getWindowHandle();//获取当前操作窗口的句柄driver.getWindowHandles();//获取测…...
【Rust学习】Rust环境搭建和Rust基础语法
本文专栏:Rust学习 目录 一,Rust环境搭建 1,C环境安装 2,Rust下载 3,Rust安装 4,Rust环境检测 二,创建Rust项目 1,rustc 2,cargo 三,输出到命令行 …...
在windows环境下通过docker-compose脚本自动创建mysql和redis
一、环境版本 在windows环境下通过docker容器运行各种服务,使用的软件版本如下: docker desktop :V4.39.0 【docker的安装环境设置略】 mysql:9.2 redis:7.4.2 二、各配置文件 1.已经解决了字符集和排序规则问题造成…...
【玩泰山派】2、制作buildroot镜像,并烧录
文章目录 前言制作buildroot镜像过程搭建环境(docker版)下载泰山派开发的sdk利用制作的镜像和下载的sdk去启动开发docker容器编译buildroot镜像 参考 前言 泰山派官方提供了不少现成的镜像 但是都买了泰山派了,肯定是想自己编译折腾下&…...
实验二 VLAN 的配置与应用
一、实验目的 1. 熟悉 VLAN 和 PORT VLAN 的原理; 2. 熟悉华为网络模拟器的使用; 3. 掌握网络拓扑图的绘制; 4. 掌握单交换机内 VLAN 的配置。 二、实验设备 PC、华为模拟器 ENSP。 三、实验步骤 知识准备:VLAN 和 PORT V…...
【C/C++算法】蓝桥杯之递归算法(如何编写想出递归写法)
绪论:冲击蓝桥杯一起加油!! 每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论: ———————— 早关注不迷路,话不多说安全带系好,发车啦&am…...
coding ability 展开第九幕(位运算——进阶篇)超详细!!!!
文章目录 前言丢失的数字两整数之和只出现一次的数字II消失的两个数字总结 前言 上一篇博客,我们已经把位运算的基础知识,以及基本运算都掌握啦 上次的习题还是让人意犹未尽,今天我们来尝试一下难一点的题目 位运算熟练起来真的让人觉得做题是…...
Python实现NOA星雀优化算法优化随机森林回归模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 在现代数据科学领域,回归分析是解决预测问题的核心工具之一。然而,在面对复…...
蓝桥云客--浓缩咖啡液
4.浓缩咖啡液【算法赛】 - 蓝桥云课 问题描述 蓝桥杯备赛选手小蓝最近刷题刷到犯困,决定靠咖啡续命。他手上有 N 种浓缩咖啡液,浓度分别是 A1%, A2%, …, AN%,每种存货都是无限的。为了提神又不炸脑,小蓝需要按比例混合这…...
异常【C++】
文章目录 异常异常的概念和基本语法异常的三个关键字: 异常抛出和被接收的过程异常的再次抛出再次抛出被非catch(...)捕捉到的异常再次抛出被catch(...)捕捉到的异常 异常规范异常安全异常的优缺点优点缺点总结&#x…...
关于图片分类任务的猜想 | 撰写论文 paper
关于图片分类任务的猜想 | 撰写论文 paper 背景Yolo 是一次巨大的飞跃过滤无关的特征Yolo 的问题 背景 在计算视觉领域,有几个关键的演变。 1)CNN 卷积的出现,这是一个大的创新; 2)从卷积到 AlexNet ,是更…...
路由器和交换机
路由器和交换机分别位于OSI模型和TCP/IP模型的不同网络层次,具体对比如下: 1. 路由器(Router) 所属层级: OSI模型:网络层(第3层)TCP/IP模型:网络互联层(Int…...
jEasyUI 表单验证
jEasyUI 表单验证 引言 jEasyUI 是一款流行的 jQuery UI 扩展库,它提供了丰富的 UI 组件和交互效果,极大地方便了前端开发工作。在 jEasyUI 中,表单验证是一个非常重要的功能,它可以帮助开发者确保用户输入的数据符合预期的格式和规则。本文将详细介绍 jEasyUI 表单验证的…...
PIKE 助力知识库进阶:多模型协作下的精准信息 “捕手”
PIKE(通常指的是字节跳动提出的一种技术)增强检索知识库是一种结合了先进的信息检索技术和知识库管理的系统。它旨在提高知识检索的准确性、效率和召回率,以更好地满足用户对知识的需求。 特点和工作原理 数据增强 :对知识库中…...
使用Ubuntu18恢复群晖nas硬盘数据外接usb
使用Ubuntu18恢复群晖nas硬盘数据外接usb 1. 接入硬盘2.使用Ubuntu183.查看nas硬盘信息3. 挂载nas3.1 挂载损坏nas硬盘(USB)3.2 挂载当前运行的nas 4. 拷贝数据分批传输 5. 新旧数据对比 Synology NAS 出现故障,DS DiskStation损坏,则可以使用计算机和 U…...
Dify票据识别遇到的分支判断不准确问题
已测试这篇文章中 https://zhuanlan.zhihu.com/p/5465385787 使用多分支条件判断使用不同的大模型识别图片内容 发现了细节问题。在使用时若不注意,分支会出现走向不准的问题。 需要关注部分 下方红框处。1,2后不能跟点。否则会出问。除此之外࿰…...
Flutter学习总结之Android渲染对比
一、Android 界面渲染机制(基于原生 View 体系) 1. 核心渲染流程(源码级解析) 三阶段渲染流程(ViewRootImpl驱动): Measure 阶段(measure()): View调用onMea…...
Media streaming mental map
Media streaming is a huge topic with a bunch of scattered technologies, protocols, and formats. You may feel like hearing fragments without seeing the big picture. Let’s build that mental map together — here’s a high-level overview that connects everyt…...
7B斗671B:扩散模型能否颠覆自回归霸权?
模型对决:从7B到671B的意外之战 参数量与性能的反差 DeepSeek V3以6710亿参数稳坐自回归模型的“巨无霸”地位,而70亿参数的Dream 7B却在多项测试中与其不分伯仲。例如,在需要复杂规划的“倒计时任务”中,Dream 7B的解题成功率比…...
WVP-GB28181摄像头管理平台存在弱口令
免责声明:本号提供的网络安全信息仅供参考,不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我联系,我将尽快处理并删除相关内容。 漏洞描述 攻击者可利用漏洞获取当前系统管…...
实验研究:不同物体与落点材质对弹起高度的影响
本研究通过控制变量法,探讨了不同物体(乒乓球和笔)在不同下落高度和落点材质条件下,其弹起高度的变化。实验结果显示,物体类型、下落高度和落点材质均对弹起高度有显著影响。其中,铁碗作为落点材质时&#…...
开源 PDF.js 文件编辑操作
一、PDF.js PDF.js 是 Mozilla 基金会推出的一个使用 HTML5 构建的 PDF 阅读器,它完全使用 JavaScript 编写。作为 Firefox 浏览器的默认 PDF 查看器,PDF.js 具有强大的兼容性和稳定性。它不仅支持 PDF 文件的查看和渲染,还提供了丰富的交互…...
hydra小记(一):深入理解 Hydra:instantiate() 与 get_class() 的区别
hydra小记(一):深入理解 Hydra:instantiate 与 get_class 的区别 深入理解 Hydra:instantiate() 与 get_class() 的区别1. hydra.utils.get_class()2. hydra.utils.instantiate()3. 总结对比 深入理解 Hydra࿱…...