nodejs-入门教程

一、Node js入门
1.如果要运行一大段代码的话,可以先写一个JS文件再运行。例如有以下`hello.js`。
    function hello() {
        console.log('Hello World!');
    }
    hello();
写好后在终端下键入`node hello.js`运行,结果如下:

    $ node hello.js
    Hello World!

2.#### require
`require`函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。模块名可使用相对路径(以`./`开头),或者是绝对路径(以`/`或`C:`之类的盘符开头)。另外,模块名中的`.js`扩展名可以省略。以下是一个例子。

    var foo1 = require('./foo');
    var foo2 = require('./foo.js');
    var foo3 = require('/home/user/foo');
    var foo4 = require('/home/user/foo.js');
    
    // foo1至foo4中保存的是同一个模块的导出对象。

另外,可以使用以下方式加载和使用一个JSON文件。

    var data = require('./data.json');
3.#### exports
`exports`对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过`require`函数使用当前模块时得到的就是当前模块的`exports`对象。以下例子中导出了一个公有方法。
    exports.hello = function () {
        console.log('Hello World!');
    };

4.#### module

通过`module`对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。
    module.exports = function () {
        console.log('Hello World!');
    };

以上代码中,模块默认导出对象被替换为一个函数。
5.#### 完整示例

例如有以下目录。

    - /home/user/hello/
        - util/
            counter.js
        main.js

其中`counter.js`内容如下:

    var i = 0;
    function count() {
        return ++i;
    }
    exports.count = count;

该模块内部定义了一个私有变量`i`,并在`exports`对象导出了一个公有方法`count`。

主模块`main.js`内容如下:

    var counter1 = require('./util/counter');
    var counter2 = require('./util/counter');

    console.log(counter1.count());
    console.log(counter2.count());
    console.log(counter2.count());

运行该程序的结果如下:

    $ node main.js
    1
    2
    3

可以看到,`counter.js`并没有因为被require了两次而初始化两次。


第二章、代码组织

1.`require`函数路径解析
`require`函数支持斜杠(`/`)或盘符(`C:`)开头的绝对路径,也支持`./`开头的相对路径。但这两种路径在模块之间建立了强耦合关系,一旦某个模块文件的存放位置需要变更,使用该模块的其它模块的代码也需要跟着调整,变得牵一发动全身。因此,`require`函数支持第三种形式的路径,写法类似于`foo/bar`,并依次按照以下规则解析路径,直到找到模块位置。

(1)内置模块

    如果传递给`require`函数的是NodeJS内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如`require('fs')`。

(2)node_modules目录

    NodeJS定义了一个特殊的`node_modules`目录用于存放模块。例如某个模块的绝对路径是`/home/user/hello.js`,在该模块中使用`require('foo/bar')`方式加载模块时,则NodeJS依次尝试使用以下路径。

        /home/user/node_modules/foo/bar
        /home/node_modules/foo/bar
        /node_modules/foo/bar

(3) NODE_PATH环境变量

    与PATH环境变量类似,NodeJS允许通过NODE_PATH环境变量来指定额外的模块搜索路径。NODE_PATH环境变量中包含一到多个目录路径,路径之间在Linux下使用`:`分隔,在Windows下使用`;`分隔。例如定义了以下NODE_PATH环境变量:

        NODE_PATH=/home/user/lib:/home/lib

    当使用`require('foo/bar')`的方式加载模块时,则NodeJS依次尝试以下路径。

        /home/user/lib/foo/bar
        /home/lib/foo/bar

2.包管理——便于管理和使用,由多个子模块组成
   在组成一个包的所有子模块中,需要有一个入口模块,入口模块的导出对象被作为包的导出对象。例如有以下目录结构。

    - /home/user/lib/
        - cat/
            head.js
            body.js
            main.js

其中`cat`目录定义了一个包,其中包含了3个子模块。`main.js`作为入口模块,其内容如下:

    var head = require('./head');
    var body = require('./body');

    exports.create = function (name) {
        return {
            name: name,
            head: head.create(),
            body: body.create()
        };
    };

在其它模块里使用包的时候,需要加载包的入口模块。接着上例,使用`require('/home/user/lib/cat/main')`能达到目的,但是入口模块名称出现在路径里看上去不是个好主意。因此我们需要做点额外的工作,让包使用起来更像是单个模块。

