主页

成长经历 | 跑 10KM 其实没有那么难

连续跑了有将近一个月了,从第一天跑跑停停 3km 就气喘吁吁,到轻松跑完 5km,虽然不去追求每天的进步,因为把事情持续做下去才是目的。但是从拉长时间维度的角度来看,不想进步都很难。

跑 10km 都不是我计划所为,再说,跑了 10km 又有什么了不起呢,还值得去计划?但是,我为什么又要写呢?虽然不是什么了不起的事情,但对我来说,人生第一次跑这么远,应该值得记录一下。

前一天

从早上开始,就感觉到格外闷热,这种天气让人异常烦躁。压抑了一天,到了下午,终于下了一场小雨,就好比在桑拿房里,往炉子里加了一盆凉水,地面滋滋往上冒气,显得更加的闷热。

晚饭的时候,小雨还没见停,想着可能没有办法下去跑步了,于是,晚饭就多吃了几口。可是,到了 8 点的时候,似乎不怎么下了。想着还是下去吧,就算不情愿跑,完成最基本的任务还是没问题的。

操场上零星的几个人,较之往常少了许多,地面依然感觉得到微微的热浪,但是,偶尔有凉凉的微风吹过,还是很舒服的。除了微风,还带有零星的雨点,空中还时不时伴随着闪电,在这种环境下跑步,还是头一回。

心情倒是不错,然而胃里却不是很舒服,肯定是那多吃的几口饭,每天晚上都是固定的饭量,中间间隔固定的时间来消化,到跑步的时候刚刚好。然而,原本计划跑不了了,就贪吃了几口,随着跑动,现在胃里正翻腾倒海,边跑边打嗝,难受了就慢一点。

即便如此,倒也算是坚持下来了,跑完了任务,之后,便是一个劲的打嗝,倒也还算舒服,总比胃胀气要好受得多。成绩就不值一提了,不过也比刚跑那会强一些。

当天

又是普通的一天,什么都和往常一样,这次晚饭吃得刚刚好,到时间准时下楼,一切都和开始跑步的每一天一样。不一样的是,这次没有跟着课程跑(大多数时候,都是选择一个跑步课程,跟着它的节奏跑),而是打开「户外跑」模式,设定一个目标,这次跟着自己的节奏跑。

目标不敢定太高,差不多 10 天前跑过 5 公里,但是跑下来后,并没有想象中的那么轻松,这次也没做好挑战的准备,再想到昨天跑得胃难受的经历,默默的把目标设置成了 4 公里,想着完成任务即可。

由于没有课程里「虚拟教练」的「催促鼓励」声,我就一直按照,我感到舒适的配速往前跑着,3 公里的时候,已然汗流浃背了,能感受到心肺功能还在不断的提升,身体在不断适应它所处的环境,往往这个时候,意志是比较薄弱的,也是最容易放弃的,想着也就剩 1km 就完成任务了,也就跑下去了。

很快「目标已达成」的声音就传到耳边,然而,我并没有停下来,因为这时既没有呼吸急促的不适感,也没有像昨天那般胃部的不适感,更没有腿部酸痛的感觉,空气又这么凉爽,明天也没啥事干的,接着往下跑呗。

就这样 5KM 到了,依然没感觉,6KM,7KM 还是一样,身体似乎已经适应这个强度,它认为,这就是正常的环境。速度也平稳得不像话,保持在 6分25秒 左右。

8KM 的时候,依然不觉得累,身体只是机械的跟着意识在往前跑,这个时候,我终于感受到意识驱动身体的感觉,人们往往都是意志丧失,而身体有余。说白了,就是不够努力。

从前认为这是需要强大的意志力,才可以做到驱动身体。这次的经历告诉我,其实不需要多大的意志力,因为在那个状态,一点也不痛苦,反倒有一点兴奋。但是,我想,肯定离不开前 20+ 天的练习。

虽然意识可以让你一直跑下去,但是身体一定会提出反抗的,渐渐的发现,口干舌燥起来,明显感觉到嘴唇干涩,一定是身体开始脱水了。从语音提示得知,离 10km 也不远了,索性就跑到 10KM 吧。

最终,在 1 个小时 5 分多钟跑完了 10KM,第一次,连续跑最远距离,跑最长时间,停下来的时候,有那么几秒钟身体是飘的,好奇妙的感觉。

后一天

原本担心,第二天可能会浑身酸痛,看来是多心了。除了比往常多睡了一个小时外,一切正常。当然,这也归功于前面 20+ 天的坚持。

我相信,身体已然接受了这样的节奏以及运动量,要说今天有什么特别的地方,那就是,中午没有休息,一下午注意力还不错,很快读完了一本技术类书籍。当然,不能跟跑步联系上,也许是早上多睡的那 1 个小时很管用,也许是那本技术书籍写得很对胃口。

还是和每天一样,到时间下楼跑步了,今天依然是设置目标 4 KM,目标不能太高,否则达不成,反而影响积极性。

跑完 4KM 时候,突然想何不冲刺一把,看看能否刷新 10 天前那次 5KM 的记录,结果当然是肯定的。

每天不用去强调所谓的进步,坚持做好你该做的事情,时间会给你答案。

阅读更多

彻底搞懂 Ajax 跨域访问:3 种解决方案

如果你了解过 Web 开发,那么你应该听说过 Ajax 跨域访问问题,如果你还稍微多了解了一点,那么,你应该知道,这并非开发本身有问题,它仅仅只是浏览器为了安全起见,设置的一个门槛而已。

Ajax 跨域访问,并非什么技术难点,所以绝大多数 Web 开发者,甚至直接都忽略它了,因为网上到处都有 Ajax 跨域的解决方案,例如 JSONP,但是,你真的了解那些解决方案的背后的原理吗?

什么是跨域访问问题

前台通过 Ajax 异步请求的方式,获取「非同域」下的后台资源,而被浏览器拦截报错,称之为:Ajax 跨域访问问题。那么,浏览器为什么会阻止你在 A 域下异步访问 B 域下的数据呢?为了安全。

当然,浏览器并非啥都「多管闲事」,你要同时满足两个条件,请求资源时,它才会阻止你。

  1. 非同域。如果是在同域(前后台不分离的项目)中,前台请求,后台响应,浏览都是不会管你的
  2. Ajax 异步请求(xhr 类型)。即使是在非同域,例如使用第三方图片资源、css 资源以及 js 资源,浏览器也是不会干涉的

说了这么多,不够直观,写个例子。 PS. 后台代码采用 Node 编写,如不了解 Node,不用担心,重点关注 Web 请求部分即可。

因为要模拟跨域,所以我们需要在本地启动,端口不一样的两个 Web 服务器,一个称之为「我方」,端口使用 8080;一个称之为「敌方」,端口使用 3000 。


本文为付费 Chat,链接地址:https://gitbook.cn/gitchat/activity/5c9b35128557e42aec4bc5c7

