my-blog icon indicating copy to clipboard operation
my-blog copied to clipboard

:book:My blog

Results 65 my-blog issues
Sort by recently updated
recently updated
newest added

### 前言 自己学习node也有些时日了。终于在前些日子,自己的第一个node项目终于上线跑了,也第一次在node方面赚到了外快的甜头。项目是一个企业内部的`oa系统`,不大。抽离表面看本质,就是一个`express框架`下的链接`mongodb`展示数据并可对数据进行`CRUD操作`的应用。代码和demo,会在之后抽离掉一些具体业务内容后放出。下面来杂谈一些总结,不足,和随想。 #### 1,关于配置文件 私认为配置文件存放着两种类型的信息:`项目运行的环境信息`和`关于项目结构的信息`。前者保证了项目需要在另一个(地理/运行)环境跑的时候可以迅速方便地迁移,后者保证项目结构的统一性,存就在这里存,改就在这里改,以点辐射面,其他的controller,service神马的要东西都来这里取,保证了项目的伸缩性。说俗话就是“这些那些都可以根据要求自己来配”。由于小项目的代码一开始是从小伙伴那接手来的,一开始并没有专门的存放各类配置文件的地方,每个业务点里的信息都是写死的,导致一个业务信息的小修改,往往就变成代码十几处的大改动,想改个mongodb配置找半天。幸好悬崖勒马,回头是岸,岸就是`/conifg/xxConfig.js`。。。 #### 2,关于视图模板 其实还是一个伸缩性的问题,对于要做若干个`大体类似`,`细节略有不同`的页面,可能大家和我的第一反应都是先做第一个页面,然后`复制黏贴`几份,把细节改一下,OK收工。的确,这样做可能在短时间内是`最快`完成任务的选择。自己的小项目当时由于第一阶段要立刻有一个展示demo,时间略紧,采取的就是这个做法:几个项目管理页面,先写好一个,复制黏贴,改改细节,轻松愉快。但是当后续需要再往里添加功能时,恍然一悟,这`坑`真是给自己挖得够大,一处实现,处处黏贴,处处修改,处处可能遗漏了某个修改,出小差错的可能随着规模的增加`线性增长`,大修改更是苦不堪言,又闻到了上文中那种业务结构没有设配置文件时的熟悉又恶心的味道,但又覆水难收,只得把坑继续堆叠。痛定思痛,在今后写视图模板时,一定要将视图模板尽量`组件化`,公用的组件公用,各页面略有不同的地方也写成组件分开存放,这样修改才能有效率和集中。自己有时会疑惑,为什么一个有经验的程序员总能`又快速又正确`的完成任务呢?可能就是因为他对业务对视图对各种需求的`抽象能力`吧。合理的抽象便意味着更少的代码量,同时也意味更低的出低级差错概率,真是有点鱼和熊掌兼得的味道。。吾将继续锻炼和求索。。 #### 3,关于各种轮子 node的迅猛发展,离开不了npm的百花齐放和神一样的用户体验,迄今为止,npm上`Top100`的库也是聆郎满目,几乎涵盖了开发需求的方方面面,自己总会纠结于在项目里到底用它们呢,还是不用呢?用的话,总觉得小小需求有点“杀鸡用牛刀”,不用的话,又在做稍微有点复杂的功能时有种重复造轮子的不爽感。对于这个纠结,自己现在的态度就是这些Top100库`只要能用到就用`。因为我们总是很难预知项目的发展,现在的简单逻辑不代表以后还是只用这些逻辑。npm这么方便,反正用了也不麻烦嘛,如果以后要用库,现在这些实现类似逻辑的代码该如何是好?比如自己在项目里做一些fs操作时,尽管知道有`fs-extra`这样的好轮子,但是想想简单逻辑就自己写吧,但随着项目的发展,一会需要写个`mkdir -p`,一会需要写个`rm -rf`。为了悬崖勒马,果断用上`fs-extra`,以至于留下一堆原生fs操作代码与fs-extra操作代码共存,看上去极其不和谐。。 #### 4,关于缓存 知道它的魔力,但是项目对数据的实时性要求较强,并且使用规模较小,想不到好的缓存切入点(除了静态资源)。。感觉加缓存是在徒增额外的复杂,有点吃力不讨好。。 #### 5,关于mongoose的virtual type 以前总纠结于virtual type何时用?现在的感觉是,只要这个数据是可以用现有数据库里的数据`简单算出来`的,就用`vurtual type`。 #### 6,关于mongodb(v2.6) 在初学node时,总会在各种地方看到mongodb已是node的缺省数据库,标配之类的话。的确,json文档模型,js操作语言看起来与node如此天造地设。但是随着时间的流逝,项目的深入,自己越来越有一个奇怪的感觉,mongodb真是node不可质疑的`真命天子`的吗?尤其是在写`传统的CRUD应用`时。众所周知,mongodb数据操作的`原子性`仅在`单个document层面`,并没有传统SQL的那些`封装事务`,`roll back`这类的功能,直接写多document的数据改写操作,其中的一步报错了,可能就要承担数据不一致的风险。当然解决方法也有如[“执行两个阶段的提交”](http://docs.mongoing.com/manual-zh/tutorial/perform-two-phase-commits.html),但总是透着一股`hack`味。或许,`传统的SQL`才更适合这类数据结构固定,并且大量依赖`“事务”`的应用吧?总感觉这会比mongodb省心省力不少。mongodb可能更适合那些数据以`结构灵活多变`,`独立性强几乎相互不耦合`为特色的应用。所以是不是该以`应用的数据模型特点`来选择自己的真命天子数据库,而不是语言。。? #### 7,关于代码的职责分离 现在的结构:...

