写在前面

从14年3月份入职之后,就一直在跟公司的一个即时 IM 的 Web APP 。15年已经来了,博客还没有一篇更新,就来聊聊在做这个 APP 的时候遇到的一些坑和一些总结吧。由于刚开始经验有限,所以在系统设计方面有很多的缺陷和不足,随着知识的积累,通过不断调整项目目录和一些规范,最终变成了下面的前端模型。

设计理念

期间重构时受到 UC前端工程实践.pdf 模块化开发 开发思想的影响,我们也在 APP 中引入了 模块化 的概念。正如里面所说:我们希望能像搭积木一样开发和维护系统,最终通过组装模块得到一个完整的应用。

  • 模块是可组合、可分解和更换的单元
  • 模块具有一定的独立性
  • 将模块所需的js、css、图片、模板维护在一起 ( 我们未采用 )
  • 组件生态,同团队不同项目之间能有可复用的模块

技术选型

前端构建工具:F.I.S

我们在 FIS 和 Grunt 之间选择了后者 - 国产的 FIS ,基于如下的考虑:Grunt 是一个基于 task 的构建工具,依赖众多的插件进行配置组织,可以解决基本的前端自动化问题。FIS 是基于工具、开发框架、本地开发环境为一体的前端解决方案,不但拥有各类工具插件,同时还针对 PC、Mobile、I18n 等业务、场景总结了很多最佳实践。简单来说就是 FIS 更适合大型工程化的项目。

模块加载器:RequireJS

通过 fis-postprocessor-amd 的支持,FIS 能很好的支持 amd ,完全满足 amdjs 规范。至于选择哪个模块加载器,似乎也不是特别重要。

动态样式语言:Less

没啥好说的,你喜欢 less 就用 less ,喜欢 Sass ,就用 Sass 。

MV* 框架:Vue.js

我们觉得 MV* 框架的选择非常重要,这关系着项目开发进度和后期维护。前端的轮子很多,我们不是大厂,没有实力也没有必要自己去造一个。一开始的提议是选用 Angular ,但是考虑到 Angular 的入门门槛,后期维护难度会加大,我们最终选择了 Vue.js (未考虑 IE 8- 的兼容)。

主要原因大概是:Vue.js 更灵活,允许你按照自己的方式来架构你的程序,而不是所有事情都必须按照 Angular 的方式运行;Vue.js 比 Angular 简单的多,所以你可以在很快的时间内学到它所有的东西然后变得更有效率。
由于 Vue.js 关注点在 MVVM 模式的 ViewModel 层,所有还需要额外的第三方库进行支持。

vue-router: flatiron/director
vue-resource: jQuery Ajax

开发目录

并不是所有的项目都适合下面的开发目录。不同的开发模式、协作、打包策略都会影响都会影响开发目录的结构,其实没必要纠结哪种开发目录更美观、更合理。还是根据实际的需求不断的调整,直至它更适合你们团队、项目。

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
Project
|-- i18n
|-- index.js
|-- index-zh_CN.js
|-- script
|-- util
|-- module
|-- home
|-- index.js
|-- service.js
|-- main.js
|-- style
|-- common
|-- widget
|-- dialog
|-- index.less
|-- index-red.less
|-- module
|-- home
|-- index.less
|-- index-red.less
|-- index.less
|-- index-red.less
|-- template
|-- widget
|-- dialog.tpl.html
|-- module
|-- home.tpl.html
|-- index.tpl.html
|-- vendors
|-- jquery
|-- jquery.js
|-- index.html
|-- fis-conf.js

子目录基本上是按照结构、样式和脚本来划分。细化到各个子目录,基本上是按照模块来划分,关于结构,也就是模板,可能很多时候一个模块的模板可以将其融合在一个文件中,所以就没有单独建立一个目录,如果一个模块的结构比较复杂,可以像脚本和样式那样来建立相应的模块目录。各个模块会有相关的主题外观。

开发一个组件

Vue.js 允许你把扩展的 Vue 子类当曾一个可重用的组件,概念上和 web 组件一样,但是不需要任何填充。为了创建一个组件,只需要使用 Vue.extend() 来创建一个子类的构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
//script/main.js

new Vue({
el: 'body',
data: {
currentView: 'home'
},
components: {
login-component: require('login/index'),
home-component: require('home/index'),
archive-component: require('archive/index')
}
})
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
//script/moudle/login/index.js

define(function(require,exports,module) {
var Vue = require('Vue');
var store = require('services/store');
var service = require('login/service');

module.exports = {
replace: true,
template: __inline('login.tpl.html'),
data: function () {
return {
email: '',
password: '',
errorTip: null
}
},
filters: {
emailValidator: {
write: function (val) {
this.errorTip = (appFunc.isEmail(val)) ? null : '邮箱必须是Email';
return val
}
},
pwdValidator: {
write: function (val) {
this.errorTip = (appFunc.isPasswd(val)) ? null : '密码在6-18位之间';
return val
}
}
},
methods: {
loginSubmit: function () {
var data = {
email : this.email,
password: this.password
};
if(!this.errorTip & data.email !== '' && data.password !== '') {
service.login(data, function(res){
if(res.result){
store.setCurrentUser(res.message.sid, res.message.user);
appRouter.setRoute('index');
}
});

}else{
this.errorTip = '请输入正确的账号、密码'
}
}
}
};

});

打包部署

通过配置 FIS 的,我们能对产出的代码进行 资源压缩 添加文件版本 资源合并 添加CDN域名 应用打包 , 其中 应用打包 比较蛋疼,我还在研究如何配置才能达到我最终想要的打包效果。

项目通过 git hook 进行部署和更新。通过生产服务器上的 FIS 对仓库源码进行编译,编译目标指向 APP 目录。

写在最后

虽然标题上写着 设计和实现 ,但是本文基本上还是介绍系统的设计为主。希望有时间能写下 Web-IM 的一些实现,主要是 IM 这块比较有意思。

因为前端的数据和界面都通过 WebSocket 主动通知来进行更新,为了减少模块间的耦合,我们还引入了一套基于事件的模块间的消息传递的组件。而且,因为 Vue.js 拥有数据双向绑定的能力,处理起频繁更新的 VM 层还是相当便捷的。

大概就写到这里了,想到了就写下来,不然过几天又忘了。