(1)#### index.js

当模块的文件名是`index.js`,加载模块时可以使用模块所在目录的路径代替模块文件路径,因此接着上例,以下两条语句等价。

    var cat = require('/home/user/lib/cat');
    var cat = require('/home/user/lib/cat/index');

这样处理后,就只需要把包目录路径传递给`require`函数,感觉上整个目录被当作单个模块使用,更有整体感。

(2)#### package.json

如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个`package.json`文件,并在其中指定入口模块的路径。上例中的`cat`模块可以重构如下。

    - /home/user/lib/
        - cat/
            + doc/
            - lib/
                head.js
                body.js
                main.js
            + tests/
            package.json

其中`package.json`内容如下。

    {
        "name": "cat",
        "main": "./lib/main.js"
    }

如此一来,就同样可以使用`require('/home/user/lib/cat')`的方式加载模块。NodeJS会根据包目录下的`package.json`找到入口模块所在位置。

3.命令行执行代码
这种使用方式看起来不怎么像是一个命令行程序,下边的才是我们期望的方式。

    $ node-echo Hello World

#### Linux

在Linux系统下,我们可以把JS文件当作shell脚本来运行,从而达到上述目的,具体步骤如下:

(1) 在shell脚本中,可以通过`#!`注释来指定当前脚本使用的解析器。所以我们首先在`node-echo.js`文件顶部增加以下一行注释,表明当前脚本使用NodeJS解析。

        #! /usr/bin/env node

    NodeJS会忽略掉位于JS模块首行的`#!`注释,不必担心这行注释是非法语句。

(2) 然后,我们使用以下命令赋予`node-echo.js`文件执行权限。

        $ chmod +x /home/user/bin/node-echo.js

(3)最后,我们在PATH环境变量中指定的某个目录下,例如在`/usr/local/bin`下边创建一个软链文件,文件名与我们希望使用的终端命令同名,命令如下:

        $ sudo ln -s /home/user/bin/node-echo.js /usr/local/bin/node-echo

这样处理后,我们就可以在任何目录下使用`node-echo`命令了。

#### Windows

在Windows系统下的做法完全不同,我们得靠`.cmd`文件来解决问题。假设`node-echo.js`存放在`C:\Users\user\bin`目录,并且该目录已经添加到PATH环境变量里了。接下来需要在该目录下新建一个名为`node-echo.cmd`的文件,文件内容如下:

    @node "C:\User\user\bin\node-echo.js" %*

这样处理后,我们就可以在任何目录下使用`node-echo`命令了。

4.工程目录
了解了以上知识后,现在我们可以来完整地规划一个工程目录了。以编写一个命令行程序为例,一般我们会同时提供命令行模式和API模式两种使用方式,并且我们会借助三方包来编写代码。除了代码外,一个完整的程序也应该有自己的文档和测试用例。因此,一个标准的工程目录都看起来像下边这样。

    - /home/user/workspace/node-echo/   # 工程目录
        - bin/                          # 存放命令行相关代码
            node-echo
        + doc/                          # 存放文档
        - lib/                          # 存放API相关代码
            echo.js
        - node_modules/                 # 存放三方包
            + argv/
        + tests/                        # 存放测试用例
        package.json                    # 元数据文件
        README.md                       # 说明文件

其中部分文件内容如下:

    /* bin/node-echo */
    var argv = require('argv'),
        echo = require('../lib/echo');
    console.log(echo(argv.join(' ')));

    /* lib/echo.js */
    module.exports = function (message) {
        return message;
    };

    /* package.json */
    {
        "name": "node-echo",
        "main": "./lib/echo.js"
    }

以上例子中分类存放了不同类型的文件,并通过`node_moudles`目录直接使用三方包名加载模块。此外,定义了`package.json`之后,`node-echo`目录也可被当作一个包来使用。

5、NPM使用
### NPM

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

+ 允许用户从NPM服务器下载别人编写的三方包到本地使用。

+ 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。

+ 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

可以看到,NPM建立了一个NodeJS生态圈,NodeJS开发者和用户可以在里边互通有无。以下分别介绍这三种场景下怎样使用NPM。

#### 下载三方包

