Contents
  1. 1. 技术栈
  2. 2. 前言
    1. 2.1. 教务信息
      1. 2.1.1. 先说说后端
      2. 2.1.2. 前端方面
    2. 2.2. 登录
      1. 2.2.1. 后端
      2. 2.2.2. 前端
    3. 2.3. 成绩查看
      1. 2.3.1. 后端
      2. 2.3.2. 前端
    4. 2.4. 课表查看and已选课程
      1. 2.4.1. 后端&前端
    5. 2.5. 反馈意见
      1. 2.5.1. 后端
      2. 2.5.2. 前端
    6. 2.6. 同年同月同日生
      1. 2.6.1. 后端
      2. 2.6.2. 前端
    7. 2.7. SEO
    8. 2.8. 一些其他的功能改进
      1. 2.8.1. 访问统计
      2. 2.8.2. 记住密码
      3. 2.8.3. 退出登录
      4. 2.8.4. loading
    9. 2.9. 增加功能
      1. 2.9.1. 数据库
    10. 2.10. CMS
    11. 2.11. ubuntu

废话不多说,直接上网站github

技术栈

前端:

  • materializecss
  • 因为用了上面那个UI库就要引入jquery
  • amd模块化
  • Sass
  • q.js一个超级轻量的路由框架
  • gulp
    • gulp-sass
    • gulp-concat(聚合多个js)
    • gulp-uglify(压缩js)

后端:
总体是node用上express加上一些中间件。

  • express
    • body-parser
    • cookie-parser
  • superagent 轻量的爬虫框架
  • superagent-charset由于学校教务处是比较老的网页还用的是GBK编码
  • cheerio可以让后端像前端用jquery那样。
  • nodemailer发送邮件的模块

前言

本来安安心心写前端,突然发现没有什么拿的出手的项目,又因为之前不能理解后端怎么跟前端配合登录或者其他的工作,于是萌生了写一个适用于手机端的教务处。
因为前期也没有想过做多少功能,前端就没有想模块化。不过是单页面应用。
后端架构一直没怎么变过

  • app.js文件连接所有
  • router文件保存路由文件
  • 不同的功能存在不同的文件夹中

教务信息

最开始写的是爬取教务处首页的教务信息,因为我老是错过信息,曾近因为错过信息没去考试……

先说说后端

思路很简单,express做服务器,superagent去做爬虫,开始的时候很容易,但是我想了一下不能一有用户就爬一次教务处,而且教务信息更新的速度很慢,于是我就想定时去访问教务处,然后把爬到的标题存下来。
然后因为页面是单页面应用,信息都是通过ajax得到的,只要有用户访问首页通过已经存下来的的标题来渲染首页。

前端方面

前端没有好说的用UI库真的很爽,一会就把首页写出来了,架构我就不说了,因为在后面有一次大改。
首页

登录

做个网站不能仅仅满足看看教务信息,于是就开始想开发教务处的别的功能,别的功能首先做的就是登录,在登录这点上要感谢星龙同学,之前一直没想通,在他的教导之下终于理解了这个机制并且顺利写出来。

后端

登录是一个模块,因为登录是用cookie去验证的,同时我是单页面应用不能跳转,所以我的思路是,登录的时候向后端发送用户名密码,然后拿到用户名密码去教务处验证争取,拿到教务处的cookie发送到前端,前端设置cookie。

前端

登录界面 登录
(忽视30天记住我,仅仅加了一下cookie的过期时间)
到现在前端也没有什么什么技术含量。我是这样写的,登录界面用一个div包括隐藏,当点击到login路由,这个div显示。
登录脑图
登录成功跳转到首页(后期这里也变化了)。

成绩查看

后端

客户端ajax这个地址后端拿到cookie去访问教务网的成绩查看的页面,然后用cherrio模块去处理,把他处理成对应的json,前端拿到json,渲染页面没有什么好说的。这里说最重要的,就是后端的模块化,相互之间没有耦合,好管理。

前端

