Talk is cheap, show me the code. 完整源码 Git 仓库地址: github.com/salonnlee/c…
Node 基础知识介绍
欢迎进入 Node.js 的世界
Node.js 是一个 JavaScript 运行平台, 其显著特征是它的异步和事件驱动机制, 以及小巧精悍的标准库.
Node 和 JavaScript 的优势之一是它们的单线程编程模型.
非阻塞 I/O的意思是, 程序可以在做其他事情的时候发起一个请求来获取网络资源, 当网络操作完成时, 将会运行一个回调函数来处理这个操作的结果.
即便你只有一个单线程、单进程的 Node Web 应用, 它也可以同时处理上千个网站访客发起的连接.
V8 一个值得称道的特性是它会被 JavaScript 直接编译为机器码.
Node 的核心模块就相当于其他语言的标准库, 它们是编写服务端 JavaScript 所需的工具. JavaScript 标准本身没有任何处理网络的东西, 甚至连处理文件 I/O 的东西都没有. Node 以最少代码给它加上文件和 TCP/IP 网络功能, 使其成为一个可用的服务器端编程语言.
文件系统库
(fs、path)、TCP 客户端和服务端
(net)、HTTP 库
(http、https)和域名解析库
(dns), 还有一个经常用来写测试的断言库
(assert), 以及一个用来查询平台信息的操作系统库
(os).
Node 支持 Chrome 调试协议, 可以在运行程序时加上 -inspect 参数:
node --inspect --debug-brk
复制代码
Node 程序主要可以分成三种类型: Web应用程序
、命令行工具和后台程序
、桌面程序
. 提供单页应的简单程序、REST 微服务以及全栈的 Web 应用都属于Web 应用程序. 命令行工具是 npm、Gulp 和 Webapck, 后台程序就是后台服务, 比如 PM2 进程管理器. 桌面程序 一般是用 Electron 框架写的软件, 如 Atom, Visual Studio Code.
libux 是提供快速、跨平台、非阻塞 I/O 的本地库.
Node 编程基础
require 是 Node 中少数几个同步 I/O 操作. 但在 I/O 密集的地方尽量不要用 require , 所有同步调用都会阻塞 Node , 知道调用完成才能做其他事情.
最终在程序里导出的是 module.exports , exports 只是对 module.exports 的一个全局引用. 所以不能把 exports 设置为别的东西, 比如 exports = Curency
. 这样 exports 就不再指向 module.exports 了.
一个 Node HTTP 服务器实例就是一个事件发射器, 一个可以继承、能够添加事件发射及处理能力的类(EventEmitter) .
Node 中的大多数内置模块在使用回调时都会带两个参数: (err, data) .
Node 的事件轮询会跟踪还没有完成的异步逻辑. 只要有异步逻辑未完成, Node 进程就不会退出. 一个持续运行的 Node 进程对 Web 服务器之类的应用来说很有必要, 但对于命令行工具这种经过一段时间后就应该结束的应用却意义不大. 事件轮询会跟踪所有数据库连接, 直到它们关闭, 以防止 Node 退出.
实现串行化流程控制和并行化流程控制.
Node Web 程序是什么
目录结构:
- app.js 程序入口
- models/ 数据库模型
- views/ 页面模板
- routes/ HTTP 请求处理器
- middleware/ 中间件
- public/ 静态资源文件夹
ORM(Object Realational Mapping, 对象关系映射
)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单来说, ORM 通过使用描述对象和数据库之间映射的元数据, 将程序中的对象自动持久化到关系数据库中.
SQLite 是进程内数据库, 你添加的所有数据都会写到一个文件里, 也就是程序停掉后再启动时数据还在.
RESTful API 已经搭建好了, 数据也可以持久化到数据库中了, 接下来该*写代码把网页转换成简化版的"阅读视图"*了.
模板引擎有很多, EJS(嵌入式 JavaScript)属于简单易学的那种.
怎么同时支持 HTML 和 JSON 两种格式呢? 使用 res.format 方法.
Express 自带一个名为 express.static 的中间件, 可以给浏览器发送客户端 JavaScript、图片和 CSS 文件.
Node 的 Web 开发
前端构建系统
Gulp 是基于流的构建系统, 我们可以通过对这些流的引导来创建构建过程.
Webpack loader 加载器 是用来转换资源文件的, 加载器是函数, 负责将输入的源文本转换为特定的文本输出.
服务器端框架
Koa 是以 Express 为基础开发的, 但它用 ES2015 中的生成器语法来定义中间件. 也就是说我们几乎可以编写异步的中间件.
[你要找那种框架?] => (全栈) => DerbyJS
=> (服务器端MVC) => [哪种MVC?] => (Rails/Django) => Sails.js
=> (Web API) => [API tools and GUI?] => (yes) => LoopBack
=> (no) => Kraken
=> (HTTP库) => [什么样的路由API?] => (ES6) => koa
=> (Connect) => [Databases/views?] => (yes) => [同构?] => (yes) => Derby
=> (no) => Kraken
=> (no) => Hapi
=> (其他) => [Databases/views?] => (yes) => Flatiron
=> (no) => Hapi
复制代码
深入了解 Connect 和 Express
Express 就是在 Connect 的基础上, 通过添加高层糖衣扩展和搭建起来的.
Middleware 中间件是所有 Connect 和 Express 程序的基础.
为了做到可配置, 中间件一般会遵循一个简单的惯例: 用一个函数返回另一个函数(闭包).
function setup(options) {
/** @desc 设置逻辑 && 中间件初始化 */
return function(req, res, next) {
/** @desc 中间件逻辑, 仍可访问 options */
};
} // => app.use(setup({ some: 'options' }));
复制代码
Connect 中有一种用来处理错误的中间件变体, 跟常规的中间件相比, 除了请求、响应对象外, 错误处理中间件的参数中还多了一个错误对象.
function errorHandler(err, req, res, next) {
// => 错误处理中间件定义四个参数
res.statusCode = 500;
switch (env) {
case 'development':
console.error('Error:');
console.error(err);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(err));
break;
default:
res.end('Server error');
}
}
connect()
.use(logger) /** @desc use 支持链式调用 */
.use(hello)
.use(errorHandler)
.listen(3000);
复制代码
npm install -g express-generator
express -e --ejs
复制代码
- app.js
- bin
- www
- package.json
- public
- images
- javascripts
- stylesheets
- style.css
- routes
- index.js
- users.js
- views
- error.ejs
- index.ejs
- layout.ejs
在线留言板需求:
- 用户可以注册, 登录, 退出
- 用户可以发消息
- 用户可以分页浏览消息
- 支持认证的简单 REST API
必要的路由分以下两种:
- API 路由
- GET /api/entries : 获取消息列表
- GET /api/entries/page : 获取单条消息
- POST /api/entry : 创建新的消息
- Web UI 路由
- GET /post : 显示创建新消息的表单
- POST /post : 提交创建新消息
- GET /register : 显示注册表单
- POST /register : 创建新的用户账号
- GET /login : 显示登录表单
- POST /login : 登录
- GET /logout : 登出
Express 有一个极简的环境驱动配置系统, 这个系统由几个方法组成, 全部由环境变量 NODE_ENV 驱动:
- app.set()
- app.get()
- app.enable(config) => app.set(config, true)
- app.disable(config) => app.set(config, false)
- app.enabled() => if enable ture or false
- app.disabled() => if disable ture or false
Express 视图配置:
- 视图查找路径;
- 配置模板引擎;
- 启用视图缓存, 减少文件 I/O
view cache 被禁用时, 每次请求都会从硬盘上读取模板. 这样无须重启程序来让模板修改生效. 启用 view cache 后, 每个模板只需要读取一次硬盘.
给被渲染视图传递数据: 作为 res.render() 的参数, 在中间件设定一个变量比如 app.locals 传递程序层面的数据 res.locals 传递请求层面的数据.
在模板发现变量 => render 有传入值吗 => res.locals 有吗 => app.locals 有吗
Express 默认向视图传递一个程序级变量 settings , 这个对象包含所有 app.set() 设定的值. app.set('title', 'My Application') => settings.title
# 在 Redis 的解压目录下
redis-server.exe. redis.windows.conf # 直接启动 redis 服务, 不用关闭 cmd 窗口
redis-cli # 进入 cli 命令行模式
复制代码
修改密码 => redis.windows.conf => requirepass
用 Redis = 想偷懒 => 借助 Reids && ES6 的特性, 不需要用复杂的数据库就能轻松创建出轻便的模型.
把校验逻辑剥离成可重用的中间件的模式, 让开发更容易、更快、更具声明性:
app.post(
'/post',
validate.required('entry[title]'),
validate.lengthAbove('entry[title]', 4),
entries.submit
);
复制代码
用户认证:
- 存储和认证已注册已注册用户;
- 注册功能;
- 登录功能;
- 加载用户信息的中间件.
Bcrypt => 加盐的哈希函数 => 第三方模块专门对密码做哈希处理, 对抗暴力破解
Node 是单线程的, 没有线程本地存储. 对于 HTTP 服务器而言, 请求和响应变量是唯一的上下文对象. 构建在 Node 之上的高层框架可能会提供额外的对象存放已认证用户之类的数据, 但 Express 坚持使用 Node 提供的原始对象. 因此, 上下文数据一般保存在请求对象上.
内容协商让客户端可以指定它乐于接受且喜欢的数据格式. HTTP 通过 Accept 请求头域提供了内容协商机制.
Web 程序的模板
MVC 的主要思想是将逻辑、数据和展示层分离. 在遵循 MVC 模式的 Web 程序中, 一般是用户向服务器请求资源, 然后 控制器 向 模型 请求数据, 得到数据后传给 视图 . 视图部分一般会用到某种模板语言, 视图会将模型返回的数据传递给 模板引擎.
常见的三种模板引擎: Embedded JavaScript (EJS) , Hogan , Pug
存储数据
MySQL , PostgreSQL , NoSQL , MongoDB , Redis , SQLite , localStorage|sessionStorage
Knex.js 是 SQL 抽象包, 被称为查询构建器.
ACID => 原子性(Atomicity), 一致性(Consistency), 隔离性(Isolation), 耐用性(Durability)
测试 Node 程序
Mocha , Vows , Chai , Should.js , Sinon.JS , Selenium
超越 Web 开发
编写命令行程序
用 Electron 征服桌面
自动化的网络抓取
Octopart , cheerio , jsdom