Blog

## 前言 之前在看许多站点的源代码时,类似这样的代码总是屡见不鲜: ``` js var sign = require('./controllers/sign'); var site = require('./controllers/site'); var user = require('./controllers/user'); var message = require('./controllers/message'); var topic = require('./controllers/topic'); var reply = require('./controllers/reply'); var...

Blog

## 前言 大名鼎鼎的[co](https://www.npmjs.com/package/co)正是TJ大神基于ES6的一些新特性开发的异步流程控制库,基于它所开发的[koa](https://www.npmjs.com/package/koa)更是被视为未来主流的web框架。之前在论坛也看了不少大神们关于co源码的分析,不过co在升级为4.X版本时,代码进行了一次颇有规模的重构,从先前的基于[thunkify函数](https://www.npmjs.com/package/thunkify),改变成了现在的**基于Promise**,并且可能在未来版本移除对thunkify函数的支持。正好小弟最近也在看TJ大神的源码,也来分享一下co(4.X)的实现思路以及源码注解。 ## 进入正题 以下是两段co(4.X)的经典使用示例: ``` js co(function* () { var result = yield Promise.resolve(true); return result; }).then(function (value) { console.log(value); }, function (err) { console.error(err.stack); }); ``` ```...

Blog

## 前言 为了更好分享和发布自己的内容,现在提供RSS服务的网站和社区非常之多,现在基于`python`,`java`等平台的RSS爬虫非常之多,所以结合node高并发特性,自己用node写了一个RSS爬虫——`rss-worker`。 代码地址:[这里](https://github.com/DavidCai1993/rss-worker) , 欢迎star,欢迎follow。 ## 简介 `rss-worker`是一个持久的可配的rss爬虫。支持多URL的并行爬取,并且会将所有条目按时间顺序进行保存,保存格式为`"时间\n标题\n内容\n\n"`来供使用或分析,支持的保存方式有`fs`与`mongodb`。 ## 结果演示 一个抓取`https://github.com/alsotang.atom`,`https://cnodejs.org/rss`,`http://segmentfault.com/feeds/blogs`内容24小时的输出(2015/5/6 19:30至2015/5/7 19:30 ): [点这里](https://raw.githubusercontent.com/DavidCai1993/rss-worker/master/example/output.txt) ## 主要流程 爬取:并发地对所有指定URL使用[superagent](https://www.npmjs.com/package/superagent)发送请求,并在所有URL全部爬取完毕后根据指定间隔再次发出爬取请求 结果更新:在内存中缓存了一个`lastUpdate`字段,与每次的爬取结果作比对 支持`fs`和`mongo`存储:利用`persistence`层提供统一接口,对外隐藏不同实现 ## 安装使用 直接通过npm: ``` SHELL npm install rss-worker --save...

Blog