前端在这里经历了大改。而且让我理解了为什么在单页面应用中为什么需要模板引擎。
下面是成绩查看的页面
成绩查看
有没有发现他跟登录的界面差不多,而且我之前的做法是这些页面本来都是写好HTML了,第一次加载就全部加在好了,通过路由控制他们的显示隐藏。我想了想,他们是共通的,同时如果每次加一个功能,就要在html中加一段代码,很不好控制,同时随着功能的增加,页面多起来第一次加载的时间会很长,于是我就思考一种解决方式,于是我就写了下面这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//common_page.js
var common_page=function(obj){
this._main=$('<div>',{
'class':'all',
'id':obj.id
});
var main_nav='<nav >'+
'<div class="container">'+
'<a href="#!home">'+
'<img src="./public/img/arrow.png" class="return" >'+
'</a>'+
'</div>'+
'</nav>'
this._main.html(main_nav);
$('<div>',{
'class':'container'
}).html(obj.content)
.appendTo(this._main);
this._main.appendTo('body');
};

每次传一个对象包括这个页面的id和内部的内容就可以这么用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var getScore_page=new common_page({
id:'score-container',
content:'<h2 class="header">'+
'本学期成绩'+
'</h2>'+
'<table class="striped ">'+
'<thead>'+
'<tr>'+
'<th >课程名称</th>'+
'<th >学分</th>'+
'<th >成绩</th>'+
'</tr>'+
'</thead>'+
'<tbody></tbody>'+
'</table>'
})

然后原本控制页面的显示隐藏用的是给每个页面加一个类all然后控制路由的时候,先去隐藏所有页面然后在显示需要显示的页面,后来想想这样太损失性能,就又引入了一个对象

1
2
3
4
5
6
7
8
9
10
11
12
var common_page_now_show={
ele:null
};
common_page_now_show.push=function(now_show){
this.ele=now_show;
};
common_page_now_show.show=function(){
this.ele?this.ele.show():null;
};
common_page_now_show.hide=function(){
this.ele?this.ele.hide():null;
}

然后给common_page加了这个方法:

1
2
3
4
5
common_page.prototype.show=function(){
common_page_now_show.hide();
common_page_now_show.push(this._main);
common_page_now_show.show();
};

这样每次只要隐藏一个不会太损失性能。
然后随之问题又来了,我想让前端向后端一样模块化,第一个想到的就是AMD,用requeir,但是我用了materializecss,他给jquery加了方法,我查了各种资料,去issue上看,去stackoverflow都没有解决,无奈最后我每个文件都写成amd的模式,然后最后合并压缩一下。不过后来真的体会这种方法的好处,各个功能不存在耦合。每次怎么增加新功能,在main中加上路由,然后写功能,最多引入一个全局变量,不过在压缩的时候自己解决了。
目录结构是这样的:
目录
PS.tool中是对cookie操作的相关函数
main.js是一些路由和一些init:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(function(){
home.router_home();
login.router_login();
detail.router_u();
getScore.router_score();
getCourseTable.router_courseTable();
getGymCount.router_gymCount();
getCourse.router_course();
feedback.router_feedback();
getHowMuchBirSame.router_sameBir();
Q.init({
key:'!',/* url里#和url名之间的分割符号 默认为感叹号 */
index:'home',/* 首页地址 如果访问到不能访问页面也会跳回此页 */
pop:function(L){/* 每次有url变更时都会触发pop回调 */
console.log('pop 当前参数是:'+L);
}
});
$(".button-collapse").sideNav();
$(document).ready(function() {
$('select').material_select();
$('.collapsible').collapsible({
accordion : false // A setting that changes the collapsible behavior to expandable instead of the default accordion style
});
});
})();

其中一个功能的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var getScore=(
function(){
var getScore_page=new common_page({
id:'score-container',
content:'<h2 class="header">'+
'本学期成绩'+
'</h2>'+
'<table class="striped ">'+
'<thead>'+
'<tr>'+
'<th >课程名称</th>'+
'<th >学分</th>'+
'<th >成绩</th>'+
'</tr>'+
'</thead>'+
'<tbody></tbody>'+
'</table>'
})
function router_score(){
Q.reg('score',function(){
ahelp.noLoginToLogin();
getScore_page.show();
$('.button-collapse').sideNav('hide');
tool.loadingShow();
getScoreAndSetTr();
});
}
function getScoreAndSetTr(){
$.post('/getScore',function(data){
var courseArray=JSON.parse(data).scoreLists;//array
//console.log(data);
//console.log(typeof data);
var str='';
for(var i in courseArray){
str+='<tr><td>'+courseArray[i].name+'</td><td>'
+courseArray[i].credit+'</td><td>'
+courseArray[i].score+'</td></tr>';
}
//console.log('1'+str)
$('#score-container table tbody').html(str);
tool.loadingHide();
})
}
return{
router_score:router_score
}
})();