需要使用三方包时,首先得知道有哪些包可用。虽然[npmjs.org](https://npmjs.org/)提供了个搜索框可以根据包名来搜索,但如果连想使用的三方包的名字都不确定的话,就请百度一下吧。知道了包名后,比如上边例子中的`argv`,就可以在工程目录下打开终端,使用以下命令来下载三方包。

    $ npm install argv
    ...
    argv@0.0.2 node_modules\argv

下载好之后,`argv`包就放在了工程目录下的`node_modules`目录中,因此在代码中只需要通过`require('argv')`的方式就好,无需指定三方包路径。

以上命令默认下载最新版三方包,如果想要下载指定版本的话,可以在包名后边加上`@<version>`,例如通过以下命令可下载0.0.1版的`argv`。

    $ npm install argv@0.0.1
    ...
    argv@0.0.1 node_modules\argv

如果使用到的三方包比较多,在终端下一个包一条命令地安装未免太人肉了。因此NPM对`package.json`的字段做了扩展,允许在其中申明三方包依赖。因此,上边例子中的`package.json`可以改写如下:

    {
        "name": "node-echo",
        "main": "./lib/echo.js",
        "dependencies": {
            "argv": "0.0.2"
        }
    }

这样处理后,在工程目录下就可以使用`npm install`命令批量安装三方包了。更重要的是,当以后`node-echo`也上传到了NPM服务器,别人下载这个包时,NPM会根据包中申明的三方包依赖自动下载进一步依赖的三方包。例如,使用`npm install node-echo`命令时,NPM会自动创建以下目录结构。

    - project/
        - node_modules/
            - node-echo/
                - node_modules/
                    + argv/
                ...
        ...

如此一来,用户只需关心自己直接使用的三方包,不需要自己去解决所有包的依赖关系。

#### 安装命令行程序

从NPM服务上下载安装一个命令行程序的方法与三方包类似。例如上例中的`node-echo`提供了命令行使用方式,只要`node-echo`自己配置好了相关的`package.json`字段,对于用户而言,只需要使用以下命令安装程序。

    $ npm install node-echo -g

参数中的`-g`表示全局安装,因此`node-echo`会默认安装到以下位置,并且NPM会自动创建好Linux系统下需要的软链文件或Windows系统下需要的`.cmd`文件。

    - /usr/local/               # Linux系统下
        - lib/node_modules/
            + node-echo/
            ...
        - bin/
            node-echo
            ...
        ...

    - %APPDATA%\npm\            # Windows系统下
        - node_modules\
            + node-echo\
            ...
        node-echo.cmd
        ...

#### 发布代码

第一次使用NPM发布代码前需要注册一个账号。终端下运行`npm adduser`,之后按照提示做即可。账号搞定后,接着我们需要编辑`package.json`文件,加入NPM必需的字段。接着上边`node-echo`的例子,`package.json`里必要的字段如下。

    {
        "name": "node-echo",           # 包名,在NPM服务器上须要保持唯一
        "version": "1.0.0",            # 当前版本号
        "dependencies": {              # 三方包依赖,需要指定包名和版本号
            "argv": "0.0.2"
        },
        "main": "./lib/echo.js",       # 入口模块位置
        "bin" : {
            "node-echo": "./bin/node-echo"      # 命令行程序名和主模块位置
        }
    }

之后,我们就可以在`package.json`所在目录下运行`npm publish`发布代码了




1.express入门
var express =require('express');
var app=express();
//demo121
app.get('/demo:id',function(req,res){
    //console.log(__dirname);
    res.send('hello world1dddd!!'+req.params['id']);
    console.log(req.cookies);
});
//app.use('/static', express.static(__dirname + '/public'));
app.use(express.static( 'public'));
// 一个简单的 logger
app.use(function(req, res, next){
  console.log('%s %s', req.method, req.url);

  next();
});
var server=app.listen(3000,function(){
    var host=server.address().address;
    var port=server.address().port;
});
2.开始
var http=require('http');
http.createServer(function(req,res){
    res.writeHead(200,{'Content-Type':'text/plain'});
    res.end('<h1>Hello World</h1>\n');
}).listen(80,'127.0.0.1');
2.定义模块
//模块的定义./public/js/circle.js
var PI=Math.PI;
exports.area=function(r){
    return r*r*PI;
}
exports.circlePreference=function(r){
    return 2*PI*r;
}
//使用
var http=require('http');
var circle=require('./public/js/circle.js');
http.createServer(function(req,res){
    res.writeHead(200,{'Content-Type':'text/plain'});
    res.end('<h1>Hello World</h1>\n'+circle.area(2));
}).listen(80,'127.0.0.1');


3.###`exports`
`exports`对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过`require`函数使用当前模块时得到的就是当前模块的`exports`对象。以下例子中导出了一个公有方法。
    exports.hello = function () {
        console.log('Hello World!');
    };
4.#### module
通过`module`对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。

    module.exports = function () {
        console.log('Hello World!');
    };

以上代码中,模块默认导出对象被替换为一个函数。

5.### 模块路径解析规则

我们已经知道,`require`函数支持斜杠(`/`)或盘符(`C:`)开头的绝对路径,也支持`./`开头的相对路径。但这两种路径在模块之间建立了强耦合关系,一旦某个模块文件的存放位置需要变更,使用该模块的其它模块的代码也需要跟着调整,变得牵一发动全身。因此,`require`函数支持第三种形式的路径,写法类似于`foo/bar`,并依次按照以下规则解析路径,直到找到模块位置。

(1) 内置模块

    如果传递给`require`函数的是NodeJS内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如`require('fs')`。

(2) node_modules目录

    NodeJS定义了一个特殊的`node_modules`目录用于存放模块。例如某个模块的绝对路径是`/home/user/hello.js`,在该模块中使用`require('foo/bar')`方式加载模块时,则NodeJS依次尝试使用以下路径。

        /home/user/node_modules/foo/bar
        /home/node_modules/foo/bar
        /node_modules/foo/bar

(3) NODE_PATH环境变量

    与PATH环境变量类似,NodeJS允许通过NODE_PATH环境变量来指定额外的模块搜索路径。NODE_PATH环境变量中包含一到多个目录路径,路径之间在Linux下使用`:`分隔,在Windows下使用`;`分隔。例如定义了以下NODE_PATH环境变量:

        NODE_PATH=/home/user/lib:/home/lib

    当使用`require('foo/bar')`的方式加载模块时,则NodeJS依次尝试以下路径。

        /home/user/lib/foo/bar
        /home/lib/foo/bar
6.#### index.js

当模块的文件名是`index.js`,加载模块时可以使用模块所在目录的路径代替模块文件路径,因此接着上例,以下两条语句等价。

    var cat = require('/home/user/lib/cat');
    var cat = require('/home/user/lib/cat/index');

这样处理后,就只需要把包目录路径传递给`require`函数,感觉上整个目录被当作单个模块使用,更有整体感。   

7.#### package.json

如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个`package.json`文件,并在其中指定入口模块的路径。上例中的`cat`模块可以重构如下。

    - /home/user/lib/
        - cat/
            + doc/
            - lib/
                head.js
                body.js
                main.js
            + tests/
            package.json

其中`package.json`内容如下。

    {
        "name": "cat",
        "main": "./lib/main.js"
    }

如此一来,就同样可以使用`require('/home/user/lib/cat')`的方式加载模块。NodeJS会根据包目录下的`package.json`找到入口模块所在位置。

8.### 工程目录

了解了以上知识后,现在我们可以来完整地规划一个工程目录了。以编写一个命令行程序为例,一般我们会同时提供命令行模式和API模式两种使用方式,并且我们会借助三方包来编写代码。除了代码外,一个完整的程序也应该有自己的文档和测试用例。因此,一个标准的工程目录都看起来像下边这样。

    - /home/user/workspace/node-echo/   # 工程目录
        - bin/                          # 存放命令行相关代码
            node-echo
        + doc/                          # 存放文档
        - lib/                          # 存放API相关代码
            echo.js
        - node_modules/                 # 存放三方包
            + argv/
        + tests/                        # 存放测试用例
        package.json                    # 元数据文件
        README.md                       # 说明文件
其中部分文件内容如下:

    /* bin/node-echo */
    var argv = require('argv'),
        echo = require('../lib/echo');
    console.log(echo(argv.join(' ')));

    /* lib/echo.js */
    module.exports = function (message) {
        return message;
    };

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

推荐阅读更多精彩内容