Day3: npm补充 和 Express

写在前面


昨天说的npm看了之后, 觉得有两个指的记录, 再就是写下Express

npm补充


npm2和npm3的区别


就本地安装的包来说, npm2npm3有一个很大的区别, 就是组织包的结构. npm2组织依赖的包是按照树形组织的. npm3将其改进为扁平结构.

npm2会将所依赖的包存放到当前目录的./node_modules/目录下. 而被安装的包又会依赖其他的包的话, 则会存放到该包的./node_modules下. 所以, 当依赖结构很复杂的时候, 目录结构会非常深. 不管是性能还是操作上, 体验都不怎么好.

your_project/
  node_modules/
    module_a@1.0/
      node_modules/
        module_b@1.0/
    module_c@1.0/
      node_modules/
        module_b@1.0
    module_d@1.0/
      node_modules/
        module_b@2.0/

而在npm3中, 采用扁平的目录结构, 二级依赖会放到当前目录的node_modules的里, 与一级包在同一目录.

your_project/
  node_modules/
    module_a@1.0/
    module_b@1.0
    module_c@1.0/
    module_d@1.0/
      node_modules/
        module_b@2.0/

尽量都安装到项目目录下的./node_modules中. 但是, 这样会遇到几个问题.

由于安装顺序不同, ./node_modules的目录结构也可能不同, 这样导致即使是相同的依赖关系, 目录结构也不一定一样. 例如在npm3例子中, 如果先安装module_d的话, module_b@2.0则会和module_d同级, 这样module_amodule_c所依赖的module_b@1.0则会在各自的./node_modules目录下, 即使是重复的. 像下面这样:

your_project/
  node_modules/
    module_a@1.0/
      node_modules/
        module_b@1.0
    module_c@1.0/
      node_modules/
        module_b@1.0
    module_d@1.0/
    module_b@2.0/

虽然一般情况下, 这个并不影响项目运行, 但是如果要达成目录结构一致的话, 解决方法是, 删除./node_modules目录下的所有文件, 重新安装. 因为npm会按照依赖包的字符表顺序排序后, 按照这个顺序进行安装.

npm安装全局包遇到权限问题


还有一点, 就是在Linux下安装npm全局包的话, 会遇到权限问题. 我以前一般是用sudo添加权限. 而又两个方法更好:

1, 用chown来改变目录的所有者, 全局包安装在npm config get prefix目录下, 将这个目录的所有者改成运行项目的人就行.

sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

2, 利用npm config set prefix <new_dir>设置到当前用户拥有读写权限的目录下. 再讲这个路径加到PATH中.

mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
export PATH=~/.npm-global/bin:$PATH
source ~/.profile

如果不想更改PATH系统变量, 可以这样. 由于npm最后会在/usr/local/bin目录下创建链接文件, 指向包的入口文件, 所以这样也是OK的.

NPM_CONFIG_PREFIX=~/.npm-global npm install -g jshint

Express


Express中, 采用的是一种中间件的方式对请求进行处理.

       +--------+--------+--------+--------+
req -> |   MW1  |   MW2  |   MW3  |   MW4  |
       | -----> | -----> | -----> | ---    |
       |    \   |    \   |    \   |    \   |
res <- |  <-/   |  <-/   |  <-/   |  <-/   |
       +--------+--------+--------+--------+

HTTP请求会依次经过每个中间件处理, 每个中间件可以选择处理该请求, 返回, 或者交给下一个中间件继续处理. 类似于Java Web的filter. 而末尾的中间件, 可以看做成action.

Express应用一般是这样.

var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
  res.send('hello world');
});

第一行加载Express模块, 第二行创建Express对象. app.get就是设置一个中间件, 这个中间件只处理GET请求. app.get第一个参数是当HTTP请求的URL匹配这个参数的时候, 运行第二个参数传入函数.

每个中间件就是一个接受三个参数的函数.

function middleware(req, res, next){
  // your code
}

Express加载中间件的方式也有很多

app.use(function(req, res, next){
  // your code.
});
app.use('url_pattern_string', function(req, res, next){
  // your code.
});

