Node基础

Node

1、process 全局对象,直接用
console.log(process.env); //得到很多系统变量与用户变量
2、接收命令行参数
// 如在命令行输入 node ./index.js 2 3
console.log(process.argv); // [ 'C:\\Program Files\\nodejs\\node.exe','F:\\node-code\\index.js','2','3' ]
3、获取命令行参数数组的 2,3 索引对应的元素并进行相关计算
let num1 = parseInt(process.argv[2]);
let num2 = parseInt(process.argv[3]); // parseInt也ok
let sum = num1 + num2;
//输出(卡顿输出)(定时输出)
console.log('计算中.....');
//2秒后输出
setTimeout(() => {
  console.log('结果为:' + sum); // 5
}, 2000);
注意: 以下代码所在的项目路径为 F:/node-code
4、filename/dirname(全局对象)
  • __filename 获取当前运行文件的目录,绝对路径
  • __dirname 当前运行文件的绝对路径
console.log(__filename); // F:\node-code\index.js
console.log(__dirname); //  F:\node-code
5、拼接路径 path.join()
// 引入核心对象
const path = require('path'); // 核心对象 path在node.exe里面
// 3段路径来自不同用户的输入
const myPath = path.join(__dirname, '//one//', '//two//', '//three///');
console.log(myPath); // F:\node-code\one\two\three\
6、根据相对路径,返回绝对路径
// 如 ./abv/efg.js 非要一个绝对路径
const str = './abv/efg.js';
let temp = path.resolve(str);
console.log(temp); // F:\node-code\abv\efg.js
7、解析路径为对象 path.parse()
const path = require('path');
// 接受一段字符串路径
let myPath = path.join(__dirname, 'jack', 'rose', 'mick.txt');
// 解析这个路径为对象,更易于操作
let pathObj = path.parse(myPath);
console.log(pathObj); // { root: 'F:\\', dir: 'F:\\node-code\\jack\\rose', base: 'mick.txt',ext: '.txt',name: 'mick'}
// base可以作为修改文件名,或后缀的方式
pathObj.base = 'mick_die.good';
// 接收路径对象,转换成路径字符串
myPath = path.format(pathObj);
console.log(myPath); //  F:\node-code\jack\rose\mick_die.good
8、IO
//  I or O?
//  I input 计算机来说,就是输入
//  O output 计算机来说 ,展现/写入数据就是输出,
9、读取文件 fs.readFile()
//读取文件
// 引入核心对象
const fs = require('fs');
fs.readFile('./a.txt', 'utf8', (err, data) => {
  if (err) throw err; // 抛到控制台显示异常信息
  // console.log(data);
  // 需要获取字符串数据,就可以调用 buffer篮子.toString(编码)函数
  // console.log(data.toString('utf8'));  默认urf8可以不传递
  console.log(data); // 如在第二个参数传了 utf8 则可以直接打印而无需转译
});
10、编写文件 fs.writeFile()
// 引入核心对象
const fs = require('fs');
fs.writeFile('./a.txt', '我今天赚了2块钱', { flag: 'a' }, err => {
  // {flag: 'a'} 则表示是追加模式 如无这个参数 文件每次编写时都会覆盖所有内容重新编写
  // window中目录层级超级深的时候,写入会报错
  if (err) throw err;
  console.log('写文件完成了');
});

// 文件编写追加方式: appendFile('path',data,callback);
fs.appendFile('./a.txt', '我今天赚了1块钱', err => {
  if (err) throw err;
  console.log('文件追加成功');
});
11、文件的一些其他操作
  • stat 获取文件状态
  • readdir 读取文件夹数据
  • access 判断文件或文件夹是否存在
