开发流程
因为自己做的是H5前端,所以对于一个项目的基本流程会偏向于前端认识。
项目开始的时候,会有项目负责人确定本次项目开发所需要的技术基础和技术框架。这是一个团队协作的项目,会用到代码托管gitlab或其他,利用rap2完成API文档托管。
1.开发共识和代码规范
作为初学者,最基本的就是遵守代码规范。我们小团队之间做的项目就有一些共同之处,这时就少不了代码的封装或者套用了,在去试着理解一些他人写的逻辑的时候,当我门的代码风格不统一或者不遵从规范来的话,首先看下来就十分的别扭,其次小团队间理解就有了一定的难度,不免团队开发效率会大大降低。当我开发时遇到问题去咨询别人的时候一个好的代码规范也会使我们的问题更加直接简明的暴露出来。后期更改需求的时候代码的可读性,良好的维护性就显得尤为重要,所有我认为作为初学者一定要关注代码规范。
2.框架、组件、工具学习
项目后会确定项目中所需要的框架或者组件,这里我们就用到了gitlub代码托管工具,vant组件,VUE知识等。及时学习\复习是个人可以承诺这个项目的基础,在自己不理解的地方及时去询问有项目经验的大佬们,可以学会到好多东西,一些需要重点掌握的知识或者一些容易忽视的细节可以更早的发现学习,避免目前的小问题造成了项目中的大问题。
3.需求分析、设计稿
然后开始进行需求分析了,一般会开会两次以上,也就是我们需要明白自己做的是一个什么样的项目,具体有哪些功能,哪些样式和细节,最好的状态是我们可以在心中构想出一个完整的页面流程,这一部分有什么细节,需要完成什么功能。当我们碰到有走不通的地方(不太明白或者应有的功能没有体现)的时候,一定要在团队需求会议上及时的提出,并且越早越好,第一次就发现问题及时解决对整个项目进度都是超大的提升。
设计稿需要结合需求文档一起看,因为在前端开发的时候,我们必须一比一精确还原,设计稿就是对页面展示样式和内容的一个定型。后面开发的时候,必须严格遵循设计稿,做到px级别的还原。
3.排期
合理的排期保证了我每天的应有的任务进度,可以使负责人把控项目的整体进度。每一个项目参与人都需要参与到排期中,做到承诺即交付,我们排期的过程就是我们承诺的过程。怎么把许下这个承诺,并保证可以完成,需要我们对自己任务和自身技术的充分评估,一旦自己的最终任务完成时间大大的长于项目整体的进度,一定要及时的向小项目负责人提出来,适量的减少任务分配,切不可打肿脸充胖子,延期才完成自己的排期。当然最后自己完成排期之后,需要向负责人过排期,充分发挥排期间的时间安排。
我在排期的过期中就没有充分考虑到接口的调用实现,以至于排期最后几天都是特别紧的.....虽然最后熬夜也完成了,这次也告诉我一定要充分考虑自己与后台对接的接口实现,还有尽量前期就尽快完成页面的样式,后期有更多的时间去处理细节和代码逻辑问题。
4.代码开发
之后就是自己负责的前端开发了,这个时候有几点很重要。
1.合理分配。我们同一模块可能有样式或者功能相同/相似的地方,为了项目整体同一和开发效率,就可以分配给一个人负责这一部分,便于大家共同使用,减少开发冗余。
2.沟通交流。每个人的完成情况,修改的地方,需要统一的地方都需要及时在群里通知。保证每个人都可以被通知到,这是对项目整体和他人的负责。
3.接口测试。一般后期后端的接口也写好了,这个时候我们需要找写自己模块的后端人员进行对接。对接不是简单的有这个字段可以传就可以了,还需要搞清传递的形式,是否必填,传递的值需要数组类型还是int类型还是其他。当后端接口没有完成的时候,前端可以模拟一些假数据进行测试, 一个理想下项目是获取到接口的时候,将这些数据直接替换一些就可以了。
4.代码托管。一般是两天一期通过gitlab去同步自己的代码,不拖拉,及时的去同步自己的代码,并及时拉取远程最新代码。
项目同步
负责人需要把控每一个模块的进度,就必须定期开项目同步会议,这个时候可能每个模块的负责人去参加,不用所有人员参加,但是项目同步会议是不可缺少的,
项目自测
开发完成之后,首先自己去测试H5前端模块可以跑的通,在去统一测试。
统一测试
这个时候可能出现好多问题,一些自测时候没有发现的问题也渐渐暴露出来。比如:由于访问量/网络等外部条件造成的网络卡顿问题怎么解决。或者按钮被狂点的时候出现多次请求,不同手机间的兼容问题,或者用户不会接下来的操作是否可以添加提示等等, 这些都是有可能自测的时候没有被发现的。
修改bug,测试
重复循环,直到最后项目没有(不能发现)问题。
本次开发中没有及时注意到的细节:
1.个别没有严格按照设计稿,精确到px级别
2.路由跳转,push和replace应用仍有部分存在问题
3.用户填写需求文档上要求的数据格式的时候,个别没有添加正则
4.后端int类型最大储存位数为11位,和一些特殊的储存长度,还有是否可以保存小数点没有及时和后端沟通,
5.按钮点击事件没有及时限制,导致可以狂点
6.有些实现方法,已形成固定的模板,没有及时咨询负责人是否可以
项目中遇到的技术问题
未解决的问题:
1.图片压缩
处理办法:交给后端去进行图片压缩等处理
vant图片上传
基础用法:
export default {
data() {
return {
fileList: [
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg'}
],
};
},
methods: {
//文件上传完毕后会触发after-read回调函数,获取到对应的file对象
afterRead(file) {
// 此时可以自行将文件上传至服务器
console.log(file);
},
},
};
需求:需要将获取到的file文件转为formData形式(或者地址值)上传到服务器
问题:倘若将fileList中绑定的文件直接转为formData形式,那么图片预览显示错误
解决:
思路:声明两个数组,fileList和fileUpList,其中fileLIst是v-model绑定的预览图片显示列表,fileUpList去获取formData形式(或者地址)上传到服务器。
以常见的单独文件上传接口(获取到url)+url上传为例:
export default {
data() {
return {
fileList: [], //v-model绑定
fileUpList: [],
};
},
methods: {
//detail可以获取到当前文件的详细信息
//删除fileList中某一项的时候也去删除对应的fileUpList
//这儿是对获取到图片地址的后可能发生的删除操作
delFileUpList(file, detail) {
this.fileUpList.splice(detail.index, 1);
},
//文件上传完毕后会触发after-read回调函数,获取到对应的file对象
afterRead(file) {
file.status = 'uploading';
file.message = '上传中...'; //vant自带 显示当前图片上传的状态
const localUrl = "......."//服务器地址
var vm = this;
let formData = new FormData();
formData.append("file", file.file);
axios({
method: "post",
url: localUrl+"/api/file/upload",
data: formData,
headers: {
Authorization: getToken(),
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
file.status = "done"
this.articleList.push(res.data);
})
.catch((er) => {
file.status = 'failed';
file.message = '上传失败';
});
},
},
};
新增时判断是否长传完成
cheakupImg(){
//periodicalImage必填,可以通过状态字符来判断
var periodicalUpload = this.periodicalImage.some(item=>{
if(item.status !== 'done'){
return false
} else {
return true
}
})
//proveImage非必填
var proveUpload = !(this.proveImage.length) || this.proveImage.some(item=>{
if(item.status !='done'){
return false
} else {
return true
}
})
console.log(periodicalUpload, pageUpload, articleUpload, proveUpload);
return (periodicalUpload && pageUpload && articleUpload && proveUpload)
},
修改时判断是否上传完成
问题:修改时有数据回显自带的,还有新上传获取到的地址值,如何判断上传的文件是否完成
backCheakupImg(){
var periodicalUpload = this.periodicalImage.some(item=>{
if(item.status !== 'done' && item.url == ''){
return false
} else {
return true
}
})
var proveUpload = !(this.proveImage.length) || this.proveImage.some(item=>{
if(item.status !='done' && item.url == ''){
return false
} else {
return true
}
})
console.log(periodicalUpload, pageUpload, articleUpload, proveUpload);
return (periodicalUpload && pageUpload && articleUpload && proveUpload)
},
日历填入格式
//yyyy-MM-dd格式
formatDate(date) {
let fullYear = `${date.getFullYear()}`;
let fullMonth = `${date.getMonth() + 1}`;
let fullDay = `${date.getDate()}`;
if (fullMonth.length < 2) {
fullMonth = "0" + fullMonth;
}
if (fullDay.length < 2) {
fullDay = "0" + fullDay;
}
return fullYear + "-" + fullMonth + "-" + fullDay;
},
//yyyy年mm月dd日
getymd(dateStr) {
var date = new Date(Date.parse(dateStr.replace(/-/g, "/")));
var mm = date.getMonth() + 1;
//月
var dd = date.getDate();
//日
var yy = date.getFullYear();
//年
return yy + "年" + mm + "月" + dd + "日";
},
改变key键
var newArr1 = res.data.periodicalCategory.map((item) => ({
key: item.key, //key是要传给后台的值
name: item.value, //name是选择框中显示的值
}));
通过key获取value
//获取到含这个key的对象
var item1 = this.JournalCategoriesItem.filter(function (item) {
return item.key == res.data.periodicalCategory;
});
//获取key值对应的name值
this.academicData.periodicalCategory = item1[0].name;
viewerjs查看图片的方式
viewerjs() {
this.$nextTick(function () {
const ViewerDom = document.getElementById("head-img");
const viewer = new Viewer(ViewerDom, {
button: false,
toolbar: false,
navbar: false,
title: false,
tooltip: false,
hide: function () {
//在图片消失的时候销毁viewer
viewer.destroy();
},
});
viewer.show();
});
},
调用接口的基本模板
//方式一
this.$toast.loading({
duration: 8000,
message: "提交中",
forbidClick: true,
});
getInfoEcho().then((res) => {
console.log(res);
vm.$toast.clear();
vm.$toast.fail("XXXXXX");
}).catch(err => {
vm.$toast.clear();
vm.$toast.fail("XXXXXX");
});
//方式二
this.$toast.loading({
duration: 0,
message: '创建中...',
forbidClick: true,
});
var vm = this;
await api.CerateNewGroup(this.newGroupMsg).then(function(res) {
if (res.code == 200) {
vm.$toast.clear();
vm.$toast.success('xxxxxxx');
setTimeout(() => {
// 延迟跳转,清除提示,
vm.$toast.clear();
vm.$router.push({
path: 'xxxxxxxxxx'
});
//延迟时间:0.8秒
}, 1000)
} else if(res.code == 2002){
vm.$toast.clear();
vm.$toast.fail('xxxxxxxxx');
} else if(res.code == 2003){
vm.$toast.clear();
vm.$toast.fail('xxxxxxx');
}
});
需要注意的地方
1.引用本地静态资源的图片时,require("../../assets/img/xxx.png")
2.图片查看方式:除了自带的,还有viewerjs依赖
3.安装后没有用到的依赖,及时卸载
4.代码前期逻辑一定要规范正确,后前发现bug的时候方便排查修改
5.涉及到多模块修改对接的时候一定要在大群通知,并by姓名
6.页面布局尽量使用flex布局