我发现很像后端的express的模式。

课表查看and已选课程

后端&前端

还是一样拿着客户端返回的cookie去访问相应的网页,然后返回相应的html注意这次是html而不是json,因为教务处的html结构很坑,处理起来很费时费力,虽然返回html不利于复用但是,方便前端只要拿着加一些类来控制样式就好。

反馈意见

后端

用了那个nodemailer模块,从前端收到消息然后发送到我的邮箱,不过到现在都没有人给我发邮件==不知道是好还是坏。

前端

一个input一个textare,没有什么说的,不过我想应该做个限制,不然我的邮箱可能会炸。

同年同月同日生

功能是显示全校有多少人跟你同年同月同日生,然后你可以选择共享你的信息,这样如果有跟你同年同月同日生的人访问这个页面就会看到你的一些信息。
同年同月生
(PS.不过后来我发现这个网页可以xss攻击,可能还可以注入==)

后端

这个后端用了数据库,研究一天的monogoDb,你可能会好奇我的数据是哪里来的,首先图书馆系统的密码是学号,但是第一次登录需要提供姓名,这个姓名我是从体育部爬下来的他也是默认学号作为密码,有些人改了,但是还是很少人的,然后就能得到大部分人的身份证,然后截取中间的部分就是出生年月,不过有的人身份证上不对我也就不管了。

前端

前端部分就没有什么好说的,很简单,加了一个分享功能。

SEO

众所周知,单页面应用的SEO很难做,上网发现一篇文章说的就是这个.
我开始以为,百度或者谷歌不会执行我首页的js,后来发现执行了,然后用了Prerender.io我也不知道有没有用,然后加了几个友情链接,我发现我的SEO上去了,并且现在有每天有几十个通过百度搜索进的我的网页。
然后我通过这篇文章理解了某些东西,大部分解决方案是服务器分辨是否为搜索引擎的爬虫然后单独渲染一个页面,但是如果与原本内容不符是要受到惩罚的。

一些其他的功能改进

访问统计

我用的是百度统计,现在的成果很让人欣慰,这是30天的访问量:
访问量

记住密码

后来一个小学妹跟我说要我做一个记住登录状态的选项,然后这里就体现出模块化的好处很好改,在tool中的设置cookie选项中加一个过期时间,然后login文件加一个30天记住我就好。

退出登录

用tool中的清除所有cookie就好。

loading

后来我发现我需要加一个loading动画,然后我发现UI库里有现成的。然后就加了。


增加功能

  • 数据库(monogoDb)
  • CMS
  • 远程主机换成ubuntu
    炫耀一下访问量已经有5000了

为什么我要突然加数据库的功能呢,因为学校的教务网最近必须在内网访问,很是不方便,于是我把一些必要的信息保存下来.

数据库

monogoDb好多坑啊,不过最后终于在我的服务器上架起来了数据库.在用monogoose连接数据库的时候出现问题,在实际的运用中,分成一个单独模块,在最开始的时候连接一次就好,而我最开始的时候,每连接一次都关闭,导致了很多问题.

CMS

现在的首页我可以自己发布信息了,然后有了数据库就很简单了.

ubuntu

ubuntu真的是一个好东西.

Contents
  1. 1. 技术栈
  2. 2. 前言
    1. 2.1. 教务信息
      1. 2.1.1. 先说说后端
      2. 2.1.2. 前端方面
    2. 2.2. 登录
      1. 2.2.1. 后端
      2. 2.2.2. 前端
    3. 2.3. 成绩查看
      1. 2.3.1. 后端
      2. 2.3.2. 前端
    4. 2.4. 课表查看and已选课程
      1. 2.4.1. 后端&前端
    5. 2.5. 反馈意见
      1. 2.5.1. 后端
      2. 2.5.2. 前端
    6. 2.6. 同年同月同日生
      1. 2.6.1. 后端
      2. 2.6.2. 前端
    7. 2.7. SEO
    8. 2.8. 一些其他的功能改进
      1. 2.8.1. 访问统计
      2. 2.8.2. 记住密码
      3. 2.8.3. 退出登录
      4. 2.8.4. loading
    9. 2.9. 增加功能
      1. 2.9.1. 数据库
    10. 2.10. CMS
    11. 2.11. ubuntu