vuejs 解决数组子组件,在子组件相同情况下,splice更换或删除数组数据,组件不刷新,组件外能刷新的问题

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>电子答题卡</title> <link rel="stylesheet" href="css/answer_card.css"> </head> <body > <div v-cloak class="answerCard" id="answerCard"> <div class="choicetype clearfix"> <div class="addtype"> <span>添加题型</span> <a v-for="(item,index) in classData" v-on:click="clicktype(item,index)" href="#"> +{{item.radiotopic}} </a> </div> </div> <div class="questiontype" v-for="(item,index) in newData"> <div class="question " v-bind:class="addClassState(item)"> <div class="title clearfix" v-on:click="isOpne(item,index)"> <div class="sort"> <em>题型排序</em> <span><i class="up" v-bind:class="{noup: index==newData.length-1}" v-on:click.stop="upClick(item,index)"></i><i class="down" v-bind:class="{nodown: index==0}" v-on:click.stop="downClick(item,index)"></i><a href="javascript:void(0)" class="del" v-on:click.stop="delClick(item,index)"></a></span> </div> </div> <component :is="curComponentFn(item)" :key="item.guid" v-bind:oitem="item" v-bind:class="{open:activeOpen==index}" v-bind:onewdata="newData" v-bind:oindex="index" ></component> </div> </div> </div> <script type="text/javascript" src="js/lib/jquery.min.js"></script> <script type="text/javascript" src="js/lib/es6-promise.auto.min.js"></script> <script type="text/javascript" src="js/lib/axios.min.js"></script> <script type="text/javascript" src="js/lib/vue.min.js"></script> <script type="text/javascript" src="js/base.js"></script> <script type="text/javascript" src="js/app/testKey.js"></script> </body> </html>

