赞
踩
2023 年一整年感觉我的进步都很小,所以自 2024 年起,我将专门开设专栏记录自己在工作期间踩过的所有坑,一来是为了有个记录,自己不会再踩,而来也是为了跟大家做一个分享,与大家进行讨论,提升自己的能力。
此为第一篇(2024 年 02 月 04 日)
问题背景:输入框输入数据后,关闭页签再次打开保留了上一次的数据。
问题描述:在 js 文件中定义了一个对象,以便对数据进行初始化,引入到需要使用的 vue 文件中,然后在 data 中定义一下。
下面是一个 demo 演示:
// index.js
exports const obj = {
name: '',
age: '',
hobbies: ''
}
// 因为不确定后端会返回什么数据,所以此时我们都用空字符串进行初始化。
// 补充:js 是弱类型语言,即使定义为空字符串,后期将其他类型的值赋值过去,都是可以的,但是这会导致数据类型不安全,最好是避免这种写法。
<!-- index.vue -->
<template>
<div>
<input v-model="obj.name" />
<input v-model="obj.age" />
<input v-model="obj.hobbies" />
</div>
</template>
<script>
import { obj } from './model/index.js'
export default {
name: 'index',
data () {
return {
obj,
}
}
}
</script>
分析过程:
首先我们可以看到,在 js 中定义了一个 obj ,它是一个对象,也就是“引用数据类型”的数据,此时它在堆中开辟一部分空间存储这个对象,然后提供一个地址值,指向这个对象,然后在栈中生成一个变量 obj ,将这个地址值赋值给栈中的变量 obj 。
第二步我们看到是在 index.vue 文件中,通过 import 将这个变量引入进来。
第三步又将引入的这个变量在 data 中定义了一下,将他变成一个响应式数据。
到此,我们的思路就已经很清晰了,在 data 中存储的是一个地址值,指向的永远是在 index.js 文件中定义的那个对象,我们通过双向绑定,输入框输入赋值等操作,操作的永远是那一个 obj 对象,而根据 js 的垃圾回收机制,我们可以得出,js 在内存中生成的全局变量,只要不刷新浏览器,那么他不会被销毁,好的,问题到这里就分析完成了。
结论:是因为 引用数据类型存储的是地址值,导致操作的始终是同一个变量。
解决思路:
既然是因为引用数据类型指向的是同一个对象引起的,那我们是不是可以在 data 中定义的时候,深拷贝一下这个 obj 对象去解决呢?答案是:当然可以!
<!-- index.vue -->
<template>
<div>
<input v-model="obj.name" />
<input v-model="obj.age" />
<input v-model="obj.hobbies" />
</div>
</template>
<script>
import { obj } from './model/index.js'
export default {
name: 'index',
data () {
return {
obj: JSON.parse(JSON.stringify(obj)),
}
}
}
</script>
这样,我们就可以确保,每一次组件创建的时候,生成一份新的 obj 数据,而在组件销毁的时候,data 中的数据也会被销毁,问题就迎刃而解了~~~
问题背景:组长让解决控制台报错,于是在控制台看到了很多个
Invalid prop: custom validator check failed for prop "XXX"
的报错。大致看一眼就是说,XXX 变量没有通过校验。问题描述:在子组件的 props 写了一个参数 XXX ,将他的 type 定义为了 String ,下面紧接着又写了一个 validate 自定义校验,然后在父组件中引用了子组件。
下面是一个 demo 演示:
<!-- subComponent.vue -->
<script>
export default {
name: 'subComponent',
props: {
identifying: {
type: String,
validator: (val) => {
return ['zhangsan'].includes(val)
}
}
}
}
</script>
<!-- parentComponent -->
<template>
<subComponent :identifying="lisi"></subComponent>
</template>
<script>
import subComponent from './subComponent.vue'
export default {
name: 'parentComponent',
components: {
subComponent
}
}
</script>
分析过程:
首先,在子组件的 props 中定义了一个参数,父组件在使用子组件的时候,需要将这个参数传递过来,但是我们看到这个参数没有写 required: true ,所以这个参数不是必传的。下面又写了一个 validator ,就是说,这个参数的校验是自己自定义的,好家伙,这么高级。
不难看出,这个自定义校验函数的意思是,只要你传递过来的 identifying 是 ‘zhangsan’ ,那么就返回 true ,否则返回 false 。
接下来我们看到父组件使用的时候,传进去了一个 identifying 是 ‘lisi’ ,并不是组件想要的 ‘zhangsan’ ,那么那个自定义校验返回的就是 false ,校验不通过。
再回过头看一下我们的报错信息:‘Invalid prop: custom validator check failed for prop “identifying”’ ,翻译一下的结果是:无效道具:道具“正在识别”的自定义验证程序检查失败。但是这只是一个警告,并不会阻塞页面渲染。
到此,问题的思路就已经很清晰了,自定义校验没有通过,也就是说,我们只要传了 identifying 参数,就一定会对它进行校验,判断它是否是 ‘zhangsan’ ,这样是很不合理的,因为这个组件的背景是一个公共组件,此处有两种场景,需要 ‘zhangsan’ 和 ‘lisi’ 都成立。
解决思路:
在这里,我提供两种方案进行解决:
<!-- subComponent.vue -->
<script>
export default {
name: 'subComponent',
props: {
identifying: {
type: String,
validator: (val) => {
return ['zhangsan', 'lisi'].includes(val)
}
}
}
}
</script>
<!-- subComponent.vue -->
<script>
export default {
name: 'subComponent',
props: {
identifying: {
type: String,
enum: ['zhangsan', 'lisi']
}
}
}
</script>
这里我们就需要考虑一个问题,如果改为采用 enum 的话,需不需要让这个参数改成必填?那我们就要去看两个条件:1. 在使用 validator 自定义校验的时候是不是必填。2. 自己公司的业务场景需要我们做什么。但是在我们公司这里的业务场景是必填的,它只有两个场景,而且必须符合某一个场景,所以在这里我就不考虑第一种条件,直接改为必填了。
作者只记录自己在公司踩过的坑,以及提供自己的解决思路,如果有误请联系作者进行修改,不接受以任何形式的诋毁谩骂。如果有更好的方案也可以联系作者进行讨论,互相学习。
如需转载请注明文章来源。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。