## 前言 这将是一个分为两部分,内容是关于在生产环境下,跑`Express`应用的最佳实践。第一部分会关注安全性,第二部分最会关注性能和可靠性。当你读这篇文章时,假设你已经对`Node.js`和web开发有所了解,并且对生产环境有了概念。 ## 概览 生产环境,指的是软件生命循环中的某个阶段。当一个应用或API已经被它的终端用户所使用时,它便处在了生产环境。相反的,在开发环境下,你任然在不断得修改和测试代码,应用也不能被外部所访问。 开发环境和生产环境经常有很大的配置上的和要求上的不同。一些在开发环境下可以使用的东西,在生产环境下,它们不一定是能够被接受的。例如,在开发环境下,我们需要详细的错误日志信息来帮助我们debug,而在生产环境下,这则会带来安全隐患。又比如,在开发环境下,你不必考虑可伸缩性和可靠性还有性能的问题,但这些在生产环境下都非常重要。 以下是将`Express`应用部署于生产环境中的一些安全性方面的最佳实践。 ## 不要使用被弃用或不可靠的`Express`版本 `Express` 2.x 和 3.x 已经不再被维护了。这些版本上的安全和性能问题都将不会被修复。所以不要使用它们!如果你还没有迁移至`Express` 4,可以参考这份[迁移指南](http://expressjs.com/guide/migrating-4.html)。 同时也保证不要使用在[安全更新列表](http://expressjs.com/advanced/security-updates.html)中列出的这些不可靠版本的`Express`。如果你不巧使用了,请升级至稳定版,最好是最新版。 ## 使用`TLS` 如果你的应用需要处理或传输敏感数据,请使用`TLS`来确保连接和信息的安全。这项技术会在数据被从客户端发出前加密它。尽管`Ajax`和`POST`请求中发出的数据看上去并不可见,但它们的网络环境仍可以被嗅探和进行中间人攻击。 你可能已经对`SSL`加密有所了解。`TLS`是进化版的`SSL`。换句话说,如果你正在使用`SSL`,请更新成使用`TLS`。大多数情况下,我们推荐使用`Nginx`来处理`TLS`。关于如何在`Nginx`(或其他服务器)上配置`TLS`,请参考[推荐的服务器配置(Mozilla Wiki)](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_Server_Configurations)。 另外,有一个可以很方便地取得`TLS`证书的工具是[Let’s Encrypt](https://letsencrypt.org/about/)。它是一个免费的,自动化的,开放的`CA`。由`ISRG`提供。 ## 使用`Helmet` `Helmet`通过适当地设置一些HTTP头,来帮助你的应用免受一些广为人知的web攻击。 `Helmet`其实就是九个设置与安全相关的HTTP头的中间件的集合: - [csp](https://github.com/helmetjs/csp)...

Blog

## 前言 ECMAScript 2015(又称ES6)提供了一个前端`JavaScript`缺失已久的特性 —— 模块。ES2015中的模块参考了CommonJS规范(目前`Node.js`的模块规范)以及AMD规范,并且尽可能的取其精华,去其糟粕: - 它提供了简洁的语法 - 以及异步的,可配置的模块加载 这篇文章将会专注于ES2015的模块语法以及注意点。关于模块的加载和打包,将会在另一篇文章中细述。 ## 为什么要使用模块? 目前最普遍的`JavaScript`运行平台便是浏览器,在浏览器中,所有的代码都运行在同一个全局上下文中。这使得你即使更改应用中的很小一部分,你也要担心可能会产生的命名冲突。 传统的`JavaScript`应用被分离在多个文件中,并且在构建的时候连接在一起,这稍显笨重。所以人们开始将每个文件内的代码都包在一个自执行函数中:`(function() { ... })();`。这种方法创建了一个本地作用域,于是最初的模块化的概念产生了。之后的CommonJS和AMD系统中所称的模块,也是由此实现的。 换句话说,现存的“模块”系统是使用已有的语言特性所实现的。而ES2015则通过添加适当的新的语言特性,来使之官方化了。 ## 创建模块 一个`JavaScript`模块就是一个对其他模块暴露一些内部属性/方法的文件。我们在这里仅会讨论浏览器中的ES2015模块系统,并不会涉及`Node.js`是如何组织它自身的模块的。一些在创建ES2015模块时需要注意的点: ### 每个模块都有自己的上下文 和传统的`JavaScript`不同,在使用模块时,你不必担心污染全局作用域。恰恰相反,你需要把所以你需要用到的东西从其他模块中导入进来。但是,这样也会使模块之间的依赖关系更为清晰。 ### 模块的名字 模块的名字由它的文件名或文件夹名所决定,并且你可以忽略它的`.js`后缀: - 如果你有一个叫`utils.js`的文件,那么你可以通过`./utils`这样的相对路径导入它...

