Photo Gallery中出现的常见需求解决方案

Ahbr1x.png

使用egg.js开发PhotoGallery管理后台过程中,遇到了一些典型需求,将处理过程整理如下,方面后面开发类似应用

用户管理

数据库

新建User表,填充表结构,至少包含账号密码两个字段。

Controller

(Ajax)登录页面,对应login行为,处理登录请求,包括以下工作:

  • 调用服务匹配用户名密码
  • (可选) 检查用户类型
  • 密码需要加密存储
  • 储存用户信息到session
  • 返回提示信息

(Ajax)注册页面,对应register行为,处理注册请求,包括以下工作:

  • 寻找重名等逻辑
  • 调用服务创建用户
  • 密码需要加密存储
  • 储存用户信息到session
  • 返回提示信息

Service

用户管理,和数据库连接。对应到controller中大多数是POST请求。

  • 新增用户
  • 获取用户
  • 用户资料修改
  • 用户删除(使用用户状态更新实现)

Router与中间件

  • 添加中间件检查是否有session,否则同一跳转/login(业务逻辑)
  • (可选) Router上使用重定向让path更友好
  • 一定不要瞎用301状态码

页面js

  • 使用Ajax或jsonp请求

无限滚动

HTML部分

  • 提前加载所有图片(后续API动态请求,DOM插入时间损耗太大)。
  • 前若干张图片(假设为K)正常展示
  • 后若干张容器使用display:noneheight: 0等手段避免展示
  • 后若干张图片使用data-src存储真实路径,避免提前加载,影响首屏时间

JS部分

  • 设置参数保存当前展示图片的数目
  • 判断是否滚动到底端
  • 上述情况下增加展示的图片数目,删除避免展示的class,替换真正的src加载图片
  • 使用节流,保证弱网络环境下,没有连续的过多图片加载,使用flag控制程序触发,在最后一张图片加载完成后,更新flag布尔值,开放权限
  • (可选),网络环境很差时,可以考虑setTimeout兜底,但不推荐
  • 注意在所有图片都加载完成时,停止监听scroll事件
1
2
3
4
5
const figure = $($('#figures').children()[index]);
if (!figure) {
$(window).off('scroll');
return;
}

文件上传

HTML部分

  • 使用包裹<input type='file'><form>
  • <input type='file'>使用display: none,通过更友好的方式trigger它的点击
  • 注意:type=fileinput标签一定要有name属性,否则不会被包裹在FormData中。
1
2
3
4
<span id="upload" class="upload">+ 点击上传</span>
<form id="upload-form" enctype="multipart/form-data">
<input type="file" accept="image/*" name="files" id="upload-file" multiple>
</form>

JS部分

  • 使用Ajax提交替换form表单替换,来实现更复杂的回调和逻辑控制
  • 通过构造FormData,提交域内文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$('#upload').on('click', () => {
$('#upload-file').trigger('click');
});
$('#upload-file').on('change', () => {
$.ajax({
url: '/gallery/upload',
type: 'POST'
cache: false,
data: new FormData($('#upload-form')[0]),
processData: false,
contentType: false,
success: data => {
console.log(data)
}
});
});

后台路由

  • 配置Ajax和jsonp安全检查

控制器端

使用插件,参考examples/multiple.js at master · eggjs/examples

HTML模板

使用ES6的模板字符串。

已知问题:

  • 似乎有些视图、逻辑未分离

使用七牛API上传文件

使用服务端上传,一次只能单张上传。官网API文档Node.js版描述的并不清楚,下面是上传的简单流程展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const mac = new qiniu.auth.digest.Mac(config.accessKey, config.secretKey);
const options = {
scope: config.bucketName,
};
const putPolicy = new qiniu.rs.PutPolicy(options);
const uploadToken = putPolicy.uploadToken(mac);
const qiniuConfig = new qiniu.conf.Config();
qiniuConfig.zone = qiniu.zone.Zone_z1;
const formUploader = new qiniu.form_up.FormUploader(qiniuConfig);
const putExtra = new qiniu.form_up.PutExtra();
formUploader.putFile(uploadToken, key, localFile, putExtra, function(respErr, respBody, respInfo) {
if (respErr) {}
if (respInfo.statusCode == 200) {
//...
} else {
//...
}
}

多文件上传使用外部队列暂存所有任务,并和回调函数关联即可实现。