第一次项目总结:(校园博览会)
第一次项目总结
第一次做项目,学习前端以来,一直都很激情饱满,这次做项目也是。但是前期还好,后期就有一点消极对待了,不过自己也及时调整过来了,这次项目不管是技术代码方面,还是团队合作方面都学到了很多。
项目流程
项目一般分为:
- 产品给出原型图(原型图里包括项目的页面、和html文件,这个文件给出了整个项目的逻辑);
- ui开始作图;
- 前端切图(静态+部分动态(如:轮播图、选项卡等)+需要从后台获取数据的代码(js));
- 前后台联调:对接口+测试+改bug(无聊和枯燥的过程,但是会有很大收获);
- 后期测试
- 结束
基本是这个流程了。
在这里说说在这次过程中犯的一些糊涂:- 前端代码大小写、缩进问题(比如:两个前端在板块相似的图是命名要统一,代码缩进以便自己或他人处理和修改);
- 后台接口大小写问题(相同的一个相应参数大小写不同或书写不一样:createdata和creatdata和createData);
- 一开始没有用github或是coding,导致前端和后端合代码困难,建议一定要用上合作工具。
- 每个组员都应该在自己的电脑上安装及配置一个服务器,如tomcat,一开始我们就是用一台电脑测,很慢效率很低;
- 一定要注重和思考效率问题。
(遇到的技术难题我下面讲)
代码书写风格细节注意
- html的每个板块都要写好注释。(这个是前端基础)
- css选择器命名规则,一般使用驼峰式书写,要语义化。建议选择器命名一律用小写。还有每个板块都应该添加注释。
1.建议:- 代码一律小写;
- 尽量用英文;
- 尽量不加中杠和下划线,如果需要统一使用中杠;
- 尽量不缩写,除非一看就明白的单词;
- 不写JS的地方不要加ID,一律用class;
- 坚持以字母开头命名选择器,这样可保证在所有浏览器下都能兼容。
2.js代码缩进:一般每一个层次缩进四个空格,各个函数实现的功能是什么一定要写好注释。
项目遇到的技术问题
- 头尾公用:需要团队写一份头尾公用的文件,单独放一份.css、.js文件,组员之间对以下选择器的命名即可;
- 分页:
- 对于网页的分页,我前前后后做了几个版本,最后在动态交互过程中都有很多大问题,不是页数获取不到,就是获取到后高亮显示错误,等等,我是试过用原生js来实现:
-
代码如下:
window.onload=function(){ var inow = 4; var json = { //请求返回的数据放这里 }; page({ id :'div1', nowNum :1, allNum :Math.ceil(json.title.length/5), callBack : function(now,all){ var num = now*5 < json.title.length ? 5 : json.title.length - (now-1)*5; var oul = document.getElementById('ull'); var ali = oul.getElementsByTagName('li'); if(oul.innerHTML == ''){ for( var i = 0; i < num; i++){ var oli = document.createElement('li'); oli.innerHTML =json.title[(now-1)*5+i]; oul.appendChild(oli); } } else{ for(var i = 0; i < num; i++){ ali[i].innerHTML=''; ali[i].innerHTML =json.title[(now-1)*5+i]; } } } }); } function page(opt){ var obj = document.getElementById(opt.id); var nowNum = opt.nowNum || 1; var allNum = opt.allNum || 5; var callBack = opt.callBack || function(){}; if( nowNum>=4 && allNum>=6 ){ var oA = document.createElement('a'); oA.href = '#1'; oA.innerHTML = '首页'; oA.style.backgroundColor = "#10B46D"; obj.appendChild(oA); } if(nowNum>=2){ var oA = document.createElement('a'); oA.href = '#' + (nowNum - 1); oA.innerHTML = '上一页'; oA.style.backgroundColor = "#10B46D"; obj.appendChild(oA); } if(allNum<=5){ for(var i=1;i<=allNum;i++){ var oA = document.createElement('a'); oA.href = '#' + i; if(nowNum == i){ oA.innerHTML = i; oA.style.backgroundColor = "#DB631E"; } else{ oA.innerHTML = i; oA.style.backgroundColor = "#10B46D"; } obj.appendChild(oA); } } else{ for(var i=1;i<=5;i++){ var oA = document.createElement('a'); if(nowNum == 1 || nowNum == 2){ oA.href = '#' + i; if(nowNum == i){ oA.innerHTML = i; oA.style.backgroundColor = "#DB631E"; } else{ oA.innerHTML = i ; oA.style.backgroundColor = "#10B46D"; } } else if( (allNum - nowNum) == 0 || (allNum - nowNum) == 1 ){ oA.href = '#' + (allNum - 5 + i); if((allNum - nowNum) == 0 && i==5){ oA.innerHTML = (allNum - 5 + i); oA.style.backgroundColor = "#DB631E"; } else if((allNum - nowNum) == 1 && i==4){ oA.innerHTML = (allNum - 5 + i); oA.style.backgroundColor = "#DB631E"; } else{ oA.innerHTML = (allNum - 5 + i); oA.style.backgroundColor = "#10B46D"; } } else{ oA.href = '#' + (nowNum - 3 + i); if(i==3){ oA.innerHTML = (nowNum - 3 + i); oA.style.backgroundColor = "#DB631E"; } else{ oA.innerHTML = (nowNum - 3 + i) ; oA.style.backgroundColor = "#10B46D"; } } obj.appendChild(oA); } } if( (allNum - nowNum) >= 1 ){ var oA = document.createElement('a'); oA.href = '#' + (nowNum + 1); oA.innerHTML = '下一页'; oA.style.backgroundColor = "#10B46D"; obj.appendChild(oA); } if( (allNum - nowNum) >= 3 && allNum>=6 ){ var oA = document.createElement('a'); oA.href = '#' + allNum; oA.innerHTML = '尾页'; oA.style.backgroundColor = "#10B46D"; obj.appendChild(oA); } callBack(nowNum,allNum); var aA = obj.getElementsByTagName('a'); for(var i=0;i<aA.length;i++){ aA[i].onclick = function(){ var nowNum = parseInt(this.getAttribute('href').substring(1)); obj.innerHTML =''; page({ id : opt.id, nowNum : nowNum, allNum : allNum, callBack : callBack }); return false; }; } }
-
- 如此冗长的代码就加大了浏览器解析的负担,也使得用户浏览网页的效率削减了许多,然后我又试着用实现选项卡的原理实现分页,这样还是行不通,尽管在静态页面上功能是实现了,但如果页面内容很大时,我们的网页就要白屏好几秒,就不说动态了,动态的时候,就连获取盒子索引都会产生很多问题。自己搜索了很多类似的文章,最后还是没能完整实现,只有求助学长了,也许你会说分页那么简单,但是在我没懂之前,他真的很难。
-
下面是一个我的组员分析之后实现分页的代码:
//分页按钮 var pageNum; var itemNum; var nowPage = 1; (function(){ //获取文章数目 $.ajax({ type : "请求方式", url : "请求接口", data : { //请求参数 }, success: function(data){ if(data.status === '0') { alert(data.message); } else{ itemNum = data.object; var pageNum = Math.ceil(itemNum/10); var pageBtnStr = ''; for(var i = 1; i <= pageNum; i++) { pageBtnStr += '<li class="page_li">'+ i +'</li>'; } $('.page_ul').append(pageBtnStr); } } }); // itemNum =100; // pageNum = Math.ceil(itemNum/10); // var pageBtnStr = ''; // for(var i = 1; i <= pageNum; i++) { // pageBtnStr += '<li class="page_li">'+ i +'</li>'; // } // $('.page_ul').append(pageBtnStr); })(); //默认页 (function(){ $.ajax({ type : "请求方式", url : "请求接口", data : { //请求参数 }, success : function(data){ console.log(data); var postContentStr = ''; var nowPage; var pageData; if (data.status === '0') { alert(data.message); } else{ pageData = data.object; for(var j=0;j<pageData.length;j++){ //拼接字符串部分 } $('.mypost_content').html(postContentStr); } } }); })(); //分页按钮点击 var index; var pageData; // pageData = [ // { // title : '11111', // userId : '222', // comment : '333', // publishDate : '444' // }, // { // title : '11111', // userId : '222', // comment : '333', // publishDate : '444' // } // ]; (function(){ $('.page_li').click(function(){ $(this).addClass('pageColor').siblings().removeClass('pageColor'); nowPage = $(this).text(); index = $(this).index(); $.ajax({ type : "请求方式", url : "请求接口", data : { //请求参数 }, success : function(data){ var postContentStr = ''; var nowPage; var pageData; if (data.status === '0') { alert(data.message); } else{ pageData = data.object; for(var j=0;j<pageData.length;j++){ //拼接字符串部分 } $('.mypost_content').html(postContentStr); } } }); // console.log(pageData[1].title); // var postContentStr = ''; // for(var j=0;j<pageData.length;j++){ // // console.log(pageData[j].title,j); // //拼接字符串部分 // } // $('.mypost_content').html(postContentStr); //按钮点击变换 if (nowPage == 1) { $('#page_prev').hide(); $('#page_next').show(); } else if(nowPage>1 && nowPage<pageNum){ $('#page_prev').show(); $('#page_next').show(); } else if (nowPage == pageNum) { $('#page_next').hide(); $('#page_prev').show(); } }).eq(0).click(); //上一页 $("#page_prev").click(function(){ index = index-1; //按钮点击变换 $('.page_li').eq(index).addClass('pageColor').siblings().removeClass('pageColor'); //实现高亮显示当前所选中的页 if (index == 0) { $('#page_prev').hide(); } else if(index>0 && index<(pageNum-1)){ $('#page_prev').show(); $('#page_next').show(); } else if (index == (pageNum-1)) { $('#page_next').hide(); } if(nowPage != 1){ nowPage = nowPage -1; //获取当前数据 $.ajax({ type : "请求方式", url : "请求接口", data : { //请求参数 }, success : function(data){ if(data.status === '0'){ alert(data.message); } else { pageData = data.object; for(var j=0;j<pageData.length;j++){ //拼接字符串部分 } $('.mypost_content').html(postContentStr); } } }); // var postContentStr = ''; // for(var j=0;j<pageData.length;j++){ // // console.log(pageData[j].title,j); // //拼接字符串部分 // $('.mypost_content').html(postContentStr); } }) //下一页 $('#page_next').click(function(){ index = index+1; //按钮点击变换 $('.page_li').eq(index) .addClass('pageColor') .siblings() .removeClass('pageColor'); if (index == 0) { $('#page_prev').hide(); } else if(index>0 && index<(pageNum - 1)){ $('#page_prev').show(); $('#page_next').show(); } else if (index == (pageNum - 1)) { $('#page_next').hide(); } if(nowPage != pageNum){ nowPage = nowPage + 1; //获取当前数据 $.ajax({ type : "请求方式", url : "请求接口", data : { //请求参数 }, success : function(data){ if (data.status === '0') { alert(data.message); } else{ pageData = data.object; for(var j=0;j<pageData.length;j++){ //拼接字符串部分 } $('.mypost_content').html(postContentStr); } } }); // var postContentStr = ''; // for(var j=0;j<pageData.length;j++){ // // console.log(pageData[j].title,j); // //拼接字符串部分 // } // $('.mypost_content').html(postContentStr); } }) })(); //注释的部分为自测内容
-
下面是分页高亮的css样式
/*分页*/ .page{ 100%; height: 35px; display: block; margin-bottom: 60px; margin-top: 30px; text-align: center; position: relative; } .page_ul{ overflow: hidden; display: inline-block; } .page_li,.page_change{ margin: 0; padding: 0; 33px; height: 33px; float: left; list-style: none; margin-right: 16px; border: 1px solid #e7e7e7; font-size: 16px; font-weight: bold; color: #666; line-height: 33px; text-align: center; cursor: pointer; } .page .page_last{ margin-right: 0; } .page .page_change{ border: 1px solid #e7e7e7; font-size: 16px; font-weight: bold; color: #666; line-height: 33px; text-align: center; 73px; height: 33px; } .page .page_next{ position: absolute; right: 140px; } .page .page_prev{ position: absolute; left: 140px; }
-
- 他的原理由三个事件组成,“上一页”、“下一页”、“每一页”,三个按钮分别绑定三个事件,分别进行不同的请求,这样即实现了动态获取数据,也不会造成前端页面多余的分页代码冗长的问题,而且很好理解。
- 对于网页的分页,我前前后后做了几个版本,最后在动态交互过程中都有很多大问题,不是页数获取不到,就是获取到后高亮显示错误,等等,我是试过用原生js来实现:
- 使用ajax交互,实现异步请求:
此次项目中我们用的都是jquery中的$.ajax()方法进行请求数据,在没有开启服务器之前,这段代码运行时会报错,所以需要自己模拟后台数据进行测试代码请求的正确与否。
下面简单谈谈什么是异步请求:
- 在计算机领域,同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待去,直到收到返回信息才继续执行下去;异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
- 我们平时经常讨论的同步问题多发生在多线程环境中的数据共享问题。即当多个线程需要访问同一个资源时,它们需要以某种顺序来确保该资源在某一特定时刻只能被一个线程所访问,如果使用异步,程序的运行结果将不可预料。因此,在这种情况下,就必须对数据进行同步,即限制只能有一个进程访问资源,其他线程必须等待。
下面是一段请求的代码:
$.ajax({
type: 'GET',//请求方式
url: '/GetUserInformation.do',//请求接口
data: {//请求参数
userId: $.cookie(COOKIE_NAME_userId)
},
success: function(data){//请求成功处理函数
var userData;
var str = '';
if(data.status === '0') {
alert(data.message);
} else {
userData = data.object;
str1 = '<h2>' + userData.username + '<a href="updatedata.html?userId='+userData.userid+'">(修改资料)</a></h2>' +
'<p id="email">' + userData.email + '</p>' +
'<p id="schoolName">"'+ data.schoolname + '</p>' +
'<p id="studentNumber">' + userData.studentnumber + '</p>';
var $str2=$("<img src='"+ userData.picture+ "'>");
//alert(userData.picture);
}
$('.mancenter_msg_box1').append(str1);
$('.mancenter_msg_box').append($str2);
}
error: function{
//请求失败处理函数
}
});
下面是自测代码:
var data = {
"emptyIdentifier": 1,
"map": {
"User": {
"email": "测试内容8fgq",
"password": "测试内容g8sw",
"picture": "测试内容6h8a",
"schoolName": 1,
"studentNumber": 73233,
"userId": 1,
"userName": 1
},
"message": "测试内容c1ek",
"status": 82861
}
}
var userData = data.map.User;
var str1 = '<h2>' + userData.username +</h2>' +
'<p id="email">' + userData.email + '</p>' +
'<p id="schoolName">"'+ data.schoolname + '</p>'
+'<p id="studentNumber">' +userData.studentnumber + '</p>';
var $str2=$("<img src='"+ userData.picture+ "'>");//字符串拼接部分
$('.mancenter_msg_box1').append(str1);//添加到html中的div盒子中
$('.mancenter_msg_box').append($str2);
/*$.ajax({
type: 'GET',
url: '/GetUserInformation.do',
data: {
userId: $.cookie(COOKIE_NAME_userId)
},
success: function(data){
var userData;
var str = '';
if(data.status === '0') {
alert(data.message);
} else {
userData = data.object;
str1 = '<h2>' + userData.username + '<a href="updatedata.html?userId="'+ userData.userid +'>(修改资料)</a></h2>' +
'<p id="email">' + userData.email + '</p>' +
'<p id="schoolName">"'+ data.schoolname + '</p>' +
'<p id="studentNumber">' + userData.studentnumber + '</p>';
var $str2=$("<img src='"+ userData.picture+ "'>");
}
$('.mancenter_msg_box1').append(str1);
$('.mancenter_msg_box').append($str2);
}
});
*/
-
用cookie对用户数据进行缓存:这部分不由我负责,具体内容可以查看我的博客:http://www.cnblogs.com/yehui-mmd/p/6106812.html
下面简单介绍一下我们对于写的引用cookie的代码:var COOKIE_NAME_userName = 'userName'; var COOKIE_NAME_password = 'password'; var COOKIE_NAME_userId = 'userId'; var COOKIE_NAME_schoolId = 'schoolId'; //判断是否缓存,如果缓存过直接调用 if($.cookie(COOKIE_NAME_userName)){ username = $.cookie(COOKIE_NAME_userName); password = $.cookie(COOKIE_NAME_password); userid = $.cookie(COOKIE_NAME_userId); schooid = $.cookie(COOKIE_NAME_schoolId); }else{ alert('登陆缓存已被清空'); }
-
查询字符串(通过查询字符串进行前端的页面交互):一般出现在a标签中,每个页面都离不开查询字符串。
如:
上一个页面中拼接字符串时a标签跳转到下一个页面; -
重绘和重排:
这是项目中的部分截图,每个板块鼠标一上去就会出现高亮并且边框消失,但是他就会出现抖动,这就造成了重排的问题,最后我的解决方案是一上去把边框变成透明色。下面我们来看看什么是重绘和重排:
- 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排。各家浏览器引擎的工作原理略有差别,但也有一定规则。简单讲,通常在文档初次加载时,浏览器引擎会解析HTML文档来构建DOM树,之后根据DOM元素的几何属性构建一棵用于渲染的树。渲染树的每个节点都有大小和边距等属性,类似于盒子模型(由于隐藏元素不需要显示,渲染树中并不包含DOM树中隐藏的元素)。当渲染树构建完成后,浏览器就可以将元素放置到正确的位置了,再根据渲染树节点的样式属性绘制出页面。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。
- 重绘是一个元素外观的改变所触发的浏览器行为,例如改变visibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排。
- 重排是更明显的一种改变,可以理解为渲染树需要重新计算。下面是常见的触发重排的操作:
1. DOM元素的几何属性变化
2. DOM树的结构变化
3. 获取某些属性 - 开发中,比较好的实践是尽量减少重排次数和缩小重排的影响范围。例如:
- 将多次改变样式属性的操作合并成一次操作。
- 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。
- 在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。
- 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。
- 在需要经常获取那些引起浏览器重排的属性值时,要缓存到变量。
前后台交互接口文档和自测
- 对于前面说到的问题,很多处于接口文档,希望也建议以后由前、后台一起写接口文档。
- 自测时因为不熟悉google调试工具,导致效率降低。
- 一般在Console(调试台)、Network(网络)这两处查看错误
- 学会用console.log("");和alert();检查报错。
- Network中的报错:
- 400错误是由于不正确的请求造成的,说明正在搜索的网页可能已经删除、更名或暂时不可用。
- 错误代码:404.1
404.1错误表明所访问 Web 站点的 IP 地址不接受对端口(请求的来源埠)的请求。一般来说,404.1 错误只会出现在具有多个 IP 地址的计算机上。如果在特定 IP 地址/端口组合上收到客户请求,而且在特定的端口上 IP 地址并没有设置为侦听,则 IIS 将返回 404.1 HTTP 错误。例如,如果一台计算机有两个 IP 地址,而只将其中一个 IP 地址配置为在端口 80 上侦听,则其他 IP 地址从端口 80 收到的任何请求都将导致 IIS 返回 404.1 错误。只应在服务级设置这一错误,因为只有当服务器上使用多个 IP 地址时它才返回给客户。 - 错误代码:404b
404b错误是由于无法找到档而造成的,通常是由于正在搜索的网页可能已经删除、更名或暂时不可用。 - 错误代码:500
500错误是由于内部服务器错误造成的。 - 错误代码:500.11
500.11错误是由于服务器关闭而造成的资源无法访问,Web 站点关闭期间无法处理请求。 - 错误代码:500.12
500.12错误是由于应用程序重新启动而造成的资源暂时无法访问,Web站点重新启动期间无法处理请求。 - 错误代码:500.13
500.13错误是由于服务器太忙而造成的,此时无法处理请求。通讯量超出 Web 站点的能力。 - 错误代码:500.14
500.14错误是由于应用程序无效而造成的,部分 Web 站点不可用。Web 站点应用程序配置存在问题,无法处理请求。 - 错误代码:500.15
500.15错误是由于请求了不不允许请求的 global.asa而造成的,你可以编辑”地址”栏中的网址,删除 global.asa,然后按 Enter。来解决这个问题。
以上是我从搜取的部分资料在这次项目中,我们主要遇到了500、400、404错误。
团队合作
- 遇到问题先自己冷静的思考一下,看看自己能不能解决,如果实在不行再找第三方求助。(循环问题)第一,在这个学习过程中,开始的时候是干劲十足但是到后面就变得有点散漫,不像其他同学那样总是积极的学习
- 在多人合作开发过程中,一定要分工明确,不要不清不楚,不专一的话,很容易导致整体方向变混乱。
- 在分工明确的情况下,要抓住主次进行工作,不需要做的地方,尽管它绊住了你,记住它,然后想办法绕过去,等回头有时间了在慢慢解决。
- 在开发过程中遇到bug,不要着急百度(google),
- 最忌讳的是凡事没有计划,所以在做项目前,进度计划表一定要确定下来,一步步跟着计划走,这样效率会提高很多。
- 前后台接口前期准备很重要
- 组员应该互相理解、信任
- 不应在团队中传播负能量
- 遇到问题是一起解决,不应推卸责任
- 不要把自己的情绪带进团队
- 团队的每个成员都是必不可少的
- 学会自己排遣压力