去年上半年开始接触了一些JS的后端编程,参与了一个基于node(其实是Meteor)的开源项目,有一些知识上的积累,现在回归了Java老本行,怕再过段时间就忘了,在这里记录一下,好记性不如烂笔头,说不定以后会有用。node大神可以看来消遣,跟我一样是外行但是需要写一些node程序的可以看看经验。这篇文章里会包含以下内容:
- 背景
- 关于Wekan
- 关于Meteor
- 关于publication&subscription
- 关于Jade&Stylus
- 关于JS
背景
第一次知道nodejs是在12年实习时,那时只感觉写出来的代码好难看,觉得JS写服务端性能肯定也不咋滴。直到2016年之前,我对于JS的使用还仅限于在网页端偶尔用来做一些字段验证,甚至连JQuery都不怎么会用,老忘记各种selector的语法。
16年开始接触了一些nodejs的框架,忽然好像发现了个新天地,ES6的语法太美妙,nodejs的「单线程异步IO」的设计也可以最大限度地发挥CPU的性能。后来为了提高工作效率,开始在公司推敏捷,于是找到了一个开源的KANBAN(关于KANBAN可以看这里)工具Wekan,使用一段时间后发现少了两个很重要的feature:
Wekan设计的理念是像Trello那样的一个开放的KANBAN平台,所以默认是开放注册的,但是有很多用户会像我这样把它部署在自己的私服上(像gitlab一样),所以希望有一个开关,可以控制不开放注册,而只能邀请注册。
在卡片中没有一个可以check的列表,让任务的管理者去更细粒度地控制这个任务。
于是去issue和PR里找,发现好多人都在等待着两个feature,于是想,等他们搞不如自己搞。于是开始了第一次真正的node项目的开发。之前做过一些基于Koa和Express的开发,和Meteor还是有很大的区别。
关于Wekan
Wekan是一个开源的KANBAN(看板管理)工具,是一种较简单(相对于Scrum和XP)的敏捷开发的理念。KANBAN工具比较有名的有Trello等。Wekan和Trello十分相似,具体的可以去查阅Wekan项目的Wiki。Wekan的优势就是,对于不想在公共平台上部署自己的项目管理工具的公司,以MIT协议开源的Wekan是一个很好的选择,如果你也有这方面的需求,不妨试一试Wekan。
Wekan is an open-source kanban board which allows a card-based task and to-do management, similar to tools like WorkFlowy or Trello.
---- Wekan Wiki
从技术上讲,Wekan的后端整体和前端的渲染引擎是基于Meteor的,前端的模板和样式使用了Jade和Stylus,对于这三个东西都是比较陌生的,本文主要讲的就是如何快速地融入这种技术栈不熟悉的项目开发中去。
关于Meteor
首先就是去搜索了一下关于这个框架的文章,这样可以简单地建立起来对它的一个感性认识。找到了一些简单的介绍,和一个中文的社区,但是刚刚建立,资料也很少。于是果断开始阅读官方文档,所幸官方文档十分详细。
Meteor是一个基于nodejs的全栈开发框架,致力于使开发者快速、高效地构建高响应的富客户端Web应用。技术栈包括MongoDB、WebSocket、Blaze等。Meteor引入了客户端DB的概念,使得Meteor开发的应用,可以天然的达到前端的实时响应。
从上图中可以看到,服务端通过publication把一部分数据发布到前端,前端通过subscription可以直接调用这些数据。对于前端来说,它只和前端数据库交互,基本上等于本地交互,所以响应很快,而前端数据库再通过和后端实时同步数据来达到和后端交互的目的。
像聊天应用这种实时性要求比较高的服务,可以直接从这种架构受益,用户几乎感受不到延迟,而且服务器上的数据更新也会实时同步到订阅它的客户端。
安装
根据官网的介绍,安装Meteor只需要在Console中执行curl https://install.meteor.com/ | sh
,然而,由于网络原因,在中国直接这么做简直是折磨人,要等到花儿都谢了(除非你有一个强大的稳定的合法的VPN)。
我的做法是首先把网页https://install.meteor.com/的内容保存到本地,下载其中TARBALL_URL
的包,保存到INSTALL_TMPDIR
中的目录,然后注释掉文件中的下载命令(echo "Downloading Meteor distribution"
这一行的后面的一坨),然后执行文件。
Meteor目录结构
Meteor也遵循「convention over configuration」,它有默认的一套目录结构,遵循这套规则可以极大地提高开发效率。Meteor项目的文件载入顺序如下:
- HTML模板文件总是比其他文件先被载入
- 以main为名字的文件总是最后被载入
- lib目录下的文件随后被载入
- 更深目录中的文件随后被载入
- 其他文件按照字符顺序被载入
还有一些特殊目录:
- imports
这个文件夹中的文件不会被载入,除非使用import
语句映入到其他文件。 - client
这个文件中的文件不会被服务端的程序载入,相当于在程序中使用if (Meteor.isClient) { ... }
来执行代码。在production环境这个文件夹下的相关文件会被压缩。 - server
与client相对应,这里的代码不会被客户端程序载入。相当于在if (Meteor.isServer) { ... }
中执行。 - public
项目顶级目录下public文件夹内的文件可以被任何人引用。
再看Wekan的目录结构如下,项目的代码结构和相互间的关系就很清晰了:
lkMacBook-Pro:wekan-setting lk$ tree -dL 2
.
├── client
│ ├── components
│ ├── config
│ └── lib
├── config
├── i18n
├── meta
│ ├── icons
│ ├── screenshots
│ └── t9n-changelog
├── models
├── node_modules
│ ├── fibers
│ ├── node-gyp
│ └── xss
├── public
│ └── fonts
└── server
├── lib
├── notifications
└── publications
21 directories
通过读文档和读代码,基本上对于现有项目的结构有了个初步认识,看到各个目录和文件,大致知道其代表的意义,那么接下来就是写代码了。对于这种已经有了大量代码基础的项目,有个优势就是「有很多可以参考的代码」,可以帮助刚接触的人更容易写出更好的代码。
关于publication&subscription
开篇时谈到的两个需求,第一个是先想到的,所以刚开始是拿这个来实验的。分析这个需求,大约有以下几个点:
要有限制注册的功能
用户注册时,必须输入邀请码,否则无法注册。要有一个默认的管理员
系统部署好之后,系统是可以任意注册的,这时需要管理员去注册一个账号,然后这个账号自然地成为管理员,然后他可以设置系统为仅限邀请用户注册。要有邀请码的管理
设计一个管理页面(Admin Panel),让管理员可以发送邀请,邀请通过邮件发送,同时要可以配置Email发件地址。
这三点对于服务端的需求很简单,首先增加两张表,一张存储系统的设置models/setting.js,一张存储邀请码models/invitationCode.js,然后在用户的表中添加isAdmin的属性。只要参照已有的一些表的写法,肯定不会错到哪里去,另外写的过程中再看一看官方文档中关于「Collection」的一章,很快就可以搞定了。
其中setting和isAdmin的属性是需要publish给前端使用,结合上边的一些知识,很容易就知道是在server/publication中稍微改动一些东西就好了。还好Meteor的文档很完整也很详细,这部分开发没有碰到什么比较大的阻力。
publication和subscription之间的通信使用了Meteor的一个自定义的RPC协议(DDP,一种基于WebSocket的自定义协议),对这个东西并没有作更深的探究,简单看了一下,貌似WebSocket协议是Meteor自己实现的,并没有使用更广为使用的WebSocket组件ws,如果服务器不支持WebSocket,则会使用sockjs实现长轮训 。
关于Jade&Stylus
Wekan的前端使用了一系列增加开发效率的JS组件,下面是其中涉及到账户系统的部分:
# Account system
accounts-password
kenton:accounts-sandstorm
service-configuration
useraccounts:core
useraccounts:unstyled
useraccounts:flow-routing
要修改和账户管理相关的东西,则首先要先去看一下这几个组件(这里花了很多时间,看文档看的想哭),然后Wekan的UI是用Jade和Stylus写的,第一次接触,这酸爽你懂的。幸好网上资料挺多,用习惯了之后觉得这两个东西真的不是一般的好,强大简介,关键是可以装逼。
关于JS
随着最近几年node商用的越来越多,JS也已经成为成本最低的全栈开发语言,而且ES6出来之后,感觉JS其语言本身已经可以和任何一门其他语言进行媲美了,纯个人观点。
关于缩进
在开始大量写JS之前,也写过一段时间底层的c语言,记忆中一个Tab代表4个空格,两个基本等价。浑然不知,Tab和空格的争论竟然这么激烈。
一直习惯写Tab,在提交项目时,想起来run一下eslint吧,结果一大堆的错误。对于开源项目来说,代码样式的一致性很重要,这里还是乖乖地全部改掉了。
Meteor建议开发者遵循Airbnb style guide ,仔细去翻了翻,一些要求其实是为了提高性能,比如const的大量使用,关键是可读性的提高会直接带来代码可维护性的提高。
Wekan的eslint都配置好了,如果懒得配置编辑器的eslint插件,只需要执行meteor npm run lint
,如果结果如下,则可以放心提交了。
lkMacBook-Pro:wekan lk$ meteor npm run lint
> wekan@0.11.1-rc1 lint ~/github/wekan
> eslint .
lkMacBook-Pro:wekan lk$
后续
这里并没有多高深的技术讨论,关于Meteor的底层实现——如DDP的实现——也并没有作很深的探究,但是做了这个项目后拓宽了一些视野,了解了node作为一个新兴崛起的技术的大概和发展现状。同时出于对WebSocket的好奇也对它做了一些小小的研究,有兴趣可以看一看:刨根问底HTTP和WebSocket协议。
对于第二个需求,有了第一个的知识基础,开发起来流畅了很多,很快就做出来了。