自传
从 2008 年, 上初中的时候, 我就开始不务正业, 在电脑上各种玩我不是一个引人注意的人, 学习成绩差, 上课睡觉, 放学上网, 可能是从解除 flash 开始, 慢慢的, 同学开始注意到我了, 用现在的话来说学习计算机技术纯属为了装逼开始的时候使用 flash 做一些简单的动画, 而后接触到了其中的 AS 脚本, 开始对使用代码表现自己对业务的逻辑这件事感兴趣可惜英语并不好, 换句话说那是的我对英语文档的阅读无法沉得住气, 所以我放弃了 flash, 转而开始学习易语言, 一种使用中文就可以表达计算机逻辑的东西, 很有意思, 最重要的不是它能够中文编程, 而是他的帮助, API 文档, 完全是汉化的, 这让那时的我非常神往估计也是那时网络犯罪未纳入刑法, 网络上充斥着大量的黑客教程, 初二的我也开始解除一些黑科技, 比如拿站, 免杀, 远控等, 那会儿的我还天真的拿易语言去做了个远控, 虽然完全是自娱自乐, 但可以算的上是我的首个桌面程序, 那会儿的杀毒软件还是比较初级的, 大多基于特征码, 正对于这样的现状, 网上流传着如 Myccl 这样的特征码定位工具, 我也是那会儿开始接触到汇编, 使用 OD, 感觉过杀软是一种刺激的开发过程, 自己学了 Win32 汇编, 幻想着使用汇编来写桌面程序当我发现做个简单的窗口也要调用 Windows 最底层的 API 的时候, 果断放弃了初中阶段就这么浑浑噩噩的, 写了些东西, 这些东西连同我家那台老爷机报废了
直到高中, 不知道什么原因买了本 C++ 的书回来看, 算是系统的了解到了些概念, 面向过程的, 面向对象的, 同时也深刻的让我体会到了作为编程人员的两个重要技术指标, 也是知道我日后学习的两个重要方向, 一个是算法, 而另一个是编程资源算法是对已产生现实问题解决方案的高度抽象, 这一部分基本上不是看一本算法导论就能搞定的, 算法能力的提高就像一种没有结果的成长过程, 因为现实所遇见的问题都是新的, 或许这些问题的分解你似曾相识, 但对于你解决这个问题往往都是杯水车薪编程资源的范围可以说是非常广的, 编程语言已有系统平台 API 开源项目开源库等都是编程资源, 人或许可以没有牛 X 的算法功力, 但是一定得有相对充实的编程资源积累, 因为这是招聘信息里最直观能表现出来的要求, 熟悉掌握 XXXXXXXX 等开源框架,"熟悉 XXX 架构", 有些公司的人事可能为了省事会直接写 N 年以上从业经验, 这一切实际上都是对编程资源的要求算法和编程资源这两个指标就像现在学科中的理科和文科一样, 都是指导产业个人发展的核心, 也正是因为我对自己认为的这两个提现开发者价值的指标深信不疑, 慢慢的开始有了辍学的想法, 因为那会儿的我, 满脑子想的是兴趣而不是生活, 也深刻认同悟道未必出家, 求学未必在校这一观点, 我为父亲写了一份游说他支持我辍学的信
或许是因为我在里面大量描述了他作为数学老师完全没接触过的事, 和我那有些天真的学习计划或许是从初中那会儿我这样的算是执着吧, 父亲看了信的当下是答应我辍学的一夜无话, 第二天就反对了, 理由也非常理性客观, 而且是我无法反驳的, 风险太高因为无论是我还是父亲身边都没有通过这种方式真正给自己一口饭吃的人, 所以我无法对这样的风险做出预估, 就乖乖接着上学, 考大学
高考成绩出来的时候, 别人选专业可能是比较费劲的, 因为他们选的大多数是学校, 然后看自己分数能选啥专业, 然后再使用仅有的两三天事件快速浏览这个专业所对应的行业发展, 就业等问题用父亲的话说, 我是比较容易的, 对应着分数软件工程专业找就行, 虽然没能辍学成功, 但是也算是成功从事了从很久以前就开始喜欢的行业吧, 从走进大学的那一天, 我的兴趣正式成为了我的生活
大学没有正经教会你什么是我上完之后最明显的感触, 大学给的不是教育质量, 而是接触新鲜事务认识自己平庸的机会, 可能也正是这些机会, 让父亲对于他的抉择更加有信心, 因为这样做, 风险小在大学我认识了能让我奋斗一生的女朋友, 我接触到了带我参加 ACM 的老师, 让我加入工作室的老师, 和我相似的许多人, 发现了软件行业的各种职业测评虽然我唯一参加的一个还没过发现了巨大的图书馆, 发现了正儿八经做业务系统是多难, 同时也发现帮别人做做作业, 讲讲课程设计是可以有经济收益的
所以虽然不想承认, 我的诸多技能是在大学期间积累下来的, 也是我现在常用的一些技术, 一些套路从年龄上说, 应该是在 18-22 岁期间, 所经历的是大学生活如果不经历这些, 谁知到生活会怎么样呢? 宝贝: 30 岁结婚, 40 岁离婚, 带个娃
万物皆 IO
一切的程序都在不停的做一件事, 那就是 IO 对内存不断 IO, 对硬盘不断 IO, 对网口不断 IO, 对数据库不断 IO, 对项目中的最小代码单元 (函数 / 成员方法) 不断 IO 总之, 它就是在不停 IO, 这也 IO 组合起来就叫业务, 它的唯一目的是为了应对业务场景
通讯问题
一个项目的启动是要有具体业务的, 没业务的项目我们称为技术 DEMO, 通常是自己学习使用无论是收集需求还是研究同类型系统, 都属于项目启动过程中非常前置的工作内容, 我们暂且不讨论, 当完全理解自己即将要做的业务场景时, 我们首先要处理通讯问题, 因为所谓的 BS 项目实质就是一种特殊的 CS 项目, 由于 C 已经 W3C 标准固化为浏览器了, 其实许多通讯方式也变得具体, 如 HTTP 这样的应用层短连接协议, 及时是大多数人认为的, 不具备通用性的通信层协议 TCP/IP,W3C 组织也推了一个 webSocket 的应用层长链接协议这两个应用层协议是 BS 项目通讯的全部, 他们对应两种大的具体的应用场景:
场景中的大部分业务发起者是浏览器, 如信息化系统, 内容管理系统等, 符合请求 / 响应模型, 使用 HTTP
场景中的大部分业务发起者是服务器, 如网页游戏, 聊天室, 实时地图等, 需求服务器为浏览器主动推送数据, 使用 WebSocket
场景中包含这两种情况, HTTP 与 WebSocket 同时使用
只有深刻全面的理解自己面对的业务场景, 你获取才能正确的选择使用哪种通讯模式, 因为通讯模式就是就是对 IO 方式的选择如果你选 HTTP, 一般的项目团队可能会在着手写代码之前, 写一份 swagger 定义, 用于大概的描述业务过程通常一份 swagger 文档的完成预示着前后端可以分开干活了, 不过在项目的不断迭代过程中, 这份 swagger 定义必然也会被改地面目全非, 所以完全不需要纠结这份 swagger 要写的多么好, 它存在的意义往往是让成员快点开始编码, 修改前后端接口的入参和出参是很普遍的事情, 这可能也是在后期两批开发人员最容易发生冲突的部分, 特别是当这种修改无法避免的时候, 如客户突然增加需求了, 以前接口出的数据前端无法完成新的需求, 而后端逻辑也需要更多的参数才能完成所以在项目提出变更的时候, 往往接口变的是入参还是出参, 影响着到底是改前端代码还是后端代码通常的项目会尽可能的只改一方, 这样出了问题也比较好排查
前端技术选型
首先我想区分一个概念, 这个概念纯属个人看法, 没有哪个行业标准明确地区分网站与 Web 应用, 我个人的区分原则如下
网站: 一种主要业务为内容发布的系统, 用户主要的目的是浏览信息, 交互频次较低系统的核心竞争力是对搜索引擎的友好性, 因为现在的内容发布性系统的主要访问量来源是搜索引擎, 这也促使这样的系统必须极快的响应搜索引擎爬虫的请求, 尽可能的在第一次请求过程中就返回足够总要的信息, 爬虫的内容分析通常是静态的, 即不予许 JavaScript 脚本执行, 所以重要的内容信息通常是不允许 JavaScript 脚本二次请求虽然这种系统中也存在二次请求的业务场景, 但那大多数是为了提高用户体验而做的, 这样的功能并非重点, 所以这种系统的架构通常是 MVC 的
Web 应用: 一种主要业务与桌面软件类似的系统, 用户通常会在某个页面上停留大部分时间, 进行一些复杂的操作, 交互频次较高系统前端资源通常是纯静态的, 也就是说无论数据库的业务数据如何变动, 从静态资源文件到客户的浏览器上这一过程, 后端是没有向前端资源嵌入业务数据的, 所以对于内容做静态分析的爬虫来说, 这个网站的内容是永远没有更新的, 因为所有的业务数据都是通过 JavaScript 脚本二次请求的对于开发者而言, 这样的系统, 其前端资源完全可以放在一个静态资源服务器中单独管理, 这样的服务器只要硬盘 IO 够高就可以胜任系统的开发模式也是前后分离的, 也是这篇随笔重点向聊的内容
目前行业中对于 Web 单页应用 (SPA) 的前端技术选型通常是选一种单页框架, 如: Angular React vue 对了, 为啥把它们称为单页框架, 而吧 JQuery 等称为库呢, 我个人的理解是前者提供了一套完全的前端开发的解决方案, 而后者更为灵活, 用于解决前端开发过程中的部分场景换而言之前者提供了表达逻辑的方法, 更像艺术, 而后者提供了解决具体问题的逻辑, 更像工具
就像艺术是因人而异的一样, 众多的前端框架也是因人而异, 无优劣之分不过众多的框架都在努力做到以下几件事:
双向数据绑定好像除了做特效, 现在的大多数前端框架都在极力避免对 DOM 的直接操作, 它即低效, 同时也极易出错转而提倡双向数据绑定, 数据的修改即可完成对页面视图的更新, 当然你也可以定制更新的方式, 也就是强行操作 DOM, 这种应用场景对于系统开发来说很少, 大多数的更新方式框架都已提供
响应式编程在框架中写代码, 即使是简单的修改一个对象的属性, 在框架的逻辑中也会处理绑定在它身上的所有监听逻辑, 感觉所有的业务都写在监听器里, 我们无需刻意的去等待一个值的变化, 因为当这种变化产生式, 框架回去主动触发你的代码
单一状态树随着以上两条的技术原则的不断发展, 很多作者也开始思考前端开发的本质, 最后他们发现实际上不止是前端, 任何应用程序在自身内部都维护了一个特殊的数据结构, 这个数据结构通常是树状的, 用于表示这个应用在每个时刻的各种状态, 以前我们或许会手动的将这些状态的变化渲染到视图上, 让用户看见现在做这一切变的简单的多, 大量的库也层出不穷, 如: VuexRedux 等, 他们固化的操作这颗状态树的流程, 使得编程过程中更加不易出错
单一状态树构造
读到这里, 我想你应该已经为你的项目做好了以下两件事
有一份针对业务的 Swagger 定义或其他类型的业务交互定义, 明确系统的数据走向, 最好有用例图或数据流图做指导
选择一个学习成本不是很大的前端框架
如果你准备好了, 我们来做接下来的事
无疑系统数据流图的端点是前端和后端, 可能后端的节点要多很多, 但是前端的节点通常只有一个那么接下来就需要构造一个能表示前端视图在每个时刻状态的树了
举个例子, 比如我想在用户的跟人详情页面中显示自己的相关信息, 昵称, 性别, 年龄等, 我们就可以这么写这颗树
- let state = {
- userInfo:{
- userName:'zmp',
- age:'22',
- sex:'男'
- }
- };
而后, 我们需要在购物车页中显示商品的个数, 商品的总价, 商品的信息列表等
- let state = {
- userInfo:{
- userName:'zmp',
- age:'22',
- sex:'男'
- },
- buy:{
- goodCount:12,
- MoneyCount:1314.0,
- goodList:[
- /*
- {
- goodName:'毛巾',
- count:1,
- money:12,
- url:'http://xxxx.xxxx/good/1001',
- }
- */
- ]
- }
- }
可见随着我们的页面增加, 或者说随着系统的功能增加, 这颗状态树的节点也在不断增加, 所以状态树的复杂程度取决于业务的逻辑复杂程度, 而在状态树构造过程中, 你可能还一句代码没有写, 所有的内容都是自己想象的, 这个阶段就是要让你大胆想象, 尽可能的让这颗树全面, 能够表达每个时刻的前端视图状态
数据库设计
在我们构造好单一客户端的状态树后, 我们就需要结核业务与客户端的状态树, 来构建服务器端的状态树了服务器也有状态, 即使是使用 HTTP 这种无状态的, 短连接协议, 也无法摆脱服务器需要状态这个事实而且服务器程序的实例很多时候不是一个, 如大规模集群的情况, 很明显就需要考虑状态一致性的问题你不能把服务器状态像客户端一样写在内存里, 因为进程的内存通常不共享, 及时共享, 代价也很大, 更何况有些服务器程序实例还不是部署在同一台机子上的
这里保存服务器状态的最佳实践无疑是数据库了, 无论何种类型的数据库, 你需要做的是保证每个请求过来, 都能在数据库里查到状态 (也就是业务数据) 如果你把客户端的那颗状态树写的足够全面, 很多时候数据库设计的过程就是结合业务翻译客户端的状态树, 通常是翻译成关系型的库表, 当然也可以翻译成非关系型的这主要取决于你选的数据库类型
后端技术选型
如果系统不是互联网产品, 通常复杂的开发过程往往是在后端 (如果是互联网产品, 通常前后端都很复杂) 因此后端技术的选型显的尤为重要, 原因是有些业务数据不是自产自销的, 即后端程序可能要整合别的什么系统, 系统与系统之间走的通讯协议可能是长连接也可能是短连接, 甚至有些项目中, 后台程序是要整合某个开源项目的, 所以我个人理解的, 传统非互联网产品的 BS 项目, 后端工作量更大
严格意义上说, 当你选择使用前后端分离的模式来做 BS 项目的时候, 后端使用何种语言完全取决于你团队开发人员的技术方向, 语言永远是用于表达逻辑的工具, 及时是想 C 那种, 在标准库中完全没有网络库的语言, 也是可以针对不同操作系统, 调用操作系统提供的网络服务来做很多出色的事情的, 你如你看 Nginx
所以后端技术的选型语言不是最主要的, 任何能操作网络的语言都是可以做后端程序的, 这其中考量比较多的是后端程序与其他第三方系统的整合, 最常见的第三方系统如数据库, 大数据平台, 当然除了这种偏技术类的存储服务, 也有很多业务类型的服务, 如人脸识别, 语音识别, 用户鉴权服务等
后端开发过程中比较难受的就是有些服务不走网络, 其提供的 SDK 对编程语言有要求, 后端的技术选型主要的工作就是在收集编程资源
业务开发
BS 项目的业务开发, 由于是前后分离的, 前后的开发通常是并行的, 且在此过程中也充斥着大量的联调测试, 不过前后开发人员讨论的重点问题也会比较单一, 通常是针对某个接口的入参出参格式的争论, 此时就提现了当初那份 swagger 定义的重要性了, 如果这一切 (前端后端) 的开发都是你一个人做, 我也十分建议你先写 swagger 定义, 这样能使你的代码修改受到限制, 降低项目失败的概率
总结与个人针对 BS 项目的技术栈
感谢你能听我唠唠叨叨到这里, 我们总结一下前后端分离模式下的 BS 项目开发过程
有一份针对业务的 Swagger 定义或其他类型的业务交互定义, 明确系统的数据走向, 最好有用例图或数据流图做指导
选择一个学习成本不是很大的前端框架
构造客户端的单一状态树
根据客户端的单一状态树设计数据库
选择一种适合后端系统做集成的编程语言
前后端同时开始编写业务代码
其中 2 与 5 可以同步进行, 其余的步骤还是非常建议按顺序来的
最后我来分享一下我个人处理 BS 项目时用到的主要技术栈
前端: Vue,Vuex,Axios/Socket.io,
有时也用 electron 来做桌面应用
后端: eggjs, 各种 eggjs 的插件如: egg-sequelize,egg-socket.io 等
数据库: mysql
缓存: redis
来源: http://www.jianshu.com/p/88903875bff6