vue中事件调用时机
场景:页面中打开一个弹框a,弹框a中点击确定打开弹框b,弹框b打开时调接口
解决:理解钩子函数调用时机
刚开始写在created里,但是只要页面打开就会调用,而且只调用一次,等到打开弹框b的时候根本不会调用
然后看了下updated这个钩子函数,更新这个时机肯定对了。结果确实是在打开弹框b之后调用的,但是会一直调用。原因是我对updated钩子函数理解有点错误,我以为的是弹框这个组件 更新的时候调用,但实际调用时机是只要该弹框中任意一个数据啊组件啊内容啊啥的有变动都会调用updated钩子函数。
实际解决,用watch监听弹框b的visible值,只要visible是true就调接口。
日期时间校验
场景:日期从几号到几号这种需要卡控校验下。结束日期大于开始日期
解决
通过Date.parse(this.startDateTime) >= Date.parse(this.endDateTime)
判断条件
Date.parse(new Date())等价于new Date().getTime()
返回的是时间戳,毫秒数,如果在东八区返回北京时间1970年01月01日08时00分00秒至现在的总毫秒数,如果在UTC区返回1970年01月01日00时00分00秒至现在的总毫秒数。与服务器所在时区相关
场景:数据是有值的,在控制台什么的也能打印,但是页面不显示
解决:大概率是数据没有双向绑定
对象或者数组里的值事先需要有想要展示在页面上的那个值,这样才能对他双向绑定渲染到页面上。
可以用$set
解决,在使用该变量的时候用$set
给他强制更新
或者刚开始给他写下这个变量赋个初值
场景:下载本地文件夹整好的文件。在本地环境可以正常下载,在测试环境或者生产环境不能正常下载
解决:大概率是路径的问题,找不到本地那个文件
文件下载路径需要url拼接下
场景:输入框按回车刷新页面,现在是弹框中输入框回车直接刷新页面了
原因:页面有form表单,表单中只有一个输入框
解决:1、form表单中加入
@submit.native.prevent
2、表单中加入submit事件,返回false,阻止form表单提交
onsubmit="return false;"
3、在页面中在加一个隐藏的输入框
<input type="text" style="display: none;">
4、去掉form表单
场景:详情页面和某个弹框样式一样,可封装成一个组件,分别在这两个地方引入该组件。进入详情页面时需要调用初始化方法,获取数据,弹框进入不需要初始化。
写在created钩子函数中,但是只能第一次打开数据正常,之后再次从表格中的不同数据进入详情页面,数据不会更新,还是第一次进入获取的数据。
刚开始以为是调用时机不对,不能写在created函数中(但是想想也没毛病,但也想不出别的原因就先按这个改了),改成watch监听一个变量,但是不会执行里面的内容。原因是每一个vue实例下面都挂了data,props等这些,为了防止变量污染,每一个vue都是独立的,下面的变量和挂载在其他实例下面的变量互不干扰,watch这个变量是两个页面调用的,分别挂载在不同的下面,属于两个独立的变量,所以watch监听无效,因为值不会改变。
解决:还是应该写在created函数里,真正原因是keepAlive缓存,在主页面使用了keepAlive组件缓存页面,在keepAlive里加上include=“要缓存的组件名” 为什么不使用exclude=“不缓存的组件名” 防止需求变更,不缓存的组件增加时,改动地方更多
场景:页面返回至前一个页面没有刷新数据
原因:是keepAlive组件缓存的问题。刷新列表的函数写在created钩子函数中调用,开启keep-alive后再次进入页面钩子函数只调用了activated,所以不会刷新数据。
(发现缓存好处很多,但问题也好多)
代码是这样写的。keep-alive缓存了ProductionDetainIndexModule这个组件。
<template>
<keep-alive include="ProductionDetainIndexModule">
<router-view></router-view>
</keep-alive>
</template>
解决:
1、把keep-alive去掉(感觉尽量不要)
2、从钩子函数触发顺序入手,在activated钩子函数中调用刷新列表函数。开启keep-alive缓存,钩子函数触发顺序:页面第一次进入,created -> mounted -> activated -> (退出页面)deactivated -> (再次进入)activated
有一种情况不适用。返回的页面是当前页面的父组件(路由嵌套情况)
两种方法解决
1)用watch监听route路由对象,调用刷新数据方法
2)beforeRouteUpdate路由导航守卫,监听路由更新
beforeRouteUpdate(to,from,next) {
next()
//获取路由名,判断是否是父组件,避免重复刷新数据
if (this.$route.name === 'CardContent) {
getFieldList(this)
}
}
3、网上还有路由加过滤,把该路由过滤掉,或者配置路由的时候meta把keepAlive给false掉
{
path: '/pay/:aid/:pid',
component: () => import ('./views/Pay.vue'),
meta:{
keepalive:false
}
}
4、在路由转换前,写个判断
网上还有很多其他方法解决,不是我这次问题的原因,但可以了解下,说不定下次遇到了
相当于强制刷新页面 两种写法(会出现瞬间空白页面,体验不好)
location.reload()
this.$router.go(-1)
this.$router.back()
也可依赖注入provide / inject实现强制刷新
app.vue
通过reload方法控制router-view显示隐藏,控制页面再次加载
<template>
<div id="app">
<router-view v-if="isRouterAlive"></router-view>
</div>
</template>
<script>
export default {
name: 'app',
provide () {
return {
reload: this.reload
}
},
data () {
return {
isRouterAlive: true
}
},
methods: {
reload () {
this.isRouterAlive = false
this.$nextTick(() => {
this.isRouterAlive = true
})
}
}
}
</script>
在需要刷新的页面inject注入reload依赖
inject: ['reload']
调用直接用this.reload
即可
场景调用接口时对dataForm集合中一个wafer字段进行赋值操作,操作会慢一拍,这个是数据没有双向绑定,但该集合中该字段我有初始化赋初值,所以为什么还是没有双向绑定
解决:当然可以用$set强行双向绑定数据,但是没有从原因上解决。
该情况导致是因为在调用接口前已经对dataForm做了一步赋值操作,而这个赋值中并没有wafer字段,所以导致了相当于对dataForm刷新了数据此时并没有wafer,所以数据没有双向绑定上。
之前赋值用的是下面这个,此时dataForm中内容和res.data中完全一致,res.data中没有的字段,即使初始化中有此时也会没有
this.dataForm = res.data
在赋值的时候可以改用这个,首先对dataForm初始化中有的字段进行赋值,然后res.data中独有的字段合并到dataForm中。相当于取并集
this.dataForm = { ...this.dataForm, ...res.data }
场景:多个下拉框联动,下拉框双向绑定id,详情接口里返回id,需要接口套接口把id转换成中文展示,所以会产生时间差,进入编辑浏览详情页面时会先闪下id再展示正常的内容。
解决:使用promise
场景:
vue报错Do not use built-in or reserved HTML elements as component id:header
解决:组件不能和html标签重复
在模板需要插入到DOM中,模板的标签名必须能被DOM正确解析。
主要有以下三种情况不能正确解析:
一:完全不合法的标签名,如</>
二:与html元素重名,会产生不确定行为。如用input作为组件名不会解析到自定义组件,用button在Chrome上正常单在IE上不正常
三:与vue保留的slot,partial,component重名。因为会优先以本身意义解析,从而产生非预期结果
场景:
vue报错Duplicate keys detected: 'C1812170006'. This may cause an update error.
解决:不影响使用
v-for循环中key值可能重复。key值必须唯一,diff算法中在比较dom树是否变化,是根据该节点的tag和key,所以key不能重复。一般也不要写成index,因为如果有对树节点进行排序的话,使用index会发生错乱。
场景:
vue报错Avoid mutating a prop directly since the value will be overwritten whenever
解决:
父组件通过props传值给子组件时,子组件中改变了props的属性值报错
一般以下两种情况容易修改prop数据
prop作为初始值传入后,子组件想把它当做局部数据来用
prop作为原始数据传入,由子组件处理成其他数据输出
如何避免:
一:定义一个局部变量,prop的值初始化。
但有一个问题,如果父组件先调用接口才能获取到prop的值,这样写会获取不到值,执行顺序不对
props: ['dataInfo'],
data: function () {
return { counter: this.dataInfo }
}
二:定义一个计算属性,处理prop的值并返回
props: ['size'],
computed: {
handSize: function () {
return this.size.trim().toLowerCase()
}
}
三:prop为boolean时,可在父组件传递时加.sync
,子组件用$emit('update:xxx')
改变值并通知父组件。子组件值改变不会影响到父组件,该方式没有改变单向数据流的特性。
控制弹框显示visible可用该方式
父组件引用弹框
<dialog
:visible.sync="visible"
:viewFlag="dialogViewFlag"
@confirm="confirm"
:row="row"
></dialog>
子组件props传入,改变值
props: {
visible: {
type: Boolean,
default: false
}
}
//关闭弹框
closeDialog(){
this.$emit('update:visible',false);
}
四:将prop定义为对象,改变对象中的值不会触发报错,页面可以正常渲染更新。
原因:参数是对象时,传给子组件的应该是类似于指针的东西,指向内存中的一个数据空间,子组件通过ObjectName.prop操作Object时,更改了内存空间的数据,但并没有改变内存空间的位置,指针仍然指向该内存空间,所以不会警告。
props:{
status:{
type:Object,
default:()=>{}
}
}
五:深拷贝。子组件接受到prop传过来的值时,对值进行深拷贝,对拷贝后的值进行操作,父组件想要获取子组件最新的值,使用this.$refs.组件名.拷贝后的值
直接获取。
可能会出现执行顺序不对,出现子组件获取不到传过来的值。可以在子组件中专门写一个深拷贝的方法,在父组件获取到值之后使用this.$refs
直接调用子组件方法进行赋值。
场景:
控制台报错(Emitted value instead of an instance of Error) the "scope" attribute for scoped slots have been deprecated and replaced by "slot-scope" since 2.5. The new "slot-scope" attribute can also be used on plain elements in addition to <template> to denote scoped slots.
解决:
要改成这个样子
组件的template中有scope
属性,需要改成slot-scope
scope属性在2.5之后版本中已经废弃,被slot-scope替代。slot-scope不只用在template元素上,也可用在其他元素
场景:
vue报错Cannot set properties of undefined (setting 'delFlag')
解决:
变量没有初始化值就进行赋值。报数据未定义赋值错误
对变量初始化
场景:一些时候层级套的可能比较深,数据已经变化,但是页面没有渲染。使用this.$set
也没有作用
解决:在这个地方使用this.$forceUpdate()
强制刷新页面,如果还是没有用,考虑下是否取到值,内容是否改对地方。
但是感觉this.$set
和this.$forceUpdate()
还是尽量少用
场景:vue报错Property or method "item" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class
解决:代码中有一处item没有定义直接使用了
场景:字符串以逗号分隔成数组this.list = row.checkRsDesc.split(',')
,数组中每个id拼成以逗号分隔的字符串this.selectedList.map(item => item.id).join(',')
场景:js正则
整数/^[0-9]*$/
小数/(^[0-9]*\.([0-9]{1}\*)$/
正数,负数或小数/^(\-|\+)?\d+(\.\d+)?$/
场景:vue报错Invalid attempt to spread non-iterable instance
解决:扩展运算符报错。对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。该报错是由于不可遍历导致的
场景:控制台报错NavigationDuplicated: Avoided redundant navigation to current location: “/“
解决:路由重复。检查router文件。检查代码看是否返回到同一页面了。我的原因是逻辑有点问题执行了两次返回同一页面,所以报错。
场景:js数组嵌套数组的数据结构。splice删除嵌套的数组的对象,第一个删除返回结果正确,多个返回结果就不对了
解决:原因是splice删除会改变数组长度,此时在进行删除传进去的索引就不正确了,不是此时想要删除的数据的索引。
一:两层循环。第一层循环是正着循环,循环外层数组,但第二层倒着循环,循环嵌套的数组,此时索引不会有误。
二:两层循环的代码优化,一层循环外层数组,二层循环优化成find找到要删除的数据,然后把该数据删掉,splice,filter都可