nodejs 入门

简介与安装

什么是 Node.js

  • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境

  • Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。

  • Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js 可以解析JS代码(没有浏览器安全级别的限制)提供很多系统级别的API,如:

  • 文件的读写

  • 进程的管理

  • 网络通信

  • ……

准备 Node.js

使用 nvm 可以在本地安装并维护多个Node.js的版本

1、项目地址:

https://github.com/creationix/nvm/blob/master/README.md

2、配置加速镜像:

export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node

命令行中的体验

$ node 进入命令行
  1. 在浏览器和node命令行中运行代码

    function add(x, y){
    
      console.log(x+y);
    
    }
    
    add(3, 4)
    
  2. 在浏览器和node命令行里调用 window, process 两个对象

nodejs可以调用操作系统内核 api(io、进程等);浏览器中可以调用浏览器内核 api(bom、dom)

执行 .js 文件

  1. 编写 index.js 文件

    console.log('hello');
    
    function add(x,y) {
    
     console.log(x+y);
    
    }
    
    add(6,7);
    
  2. node index.js 执行代码(省略 .js 后缀也可以)

一些工具

  • npm:包管理器

  • cnpm:使用国内镜像

    sudo npm install -g cnpm --registry=https://registry.npm.taobao.org
    
  • gulp:文件管理、css预编译等

  • @vue/cli:vue 官方脚手架工具

  • nodemon:可以实时监听脚本的变化以及自动执行

    cnpm install -g nodemon
    nodemon xxx.js
    

使用不同版本的 Node.js

查看有哪些版本:

nvm ls

通过以下两个命令都可以查看正在使用的版本

# 1
nvm run node –version
# 2
npm -v

以某个版本运行某个脚本文件:

  • 方式一

    # 直接通过 nvm run 命令执行脚本
    nvm run v6.11.2 script.js
    
  • 方式二

    # 在当前目录创建一个.nvmrc 版本文件, 文件内容为某个版本,如:8.4.0。然后执行以下命令
    nvm use
    # 再执行具体的脚本
    node script.js
    

Node.js 特性

[CommonJS规范](http://www.commonjs.org

)

Node.js 模块

package.json

在当前项目目录(ECMAScript工程)下创建一个 package.json 文件记录工程的信息以及其依赖的模块,由 npm 或者 yarn 等工具读取管理。使用命令即可在当前目录下创建一个该文件,即初始化一个工程:

npm init
{
  "name": "nodejs-day1-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    // commonjs版本约定有三位,分别是 major、minor、patch
    // ^ 表示 major 固定,取最新版本;~ 表示 major 和 minor 固定,取最新版本;* 表示取最新版本
    "underscore": "^1.8.3"
  },
// 开发环境的依赖。  
  "devDependencies": {
    "cheerio": "^0.22.0",
    "gulp": "*"
  },
  // scripts 模块定义一些自定义 npm 命令。如下面生成的命令为 npm test、npm start;分别将它们的 value 作为命令执行。
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC"
}
 

安装模块

当使用 -g 的时候表示在全局安装模块

# 在全局安装 request 模块
npm install request -g

没有 -g 的时候表示在当前工程中创建一个模块,默认认为在当前目录下存在一个 package.json 文件:

npm install request --save

--save 表示将这个模块记录到当前项目的 package.json 的依赖项中:

# package.json 的 dependencies 中记录了 request 模块
{
  "name": "hello",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "request": "^2.81.0"
  }
}

当在当前工程安装完成依赖模块之后,会在工程目录下生成一个 node_modules 目录,里面就是安装的依赖包

模块定义、导出和导入

commonjs

导出模块为 hello 函数为一个名为 hello 的模块

const hello = () => {
  console.log('hello ~');
}
//module.exports 是写死的语法,相当于一个模块集合对象,下面的 require 得到的就是这个对象,通过 .模块名 获取具体的模块
module.exports.hello = hello

导入自己定义的模块,使用 ./ 表示这个模块是在当前自己项目中定义的,避免如扫描内置模块或者第三方模块

// require 中指定模块所在的 js 文件路径
const greeting = require('./src/greeting.js')

greeting.hello()

模块化参考阅读

commonjs、amd、requirejs、es6模块

ECMAScript 6 modules: the final syntax

npm 的一些命令

# -g、--global 表示全局。不加就是当前项目
npm list # 查看安装包列表
npm info  # 查看具体包的所有版本及其它详细信息
npm init # 初始化工程包管理文件配置信息
npm install # 按照当前目录的 package.json 文件依赖进行安装
npm install @ # 安装具体版本的包
npm install  --save # 安装模块并添加到配置文件依赖项 
npm uninstall  --save-dev # 卸载模块并将其在配置文件的开发依赖项中删除
npm outdated # 显示安装的包的版本、配置文件中依赖的可以下载的最新版本、最新版本
npm cache clean # 清除 npm 包管理缓存

Node.js 内置常用模块

URL

对一个字符串安装 URL 格式解析成一个 json 对象