// 接收命令行参数,根据该目录,读取目录下的所有文件并输出(遍历文件夹)
const path = require('path');
const fs = require('fs');
//引入别人包的入口的文件
// 1: 接收命令行参数  node ./01_read_dir_files.js .//xxx//xxx//xx
// 2: 修正该路径 path.resolve(process.agrv[2]);
let inputPath = path.resolve(process.argv[2]);
// 3: 判断该路径是否存在 fs.access(fs.constants.F_OK)
function testReadFiles(dir) {
  try {
    fs.accessSync(dir, fs.constants.F_OK); //判断该路径是否存在 同步写法
    let state = fs.statSync(dir); // 获取文件的状态
    if (state.isFile()) {
      //判断是否是文件
      console.log(dir);
    } else if (state.isDirectory()) {
      //判断是否是文件夹
      let files = fs.readdirSync(dir); // 读文件
      files.forEach(file => {
        testReadFiles(path.join(dir, file));
      });
    }
  } catch (e) {
    console.log(e);
    console.log('该文件或文件夹不存在!');
  }
}

// 只判断一次
// fs.accessSync(dir, fs.constants.F_OK);
testReadFiles(inputPath);
12、创建服务器
// 1:引入核心对象http
const http = require('http');
// 2: 用这个东西创建服务器
let server = http.createServer();
// 3: 基于事件, 很多的on('xxx')
server.on('request', (req, res) => {
  //不管请求是什么,都返回同一个数据
  res.end('xxx');
});
// IP 找计算机,  端口找程序
server.listen(8888, () => {
  console.log('服务器启动在8888端口');
});
13、EventEmitter 类
  • events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
    你可以通过 require("events");来访问该模块。
// 引入 events 模块
var events = require('events');
// 创建 emitter 对象
var emitter = new events.EventEmitter();
// 监听函数
emitter.on('someEvent', function(arg1, arg2) {
  console.log('listener1', arg1, arg2);
});
// 监听函数
emitter.on('someEvent', function(arg1, arg2) {
  console.log('listener2', arg1, arg2);
});
// 触发函数
emitter.emit('someEvent', 'arg1 参数', 'arg2 参数');

// 结果:
// listener1 arg1 参数 arg2 参数
// listener2 arg1 参数 arg2 参数
14、util
  • util 是一个 Node.js 核心模块,提供常用函数的集合,用于弥补核心 JavaScript 的功能 过于精简的不足。
// util.inspect
// util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。
var util = require('util');
function Person() {
  this.name = 'byvoid';
  this.toString = function() {
    return this.name;
  };
}
var obj = new Person();
console.log(util.inspect(obj));
console.log(util.inspect(obj, true));
// Person { name: 'byvoid', toString: [Function] }
// Person {
//   name: 'byvoid',
//   toString:
//    { [Function]
//      [length]: 0,
//      [name]: '',
//      [arguments]: null,
//      [caller]: null,
//      [prototype]: { [constructor]: [Circular] } } }

// util.isArray(object) 判断是否为数组
util.isArray(new Array()); // true
util.isArray({}); // false

// util.isRegExp(object) 判断是否为正则表达式
util.isRegExp(/some regexp/); // true
util.isRegExp({}); // false

// util.isDate(object) 判断是否为日期
util.isDate(new Date()); // true
util.isDate({}); // false
15、querystring 模块
  • querystring 从字面上的意思就是查询字符串,一般是对 http 请求所带的数据进行解析。
    • querystring.parse parse 这个方法是将一个字符串反序列化为一个对象。
    const querystring = require('querystring');
    querystring.parse('name=whitemu&sex=man&sex=women');
    /*
        return:
      { name: 'whitemu', sex: [ 'man', 'women' ] }
    */
    
    • querystring.stringify stringify 这个方法是将一个对象序列化成一个字符串,与 querystring.parse 相对。
    • querystring.escape escape 可使传入的字符串进行编码
    • querystring.unescape unescape 方法可将含有%的字符串进行解码
16、GET/POST 请求
  • 获取 GET 请求内容
var http = require('http');
var url = require('url');

