開發(fā)流程
因為自己做的是H5前端,所以對于一個項目的基本流程會偏向于前端認識。
項目開始的時候,會有項目負責(zé)人確定本次項目開發(fā)所需要的技術(shù)基礎(chǔ)和技術(shù)框架。這是一個團隊協(xié)作的項目,會用到代碼托管gitlab或其他,利用rap2完成API文檔托管。
1.開發(fā)共識和代碼規(guī)范
作為初學(xué)者,最基本的就是遵守代碼規(guī)范。我們小團隊之間做的項目就有一些共同之處,這時就少不了代碼的封裝或者套用了,在去試著理解一些他人寫的邏輯的時候,當(dāng)我門的代碼風(fēng)格不統(tǒng)一或者不遵從規(guī)范來的話,首先看下來就十分的別扭,其次小團隊間理解就有了一定的難度,不免團隊開發(fā)效率會大大降低。當(dāng)我開發(fā)時遇到問題去咨詢別人的時候一個好的代碼規(guī)范也會使我們的問題更加直接簡明的暴露出來。后期更改需求的時候代碼的可讀性,良好的維護性就顯得尤為重要,所有我認為作為初學(xué)者一定要關(guān)注代碼規(guī)范。
2.框架、組件、工具學(xué)習(xí)
項目后會確定項目中所需要的框架或者組件,這里我們就用到了gitlub代碼托管工具,vant組件,VUE知識等。及時學(xué)習(xí)\復(fù)習(xí)是個人可以承諾這個項目的基礎(chǔ),在自己不理解的地方及時去詢問有項目經(jīng)驗的大佬們,可以學(xué)會到好多東西,一些需要重點掌握的知識或者一些容易忽視的細節(jié)可以更早的發(fā)現(xiàn)學(xué)習(xí),避免目前的小問題造成了項目中的大問題。
3.需求分析、設(shè)計稿
然后開始進行需求分析了,一般會開會兩次以上,也就是我們需要明白自己做的是一個什么樣的項目,具體有哪些功能,哪些樣式和細節(jié),最好的狀態(tài)是我們可以在心中構(gòu)想出一個完整的頁面流程,這一部分有什么細節(jié),需要完成什么功能。當(dāng)我們碰到有走不通的地方(不太明白或者應(yīng)有的功能沒有體現(xiàn))的時候,一定要在團隊需求會議上及時的提出,并且越早越好,第一次就發(fā)現(xiàn)問題及時解決對整個項目進度都是超大的提升。
設(shè)計稿需要結(jié)合需求文檔一起看,因為在前端開發(fā)的時候,我們必須一比一精確還原,設(shè)計稿就是對頁面展示樣式和內(nèi)容的一個定型。后面開發(fā)的時候,必須嚴格遵循設(shè)計稿,做到px級別的還原。
3.排期
合理的排期保證了我每天的應(yīng)有的任務(wù)進度,可以使負責(zé)人把控項目的整體進度。每一個項目參與人都需要參與到排期中,做到承諾即交付,我們排期的過程就是我們承諾的過程。怎么把許下這個承諾,并保證可以完成,需要我們對自己任務(wù)和自身技術(shù)的充分評估,一旦自己的最終任務(wù)完成時間大大的長于項目整體的進度,一定要及時的向小項目負責(zé)人提出來,適量的減少任務(wù)分配,切不可打腫臉充胖子,延期才完成自己的排期。當(dāng)然最后自己完成排期之后,需要向負責(zé)人過排期,充分發(fā)揮排期間的時間安排。
我在排期的過期中就沒有充分考慮到接口的調(diào)用實現(xiàn),以至于排期最后幾天都是特別緊的.....雖然最后熬夜也完成了,這次也告訴我一定要充分考慮自己與后臺對接的接口實現(xiàn),還有盡量前期就盡快完成頁面的樣式,后期有更多的時間去處理細節(jié)和代碼邏輯問題。
4.代碼開發(fā)
之后就是自己負責(zé)的前端開發(fā)了,這個時候有幾點很重要。
1.合理分配。我們同一模塊可能有樣式或者功能相同/相似的地方,為了項目整體同一和開發(fā)效率,就可以分配給一個人負責(zé)這一部分,便于大家共同使用,減少開發(fā)冗余。
2.溝通交流。每個人的完成情況,修改的地方,需要統(tǒng)一的地方都需要及時在群里通知。保證每個人都可以被通知到,這是對項目整體和他人的負責(zé)。
3.接口測試。一般后期后端的接口也寫好了,這個時候我們需要找寫自己模塊的后端人員進行對接。對接不是簡單的有這個字段可以傳就可以了,還需要搞清傳遞的形式,是否必填,傳遞的值需要數(shù)組類型還是int類型還是其他。當(dāng)后端接口沒有完成的時候,前端可以模擬一些假數(shù)據(jù)進行測試, 一個理想下項目是獲取到接口的時候,將這些數(shù)據(jù)直接替換一些就可以了。
4.代碼托管。一般是兩天一期通過gitlab去同步自己的代碼,不拖拉,及時的去同步自己的代碼,并及時拉取遠程最新代碼。
項目同步
負責(zé)人需要把控每一個模塊的進度,就必須定期開項目同步會議,這個時候可能每個模塊的負責(zé)人去參加,不用所有人員參加,但是項目同步會議是不可缺少的,
項目自測
開發(fā)完成之后,首先自己去測試H5前端模塊可以跑的通,在去統(tǒng)一測試。
統(tǒng)一測試
這個時候可能出現(xiàn)好多問題,一些自測時候沒有發(fā)現(xiàn)的問題也漸漸暴露出來。比如:由于訪問量/網(wǎng)絡(luò)等外部條件造成的網(wǎng)絡(luò)卡頓問題怎么解決。或者按鈕被狂點的時候出現(xiàn)多次請求,不同手機間的兼容問題,或者用戶不會接下來的操作是否可以添加提示等等, 這些都是有可能自測的時候沒有被發(fā)現(xiàn)的。
修改bug,測試
重復(fù)循環(huán),直到最后項目沒有(不能發(fā)現(xiàn))問題。
本次開發(fā)中沒有及時注意到的細節(jié):
1.個別沒有嚴格按照設(shè)計稿,精確到px級別
2.路由跳轉(zhuǎn),push和replace應(yīng)用仍有部分存在問題
3.用戶填寫需求文檔上要求的數(shù)據(jù)格式的時候,個別沒有添加正則
4.后端int類型最大儲存位數(shù)為11位,和一些特殊的儲存長度,還有是否可以保存小數(shù)點沒有及時和后端溝通,
5.按鈕點擊事件沒有及時限制,導(dǎo)致可以狂點
6.有些實現(xiàn)方法,已形成固定的模板,沒有及時咨詢負責(zé)人是否可以
項目中遇到的技術(shù)問題
未解決的問題:
1.圖片壓縮
處理辦法:交給后端去進行圖片壓縮等處理
vant圖片上傳
基礎(chǔ)用法:
<van-uploader v-model="fileList" :after-read="afterRead" @delete="delFileUpList" />
export default {
data() {
return {
fileList: [
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg'}
],
};
},
methods: {
//文件上傳完畢后會觸發(fā)after-read回調(diào)函數(shù),獲取到對應(yīng)的file對象
afterRead(file) {
// 此時可以自行將文件上傳至服務(wù)器
console.log(file);
},
},
};
需求:需要將獲取到的file文件轉(zhuǎn)為formData形式(或者地址值)上傳到服務(wù)器
問題:倘若將fileList中綁定的文件直接轉(zhuǎn)為formData形式,那么圖片預(yù)覽顯示錯誤
解決:
? 思路:聲明兩個數(shù)組,fileList和fileUpList,其中fileLIst是v-model綁定的預(yù)覽圖片顯示列表,fileUpList去獲取formData形式(或者地址)上傳到服務(wù)器。
以常見的單獨文件上傳接口(獲取到url)+url上傳為例:
export default {
data() {
return {
fileList: [], //v-model綁定
fileUpList: [],
};
},
methods: {
//detail可以獲取到當(dāng)前文件的詳細信息
//刪除fileList中某一項的時候也去刪除對應(yīng)的fileUpList
//這兒是對獲取到圖片地址的后可能發(fā)生的刪除操作
delFileUpList(file, detail) {
this.fileUpList.splice(detail.index, 1);
},
//文件上傳完畢后會觸發(fā)after-read回調(diào)函數(shù),獲取到對應(yīng)的file對象
afterRead(file) {
file.status = 'uploading';
file.message = '上傳中...'; //vant自帶 顯示當(dāng)前圖片上傳的狀態(tài)
const localUrl = "......."http://服務(wù)器地址
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必填,可以通過狀態(tài)字符來判斷
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)
},
修改時判斷是否上傳完成
問題:修改時有數(shù)據(jù)回顯自帶的,還有新上傳獲取到的地址值,如何判斷上傳的文件是否完成
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值對應(yīng)的name值
this.academicData.periodicalCategory = item1[0].name;
viewerjs查看圖片的方式
<img id="head-img" @click="viewerjs()" class="round-icon" :src="avatarName" />
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();
});
},
調(diào)用接口的基本模板
//方式一
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: '創(chuàng)建中...',
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(() => {
// 延遲跳轉(zhuǎn),清除提示,
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.引用本地靜態(tài)資源的圖片時,require("../../assets/img/xxx.png")
2.圖片查看方式:除了自帶的,還有viewerjs依賴
3.安裝后沒有用到的依賴,及時卸載
4.代碼前期邏輯一定要規(guī)范正確,后前發(fā)現(xiàn)bug的時候方便排查修改
5.涉及到多模塊修改對接的時候一定要在大群通知,并by姓名
6.頁面布局盡量使用flex布局