// 第一个参数传入的就是 URL 字符串
// 第二个参数是一个boolean值,表示是否将 query 串也解析成一个json对象,否则原封不同就是一个字符串('&'分隔)
// 第三个参数也是一个boolean值,表示在协议未确定的情况下是否推测解析出 host 和 port
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
// 将上面的返回值,即一个 json 对象转成一个 url 字符串
url.format(urlObject)
// 拼接 URL 字符串,from 一般是一个 host、to 一般是一个 path
url.resolve(from, to)

querystring

// 将一个对象转成一个字符串,默认按照 http URL 中的 query 串的格式进行转换
// 第一个参数就是要转换的 json 对象
// 第二个参数是每一个属性键值对之间的分隔符
// 第三个参数是每一对属性键值之间的分隔符
querystring.stringify(obj[, sep[, eq[, options]]])
// 和上面的方法相反
querystring.parse(str[, sep[, eq[, options]]])
// 对传入字符串进行 URL 编码
querystring.escape(str)
// 对传入字符串进行 URL 解码
querystring.unescape(str)

http、https

小爬虫实现:

var http = require('http')
var https = require('https')
var cheerio = require('cheerio')
var url = 'https://www.lagou.com/'

function filterMenu(html) {
  var $ = cheerio.load(html)
  var menu = $('.menu_main')
  var menuData = []
  menu.each(function(index, value){
    var menuTitle = $(value).find('h2').text()
    var menuLists = $(value).find('a')
    var menuList = []
    menuLists.each(function(index, value){
      menuList.push($(value).text())
    })
    menuData.push({
      menuTitle: menuTitle,
      menuList: menuList
    })
  })
  return menuData;
}

function printMenu(menu) {
  menu.forEach(function(value){
    console.log(value.menuTitle + '\n')
    value.menuList.forEach(function(value){
      console.log(value)
    })
  })
}

https.get(url, function(res){
  var html = ''
  res.on('data', function(data){
    html += data
  })

  res.on('end', function(){
    var result = filterMenu(html)
    printMenu(result)
  })

  res.on('error', function(err){
    console.log(err)
  })
})

事件监听处理机制

// 引入事件机制模块
const EventEmitter = require('events')

// 实现自己的类继承事件机制类
class Player extends EventEmitter {}

// 新建事件机制对象
var player = new Player()

//on 方法表示发布某个事件监听及其处理器,且一直监听;once 方法表示只监听处理一次该事件
//第一个参数是事件名称,第二个参数是事件处理函数
player.once('play', (track) => {
  console.log(`正在播放:《${track}》`)
})

//emit 方法表示触发某个事件
//第一个参数是事件名称,第二个参数是事件本身
player.emit('play', '精绝古城')
player.emit('play', '黄皮子坟')

File System

  • 得到文件与目录的信息:stat
  • 创建一个目录:mkdir
  • 创建文件并写入内容:writeFile,appendFile
  • 读取文件的内容:readFile
  • 列出目录的东西:readdir
  • 重命名目录或文件:rename
  • 删除目录与文件:rmdir,unlink

Stream

  • 读取文件流
  • 可读流的事件
  • 可写的文件流
  • pipe链式使用
  • pipe

网络

net 模块 socket 编程

SocketServer.js:

var net = require('net')

var chatServer = net.createServer(),
    clientMap = new Object()

var i = 0 //连接名称的流水号
chatServer.on('connection', function (client) {
  console.log('客户端有人连接~')
  client.name = ++i
  clientMap[client.name] = client

  // 对客户端发送消息的监听
  client.on('data', function (data) {
    console.log('客户端传来:' + data)
    broadcast(data, client)
  })

  // 数据错误事件处理
  client.on('error', function (e) {
    console.log('client error' + e);
    client.end()
  })

  // 客户端关闭事件
  client.on('close', function (data) {
    delete clientMap[client.name]
    console.log(client.name + '下线了');
    broadcast(client.name + '下线了', client)
  })
})

// 消息广播
function broadcast(message, client) {
  for (var key in clientMap) {
    clientMap[key].write(client.name + 'say:' + message + '\n')
  }
}

chatServer.listen(9000)

SocketClient.js:

var net = require('net')
var port = 9000
var host = '127.0.0.1'

var client = new net.Socket()
client.setEncoding = 'UTF-8'

// 连接服务器
client.connect(port, host, function () {
  client.write('您好')
})

client.on('data', function (data) {
  console.log('服务端传来:' + data);
  say()
})

client.on('error', function (err) {
  console.log('error' + err);
})

client.on('close', function () {
  console.log('connection closeed');
})

const readline = require('readline')

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})

function say() {
  r1.question('请输入:', (inputStr) => {
    if (inputStr != 'bye') {
      client.write(inputStr + '\n')
    } else {
      client.distroy() //关闭连接
      r1.close()
    }
  })
}

浏览器原生支持 WebSocket

WsServer.js