var oData={ timelimit:0, //设置时间 istime:true, //是否设置时间开关 allWrongNum:0, //错题数 countDown:0, //倒计时 activeIndex: -1, //题型当前选中 activeOpen:0, //当前选中题型展开 curComponent:"", //加展相应组件 classData:[], //记录题型数据 newData:[], //得到新的数据(用于编辑POST) getData:[], //得到新的数据(用于GET) } // 注册组件4 var custom4=Vue.component('custom4', { props: ["oitem","onewdata","oindex"], data:function(){ return { odata:this.oitem, list: [], anewdata:this.onewdata, aindex:this.oindex, asortnum:[], } }, template: '<div class="cont">'+ '<ul class="clearfix">'+ '<li v-for="(oitemli,index) in creadData">'+ '<em>{{(index+1)+asortnumHandler}}.</em>'+ '<p>'+ '<input type="text" v-for="(oitemspan,index1) in oitemli.selected" placeholder="参考答案" v-model=oitemspan v-on:keyup.self="addOn(oitemspan,index,index1)" v-bind:class="{on:oitemspan!=\'\'}"/>'+ '</p>'+ '</li>'+ '</ul>'+ '</div>', computed: { creadData:function(){ this.list=[]; this.creadDaan(); for(var i=0;i<this.odata.num;i++){ var c=this.creatAnswer(parseInt(this.odata.option)) for(var m=0;m<c.length;m++){ var oindex=this.odata.answer[i].text[m]; c.splice(m, 1); c.splice(m, 0, oindex); } var item = { selected: c, } this.list.push(item) } return this.list }, sortNumHandler:function(){ var alln=[]; for(var i=0;i<this.anewdata.length;i++){ alln.push(this.anewdata[i].num) } this.asortnum=alln return this.asortnum }, asortnumHandler:function(){ var ao=0; if(this.aindex==0){ ao=0; return ao } for(var i=0;i<this.sortNumHandler.length;i++){ ao+=parseInt(this.sortNumHandler[i]) if(i==this.aindex-1){ return ao } } } }, methods:{ creatAnswer:function(n){ //生成答案 var letter=[]; for(var i=0;i<n;i++) { letter.push(""); } return letter; }, creadDaan:function(index){ var c = this.creatAnswer(parseInt(this.odata.option)) var createLength=this.odata.num-this.odata.answer.length; for (var i = 0; i < createLength; i++) { var n={text:c} this.odata.answer.push(n) } return this.odata.answer }, }, created: function() { this.creadDaan(); } }) // 注册组件5 var custom5=Vue.component('custom5', { props: ["oitem","onewdata","oindex"], data:function(){ return { odata:this.oitem, list: [], anewdata:this.onewdata, aindex:this.oindex, asortnum:[], handPop: -1, keepCanvas:null } }, template: '<div class="cont look_subjective" id="look_subjective">'+ '<ul class="clearfix">' + '<li v-bind:id="\'subjective\'+(index + 1)" v-for="(oitemli,index) in creadData">'+ '<em>{{(index+1)+asortnumHandler}}.</em>'+ '<div class="upload" v-bind:class="{on:oitemli.options.tit!=\'\'||oitemli.options.img!=\'\'}" >'+ '<p class="tit" >'+ '<textarea type="text" maxlength="400" v-model="oitemli.options.tit" v-on:keyup.self="addTit(oitemli.options.tit,index)" placeholder="请编辑文字" />' + '</p>'+ '</div>'+ '</div>'+ '</li>'+ '</ul>'+ '</div>', computed: { creadData:function(){ this.list=[]; this.creadDaan(); for(var i=0;i<this.odata.num;i++){ var item = { options: this.odata.answer[i], } this.list.push(item) } return this.list }, sortNumHandler:function(){ var alln=[]; for(var i=0;i<this.anewdata.length;i++){ alln.push(this.anewdata[i].num) } this.asortnum=alln return this.asortnum }, asortnumHandler:function(){ var ao=0; if(this.aindex==0){ ao=0; return ao } for(var i=0;i<this.sortNumHandler.length;i++){ ao+=parseInt(this.sortNumHandler[i]) if(i==this.aindex-1){ return ao } } } }, methods:{ creadDaan: function (index) { var createLength = this.odata.num - this.odata.answer.length; for (var i = 0; i < createLength; i++) { var n = { tit: '', img: [] } this.odata.answer.push(n) } return this.odata.answer }, }, created: function() { this.creadDaan(); }, }) var editApp=new Vue({ el:'#answerCard', data:oData, filters:{//过滤器 numChinese: function (n) { //提供中文数字 var cnum = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']; var s = ''; n = n + 1; var tenNum =parseInt(n / 10); var units = n % 10; if (tenNum === 1) s += "十"; else if (tenNum > 1) s += cnum[tenNum] + "十"; if (units > 0) s += cnum[units]; return s; } }, methods:{ newDataFn:function(idata){ //根据state生成数据 this.newData=[]; if(idata){ for (var n = 0; n < idata.length; n++) { this.newData.push(idata[n]) } return this.newData } }, clicktype: function (todo, index) { //选择题型 //todoPop(todo); todo.guid = guid(); this.newData.push(JSON.parse(JSON.stringify(todo))); this.activeOpen = this.newData.length - 1; }, isOpne:function(todo,index){ //组件按需展开 for(var i=0;i<this.newData.length;i++){ this.activeOpen=index; } }, delClick:function(todo,index){ //删除题型 this.newData.splice(index, 1); //var sss= JSON.parse(JSON.stringify(this.newData)); //this.newData = null; //this.newData = sss; }, upClick:function(todo,index){ //排序向下 if(index==this.newData.length-1){ return } var ss = this.newData.splice(index, 1); //var newss = JSON.parse(JSON.stringify(ss)); this.newData.splice(index + 1, 0, ss[0]); //this.$set(this.newData, index + 1, ss[0]); //Vue.set(this.newData, index + 1, ss[0]) this.isOpne(todo,index) }, downClick:function(todo,index){ //排序向上 if(index==0){ return } var ss=this.newData.splice(index, 1); this.newData.splice(index-1, 0, ss[0]); this.isOpne(todo,index) }, addClassState:function(todo){ //自动生成题数类型加class if(todo.radioid=="itemFill"){ return {itemFill:true} } if(todo.radioid=="itemSubjective"){ return {itemSubjective:true} } }, curComponentFn:function(todo){ if(todo.radioid=="itemFill"){ return "custom4" } if(todo.radioid=="itemSubjective"){ return "custom5" } }, innt:function(){ //初始化渲染页面 this.newDataFn(this.getData) }, }, created: function() { var _this=this; _this.getData = []; var classStr="[{\"guid\":null,\"radiotopic\":\"填空题\",\"radioid\":\"itemFill\",\"isselect\":true,\"num\":\"1\",\"option\":\"1\",\"fraction\":\"1\",\"answer\":[]},{\"guid\":null,\"radiotopic\":\"主观题\",\"radioid\":\"itemSubjective\",\"isselect\":true,\"num\":\"1\",\"option\":\"1\",\"fraction\":\"1\",\"answer\":[]}]"; var classtype = JSON.parse(classStr); _this.classData = classtype ; _this.innt() } }) //生成guid ,解决排序 function guid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); return v.toString(16); }); } //选择题型弹出层 function todoPop(todo){ alert(todo.radiotopic+"弹出层") var mydata={ "num":"4", "option":"4", "fraction":"10", } todo.num=mydata.num; todo.option=mydata.option; todo.fraction=mydata.fraction; } //获取class function getByClass(clsName, parent){ var oCls=parent.getElementsByTagName('*')//获取所有的标签元素 var arr=[]; for(i in oCls){ //对遍历的标签元素与要查找的元素进行判断 if(oCls[i].className==clsName){ arr.push(oCls[i]) } } return arr }
var ss = this.newData.splice(index, 1);
this.newData.splice(index + 1, 0, ss[0]);
通过splice进行数组切换后发现,组件内视图没有刷新,但外面的标题刷新了,如下图。
添加2个主观题
数组改变后
原因是vue内部组件缓存机制导致的。,解决方案:为每个组件添加key, 下图是官方解释
<component :is="curComponentFn(item)" :key="item.guid" v-bind:oitem="item" v-bind:class="{open:activeOpen==index}" v-bind:onewdata="newData" v-bind:oindex="index" ></component>
去掉 :key="item.guid" 就会重现
demo https://pan.baidu.com/s/1cCqsnO
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)