0 前言
在 Vue学习笔记7:使用v-for指令渲染列表_PurpleEndurer@5lcto的技术博客_51CTO博客 中,我们使用Vue的v-for指令来改造代码,两个水果列表的网页元素描述代码都得到了大幅度的精简,代码变得更清晰,更便于维护。
与此同时,这次改造也引出了两个新的问题:
1.用户选定水果列表中的水果名称没有保留之前设定的颜色,全部以黑色来显示,不够亮丽醒目。
2.当用户选定喜欢的水果后,在选定水果列表这里没有作出响应,把用户选定的水果名称显示出来,把其它水果名称隐藏起来。
出现第1个问题的原因是我们没有把水果的颜色提取到 水果信息数组aFruit中并输出。
出现第2个问题的原因是我们没有把水果的id提取到 水果信息数组aFruit中并输出,而showFruit(v)函数是需要使用id来控制网页元素的。
在解决这两个问题的过程中,我们会发现实际上远没有上面讲的这么简单。
下面以 Vue学习笔记7:使用v-for指令渲染列表_PurpleEndurer@5lcto的技术博客_51CTO博客 中的最终代码:
<script setup> import { ref } from 'vue' var aFruits = ref(["苹果", "桔子", "葡萄"]); /* var aFruits = ref({apple:"苹果", orange:"桔子", grape: "葡萄"}); */ function showFruit(v) { document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); } </script> <template> <p>用Vue指令v-for循环输出网页元素描述代码</p> <p style="margin-left:20%; color:purple; font-weight:bold;"> by PurpleEndurer </p> <p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value}}" name="fruit" @click="showFruit('{{value}}')" /> {{value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value}}" name="fruit" @click="showFruit('{{value}}')" /> {{value}} </label> </textarea> //--> </p> <p>你喜欢的是:</p> <p v-for="value in aFruits">{{value}}</p> </template>
为基础进行研究与改进。
1 第1次改进代码
1.1 改进水果信息数组
在 Vue学习笔记7:使用v-for指令渲染列表_PurpleEndurer@5lcto的技术博客_51CTO博客 中,我们分析了水果列表1
<p> <label> <input type="radio" value="苹果" name="fruit" onchange="showFruit(this.value)" /> 苹果 </label> </p> <p> <label> <input type="radio" value="桔子" name="fruit" onchange="showFruit(this.value)" /> 桔子 </label> </p> <p> <label> <input type="radio" value="葡萄" name="fruit" onchange="showFruit(this.value)" /> 葡萄 </label> </p>
和水果列表2:
<p id="pApple" style="color:red">苹果</p> <p id="pOrange" style="color:orange">桔子</p> <p id="pGrape" style="color:purple;">葡萄</p>
其中的有效信息有3项:
id :如pApple
color :如 red
value :如 苹果
当时我们为了突出重点,只使用其中的value这项信息定义了水果信息数组:
var aFruits= ref(["苹果","桔子","葡萄"]);
1.
现在我们要对这个数组进行改进,把id和style两项信息也加进去,形成如下【代码1.1】:
var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]);
1.2 改写网页元素描述代码
1.2.1 改造用户可选水果列表的描述代码
之前的代码是:
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value}}" name="fruit" @click="showFruit('{{value}}')" /> {{value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value}}" name="fruit" @click="showFruit('{{value}}')" /> {{value}} </label> </textarea> //--> </p>
其中value的值就是数组aFruits:
var aFruits= ref(["苹果","桔子","葡萄"]);
1.
的元素,如'苹果',我们直接使用value就行了。
现在水果信息数组aFruits已经从一个一维的字符串数组改进成为一个对象数组:
var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]);
对于
<p v-for="value in aFruits">
来说,value的值是一个对象,比如:[{id:'Apple', color:'red', value:'苹果'}
现在我只需要对象中的value属性的值,即value.value。因此描述代码要改为【代码1.2.1】:
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> //--> </p>
1.2.2 改造显示用户选定水果列表的描述代码
之前的代码是:
<p>你喜欢的是:</p> <p v-for="value in aFruits">{{value}}</p>
现在水果信息数组aFruits已经从一个一维的字符串数组改进成为一个对象数组,并且我们要把对象中的id、color和value属性值都用上,所以改为【代码1.2.2】:
<div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </div>
1.2.3 修改技术改进说明
将
<p>用Vue指令v-for循环输出网页元素描述代码</p>
改为
<p>用Vue指令v-for循环输出网页元素描述代码追加颜色</p>
1.3 第1次改进的最终代码
汇总以上修改后的【代码1.3】为:
<script setup> import { ref,reactive } from 'vue' var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]); function showFruit(v) { document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); } </script> <template> <p>用Vue指令v-for循环输出网页元素描述代码追加颜色</p> <p style="margin-left:20%; color:purple; font-weight:bold;"> by PurpleEndurer </p> <p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> //--> </p> <p>你喜欢的是:</p> <div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </div> </template>
1.4 第1次改进代码的运行效果
从运行结果来看,不仅用户选定水果列表中的水果名称没有颜色这个问题没解决,而且当用户选定喜欢的水果后,在选定水果列表这里没有作出响应这两个之前说到的问题同样没有解决,此外我们还看到了出错信息:
Cannot read properties of null (reading 'style')
2 调试代码
【代码1.3】看起来是挺完美的,但运行效果不佳,理想和现实有时差距就是这么大。
我们要添加一些辅助的调试代码来分析它运行不如预期的原因。
2.1 增加调试代码
2.1.1 修改用户可选水果列表的描述代码
将【代码1.2.1】
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> //--> </p>
改为【代码 2.1.1】:
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> </p>
我们把原先注释起来的<textarea>……</textarea>重新启用,用来查看一下Vue生成的代码。
2.1.2 修改显示用户选定水果列表的描述代码
为了观察Vue生成的代码,我们也给【代码1.2.2】
<div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </div>
增加<textarea>……</textarea>,变为【代码2.1.2】:
<div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> <textarea> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </textarea> </div>
2.1.3 修改showFruit(v)函数
我们看到的出错信息:
Cannot read properties of null (reading 'style')
应该是执行showFruit(v)函数中的代码引起的,而且很可能跟传入参数v的值有关,所以我们给showFruit(v)函数的函数体【代码】:
function showFruit(v) { document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); }
增加一条语句:alert(v),来显示传入参数v的值,即【代码2.1.2】
function showFruit(v) { alert(v);//显示传参数v的值 document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); }
2.2 调试代码
增加以上调试代码后的最终代码【代码2.2】如下:
<script setup> import { ref,reactive } from 'vue' var aFruits = ref([{id:'pApple', color:'red', value:'苹果'}, {id:'pOrange', color:'orange',value:'桔子'}, {id:'pGrape', color:'purple', value:'葡萄'} ]); function showFruit(v) { alert(v);//显示传参数v的值 document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); } </script> <template> <p>用Vue指令v-for循环输出网页元素描述代码追加颜色</p> <p style="margin-left:20%; color:purple; font-weight:bold;"> by PurpleEndurer </p> <p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> </p> <p>你喜欢的是:</p> <div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> <textarea> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </textarea> </div> </template>
2.3 调试代码运行效果
从<textarea></textarea>中的显示的代码来看,aFruits数组中各个对象的属性都在{{}}中成功解包引用出来了。
但是代码没有生效,比如显示用户选定水果列表的第1项的描述代码:
<p id="Apple" style="color:red"> 苹果 </p>
已经通过style的color指定了红色,但是页面上显示出来的‘苹果’仍然是黑色。
我们再用鼠标操作,点击选择水果看看是什么效果:
当我们点击选择自己的喜欢的水果,比如苹果后,会执行showFruit(v)函数,我们把所点水果的value(按理来说就是'苹果')作为showFruit函数传入参数值赋给v。
在showFruit(v)函数内容,会首先显示传入参数v的值,按理来说应该显示的是‘苹果’,但实际显示的是:
这说明在【代码2.1.1】中的语句:
<input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" />
中
@click="showFruit('{{value.value}}')"
引用的{{value.value}} 没有成功解包引用出来。
3 分析原因
经过认真分析思考和反复测试,梳理出了引起问题的原因:
3.1 HTML标签中的属性书写语法错误
HTML标签中的属性,如果需要引用数据的,要使用v-bind指令进行绑定。
比如,用户可选水果列表的描述代码【代码1.2.1】中的
<input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" />
1.
正确写法是
<input type="radio" :value=value.value name="fruit" @click="showFruit(value.value)" />
1.
<input>标签的value属性要引用水果信息数组aFruits中对象的value属性,可以写为:
v-bind:value=value.value
1.
由于“v-bind:”指令可以简写为“:”,所以我们通常写为:
:value=value.value
1.
3.2 HTML标签内部引用数据不需要{{}}
在HTML标签内部引用数据时,数据不需要使用{{}}来包括。
比如,用户可选水果列表的描述代码【代码1.2.1】中的
<input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" />
1.
<input>标签的click事件的监听函函要将水果信息数组aFruits中对象的value属性值作为传入参数值,代码:
@click="showFruit('{{value.value}}')" />
1.
这种写法是错误的,正确的写法写为:
@click="showFruit(value.value)"
1.
3.3 函数showFruit中使用的id值与aFruits中定义的id值不匹配
函数showFruit中语句:
document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none");
document.getElementById()使用的id值pApple、pOrange、pGrape均是以p开头的
而在数组aFruits中:
var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]);
我在提取id信息时,把开头的字母p去掉了。
4 第2次改进代码
4.1 改写JavaScript脚本
4.1.1 改进水果信息数组
将【代码1.1】
var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]);
改为【代码4.1.1】
var aFruits = ref([{id:'Apple', color:'red', value:'苹果'}, {id:'Orange', color:'orange',value:'桔子'}, {id:'Grape', color:'purple', value:'葡萄'} ]);
作了2点改动:
1.给所有对象的id值的开头加上p。
2.将color属性性从 color:'颜色' 改为color:'color:颜色' ,如:color:'red' 改为 color:'color:red'。这样修改是方便 显示用户选定水果列表的描述代码的使用。
4.2 改写网页元素描述代码
4.2.1 改写用户可选水果列表的描述代码
将【代码 2.1.1】
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits"> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> <!-- <textarea> <label> <input type="radio" value="{{value.value}}" name="fruit" @click="showFruit('{{value.value}}')" /> {{value.value}} </label> </textarea> //--> </p>
改写为【代码 4.2.1】
<p>你喜欢哪种水果?</p> <p v-for="value in aFruits" > <label> <input type="radio" :value=value.value name="fruit" @click="showFruit(value.value)" /> {{value.value}} </label> </p>
4.2.2 改造显示用户选定水果列表的描述代码
将【代码1.2.2】:
<div v-for="value in aFruits"> <p id="{{value.id}}" style="color:{{ value.color }}"> {{value.value}} </p> </div>
改写为【代码4.2.2】:
<p>你喜欢的是:</p> <div v-for="value in aFruits"> <p :id=value.id :style=value.color> {{value.value}} </p> </div>
4.3 第2次改进的最终代码
综合汇总以上修改的最终代码如下:
<script setup> import { ref } from 'vue' var aFruits = ref([{id:'pApple', color:'color:red', value:'苹果'}, {id:'pOrange', color:'color:orange',value:'桔子'}, {id:'pGrape', color:'color:purple', value:'葡萄'} ]); function showFruit(v) { //alert(v);//显示传参数v的值 document.getElementById('pApple').style.display = (v=='苹果' ? "inherit" : "none"); document.getElementById('pOrange').style.display = (v=='桔子' ? "inherit" : "none"); document.getElementById('pGrape').style.display = (v=='葡萄' ? "inherit" : "none"); } </script> <template> <p>用Vue指令v-for循环输出网页元素描述代码追加颜色</p> <p style="margin-left:20%; color:purple; font-weight:bold;"> by PurpleEndurer </p> <p>你喜欢哪种水果?</p> <p v-for="value in aFruits" > <label> <input type="radio" :value=value.value name="fruit" @click="showFruit(value.value)" /> {{value.value}} </label> </p> <p>你喜欢的是:</p> <div v-for="value in aFruits"> <p :id=value.id :style=value.color> {{value.value}} </p> </div> </template>
4.4 第2次改进代码的运行效果
从运行效果来看,两个问题都解决了。
5 小结
我们通过两次改进代码,认真分析问题和反复测试,找出了引发问题的原因,顺利解决了之前遇到的两个问题。
在解决问题的过程中有两点收获。
5.1 基础知识要学得扎实
引发问题的原因,基本上都是基础知识掌握得不够扎实。
所以我们学习新知识,不能急功好进,好大喜功,而要稳扎稳打,叫透基础知识,在后面就可以少费时间少走弯路。
5.2 一些改进方向
5.2.1 关于HTML标签的属性引用的数据的实现方法
对于要HTML标签的属性要引用的数据的情况,除了可以使用vue提供的v-bind指令进行绑定这种方法外,我们还可以考虑在使用v-for指令循环生成HTML标签后,再使用window.onload或setTimeout()过一定时间后使用HTML COM访问生成的HTML标签来设置属性值。
5.2.2 改进函数showFruit
函数showFruit的代码通用性不强,维护起来不方法,可以改进一下。