http
  .createServer(function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    // url.parse(url, true)  第二个参数为true时解析的 query 为对象 false则为路径
    console.log(url.parse(req.url, true));
    res.end('打印成功')
  })
  .listen(3000);

  // 在浏览器中访问 http://127.0.0.1:8888/user?name=xiaoming&url=www.baidu.com 然后查看返回结果:
  /*
    Url {
      protocol: null,
      slashes: null,
      auth: null,
      host: null,
      port: null,
      hostname: null,
      hash: null,
      search: '?name=xiaoming&url=www.baidu.com',
      query:
      [Object: null prototype] { name: 'xiaoming', url: 'www.baidu.com' },
      pathname: '/user',
      path: '/user?name=xiaoming&url=www.baidu.com',
      href: '/user?name=xiaoming&url=www.baidu.com'
    }
  /*
  • 获取 POST 请求内容
var http = require('http');
var querystring = require('querystring');

var postHTML =
  '<html><head><meta charset="utf-8"><title>Node.js 实例</title></head>' +
  '<body>' +
  '<form method="post">' +
  '网站名: <input name="name"><br>' +
  '网站 URL: <input name="url"><br>' +
  '<input type="submit">' +
  '</form>' +
  '</body></html>';

http
  .createServer(function(req, res) {
    var body = '';
    req.on('data', function(value) {
      body += value;
    });
    req.on('end', function() {
      // 解析参数
      body = querystring.parse(body);
      // 设置响应头部信息及编码
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf8' });

      if (body.name && body.url) {
        // 输出提交的数据
        res.write('网站名:' + body.name);
        res.write('<br>');
        res.write('网站 URL:' + body.url);
      } else {
        // 输出表单
        res.write(postHTML);
      }
      res.end();
    });
  })
  .listen(3000);

express 的使用

  • 安装npm i express -S
  • 1:引入 express 第三方对象
  • 2:构建一个服务器对象
  • 3:开启服务器监听端口
  • 4:处理响应
  • 在 express 中,保留了原生 http 的相关属性和函数
1、使用 express 开启一个服务
// - 1:引入express第三方对象
const express = require('express'); // 自动逐级向上查找node_modules/express的文件夹-> package.json(main属性) || express的文件夹/index.js
// - 2:构建一个服务器对象
let server = express(); // let server = http.createServer();
// - 3:开启服务器监听端口
server.listen(8888);
// - 4:处理响应
server.use((req, res) => {
  // 使用(请求与响应的过程中)
  res.end('hello world!!'); // 原生API
});
2、next()的使用
const express = require('express');
let app = express();

app.listen(8888, () => {
  console.log('服务器启动在8888端口');
});

// 1: app.use是请求与响应中执行的一件事,按代码顺序来执行 请求路径相同时只会执行第一个 next可以触发下一个
// 2:next() 是放行到下一件事的开关
// 3:如果全next,最终没有end页面数据,框架帮我们处理了
//     status:404

// 用户选择性url开头的部分,选择性调用对应的事
app.use('/sucai', (req, res, next) => {
  console.log('萝卜');
  next(); // 放行开关
});
// 一件事
app.use('/sucai', (req, res, next) => {
  console.log('白菜');
  next();
});
app.use('/huncai', (req, res, next) => {
  console.log('牛肉');
  next();
});
app.use('/huncai', (req, res, next) => {
  console.log('羊肉');
  next();
});
3、router
  • 使用步骤
    • 1:获取路由中间件对象 let router = express.Router();
    • 2:配置路由规则 router.请求方式(URL,fn事)
      • fn 中参数有 req,res,next
    • 3:将 router 加入到应用app.use(router)
const express = require('express');
let server = express();
// - 1:获取路由中间件对象 let router = express.Router();
let router = express.Router();
// - 2:配置路由规则 router.请求方式(URL,fn事)
router
  .get('/login', (req, res) => {
    res.end('login page');
  })
  .get('/register', (req, res) => {
    res.end('register page');
  });
//   - fn中参数有req,res,next
// - 3:将router加入到应用server.use(router)
server.use(router);
server.listen(8888);
4、res 扩展函数
const express = require('express');

let server = express();

// - res.json() 响应数据,最常用 , 返回ajax数据
// - res.redirect() 重定向
// - res.download() 下载
// - res.jsonp() 跨域处理

// - 1:获取路由中间件对象 let router = express.Router();
let router = express.Router();
// - 2:配置路由规则 router.请求方式(URL,fn事)
router
  .get('/json', (req, res) => {
    res.json([{ name: 'jack' }]); // res.end只能响应string||读文件中的data Buffer
  })
  .get('/redirect', (req, res) => {
    res.redirect('http://www.baidu.com');
  })
  .get('/jsonp', (req, res) => {
    res.jsonp('jack love rose');
  })
  .get('/download', (req, res) => {
    res.download('./app.js'); // 注意文件是如何被下载成功的
    // 基于服务器回写的content-type。等头信息
  });
//   - fn中参数有req,res,next
// - 3:将router加入到应用server.use(router)
server.use(router);
server.listen(8888);
5、使用 art-template 模板引擎
  • 下载 express-art-template art-template
  • npm i express-art-template art-template -S
  • app.js 中配置
    • 注册一个模板引擎
      • app.engine('.html',express-art-template);
        • 设置默认渲染引擎app.set('view engine','.html');
    • res.render(文件名,数据对象);
    • express 这套使用,默认在当前 app.js 同级的 views 目录查找
// ./index.js
const express = require('express');
let server = express();
// / - 注册一个模板引擎
//   - app.engine('.html',express-art-template);
      // 渲染文件的后缀名(引擎名称)
server.engine('.html',require('express-art-template'));
// 区分开发和生产环节的不同配置
server.set('view options', {
    debug: process.env.NODE_ENV !== 'production',
      // debug :  不压缩,不混淆,实时保持最新的数据
      // 非debug: 压缩/合并, list.html 静态数据不回实时更新(服务器重启才更新)
    imports:{
      // 数据的导入,和过滤显示的操作
      num:1,
      reverse:function(str) {
        return '^_^' + str + '^_^';
      }
    }
});
// 配置默认渲染引擎
server.set('view engine','.html');  // 设置模板引擎的类型为html
let router = express.Router();
router.get('/hero-list',(req,res) => {
  res.render('list.html',{
    heros:[{name:'貂蝉'},{name:'吕布'},{name:'董卓'}]
  });
})
server.use(router);
server.listen(8888);

// ./views/list.html
<html>
  <head>
    <meta charset="UTF-8">
    <title>Document</title>
  </head>
  <body>
      我是英雄列表
      <p>{{num}}</p>  //  1
      <p>{{reverse('abcde')}}</p>  // '^_^abcde^_^'
      <ul>
        {{each heros}}
        <li>{{$value.name}}</li>  // 输出数据
        {{/each}}
      </ul>
  </body>
</html>

补充:

// views, 设置放模板文件的目录,比如:
app.set('views', './views'); // 那么渲染的文件就是在这个路径下面找比如渲染index,就是就是找到./views/index.xxx文件

// view engine, 模板引擎,比如:
app.set('view engine', 'jade');
//这个接着上面,渲染文件index.xxx,那根据这个规则最后添加后缀为,index.jade,但是还不能生成html,也就是说,如果不设置这个,那么render的时候就要指定index.jade,而不是index就够了。app.engine的作用就是根据上面解析的文件名,调用相应的引擎,来把这个jade文件生产html文件。
6、next all
// ... 省略部分代码
router
  .get('/', (req, res, next) => {
    // 假如获取文件
    let errorPath = './abc/e.txt';
    try {
      fs.readFileSync(errorPath);
      res.render('index');
    } catch (err) {
      // throw err;  // 给用户看到了异常,太恶心
      next(err); // 触发一个具备4个参数的中间件函数
    }
  })
  // 最后一条路由中 判断路由的错误 如果路由错误将触发
  .all('*', (req, res) => {
    res.send('地址错误,您去首页吧');
  });
server.use(router);
// 处理错误(参数位置错误优先) -> 优雅的用户体验
server.use((err, req, res, next) => {
  res.send(
    '<h1>亲爱的用户,您访问的页面,有事儿了,<a href="/">去首页看看?</a></h1>'
  );
});
server.listen(8888);
7、nodemon
  • 修改代码自动重启刷新 不需要手动重启
  • 安装全局命令行工具 npm i -g nodemon
  • 进入到指定目录命令行 nodemon ./xxx.js
  • 手动触发重启,在命令行输入 rs 回车
8、静态文件
  • Express 提供了内置的中间件 express.static 来设置静态文件如:图片, CSS, JavaScript 等。
    你可以使用 express.static 中间件来设置静态文件路径。例如,如果你将图片, CSS, JavaScript 文件放在 public 目录下,你可以这么写:
    app.use('/public', express.static('public'));
    
9、路由
var express = require('express');
var app = express();

//  主页输出 "Hello World"
app.get('/', function(req, res) {
  console.log('主页 GET 请求');
  res.send('Hello GET');
});

//  POST 请求
app.post('/', function(req, res) {
  console.log('主页 POST 请求');
  res.send('Hello POST');
});

//  /del_user 页面响应
app.get('/del_user', function(req, res) {
  console.log('/del_user 响应 DELETE 请求');
  res.send('删除页面');
});

//  /list_user 页面 GET 请求
app.get('/list_user', function(req, res) {
  console.log('/list_user GET 请求');
  res.send('用户列表页面');
});

// 对页面 abcd, abxcd, ab123cd, 等响应 GET 请求
app.get('/ab*cd', function(req, res) {
  console.log('/ab*cd GET 请求');
  res.send('正则匹配');
});

var server = app.listen(8081, function() {
  var host = server.address().address;
  var port = server.address().port;

  console.log('应用实例,访问地址为 http://%s:%s', host, port);
});

mongodb

mongod --dbpath='./data'

如果出现 waiting for connections on port 27017 就表示启动成功。

  • mongodb 常用启动参数
选项 含义
--bind_ip 绑定服务 IP,若绑定 127.0.0.1,则只能本机访问,不指定默认本地所有 IP
--port 指定服务端口号,默认端口 27017
--logpath 指定 MongoDB 日志文件,注意是指定文件不是目录
--logappend 使用追加的方式写日志
--dbpath 指定数据库路径
--directoryperdb 设置每个数据库将被保存在一个单独的目录
--serviceName 指定服务名称
--serviceDisplayName 指定服务名称,有多个 mongodb 服务时执行
--install 指定作为一个 Windows 服务安装。
1、将 MongoDB 服务器作为 Windows 服务运行

像上面那样启动 mongodb,发现没办法输入命令行了,这是可以采用打开多个窗口来连接,但这样就太麻烦了,解决办法就是将 MongoDB 服务器作为 Windows 服务运行。
输入以下命令(需要以管理员身份打开命运提示符):

F:\mongodb\bin>mongod --dbpath "f:\data\db" --logpath "f:\data\log\mongodb.log" --serviceName "mongodb" --serviceDisplayName "mongodb" --install

如看到了类似如下输出:

2016-10-20T23:32:46.339+0800 I CONTROL  log file "f:\data\log\mongodb.log" exists; moved to "f:\data\log\mongodb.log.2016-10-20T15-32-46".

说明 mongodb 服务安装成功。启动 mongodb 服务:

F:\mongodb\bin>mongo

MongoDB 服务已经启动成功。
2、数据库操作
  • 查询有哪些数据库

    • 查询数据库:show dbs;
    • 切换数据库: use 数据库名; 如无这个数据库则自动创建并切换,创建后如果 show dbs 会发现并没有刚刚创建的库,需加入一些数据才能显示 如db.test.insert({"name":"菜鸟教程"})
  • 删除数据库: db.dropDatabase() 删除当前数据库,默认为 test,你可以使用 db 命令切换数据库删除

  • 查询当前 db 下有哪些集合

    • show collections;
  • 在 test 数据库中创建 mycol2 集合:db.createCollection("mycol2")

  • 在 MongoDB 中,你不需要主动创建集合。当你插入一些文档时,MongoDB 会自动创建集合

  > db.mycol2.insert({"name" : "小明"})
  > show collections
  mycol2
  • 删除集合 db.集合.drop()

    • 如删除 mycol2 集合 db.mycol2.drop()
  • 向集合中插入数据文档 db.集合.insert()db.集合.save()

    • 第一种: db.mycol2.insert({'name': '小明'})
    • 第二种: db.mycol2.save({'name': '小明'})

以上实例中 mycol2 是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。

  • 删除集合中的数据文档 db.集合.remove(<query>, <justOne>, <writeConcern>)
    • query :(可选)删除的文档的条件。
    • justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
    • writeConcern :(可选)抛出异常的级别

db.mycol2.remove({'title':'MongoDB'}, 1) 表示删除 mycol2 里满足条件的文档 且只删除一个文档

  • 查询集合中的数据文档 db.集合.find()

    • db.mycol2.find()
  • 更新集合中的数据文档 db.集合.update()

    • db.集合名.update({匹配条件对象},{$set:{修改后的对象}});
    • db.mycol2.update({name:'小明'},{$set:{contry:'印度'}});;
3、条件查询
  • db.mycol2.find({name:'小明'}) 查询姓名为小明的学生
  • db.mycol2.find({score:{$gt:90}}) 查找成绩大于 90 分的学生 $lt 小于
  • db.mycol2.find({score:{$ne:88}}) 查询数学成绩不等于 88 的同学
  • db.mycol2.find({score:{$gt:200}}) 查询总分大于 200 分的所有同学
4、分页
  • db.mycol2.find().skip(3).limit(3);
  • db.集合名称.find().跳到 3 显示 3 条
5、排序
  • db.mycol2.find().sort({key:排序方式});
  • db.mycol2.find().sort({'score':1}); //正数代表升序,负数代表降序
6、模糊匹配
  • db.mycol2.find({name:{$regex:'小'}}); 查找集合中的 name 属性有'小'字的数据
  • db.mycol2.find({name:{$regex:'明'}}); 查找集合中的 name 属性有'明'字的数据
7、聚合函数
  • 添加基础数据:
    db.mycol2.save({contry:'中国',name:'小明',score:77});
    db.mycol2.save({contry:'中国',name:'小红',score:88});
    db.mycol2.save({contry:'中国',name:'小张',score:99});
    db.mycol2.save({contry:'美国',name:'jack',score:45});
    db.mycol2.save({contry:'美国',name:'rose',score:67});
    db.mycol2.save({contry:'美国',name:'mick',score:89});

    • db.mycol2.aggregate([{$group:{_id:"$contry",num:{$sum:'$score'}}}]) 以 contry 为划分为组 以 score 为计算的总和
      结果:
      { "_id" : "美国", "num" : 201 }
      { "_id" : "中国", "num" : 264 }
8、联合查询
// 添加数据
db.orders.insert([
  { _id: 1, item: 'almonds', price: 12, quantity: 2 },
  { _id: 2, item: 'pecans', price: 20, quantity: 1 },
  { _id: 3 }
]);
db.inventory.insert([
  { _id: 1, sku: 'almonds', description: 'product 1', instock: 120 },
  { _id: 2, sku: 'bread', description: 'product 2', instock: 80 },
  { _id: 3, sku: 'cashews', description: 'product 3', instock: 60 },
  { _id: 4, sku: 'pecans', description: 'product 4', instock: 70 },
  { _id: 5, sku: null, description: 'Incomplete' },
  { _id: 6 }
]);
db.orders.aggregate([
  {
    $lookup: {
      from: 'inventory',  // 要关联的集合
      localField: 'item', // 当前的集合的字段
      foreignField: 'sku', // 要关联的集合的字段
      as: 'inventory_docs' // 返回的字段名
    }
  }
]);
// 查询结果
 { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2, "inventory_docs" : [ { "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 } ] }
 { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1, "inventory_docs" : [ { "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 } ] }

推荐阅读更多精彩内容