Node.js 101: create a package

这是我为公司同事科普如何开发 Node.js app 的系列文章。

什么是 Node.js? #

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

如何安装? #

Downloadbrew install node

如何运行 Node.js #

# bash script, works on Linux / Mac
touch demo.js
echo "console.log('hello world)" > demo.js
node --harmony demo.js
# output "hello world"

node --hamrony -e "console.log('hi there')"
# output "hi there"

什么是 --harmony #

Harmony 可以理解为一个为了解决 ECMAScript / JavaScript 混乱局面的项目。

作为一个 JavaScript 开发者都应该去了解 ECMAScript 5,作为一个 Node.js 开发者更应该去了解 ECMAScript 6。

因此,Chrome 在内核里加入 ECMAScript 5 / 6 的内容,并提供选项,供开发者尽早试验新的特性;而基于 V8 引擎的 Node.js 自然也会有,这个选项就是 --harmony

具体的细节请善用 Google,Node.js 能用的 ES 6 特性请查阅 GitHub 上的 node-es6-examples

只要记住跑 Node.js 脚本时,加上 --harmony 即可:node --harmony

Linux / Mac 使用者可以建立别名:alias node='node --harmony'

Windows?再见。

注:本系列所有代码均运行在 Harmony 模式下,Node.js 版本为 0.11.13

如何写一个 Node.js 项目 #

如果一个教程只是围绕 hello world 就太没趣了,本文将通过 Sagase package 介绍如何写一个实际有用的项目。

Sagase 项目是提供一个使用正则搜索文件的工具,包括命令行和用于 Node.js 的库。项目结构如下:

lib/
    cli.js
    sagase.js
Gruntfile.js
package.json

先记住这么多,稍后一一解释。

Node.js package #

Node.js package 是 Node.js 的代码包 (集合) ,可以是:

如果用过 Ruby 的 Gems、Python 的 pip,应该很容易就能理解。

几乎所有的 Node.js 项目都是一个 package——虽然也可以用独立的 .js,但效率将大打折扣。

npm #

Node.js package 通过 npm 管理,常用命令:

npm init
npm install PACKAGE
npm uninstall PACKAGE
npm update

创建 Node.js 项目 #

在命令行执行 npm init,回答若干问题,或者一路回车,将会自动创建一个典型的 package.json,这时候就可以开始写 Node.js 项目了。

package.json #

每一个 Node.js 项目基本会有一个 package.json,用于描述这个包功能、作者等信息,同时也包括运行环境、依赖项等等重要信息。一般长这样:

{
  "name": "sagase",
  "version": "0.1.1",
  "main": "lib/sagase.js",
  "description": "Searching files recursively.",
  "bin": {
    "sagase": "lib/cli.js"
  },
  "dependencies": {
    "async": "^0.9.0",
    "bluebird": "^2.1.2",
    "chalk": "^0.4",
    "fs-extra": "^0.8",
    "lodash": "^2.4.1",
    "nomnom": "^1.6"
  },
  "devDependencies": {
    "grunt-contrib-jshint": "~0.7.0",
    "grunt-contrib-watch": "~0.5.0",
    "jshint-stylish": "~0.1.3",
    "load-grunt-tasks": "~0.2.0",
    "time-grunt": "~0.2.0"
  },
  "engines": {
    "node": ">= 0.8"
  }
}

不太重要的就略过,请自行查看官方文档

"main": "lib/sagase.js"

指明这个包的入口,就像 C 的 main。如果包的根目录存在 index.js,即使没有 main 也没问题的,否则引用这个包的时候将会报错:Error: Cannot find module 'xxxx'。建议遵循 index.js 命名习惯。

"bin": {
  "sagase": "lib/cli.js"
}

告诉 npm 将 lib/cli.js 软链接到系统的可执行文件目录,并命名为 sagase,这样就可以在命令行里直接使用这个包。

"dependencies": {
  "async": "^0.9.0",
  "bluebird": "^2.1.2",
  "chalk": "^0.4",
  "fs-extra": "^0.8",
  "lodash": "^2.4.1",
  "nomnom": "^1.6"
},
"devDependencies": {
  "grunt-contrib-jshint": "~0.7.0",
  "grunt-contrib-watch": "~0.5.0",
  "jshint-stylish": "~0.1.3",
  "load-grunt-tasks": "~0.2.0",
  "time-grunt": "~0.2.0"
}

dependenciesdevDependencies 代表着这个项目依赖什么包。npm 在 npm install 的时候,会根据这个包所在的环境安装相应的依赖包,并存放在项目根目录的 node_modules 文件夹,当需要使用时就像官方包一样即可:

const async = require('async'), fs = require('fs')

devDependenciesdependencies 的区别在于,如果这个包是项目的主包,npm install 时将会同时安装所有依赖包;如果这个包是别的项目的依赖包,devdependencies 将被忽略,不会安装。

因此,一些代码测试、打包等流程需要用到的包就可以放在 devDependencies。当在别的项目里使用这个包时,就不用浪费时间安装这些包。

^0.9.0 则是指定可安装的版本范围,此例指 >= 0.9.0 && < 0.10.0

"engines": {
  "node": ">= 0.8"
}

指定这个包运行时所需的 Node.js 版本,避免 API 版本不一致造成运行失败。建议 >= 0.10>= 0.11.9

关键部分就这么多,上述的 package.json 将允许我们可以通过下述方式使用这个包。

首先是命令行模式,因为 Sagase 已发布到 npm 的公共服务器,可以通过 -g 参数安装到全局环境:

npm install -g sagase

sagase ./assets .js$ .min.js

在 Node.js 项目中使用则无需安装到全局环境,但一般使用 --save 在安装的同时把这个包添加到项目的 package.json:

npm install --save sagase
var find = require('sagase').find

find({
  folder: './',
  pattern: /.js$/,
  exclude: /.min.js/
}).then(function (files) {
    // do whatever you want
})

这一篇到此结束,下一篇开始讲开发部分。

 
17
Kudos
 
17
Kudos

Now read this

Use io.js and Node.js with Homebrew at OS X

Install [io.js] and [Node.js] with [homebrew] is super easy: brew install iojs node But you make notice this message for io.js formula: This formula is keg-only. iojs conflicts with node (which is currently more established) Because of... Continue →