阅读更多

慢慢过成自己想要的样子 | 5月份总结

在「是时候要走了,结束第一个职场5年」这篇文章中写过一段话。

我想要读更多的书,不仅仅只是技术方面,而是全方面的输入 我想要接着写作,不再是零散的几篇文章,而是有系统的去输出价值 我想要出去旅行,去布达拉宫,去青海湖,去感受生活 我还想要实现一款真正意义上的产品,对更多的人产生价值 当然,最想要的是陪伴她成长,带她去体验生活的酸甜苦辣,让她更早的发觉自己的梦想,从而去追逐,因为没有梦想的人生,是可怕的。

我想要这样的生活。然而,现实永远不会那么完美,我只能尽量去实现我的梦想。总体来说,最近一段时间过得还是比较充实的,这里简单总结一下:

  • 读完了两本书,一本关于写作,一本关于跑步。读书感悟点这里查看
  • 学习了两个产品专栏,预计 6 月中旬学完,然后整理出学习笔记
  • 公众号推送了 7 篇,包括这篇,重新思考公众号要写什么,点这里查看
  • 每天跑步 30 分钟,从 11 号开始从未间断,目前已跑了 20 天
  • 每天读英文原著 10 分钟,正在读第 3 本
  • 在 GitChat 上写了一篇技术分享,长文(6000字+),免费的

遗憾的是,对于要做什么产品,我目前还不知道方向;对于旅行,近期依然只是奢望。但能把读书跑步这两件事情坚持下来,自认为已然是个不错的成绩了。

关于读书

以前总是很羡慕那些一周读完一本书的人,有的甚至是一天一本,都怀疑他们是看完的还是翻完的。

最近两周时间,我竟然也不知不觉中读完了两本书,自己都觉得不可思议,我是那种看书很慢的人,通常情况下,书还没看两页,思绪就已经不知道飘到哪去了。注意力很难集中,原因可能有两个:

  • 一方面,环境因素导致。环境因素自不必多说,找个安静的地方最为重要
  • 另一方面,电子设备干扰因素导致。以前总是看电子书,干扰因素太多,而最近读的都是纸质书,翻书的感觉很有仪式感,容易集中注意力,而且涂涂画画更能加深印象

一周一本书(大约200来页的书),其实真的很容易办到。我每天大概就花了 1 个小时的时间,除去记笔记以及分享的时间,集中精力在看书,其实也就 40 分钟。况且,我读书的速度是中等偏下的,一般人只要每天集中精力半个小时,要达成一周一本书,是很容易办到的事情。

另外,读书时,随手记笔记的习惯,真的很管用。每次看到好的句子,亦或是有体会的地方,我都会直接写在旁边,便于后续翻阅,同时,也加深了印象。

关于跑步

锻炼身体和学习英语,可能是我这代人「最重要而又最不紧急」的事情了,也是下定决心次数最多,最想要做却又做不好的事。

生命在于运动,运动有很多种方式,有的人喜欢球类运动,有的人喜欢水上运动,有的人喜欢健身类,甭管你采取什么方式,坚持下去,才是最有效的。

我之前尝试过很多种方式,使用器械或无器械,在户外或在家里,健身房也办过卡,最后都没能坚持下来。到最后,发现最适合我的方式,还是跑步。

没坚持下来的主要原因就是觉得麻烦,其实人就是这样,对于不擅长的事情,受不得一丁点的困难,本能的退缩和烦躁。

去健身房还要想着咋去呀,骑车还是开车还是走着呀,真麻烦;练器械还要考虑这个咋用呀,我这动作标准不标准呀,要不要请个教练啥的,咋这么麻烦;也许一次两次或者一周两周能够凭着意志力坚持下去,但是,意志力这东西可是有消耗的。

所以,我采用了一种最简单的方式:跑步。没有那么多的技巧可言,不需要准备工具,也无需去学习什么,甚至服装都不用换,走下楼,跑起来就好了。

截止写这篇文章时,我已经坚持跑了 20 天,每天半个小时,第一周跑 3 公里就已经到达极限了,到现在能够轻松跑完 5 公里了。

从成绩上来看,可能不值一提。但,重要的是成绩吗?把事情持续做下去,本身就是目的,我希望能够一直跑下去,像村上君一般。

阅读更多

公众号到底要写些什么?

最近一段时间,胡乱推送了几篇文章,看了下主题,也真够随意的。有关于技术笔记的,有关于读书感悟的,有关于工具推荐的,还有情感宣泄的。乱得自己都不忍直视了。

1.

公众号到底要写些什么内容,从一开始,就不太清晰。当然,也跟没有持续输出有关。

以前,更新周期较长,几个月才会冒出一篇推送,自然也不会觉得有什么问题。最近更新频率高了,内容多了起来,慢慢发现,没有一个清晰的定位,就会显得乱且杂。

认识我的朋友,知道是我在写,就当是了解我动态的一种渠道了。不认识我的人,可能因为某一篇文章,关注了我,然后一翻历史信息,心想乱七八糟的什么玩意,他到底是干嘛的?

2.

通过公众号,我到底想要表达什么?这个问题我也一直在思考,大多数人可能会想到「自媒体」,后来我发现,这个定位对于我来说,似乎不太恰当。

首先,我没有那么宏伟的目标,想要把公众号做成媒体性质。起初就是为了好玩,当作博客的载体,后面渐渐想要发表一些自己的看法,获得认同。

其次,最近一段时间,我似乎想通过公众号去证明点什么,证明写的东西确实有用?对读者有帮助?或是,证明自己的价值?说不太清楚,反正就是想把这个事情做下去。

3.

想要把这个事情做下去,就得想明白,这件事情长期的收益是什么?也就是做这个事情的原动力:我想要通过它,给自己或者读者带来一些价值。

根据自身情况,我初步定了三类输出主题:

成长经历

这个主题更多是为了给自己带来价值。通过分享审视自己的内心,时刻关注自己在想什么,在干什么;同时,也是为了爽,如果不能在自己的公众号,说点自己想说的,那也太无趣了。

读书感悟

这个应该能给读者提供一些价值,我将不定期推荐一些好书。当然前提是我读过的,并且认为有收获的。可以帮助你过滤信息。

产品技术

这个范围就比较大了,分为两大块:产品和技术。产品为主,技术为辅,这里限定为互联网产品。对于产品技术,我也只是一个学习者,也是希望在分享过程中,不断提升自身能力。如果你也恰好对这方面感兴趣,可以联系我,共同进步。

4.

最后,关于我是谁,这里就不再多说了,关注公众号后,点击「文章合集 - 成长经历」菜单。

阅读更多

读书感悟 | 写作与跑步有什么关系?

这半个月的时间,我读完了两本书,一本是关于写作的书,叫《七十二堂写作课》;另一本是关于跑步的书,叫《当我谈跑步时,我谈些什么》。