Blog

在 Node.js 中,Buffer 常常用来存储一些潜在的大体积数据,例如,文件和网络 I/O 所获取来的数据,若不指定编码,则都以 Buffer 的形式来提供,可见其地位非同一般。你或许听说过,Buffer 的创建,是可能会经过内部的一个 8KB 池的,那么具体的规则是什么呢?可以创建一个新 Buffer 实例的 API 那么多,到底哪些 API 会经过,哪些又不会经过呢?或许你在阅读文档时,还看到过许多形如 `Buffer#writeUInt32BE` , `Buffer#readUInt32BE` 等等这类固定位的数字的读写操作,它们具体是如何实现的呢? 现在让我们一起跟着 Node.js 项目中 `lib/buffer.js` 中的代码,来一探究竟。 ## 8KB 池分配规则 统计一下,当前版本的...

Blog

## 前言 今天闲来时看了看ES7中的新标准之一,装饰器(Decorator)。过程中忽觉它和`Java`中的注解有一些类似之处,并且当前版本的`TypeScript`中已经支持它了,所以,就动手在一个Web应用Demo中尝鲜初体验了一番。 (装饰器中文简介:[这里](http://es6.ruanyifeng.com/#docs/decorator)) 最终效果: ``` ts // UserController.ts 'use strict' import {router, log, validateQuery} from './decorators' import {IContext} from 'koa' export class UserController { @router({ method: 'get', path: '/user/login'...

Blog

在`Node.js`中,流(`Stream`)是其众多原生对象的基类,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。在它对外暴露的接口中,最为神奇的,莫过于导流(`pipe`)方法了。鉴于近期自己正在阅读`Node.js`中的部分源码,也来从源码层面分享下导流的具体实现。 ## 正题 以下是一个关于导流的简单例子: ``` js 'use strict' import {createReadStream, createWriteStream} from 'fs' createReadStream('/path/to/a/big/file').pipe(createWriteStream('/path/to/the/dest')) ``` 再结合[官方文档](https://nodejs.org/dist/latest-v5.x/docs/api/stream.html#stream_stream),我们可以把`pipe`方法的主要功能分解为: - 不断从来源可读流中获得一个指定长度的数据。 - 将获取到的数据写入目标可写流。 - 平衡读取和写入速度,防止读取速度大大超过写入速度时,出现大量滞留数据。 好,让我们跟随`Node.js`项目里`lib/_stream_readable.js`和`lib/_stream_writable.js`中的代码,逐个解析这三个主要功能的实现。 ### 读取数据 刚创建出的可读流只是一个记录了一些初始状态的空壳,里面没有任何数据,并且其状态不属于官方文档中的流动模式(flowing mode)和暂停模式(paused mode)中的任何一种,算是一种伪暂停模式,因为此时实例的状态中记录它是否为暂停模式的变量还不是标准的布尔值,而是`null`,但又可通过将暂停模式转化为流动模式的行为(调用实例的`resume()`方法),将可读流切换至流动模式。在外部代码中,我们可以手动监听可读流的`data`事件,让其进入流动模式: ``` js...

Blog

`Node.js`中的流十分强大,它对处理潜在的大文件提供了支持,也抽象了一些场景下的数据处理和传递。正因为它如此好用,所以在实战中我们常常基于它来编写一些工具 函数/库 ,但往往又由于自己对流的某些特性的疏忽,导致写出的 函数/库 在一些情况会达不到想要的效果,或者埋下一些隐藏的地雷。本文将会提供两个在编写基于流的工具时,私以为有些用的两个tips。 ## 一,警惕`EventEmitter`内存泄露 在一个可能被多次调用的函数中,如果需要给流添加事件监听器来执行某些操作。那么则需要警惕添加监听器而导致的内存泄露: ``` js 'use strict'; const fs = require('fs'); const co = require('co'); function getSomeDataFromStream (stream) { let data = stream.read(); if (data)...

Blog