使用app对象有很多方法, 其中, use表示所有HTTP请求都需要经过这个中间件, 还有几个针对HTTP请求类型的, 比如get, post, put, delete等等. 这些只处理相应类型的HTTP请求, 不处理除此之外其他类型的请求.

更好的组织代码的方式是利用Router. 在router对象上设置各种处理, 然后将这个router作为中间件使用.

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

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

module.exports = router;

然后再添加到app

var express = require('express');

var routes = require('./routes/index');

app.use('/routes', routes);

这样, 访问/routes/index会首先匹配到route对象, 然后在这个对象进行下一步的匹配, 匹配到/index, 就执行这里的方法, 渲染index模板并返回给浏览器.

总体看来, 可以认为Express是对HTTP请求的URL和中间件进行匹配的. 匹配过程中会找到一些符合的中间件, 执行这些中间件. 中间件有很多种, 有针对某种HTTP请求类型的中间件, 也有不管HTTP请求类型, 都要执行的中间件. 这都是通过在设置中间件时, 使用哪种方法设置来区别的.

app.use() // HTTP 所有类型的请求
app.get() // HTTP GET请求
app.post() // HTTP POST请求
...

Express支持很多请求类型.

最重要的还有中间件接受的参数, req, res,和next.

Request


Request用来封装HTTP请求的信息.

路由匹配过程中可以匹配参数.

app.get('/user/:id', function(req, res){
  res.end('' + req.params.id);
});

get参数可以在req.query中访问

// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse

req.query.order
// => "desc"

req.query.shoe.color
// => "blue"

req.query.shoe.type
// => "converse"

访问HTTP请求头

req.get('Content-Type');
// => "text/plain"

req.get('content-type');
// => "text/plain"

req.get('Something');
// => undefined

其他的方法可以查Express 4.x Request

Response


res.locals针对当前请求, 保存一些信息, 可以被模板引擎直接访问, 也可以在其他的中间件中访问到. 不同请求, locals是新的对象, 请求之间是隔离的.

res.locals.title = 'Express';
res.render('index'); // 在模板引擎中可以直接访问title变量, 值为上面指定的`Express

app.use(function(req, res, next){
  res.locals.user = req.user;
  res.locals.authenticated = ! req.user.anonymous;
  next(); // 下一个中间件可以访问这两个变量
});

通过res可以设置cookie

// res.cookie(name, value [, options])
res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true });
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });

响应有很多种

  • res.json([body])返回JSON
  • res.jsonp([body])返回JSONP
  • res.download(path [, filename] [, fn]), 返回文件, 使浏览器下载
  • res.redirect([status,] path)返回重定向
  • res.render(view [, locals] [, callback])进行模板渲染, 然后传输给浏览器

进一步的参考Express 4.x Response

最后


Koa出来之前, Express很肿胀, 到了4.x的时候, Express将很多东西都作为中间件单独提供, 清爽很多, 而且官网首页的一句话介绍也改了. 有过一段时间, 想学学connect. 但是放弃了, 觉得Express更方便一些.

对于Koa, 使用generator function的确很好, 但是函数的上下文有点混乱, 等Express的文档看完了, 有空好好学学ES6`

对于看ES6的内容来说, 大概看了下, 觉得阮一峰的<ECMAScript 6 入门>写的非常好. JavaScript语言有很多不符合直觉的地方, 很别扭, 比如:

// Chrome 版本 47.0.2526.111 m (64-bit)
[] + []
// => ""
{} + []
// => 0
[] + {}
// => "[object Object]"
{} + {}
// => NaN
NaN === NaN
// => false

// 还有很多...

JavaScript也有很多不好的地方. 比如不同浏览器支持的程度不一样, 新的标准出来了, 浏览器要跟上也得过段时间; 不同浏览器又会实验性地拓展内容; 为了使用浏览器未实现的语言标准, 出现了中间层来转换(babel). 为了磨平浏览器对于语言支持的区别, 也出现了shim / polyfill; ES6还没学会呢, ES7就快来了(已经有不少草案了);

虽然JavaScript作为服务器端脚本, 相对其他的脚本语言有所欠缺, 但是我等相信, 未来更好.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容