他们有什么关系吗?其实,没什么关系,正好这两本书离我最近,顺手就拿下来一块阅读了。如果非要扯上点关系,那就是,写跑步那本书的作者正好比较会写作,好吧,我似乎说了句废话。

《七十二堂写作课》

这原本是中学时代就应该阅读的书籍,现在让我读来,竟然感到收获满满,可想而知中学时代是多么不爱学习,现在恶补应该还来得及。

它可以说是一本关于写作的工具书,介绍写作最原始的方法论。它告诉你写作到底在写些什么?有哪些类型的文体?他们之间都有什么样的区别?如何选取文章所需的素材?文章情感有几种表达方式?什么样的文章才算是议论文?等等。它不会教你怎么起个好标题?如何获得十万加阅读量?如何设计套路吸引你的读者?

这是一个浮躁的世界,表面功夫或许能一时奏效,但不代表能终生受用。我们更应该学的是,写作到底在写什么?事物记叙方式如何得体大方?与他人写信时,如何注意礼仪?抒发情感时,如何通过文字控制情感流露?发表观点时,如何合情合理?

我觉得这些才是最重要的,才需要去深入学习和体会的。而这些答案都可以在书中找到。当然,除了上述这些内容,还有一些关于诗歌,散文,游记等等文体的介绍,几乎涵盖了写作的方方面面。

你可能会问,读完了,写作水平有提高吗?我想说的是,哪能指望看完一本书,水平就能有提升。能在写作的时候,脑海中划过一丝书中所述,就已经不错了。

好书只会给你正确的方向,想要提升能力,怎能绕开刻意练习,多思考多写才是正道,所有事情都是这样。

《当我谈跑步时,我谈些什么》

这本书的作者是日本知名作家——村上春树。他是怎样的一个人,我就不做评价了,也没有那个资格,他的书我才读了这一本。

这是一本他在写自己的书,不同于一般的传记类书籍,他是通过跑步这件事情,在写自己对生活的态度。读完后,让我肃然起敬。

我非挑战纪录的无邪青年,亦非一架无机的机器,不过是一介洞察了自身的局限,却尽力长期保持自己的能力与活力的职业小说家。

这是书中他对自己的一句定位。职业是小说家,但让我印象深刻的是,他真的是洞察了自身局限

这不是一句随口说出的话,他通过体能极限运动(马拉松 & 铁人三项),不断的在挑战自身的局限。

勉励,乞求,恭维,申诉,鼓舞。只剩下最后一点点啦,求求你们好歹忍耐,再拼一下。

读到这段的时候,我似乎被震惊了,这是他的意识跟身体在较量。很多事情,人们通常都是意志先崩溃,而身体有余。但他却在用意识推着自己前进,意志力实在可怕。

如果全书都在讲这个,难免有点枯燥无味了,或是又一则心灵鸡汤。书中不乏出现村上春树对于人生的思考。从这些思考可以看出,他也只是一个平凡的人,却异常努力而已。

大学毕业后,经营了一家类似酒吧餐饮的门店,工作异常辛苦,常常从清晨干活到深夜。30岁的时候,下决心开始写小说,一面经营着店面,还要抽出时间写小说,可以想象这样的日子有多煎熬。但是,他却这样坚持了 3 年。

坚持下去总归是有回报的,小说取得一些成绩后,决定关掉门店,一门心思投入到自己的副业中。如果说,坚持做副业 3 年的时间,能够难倒 50% 人的话,这个「放弃稳定工作及收入,投身未知未来的梦想」的决定,大概会让 90% 的人望而却步吧。

找到自己真正喜欢的事情,并不容易,有的人穷尽一生,也找不到。书中大部分的篇幅,都是在写他参加马拉松比赛、参加铁人三项比赛的经历。

从他的描述中,我都能感受到身体上沉重的压力,但是,他却能够这么一直跑下去。读到最后的时候,我甚至都开始有些羡慕他了。

并不是有个人跑来找我,劝诱我说「你跑步吧」,我就沿着马路开始跑步。也没有什么人跑来找我,跟我说「你当小说家吧」,我就开始写小说。突然有一天,我出于喜欢开始写小说。又有一天,我出于喜欢开始在马路上跑步。不拘什么,按照喜欢的方式做喜欢的事情,我就是这样生活的。

看到这段,我默默在旁边注释了两个字「得瑟」。

阅读更多

全栈开发入门实战:后台管理系统

本文首发于 GitChat 平台,免费 Chat 链接:全栈开发入门实战:后台管理系统


感谢你打开了这篇 Chat,在阅读之前,需要让你了解一些事情。

第一,本 Chat 虽然免费,不代表没有价值,我会将个人全栈开发的经历叙述给你,希望对你有一些帮助; 第二,文中所使用的技术栈并非最新,也并非最优。后台管理系统更多是 2B 端的产品,通常是业务优先。本 Chat 的目的是为了让你能够快速上手全栈开发。 第三,本 Chat 虽然名为全栈开发,但是不会带你做完一个完整的后台管理系统项目。一是由于篇幅有限,二是由于时间关系,个人精力也有限。

正文

本 Chat 的内容,正如 Chat 简介中所描述,将分为以下 5 大块:

  1. 开发准备
  2. 前台样式
  3. 数据库连接
  4. 前后台交互
  5. 线上部署

你可能会发现,好像不知道要做什么,没错,后台管理系统一般都是企业内部定制开发,通常是对业务的数据管理,具体做什么功能由业务决定,但多数功能都是围绕着表格或者表单。

上面列举的仅仅是全栈开发的大致流程。首先,要做一些准备工作,例如:开发环境、编辑器环境以及依赖包配置等等工作;其次,我们要选定一个后台模版样式,快速套用,实现业务功能。(当然,你要自己开发也行,但不建议这么做,除非为了学习);然后,根据业务做数据库的设计,编写后台数据处理逻辑,以及前后台数据交互等功能;最后,测试并部署上线。

这里的示例,将实现一个学生数据的在线管理需求,其实就是一个在线表格,包括添加,删除功能,系统层面包括登录退出功能。麻雀虽小,五脏俱全,整体架子搭好了,再在上面添加功能就简单多了。好了,现在就开始全栈之旅吧。

开发准备

启动项目

首先要做的是,开发环境的安装,这里就不多说了,关于 Node 环境的安装,默认你已经搞定了。

既然采用 Express 作为 Web 框架,Express 也是要安装的,有了 Node 环境,安装 Express 就简单多了。我们直接上手 Express 的脚手架,全栈开发关键要速度。

npm install express-generator -g

一定记得是全局安装。安装完成之后,输入 express -h 可以查看帮助。这里选用 ejs 模版引擎,为什么?因为我顺手而已,这个不重要。找个合适的目录,运行下面命令:

express -e node-web-fullstack-demo

生成项目目录之后,首先要安装依赖,如下命令:

cd example-node-web-fullstack
npm install