var WebSocketServer = require('ws').Server,
    wss = new WebSocketServer({port: 9000})

var clientMap = new Object()
var i = 0

wss.on('connection', function (ws) {
  console.log(ws + '上线')
  ws.name = ++i
  clientMap[ws.name] = ws
  ws.on('message', function (message) {
    broadcast(message, ws)
  })
  ws.on('close', function () {
    // global.gc() //调用内存回收
    console.log('离开');
  })
})

function broadcast(msg, ws) {
  for (var key in clientMap) {
    clientMap[key].send(ws.name + '说: ' + msg)
  }
}

WsClient.js,被聊天框页面用 script 标签引入,可以看到 WebSocket 对象是浏览器内置对象,不用导入:

var ws = new WebSocket('ws://127.0.0.1:9000/')

ws.onopen = function () {
  ws.send('大家好')
}

ws.onmessage = function (event) {
  var chatroot = document.querySelector('#chatroom')
  chatroom.innerHTML += '
' + event.data } ws.onclose = function () { alert('Closed') } ws.onerror = function (err) { alert('Error:' + err) }

聊天框页面:




  
  
  
  ws client



  

WebSocket

兼容性好的 socketio

上面的 WebSocket 实现需要兼容 html5 的浏览器才可行。Socket io 则兼容一切浏览器。

第三方模块

Async

示例:

var async = require('async')

console.time('test')
// 串行无关联:series 方法
// 两个参数,第一个参数是一个函数数组,数组里面的函数将会被串行调用。第二个参数是一个回调函数,将会在函数数组里面的函数都调用完成后被调用
async.series([
  function (callback) {
    setTimeout(function(){
      callback(null, 'one')
    }, 2000)
  },
  function (callback) {
    setTimeout(function(){
      callback(null, 'two')
    }, 5000)
  }
], function(err, results){
  console.log(results)
  console.timeEnd('test')
})

async.series({
  one: function (callback) {
    setTimeout(function(){
      callback(null, '1')
    }, 1000)
  },
  two: function (callback) {
    setTimeout(function(){
      callback(null, '2')
    }, 2000)
  }
}, function(err, results){
  console.log(results)
  console.timeEnd('test')
})

// 并行无关联:parallel 方法
// 两个参数,第一个参数是一个函数数组,数组里面的函数将会被异步调用。第二个参数是一个回调函数,将会在函数数组里面的函数都调用完成后被调用
async.parallel([
  function (callback) {
    setTimeout(function(){
      callback(null, 'one')
    }, 2000)
  },
  function (callback) {
    setTimeout(function(){
      callback(null, 'two')
    }, 5000)
  }
], function(err, results){
  console.log(results)
  console.timeEnd('test')
})


// 串行有关联:waterfall 方法
// 两个参数
// 第一个参数是一个函数数组,数组里面的函数将会被串行调用,且前一个函数的参数 callback 就是下一个函数的引用,依次形成过滤器/责任链模式。
// 第二个参数是一个回调函数,将会在函数数组里面的函数都调用完成后被调用
async.waterfall([
  function (callback) {
    callback(null, 'one', 'two')
  },
  function (arr1, arr2, callback) {
    callback(null, arr1, arr2, 'three')
  },
  function (arr1, arr2, arr3, callback) {
    callback(null, [arr1, arr2, arr3, 'done'])
  }
], function(err, results){
  console.log(results)
})

Express

一个 Web 开发脚手架工具。 使用它可以生成一个格式化的工程项目,里面包含了一些已经写好的结构化代码,以及一个依赖了固定功能的 Web 开发模块的 package.json 文件。基于这个工程就可以快速开发一个 Web 项目。

EJS 简介

EJS 是 Express 中依赖的一个模板引擎语言。

EJS是一个简单高效的模板语言,通过数据和模板,可以生成HTML标记文本。可以说EJS是一个JavaScript库,EJS可以同时运行在客户端和服务器端,客户端安装直接引入文件即可,服务器端用npm包安装。

EJS 的特点

  • 快速编译和渲染

  • 简单的模板标签

  • 自定义标记分隔符

  • 支持文本包含

  • 支持浏览器端和服务器端

  • 模板静态缓存

  • 支持express视图系统

EJS 的成员函数

Render(str,data,[option]):直接渲染字符串并生成html

  • str:需要解析的字符串模板
  • data:数据
  • option:配置选项

EJS 的常用标签

  • <% %>流程控制标签
  • <%= %>输出标签(原文输出HTML标签)
  • <%- %>输出标签(HTML会被浏览器解析)
  • <%# %>注释标签
  • % 对标记进行转义

Mocha

Mocha(发音"摩卡")诞生于2011年,是现在最流行的JavaScript测试框架之一,在浏览器和Node环境都可以使用。所谓"测试框架",就是运行测试的工具。通过它,可以为JavaScript应用添加测试,从而保证代码的质量。

chai

assert/断言库。

  • should风格的断言
  • expect风格的断言

   转载规则


《nodejs 入门》 阿钟 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录