Vue前后端数据交互与显示
一、技术概述
将后端所计算的数据呈现在前端页面的相应位置并根据用户点击操作改变相应的数据和界面,再传值给后端。该技术是Web开发必备,是前后端交互的纽带。难点在于获取后端数据并且防止数据联动。
二、 技术详述
1. 从接口获取后端数据
(1) 仔细查看后端所传数据的类型。主要是区分数组和单个数据。查看后端的请求方式,区分post或者get。
(2) 首先,在data中return一个xxxData:[]数组或一个变量xxxData:<类型>来接收后端传来的数据。
(3) 在方法中定义一个请求函数,比如我们这里函数名定义为update。请求函数中最主要的为请求语句通过api获取后端数据。
{params:this.xxx}中填写的是所携带的参数。
当get时,params作为一个关键字,总领所有携带参数的传递,例如传递参数的名字为id,值为data中已声明的值myId,那么在get请求语句中可以写成:
update(){
this.$http.get(baseURL+`api/条件`,{params:{id:this.myId}}).then(function(res){
this.memberData = res.body;
});
},
当post时,可以不加params关键字。直接写成:
update(){
this.$http.get(baseURL+`api/条件`,{id:this.myId}).then(function(res){
this.memberData = res.body;
});
},
返回的参数在then之后的匿名函数里。
这里baseURL是项目的路径,如果项目部署在服务器上面一般格式为www.XXX.com/项目名,之后的api是后端封装的api接口。
api/条件
这个条件中往往会出现前端定义的变量,在传值时若将其直接写入便会成为接口地址的一部分。要想让其代表它内在的值,则使用${}取值。例如:
console.info(`大家好,我叫${name},今年${age}岁了`)
// 等价于
console.info('大家好,我叫' + name + ',今年' + age + '岁了')
(4) 此时这个请求操作是没有调用,是默认执行的,所以要在mounted里面实时执行。
整体代码呈现例如:
<script>
export default {
data(){
memberData[],//等待存放后端数据的接收数组
},
mounted(){
this.update();//在html加载完成后进行,相当于在页面上同步显示后端数据
},
methods:{
update(){
this.$http.get(`/api/project/${this.$store.state.project.id}`, {
project_id:this.$store.state.project.id,
}).then(doc => {
var code = doc.data.status;
var msg = doc.data.msg;
if (code == 0){//请求成功,可以根据不同的状态码返回值做出相应的动作
this.memberData = doc.data.data.member//本数组存入后端数
}
})
},
},
};
</script>
在以上例子中,doc接收返回的参数,以doc.data开头获取。其中后端传送的数据又具有data结构,所以又再次.data,深入再次获取其中的member数据。
注意:在我刚开始学习获取数据的时候,将请求URL错用单引号(')引用。这里是使用反单引号(`)。
在使用vue编程中,组件里面绑定的事件如果有传入事件名称字符串/字符串参数,这个时候光用单双引号会出现string is undefined,这个时候我们就需要用到反单引号。
对比后端项目文档:
2. 前端向后端传值的交互
前端向后端传值和之前提到的携带参数的概念一样,是同样的方法。但是不同点在于这是以前端向后端传参为主的交互,所以携带参数很多的情况下,容易造成代码过长,阅读书写繁琐的问题。这就可以建立中间变量结构,统一传值,这时携带参数只需要填写一个。例如:
var obj = {//将所有携带参数放在一起
project_id:this.$store.state.project.id,
id:this.id,
finish:checked,
name:this.flowName
}
this.$http.post(`/api/project/${this.$store.state.project.id}/task/update`, obj)//直接传值的合集
.then(doc => {
var code = doc.data.status;
var msg = doc.data.msg;
if (code == 0){
this.update()//更新后端数据后自动刷新前端,随着更改外观
}
else{
this.$alert(msg,'false')
}
});
3. 显示获取到的数据
相对于获取数据而言,显示数据就显得简单许多了。
首先后端传来的数据肯定是很多层结构或者是一个集合,所以在用一个大数组接受后台数据的同时,在data return中要声明几个自己需要显示的具体的变量,后台数据要分清楚存入前端变量中才能被前端所使用。将数组中的数据再次分离。例如:
getTaskData:function(){
this.$http.get(`/api/project/${this.$store.state.project.id}/task/info? id=${this.messageId}`,//根据后端提供的URL,其中?后跟参数要注意写法${}
{params:{project_id:this.$store.state.project.id,task_id:this.messageId}})
.then(doc=>{
if(doc.data.data){//当有数据传来时才可获取。若是为空,则在传来的data下再次.xxx寻找结构中的子变量则会出错。
this.taskData=doc.data.data;//所谓的整体大数组,包含了所有传来的数据
this.defaultChecked=this.taskData.finish;//细分传来的数据结构并放入已声明过的变量
this.taskRemarks=this.taskData.remarks;
}
else
this.taskData=null;
}).catch(err=>{//处理错误的写法
this.$alert("未知错误", "false"); //服务器还没搭起来
})
},
在第一次接触接收数据时我就有个疑问,一直不知道类似于这样的“子数据”怎么获取:
获得了具体的数据之后,想要显示在html里。一般来说,将变量或者其代表的信息显示在网页上大多是插入在html标签中,变量作为属性值就要使用v-bind来实现。v-bind就是用于绑定数据和元素属性的。例如:
<a href="myHome.com">OK</a>
想要实时更新href属性值则需要绑定自定义的变量上去,而在双引号中的变量都会被当作字符串。这时我们需要用v-bind实现。绑定之后,对应的值要去vue的数据里面找。当我们在控制台改变url时,对应也会变化。相同的,我们还可以绑定图片src属性、class属性。
//这里url是在data中return的自定义变量,存储链接字符串
//url:"MyHome.com",
<a v-bind:href="url">OK</a>
//简写为(我简记为在需要变量名作为属性的时候,在属性前加冒号)
<a :href="url">OK</a>
我在刚开始想实时变化页面显示数据的时候,即根据后端传来的数据更改页面显示标签属性的时候,错误使用dom控制元素显示。因为之前没有接触过Vue,所以我对于界面元素的更改的意识停留在 “document.getElementById('xxx').<属性>=xxx” 的阶段,这样做起来代码很繁琐,效率也低,增加了代码的耦合性。
4. 防止数据联动
在任务面板的模块,为要根据不同的点击显示不同的任务详情就要传递每个任务唯一的id。而在显示详情后有更改信息的功能,在这随意的更改可能会影响其他任务的信息,造成信息错乱。主要原因是刚开始我们监听了所有组件的更改,例如这段代码在更改任务紧急程度的时候调用:
onFlowPri(pri){
this.taskpriority = pri
this.$http
.post(`/api/project/${this.$store.state.project.id}/task/update`, {//当紧急程度一变化的时候就向后端传输数据,仅一项变化,更改的却是全部数据,这时传输其他旧数据就会遇到问题
project_id:this.$store.state.project.id,
id:this.id,
remarks:this.flowMarks,
name:this.flowName,
finish:this.finish,
priority:pri,
})
.then(doc => {
var code = doc.data.status;
var msg = doc.data.msg;
if (code == 0){
this.update()
}else{
this.$alert(msg,'false')
}
});
},
而在用户是否保存不得而知的时候太早的传输更改数据,积极的监听会造成错误。增加了代码的繁琐程度。尤其是和用户交互的数据足够多的时,会造成混乱。好的方式应在用户选择确认保存后再将整体表格中要求填写的数据移交给后端,这样一次性的传输保证了数据的准确性。
三、技术问题
1. 界面自动刷新
问题描述:在用户改变某些功能性质时,界面所表现出来的数据或者组件不能实时变化,即需要用户手动刷新整个页面。
解决方法:首先界面的加载是要靠mounted方法中定义的获取后台数据的方法(在这里一般将该方法命名为update)显示。作为钩子函数,它向后端请求,拿回数据,配合路由器钩子做一些事情。主要是依靠api拿数据 ,在mounted和改变的时候直接调用update就可以。即在用户点击事件发生之后在相应事件的位置调用update重新从后台获取新数据。
2. 获取数据数组出错
问题描述:后端传入前端的是一个数组,前端接收到自己的数组之后,二次使用push新的元素进入时报错。
问题表面看起来很简单找出原因,是因为我所push的数组不被承认为数组。但是我反复检查过确实在data中接收数据类型被声明为数组。这个报错也找不到具体的位置,它报错全是从runtime运行时缓存读的,一个源文件都没映射到。是个玄学问题。
解决方法:选定项目。就是这么简单。我们的产品是要通过选择项目来向后台传送数据的,即项目ID。所以在报错信息不明确的时候要尝试发现有无信息没读到的问题。
3. 传值显示值(针对时间)
问题描述:前后端数据交互中比较绕的问题就是时间作为DateTime类型传值的时候的数据类型转换。以及在使用时间选择器的时候将组件中的时间显示为所传值的时间。
如图设置时间位置所显示的时间是错误的,而控制台输出的是从后台传入的正确时间。即时间无法实时在时间选择框中显示。
从设置时间等时间选择框中更改时间并把其传入到后端时,会有类型不匹配的问题。即String和DateTime的转换。
解决方法:在时间选择器的属性中加入:value属性并以moment约束要显示的时间变量例如:
:value="[moment(taskDetails.t_begin), moment(taskDetails.t_end)]"
时间格式化组件moment的使用:需要在script中导入组件,并在methods中声明moment。
<script>import moment from 'moment'</script>
若要将时间数据传回后端的话需要将String类型的数据转换,即需要声明定义一个toDateTime函数:
function toDateTime(time) {
var date = new Date(time);
var Y = date.getFullYear() + '-';
var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
var D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
var h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
var m = (date.getMinutes() < 10 ? '0'+date.getMinutes() : date.getMinutes()) + ':';
var s = (date.getSeconds() < 10 ? '0'+date.getSeconds() : date.getSeconds());
strDate = Y+M+D+h+m+s;
return strDate;
}
在传值的时候直接规范化。
四、 总结
- Vue中数值传送是比较简单的部分,主要是准备好等待接收的变量,保证数据成功被载入,数据的类型问题也要注意,要仔细核对后端寄来的数据。Vue的前后端交互都是有较简单实用的模板来实现的,所以很好掌握。
- 显示数据的时候要参透不同组件的展示方式,有的利用item循环展示,有的直接引用,更多是用v-bind来实现变量影响显示。
- 本次项目开发中,任务面板的开发和代码的编写是很复杂的,容易混淆,因为有多个弹出窗口以及面板,上面用户所填写的内容还有从后台接收所显示的内容都是不同的,它们之间的数据联动是很可怕的,稍不注意就容易一个新面板数据的填写覆盖掉以前已显示的旧数据。尤其对于表单一类需要用户参与填写的组件来说,需要实时监控每个可能交互的子组件,但是千万要注意不一定是每个子组件都需要有及时的反馈,不然产生的不必要的关联动作有可能会影响到最后信息的存储。尤其是带有取消的表单,即需要一键归零,过早的产生相应动作会适得其反。
五、 参考
-
《vuejs前后端数据交互之从后端请求数据的实例》 作者:caixiaodaohaha