等待安装完成,我们就可以启动项目了,使用命令 npm start ,去浏览器中,打开网址:http://localhost:3000,看到写着 Express 的首页,代表你的项目启动成功了。

编辑器环境配置

一个好的编码环境,可以让你项目开发效率加倍。

首先介绍一个编辑器配置 EditorConfig,这是一个编辑器的小工具。它有什么作用呢?简而言之,就是让你可以在不同的编辑器上,获得相同的编码风格,例如:空格缩进还是 Tab 缩进?缩进几个空格?

你可能觉得诧异,这个不是在编辑器上设置就可以了吗?没错,假设你从始至终都是在同一个电脑同一个编辑器上编码,那么可以忽略它。如果存在多电脑配合,亦或是多个编辑器配合,那么它就是神器。它几乎支持所有的主流编辑器,不用单独去编辑器中设置,配置文件就在项目中,用那个编辑器打开项目,都能获得一致的编码风格。

使用方法很简单,在根目录中新建 .editorconfig 文件即可。

# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Matches multiple files with brace expansion notation
# Set default charset
[*.*]
charset = utf-8

# 4 space indentation
[*.js]
indent_style = space
indent_size = 4

# Indentation override for all JS under lib directory
[views/**.ejs]
indent_style = space
indent_size = 2

上面这个示例,设置了 js, ejs 的缩进,并通过 insert_final_newline 设置了文档末尾默认插入一个空行。还有一些有意思配置,建议你查看一下官方文档,就明白了,非常好用的一个小插件。

代码检查工具

使用 js 编码,建议最好使用一款代码检查的工具,不然写到后面就会很尴尬,往往一个小的语法错误,会让你抓狂好久。代码检查工具推荐 ESLint

使用方法也很简单,首先安装要它,npm install eslint --save-dev,代码检查工具通常只需要安装在开发依赖里即可。紧接着你应该设置一个配置文件:./node_modules/.bin/eslint --init,然后,你就可以在项目根目录下运行 ESLint:./node_modules/.bin/eslint yourfile.js 用来校验代码了。如果你使用的是 IDE 编码,一般都会有 ESLint 的插件来校验代码,例如在 VS Code 中安装 eslint 插件,就可以实时校验正在编辑的文件了。

配置文件里的设置参数就多了,建议自行查看官方文档。如下示例:

{
    "rules": {
        "semi": ["error", "always"],
        "no-undef": "off",
        "no-unused-vars": "off",
        "no-console": "off"
    },
    "env": {
        "node": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    },
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
    }
}

有时候你可能会觉得很烦,默认推荐的 eslint 校验规则管得太多了,上述中我就手动关掉了「不允许使用 console」「不允许存在未定义变量」等配置。有些人可能干脆关掉了校验,但是,我还是强烈建议启用,毕竟规范的编码效率会更高。

前台样式

前面做了那么多工作,但似乎一点效果也看不到,别着急,这一部分的内容,就能让你直接看到效果。

前端的工作其实是非常费时的,它包括页面设计及布局等等,往往一个页面光调样式就要花费很长的时间。假如你是想快速搭建一个后台管理系统,不建议自己去写前端页面代码。很有可能,页面样式还没有出来,就已经放弃了。除非你是单纯想要学习。

建议的做法是,快速套用一个前端模版库,将大体的页面结构搭建出来,然后在上面添加业务功能。 这里采用 AdminLTE 这套模版库,大概效果图如下:

它是基于 bootstrap3 的后台模版,几乎不再需要做 CSS 的开发,一下子就解决了前端界面设计的大问题。下面来看看如何套用它。

下载安装

推荐采用 bower 包管理器来安装,安装命令:bower install adminlte 等待下载就可以了。下载完成后,你会发现bower_components/ 目录里多了好多目录,这些都是它的依赖包。

接下来,我们还要将整个项目下载下来,得到它的示例代码,打开其中的 index.html 文件,分析一下它的页面结构,好便于后面的拆解。如下图:

拆解页面结构

这是后台首页的一个基本结构,分为:main-headermain-sidebarcontent-wrappermain-footercontrol-sidebar ,于是,我们按照这几大模块,拆分页面,分别保存为 layout 模版。在项目 views 下新建两个目录:backend 以及 frontend ,分别对应后台页面以及前台页面。

backend 里再新建 layout 目录以及 index.ejs 文件,layout 中用来保存通用的页面代码,index.ejs 则代表后台首页。拆解完的 index.ejs 代码如下:

<% include ./layout/head.ejs %>
<!-- custom css files -->

<% include ./layout/header.ejs %>
<% include ./layout/main-sidebar.ejs %>

<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
  ... 页面代码 ...
</div>

<% include ./layout/control-sidebar.ejs %>
<% include ./layout/footer.ejs %>

<!-- custom javascript files -->

<% include ./layout/foot.ejs %>

相信你一眼就能看出上面这段代码的意思,将一个页面拆分后,再重组,这就是最终的首页。其他的所有页面都可以通过这样的方式,进行重组,我们要写的代码,仅仅只是修改 <div class="content-wrapper"></div> 里的页面即可。

配置静态资源目录

现在的页面,我们还不能访问,因为页面中链接的 CSS 以及 JS 文件路径都不对,模版里引用的都是相对路径,我们需要将它改为本项目里的绝对路径。打开 layout/head.ejs 文件,部分代码如下:

<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="bower_components/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="dist/css/AdminLTE.min.css">

套用模块的好处就是,这些链接库的地址基本上没太大的变化,我们只需要修改一下根目录就可以了。修改之前,我们需要先配置一下项目的静态资源目录,打开项目根目录下的 app.js 文件,加上一行代码如下:

...
app.use(express.static(path.join(__dirname, 'public')));
// 新添静态资源目录 bower_componennts
app.use(express.static(path.join(__dirname, 'bower_components')));
...

新加的这句代码意思是,将项目中 bower_components/ 目录设置为静态资源访问的根目录,那么以上的那些静态资源,我们就知道怎么引入了,修改如下:

<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="/font-awesome/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="/admin-lte/dist/css/AdminLTE.min.css">

同样的,JS 文件的引用路径也要进行修改,打开 footer.ejs 文件,以 bower_components/ 为根目录,修改对应的 JS 文件路径即可。好了,该页面所有引用的静态资源路径,都已经修改正确了,接下来就是编写页面路由了。

PS. 页面中的 JS 库并非都与自己的项目相匹配,后续要根据实际情况,进行增减。

编写页面路由

页面准备好了,我们需要去编写访问路由,在这点上,Node 就与 PHP 和 Java 有所区别了,Node Web 开发不需要再搭建一个 Web 服务器,PHP 需要 Apache ,Java 需要 Tomcat,而 Node 把它交给你,让你去自定义。我们打开项目 routes/index.js 路由文件,编写代码如下:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

// My backend index page
router.get('/backend', function(req, res, next){
  res.render('backend/index.ejs', {});
});

module.exports = router;

我们加了一个路由 /backend ,渲染了上面准备好的 index.ejs 页面,好了,我们重启一下项目,哦,对,如果已经安装了 supervisor ,不需要手动重启了,直接访问 http://localhost:3000/backend

细心的你可能发现,这个页面有些元素显示不正常,这是因为有些静态资源,我们没有安装而导致的加载失败,这个无需担心,因为实际页面还需要很多的时间去打磨,这个要根据实际业务来决定页面的内容,这里就不去展开了。

接下来的事情,就是改写页面代码了,整体的样式结构都有了,剩下就是对页面的删删减减了。

数据库连接

根据上面的内容,相信你能够添加完页面路由。当全部页面改写完毕之后,我们就相当于得到了,一个静态的后台管理系统。要达到的效果就是:点击左侧相应的导航,可以实现页面跳转,但是没有实际的数据,页面中的数据都是写「死的」,因为我们还没有连接数据库,接下来的工作就涉及到数据库的连接了。

设计数据表

不管你选择哪一个数据库,首先都要做设计数据表,对于数据表的设计以及业务埋点等内容,都可以当作一本书来讲,这里不会有那么详细的内容,来教你如何设计数据表,以及业务功能埋点。但有个建议需要提一下:不要着急去学习 SQL 语言,你可能会说,不学习 SQL 怎么设计数据表呢?怎么操作数据库呢?

这里需要强调的是,不是不需要学,而是开始的时候,不用着急去学。全栈开发重要的是快,不然你全栈干啥呢?在实际的场景中,业务才是最重要的。

首先打开你的 Excel(或在线表格应用) 把表的设计做出来再说。例如下图是我当时一个项目的数据表设计:

最重要的是要去做这个事情,而不是去学数据库,别担心做得不够好,一次一次的实战会让你越来越熟练。做完这个工作,全栈开发基本算是完成了 40%,在这个过程中,一定要深入去分析业务流程,把该用到的不该用到的都要考虑进去,前期可以不做开发,但是必要的字段一定要预留。

编写数据库模块

通常情况下,最好找一个趁手的数据库 GUI 工具,把数据库以及相关的表创建出来。这一步做完之后就是编写连接数据数据的代码了,建议在项目的根目录中,新建名为 db 的目录,用来存放所有关于数据库操作的代码。

这里我们选用 MySQL 数据库,Node 连接 MySQL 还需要安装一个数据库模块。在项目目录,执行命令 npm install --save mysql 即可,安装完成之后,就可以通过下面的示例,尝试连接数据库了。

var mysql = require('mysql');
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'me',
  password : 'secret',
  database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;
  console.log('The solution is: ', rows[0].solution);
});
connection.end();

这是官方提供的一个简单例子,从上面的例子可以得出:使用createConnection(option)方法创建一个连接对象,然后连接对象的connect()方法创建连接,最后使用query()方法执行SQL语句,返回结果作为回调函数的参数rows返回,rows为数组类型。

通常情况下,我们会使用连接池进行连接,连接池具体实现原理就不多展开了,可以查看一下官方文档。下面直接给出使用示例。

// connect.js 使用getConnection方法
var mysql = require('mysql');
var config = require('./config.json');
// 将配置写入配置文件中
var pool = mysql.createPool(config.mysql);
exports.querySQL = function(sql, callback){
    pool.getConnection(function(err,conn){
        conn.query(sql,function(err,rows,fields){
            callback(err,rows,fields); 
            conn.release();   // 不要忘了释放
        });        
    });
}

使用的时候,直接使用querySQL方法即可,如下:

// db.js 查询用户信息
var connect = require('./connect.js');
exports.getUser = function(username,callback){
    var sql = 'select * from user where username = "' + username + '"';
    connect.querySQL(sql,function(err,rows,fields){
        callback(err,rows,fields);        
    });
};

上面示例中,直接将 sql 语句写进代码中了,通过执行 sql 语句来获得数据库数据。

这种方式优点是比较直观,但是缺点就太多了。不安全拼接字符串容易错可扩展性差等等。在实际项目中,我们往往都会选用一款 ORM 框架来连接数据库。

使用 ORM 框架

关于 Node 使用 ORM 框架的介绍,我之前单独写了一篇 Chat,同样是免费的,这里我就不再赘述了。

请点击链接查看:如何使用 Sequelize 框架快速进行 Node Web 开发

前后台交互

通常情况下,前后台交互的开发会涉及到多个部门多个岗位协作完成,除了前后台,近几年也分离出中台的概念,专门提供接口服务。

而对于全栈开发来说,这个环节显然要简单得多了,虽然工作流程可以省,但是,必要的前中后台的框架还是要有的,分离开的好处是尽量降低功能耦合性,便于后期维护。

前台页面获取动态数据通常有的两种方式:

  • 一是,被动获取,页面被渲染,后台传参给前台;
  • 二是,主动获取,前台主动请求,异步获取数据。

后台传参方式

关于页面展示,前面已经讲过了,根据模块改写页面,然后编写路由,就可以进行访问了。但问题是,目前页面上的一些内容都是静态的,例如:站点的名称,Logo以及标题等等。

通常情况下,这些内容不应该是写死在页面中的,而应该是动态的,从后台传来的,甚至应该是写入数据库的,将来做个修改页面,就可以随时修改网站标题以及 Logo 等信息。

这里就涉及到后台传参的相关知识点了。Express 框架给我们提供了三种传参的方式:

(1)应用级别的传参

使用 app.locals 定义参数。例如:在 app.js 文件中,routes(app); 语句之前加入 app.locals.hello = "Node",我们在任何页面中,使用 hello 这个参数都是没问题的。如何使用?建议先了解下模版引擎的用法,这里是 ejs。

(2)路由级别的传参

使用 res.locals 定义参数。例如下面例子:

app.use(function (req, res, next) {
    res.locals.web = {
        title: 'STU INFO',
        name: '数据管理',
        desc: '学生数据管理',
        verifier: 0
    };
    res.locals.userInfo = {
        username: 'admin',
        title: '管理员'
    };
    res.locals.url = req.url.split('?')[0];
    next();
});

通过一个中间件,将站点信息写入 res.locals 对象中,于是我们在对应的页面中,就可以使用 web 以及 userInfo 等对象参数了。例如下面登陆页面部分代码(注意:这里是 ejs 模版引擎的写法):

<div class="login-logo">
    <a href="/" title="<%= web.desc %>"><b><%= web.title %> </b><%= web.name %></a>
</div>

(3)页面级别的传参

使用 res.render() 方法定义参数,这应该是最常用的一种方式,例如页面的 title 属性,每个页面都是不一样的,所以在渲染模版的时候,传入 title 参数,是最合适的。例如登陆页面的路由:

router.get('/login', function(req, res, next) {
  res.render('login', {
      pageInfo:{
          title: '登陆页面',
      }
  });
});

示例中,定义了一个 pageInfo 的对象,将其当作参数传输到了前台。

前台异步获取

前台主动获取数据的例子很多,通常都是通过 Ajax 异步的方式。典型的例子就是在线表格功能,因为后台管理系统几乎离不开在线表格的功能。

关于在线表格的内容,我之前写过一篇 Chat,建议阅读,同样是免费的。链接:如何快速将线下表格数据线上化

这里我重点介绍下,后台数据如何产出?首先要明白,数据肯定从数据库中获得,我们编写数据库 API 的时候,就应该将对应的方法封装好,前台通过访问数据路由,路由调用对应的数据库 API 即可获得数据,并返回前台。

下面看下路由的代码示例:

// api
const studentHandler = require('../../db/handler/studentHandler.js');
// 当前学生列表
router.get('/list', function(req, res, next) {
    let state = 0 ; // 代表当前在校
    studentHandler.getStudentList(state, function(p){
        res.send(p);
    });
});

对应的数据 API 封装在了 db/handler 目录中,路由通过调用 getStudentList 方法,即可获得对应的学生列表,并通过 res.send(p) 方法返回给前台。

再来看看数据库 API 是如何编写的?代码示例如下:

const { Student } = require('../relation.js');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
module.exports = {
    // 根据状态查询学生列表
    getStudentList: function(state, callback){
        Student.findAll({
            where: {
                state: state
            }
        }).then(function(data){
            callback(data);
        }).catch(function(err){
            callback(err);
        });
    },
    // 批量添加学生
    uploadStudent: function(data, callback){
        Student.bulkCreate(data).then(function(){
            callback();
        }).catch(function(err){
            callback(err);
        });
    }
};

重点看下 module.exports 对外的接口,篇幅原因,这里只提供了两个调用方法。如果这里的代码看不明白,请翻回去查看「使用 ORM 框架」这节内容中,推荐的那篇 Chat。

通常情况下,大部分功能都是通过「后台传参」以及「异步获取」这两种方式配合来实现的。

有时候甚至是取代关系,你可能会发现有些后台管理系统,进去之后,从始至终 URL 就没有变过,这一类的后台管理系统的功能实现,全部采用前台异步获取的方式。我本人不太推荐这种方式,因为想要单独展现某个页面的时候,就非常的尴尬,同时也非常不利于页面维护。

线上部署

当项目开发完成,本地测试完毕之后。下一步,就是部署上线了。上线部署原本是个比较繁琐的工作流程,特别针对多系统联动的情况。这里我们不考虑复杂的情况,单纯讲独立开发的后台管理系统如何快速部署到线上?

你可能会说,这有啥好讲的,把本地代码同步到服务器上,然后在服务器上启动不就得了。没错,大体是这么个意思,但是其中还是有一些点需要注意的。

使用进程管理工具启动项目

这里我推荐使用 supervisor,也没其他原因,顺手而已。它是一个进程管理工具,当线上的 Web 应用崩溃的时候,它可以帮助你重新启动应用,让应用一直保持线上状态。

安装方法很简单,在命令行中,输入 npm install -g supervisor 即可。安装完成之后,我们需要修改一下项目的启动命令,打开 package.json 文件,编辑如下:

...
"scripts": {
  "start": "supervisor --harmony -i views/,public/ ./bin/www",
},
...

supervisor --help 查看使用方式,以上命令,配置了 --harmony 模式启动 Node ,同时,使用 -i 参数忽略了 views/ 以及 public/ 目录。

修改完成后,我们依然使用 npm start 启动项目,不一样的是,当我们修改了除 views/ 以及 public/ 目录以外的文件后,服务将会自动重启,以确保线上一直运行的是最新版项目。

使用 Git 同步代码

将代码从本地拷贝到线上,有很多种办法。这里推荐使用 Git 工具,如果没有自己的 Git 服务器,那么就使用 GitHub 类公共 Git 服务平台。大可不必担心代码泄露的问题,GitHub 不是已经提供私有仓库免费的功能了嘛,没事,放心用吧。

具体操作我就不再赘述了。

使用 Nginx 反向代理服务器

为了确保性能,不建议直接在线上环境,通过 npm 或 supervisor 直接启动项目,虽然 node 本身的性能并不差,建议还是在 node 服务中间,再加一层 Web 服务器当作方向代理,与 Node 最配的当然是 Nginx 了。

安装 Nginx 不必多说,下面贴出 Nginx 对应的配置文件内容供参考。

server {
    listen       80;
    server_name xxx.com

    charset utf-8;

    #此处配置你的访问日志,请手动创建该目录:
    access_log  /var/log/nginx/js/access.log;

    location / {
        try_files /_not_exists_ @backend;
    }

    # 这里为具体的服务代理配置
    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;
        proxy_set_header   X-Forwarded-Proto $scheme;

        #此处配置程序的地址和端口号
        proxy_pass http://127.0.0.1:8080;
    }
}

关键记住最后一句:proxy_pass 代理的是哪个程序地址和端口号。

最后,启动 Node 服务,启动 Nginx 服务,然后访问对应服务地址,大功告成。

总结

这篇 Chat 从 5 个方面讲述了全栈开发一个后台管理系统的大致流程以及部分实战内容。由于内容量远超出了预期,在写的过程中,不断的删减了很多篇幅。使得 Chat 的整体效果,没有达到我的预期。

也许这个内容,通过一篇 Chat 确实很难容下吧。所以,我计划写完整个课程,内容包括 Node 基础、进阶应用以及Web 实战等。从最基本的原理开始介绍,到最后全栈开发实战。

课程以开源免费的形式公开,GitHub 仓库地址:Node 全栈开发入门课程,更新频率不定,欢迎关注。

同时,也可以关注我的微信公众号:自由产品之路,以便了解最新进展。

谢谢观看~

阅读更多

把事情持续做下去,本身就是目的

看起来,好像又是一篇鸡汤文章,强调所谓的「坚持」。但是,这次想写点不一样的东西。自认为有点价值,希望你能读完。

1.

首先看一个例子,不知道是在哪篇罗辑思维上读到的。讲的是哥伦布发现新大陆的例子。当然不是讲历史,讲的是背后的故事。

我们在课本里学到的知识,哥伦布是个伟大的航海家,历经千辛万苦,在海上漂泊几个月,终于发现美洲新大陆的励志故事。

然而背后的故事可能没有那么完美。

首先,哥伦布最初的目的是要去亚洲,而不是美洲。他到临死前都不承认,自己并没有到达亚洲,而是到了美洲大陆。为什么他一心想要去亚洲?并非为了探险,而是听说亚洲的印度以及中国是古老的东方大国,遍地是黄金。基于这样的目的,才是他冒险航海去亚洲的动力。

其次,在 15 世纪末那个年代,还不能准确的知道地球的形状与大小。他当时预估的航海距离只有实际的 1/8 。

所以,哥伦布发现美洲新大陆,并非从一开始就准确知道目标,甚至与实际相差甚远。假如他从最开始就知道真相,距离远比想象的远,而且到达的地方不是「遍地黄金」的亚洲,而是「荒无人烟」的美洲。不知道他是否还会继续前往,假设他不去了,断然就没有后续「发现新大陆」的伟大航海家了。

2.

罗胖给我们的解释是「往往是认知谬误,才有可能成功。如果你的认知正确,你可能什么也做不成」。他的意思是,假如从一开始,就意识到问题的困难性以及复杂性,很多事情甚至都不会开始。

不可否认,这是一个不错的解释。而我却体会到另外一层意思,「很多时候,事情的目的并不重要,重要的是持续做下去」。哥伦布最终要去哪并不重要,重要的是,他出发了,并且没有回头。

我想强调的不是「认知谬误的问题」,而是「目的的问题」。目的会随着事情的进展不断的变化,与其在最开始过分的强调目的,不如先把简单的事情持续做下去。目的性太强,往往不是好事。

3.

这段时间,我连续跑步了 10 天,每天将近半个小时,这是时隔一年多后,重新开始运动。原本我预估开始的几天应该很难熬过,因为太长时间没有运动,头几天必然会伴随着肌肉的酸痛,这可能让自己坚持不下去,然而事实并非我料想的那样。

第二天,浑身就已经酸痛不已,第三天第四天,几乎是伴随着痛楚跑完了,但奇怪的是,并没有出现所谓的抵抗情绪。第五天第六天,身体已经适应,每天都会有进步(配速上),第七天第八天,还在挑战自己前一天的成绩,一直在进步。

第九天,也就是昨天,我开始有了抵抗情绪,不想去跑了。相对之前的状况,明明条件更好(身体不酸痛),反倒心理上开始抵触,然后给自己找各种理由,天气不好,降温了,风太大了……

好在抵抗情绪并不强烈,我依然完成了任务。在跑的过程中,想明白了这件事情,抽空记了下来。

目的在不自觉中就发生了改变,不断给自己施加「要进步」的压力,总有一天会崩塌。其实,很多事情都是这样,随着事情的发展,渐渐忘了自己的初衷,忘了自己当时为什么开始。

开始做,就坚持做下去,持续做下去本身就是目的。过早的追求目的化,或许并不是一件好事。

第十天的跑步,我又回到了第一天的成绩,我退步了吗?并没有,我又多坚持了一天,我进步了。

阅读更多

在职场中,别那么矫情

今天想写点私事,工作这么多年,竟然才明白这么个道理。职场不同于生活,不要太过于矫情,甭管你认为自己多有价值,只要你离开了,对于公司来说,你什么都不是。

1.

离开前东家已经有一段时间了,在最后的大半年里,独自一人开发了一套数据平台,功能没有多复杂,但确实解决了部门长期以来,数据收集汇总的痛点。对于数据提交人员以及整理者来说,帮他们省了不少的功夫。直属领导也表示认可(Maybe),说要给我申请奖金,没成想,我竟然提出了离职,离职的原因与工作无关,属于个人原因。

离职半个月后,收到了最后一个月的工资,一看比平常多了一笔钱,由于已经无法登陆人事系统,自以为是给我发的奖金,还略有感动,人都已经离职了,还给申请了一笔奖金。

离职前的谈话,气氛非常的融洽。一方面表示想回来了,随时欢迎,虽然是客套话,但是听着挺舒服;另一方面,表示系统后续如果有新的需求,可以让我有偿帮忙开发,因为那个数据平台是我独立完成,另找人开发成本可能更高。

心中想着,这也算是一个 Happy Ending,在离职后的那篇文章里,我更多表现出的是怀念。

2.

然而,事实并非我想的那样,就在上周,直属领导给我电话,嘘寒问暖几句后,就谈到了最后一个月工资的事情,我原本以为是告诉我一声,对我作出的贡献表示感谢。而实际的情况是,让我把这部分钱退回去,说是组织的大领导以奖金的名目申请了一笔钱,以工资奖金的形式发放之后,然后让大家交上去,再用这笔钱用做它途(公司惯用的「洗钱」操作)。

我听完第一反应是:「好的,我转给你」。在我的价值观里,不是我的,我永远不会要。虽然这笔钱,我完全可以拒绝退还。甚至因为信任,我连求证都没做,就把钱转回去了,转账的时候,除了有一些心寒,无其他感觉。

原本是一件小事,为何我会这么在乎?回家告知媳妇,她说很正常啊,是你自己自以为是,职场原本就是这样,只有家人才是最可靠的。我说,道理虽然是这样,但是心里就是不是滋味…

偶然机会,我随手打开了曾经开发的系统,发现怎么也登录不上去。利用非正常手段(毕竟是我开发的),登录到后台看了下,原来自己的账号都被禁用了。同样是再正常不过的一件事,我却感受到冷漠,当时竟然产生了作恶的想法(删库)。

3.

当然,我没有那么做。回头想了想,别人并没有做错什么事情,只是自己太认真,太矫情,把人家随口说出的客套话当真了。现在想来,已没有了任何情绪,只是对离职前谈话场景中,那个 Happy Ending 的画面,感到恶心。

这些事情,对于职场阅历丰富的人来说,可能再正常不过了。在公司任职期间,你所产生的价值本就是属于公司,公司给你发工资,已经给你报酬了,不要认为还会有其他所谓的情意,否则,就是你太过矫情。

对于我来说,这是一个教训。原本这就是职场,只是你发觉太晚。这件事后,我对自己说,什么事情,都别那么矫情。总想着自己付出的努力,都必须立马有回报。现在我慢慢开始懂了,付出的努力,终归还是有回报的,只是没有那么快。在没有获得回报之前,什么都不要想,修炼自己就好了。

正文完。


上面内容仅仅是我个人的经历,请不要以偏概全。

在我的职场中,还是有例外的。至今仍然非常感谢我的第一位领导,能感受到她当时是真心在教我工作。

阅读更多

祝自己生日快乐,而立之年之际

子曰:”吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩。”
《论语·为政》

截止 2018 年底,人生几件大事基本上定了下来,眨眼间,已然是而立之年,生活按部就班的进行着。如果说前面 25 年的学生生涯,能勉强拿 50 分的话,通过工作这 5 年的努力,我的人生应该能混个及格,但是离优秀还有很长一段距离。

被安排的生活

我从小都是一个乐于安逸的人,很多事情的发展,大多数都不是我所决定的,而是被生活一步一步地推着往前走,这个过程虽然进步很慢,倒还算是平稳。

  • 从南方小镇,到北方上学
  • 毕业后,来到北京这座城市工作
  • 工作第 2 年,跟恋爱 7 年的她,修成正果
  • 工作第 4 年,基本定居在了这座城市
  • 工作第 5 年,小宝宝出生

过去 30 年,生活被安排得一帆风顺,虽然有些波折,但几乎没有什么大风大浪。这样的生活已然很美好了,抛开短暂的困难,长远望去,可以预见,将来只会越来越好。

内心的煎熬

工作的这 5 年时间,总体来说还算比较满意。但却感觉越来越不会「工作」了,经历得多了,越来越不认识自己,越来越不适应这个环境。

面对太多的真真假假,虚虚实实,一度让自己迷失了方向。人们总说,做人要「内方外圆」,而更多时候,环境只会让你变得「内圆外圆」,可是我发现,自己竟然变得「内方外方」,尤其是最近这两年。

本以为看得多了,见得多了,也就习惯了,看惯了。然而事实并非如此,真相是,看得越多,越看不惯,就越想改变点什么,改变不了环境,那就改变自己。这种感觉,越来越强烈。越是强烈,越感到力不从心。

媳妇说,你终于开始想事情了,白头发越来越多了。时不时的想要开导我一下。

所追求的方向

三十而立,四十而不惑

而立之年,指的是一个人到了三十岁,应该是人格自立、学识自立、事业自立的年龄。如果按这个标准来说的话,倒也没有辜负走过的这三十年。而要达到「不惑」,可能还需要 10 年的时间吧。

改变不了环境,那就改变自己。改变自己,不是指逆来顺受的接受环境的压力,而是要奋起直追,将压力变成前进的动力。

赶在而立之年的尾巴,才定下自己想要前进的方向,不知是否值得庆幸,希望接下来的 5-10 年,不断提高自己,提前完成不惑。

就这样,祝自己生日快乐,祝大家节日快乐。

阅读更多

为了用 Markdown 写微信公众号,自定义了一个编辑器

身边应该有不少朋友在写公众号,不知道你们用的是什么编辑器呢?

上面这些编辑器,应该是小编们用得最多的几款,它可以快速添加各种样式插件,使文章更吸引眼球,让用户看起来很爽,欲罢不能。毕竟他们最注重的是阅读量,而非内容。

我也有写文章的习惯,但我不是小编,格式上只追求简约大方即可,重点在文字上,希望内容能产生一些价值。自从用了 Markdown 标记语言后,写文章就再也没用过其他的编辑器。

什么是 Markdown?

什么是 Markdown?它是目前最流行的写作标记语言,没有之一。它能够通过简单标记就能实现文档格式,让写作者专注于内容

由于它是纯文本,所以它可以像代码一样进行版本管理,同时,它对 Web 也非常友好,方便转成 HTML,甚至直接将 Markdown 文档生成静态站点,很多免费的自建博客服务都是采用的这种方案。目前几乎所有主流的写作平台,其编辑器都支持 Markdown。

一些 Markdown 编辑器,甚至在其基础上,扩展了很多功能。例如:公式及表格的支持。前段时间,一个外国小哥的数学笔记火了,全程像敲代码一般,紧追数学老师板书,记了 1700 + 页笔记。感兴趣可以看下这篇文章:

1700页数学笔记火了!全程敲代码,速度飞快易搜索,硬核小哥教你上手LaTeX+Vim

为什么不支持 Markdown?

虽然支持它的编辑器多到数不过来,可是国内最著名的内容发布平台 —— 微信公众号,它的编辑器就不支持 Markdown,可算折腾坏了一帮写作者。为什么不支持?可能有两个原因吧。

用户学习成本

Markdown 语法虽然简单,但对大部分人来说,毕竟也是一门新的知识,这就增加了用户的学习使用成本。你要知道,绝大部分的人,哪怕一点点的新东西都是接受不了的,微信没把编辑器做成 Word 的样式,我觉得已经是克制了的。

支持的样式并不完整

Markdown 只提供了简单的样式标签,例如:标题,引用,链接等等,而对于复杂的样式,例如文字背景色等等,它是不支持的。

而微信面对的是全体创作者,单纯使用 Markdown 编辑器定然不能满足基本需求,而兼容 Markdown 的需求优先级并不高。

所以,一直以来,微信并不支持 Markdown,但我猜测,支持 Markdown 的功能迟早还是会做的。

Markdown 写公众号的痛

你不是说了 Markdown 对 Web 非常友好嘛,转成富文本样式,然后贴到微信公众号的编辑器里,就可以了呀。道理是没错,但这正是折腾人的关键所在,因为 Markdown 只定义了基础的标签,而没有样式。

于是,各家的 Markdown 编辑器导出的样式都是有差异的,并且,跟微信公众号上的默认样式也不兼容。所以,贴进去后,显示出来的样式各不相同,如果文章里再有一些非标准的 Markdown 语法,显示出来更是五花八门了。一直以来都很纠结,对于一些样式复杂的文章,几乎就不发公众号了,因为调整样式,就要耗费大量的精力。

除了样式兼容性问题,还有微信公众号对外部链接限制问题,公众号内文章只允许引用微信内部链接,不支持外部链接。文章里若添加了第三方链接,我们只能硬生生的将 链接重新再贴进去。或者通过「阅读原文」链接原文地址。

解决方案

微信公众号编辑器都几乎成为一种新的行业了,文章开头的那些编辑器,都能自力更生了,说明对小编们来说,公众号编辑器问题真的是一个痛点。

而对于适配公众号样式的 Markdown 编辑器,几乎没有,之前接触过 Md2All,功能上很完整,但是它并非只针对微信公众号,所以在细节方面的处理,总是差强人意。

一直想着,应该写一款符合自己样式风格的公众号编辑器。然而,一个偶然机会,发现了它,花三小时写这个工具,只为一分钟拯救公众号排版

在程序世界里,有句老话说「不要重复造轮子」,你费好大劲去研究怎么做,没准别人已经都出成品了,这样就造成了资源浪费。幸运的是,我的「轮子」还没有开始动手呢。

总要做点自己的贡献

这款公众号编辑器基本符合我的心里预期,简单的界面,优雅的样式风格,你看到的这篇文章的样式,就是通过它生成的。

它还有一些特殊的增强项,比如将外部链接自动生成脚注,放在文章底部;支持日语注音假名和汉语拼音,例如:小夜時雨【さ・よ・しぐれ】 上海【Shàng・hǎi】

虽然默认的样式已经基本满足我了,但还是缺少一些自定义的功能,比如字体大小的设置,以及主题切换等功能,作者并没有实现。但是,作者已经将源代码开源了,而我们不能只知道一味索取,而不懂得付出。

于是,我便动手实现了这部分的功能。好在代码并不复杂,简单研究一下,周末花了点时间,就把这部分的功能填补上了。

最后

这个过程还是很有趣,不仅解决了自己的痛点,还学到了知识,同时还做出了一点点自己的贡献。

如果你也喜欢这篇文章的样式,这个微信公众号编辑器分享给你。微信公众号格式化编辑器 - 作者原版本

根据源代码,我重写了一个后台服务版本,计划提供一些在线 API 的功能。仓库地址:wechat-format-server: 基于 wechat-format 的服务端版本

阅读更多