NodeJS

Posted by violetks on December 15, 2019

运行命令:node server.js

一、创建服务器

server.js:

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  // 读取 www 文件夹中的 index.html 文件
  // req.url 是 /index.html
  var file_url = './www' + req.url;
  fs.readFile(file_url, (err, data) => {
    err ? res.write("404") : res.write(data);
    res.end();
  });
});

server.listen(8080);
// 访问 localhost:8080/index.html,显示内容

二、get 数据解析

HTML 页面中通过 get 请求提交表单,需要转为 json 对象。
index.html:

<body>
  <form action="http://localhost:8080/aaa" method="get">
    用户名:<input type="text" name="user" value=""><br>
    密码:<input type="password" name="pass" value=""><br>
    <input type="submit" value="提交"><br>
  </form>
</body>

方法一:引入 querystring 模块将 url 转为 json 对象
querystring 适用于解析user=xl&pass=123这种形式。
server.js:

const http = require('http');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  console.log(req.url);  // 不用 querystring 之前:'/aaa?user=xl&pass=123'
  var json = {};
  if (req.url.indexOf('?') != -1) {
    var arr = req.url.split('?');
    var url = arr[0];
    json = querystring.parse(arr[1]);
  } else {
    var url = req.url;
  }
  res.end();
})

server.listen(8080);

方法二:引入 url 模块将 url 转为 json 对象
url 适用于解析/aaa?user=xl&pass=123这种形式。
server.js:

const http = require('http');
const urlLib = require('url');

http.createServer((req, res) => {
  var obj = urlLib.parse(req.url, true);
  var url = obj.pathname;    // '/aaa'
  var json = obj.query;      // {user:xl,pass:123}
  res.end();
}).listen(8080);

三、post 数据解析

const http = require('http');
const querystring = require('querystring');

http.createServer((req, res) => {
  // post 请求将 data 数据切分为多段传输
  var str = '';   // 接收数据
  var i = 0;
  req.on('data', data => {
    console.log(`第${i++}次收到数据`);
    str+=data;
  })
  // end 数据全部到达
  req.on('end', () => {
    var json = querystring.parse(str);
    console.log(json);
  })
}).listen(8080);

四、服务器整合

const http = require('http');
const fs = require('fs');
const urlLib = require('url');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  // GET
  var obj = urlLib.parse(req.url, true);
  var url = obj.pathname;
  const GET = obj.query;

  // POST
  var str = '';
  req.on('data', data => {
    str+=data;
  });
  req.on('end', () => {
    const POST = querystring.parse(str);

    // 文件请求
    var file_url = './www'+url;
    fs.readFile(file_url, (err, data) => {
      err ? res.write('404') : res.write(data)
      res.end();
    });
  });
});

server.listen(8080);

五、实现注册、登录

  1. 定义接口
    注册:/user?type=register&username=xxx&password=123456
    登录:/user?type=login&username=xxx&password=123456
    返回:{"status":false,"msg":"请求失败"}
  2. server.js
const http = require('http');
const fs = require('fs');
const urlLib = require('url');
const querystring = require('querystring');

// 构造数据
var users={};  // {"ray":"123456","zack":"123456"}

const server = http.createServer((req, res) => {
  // 解析数据
  var str='';
  req.on('data', data => {
    str+=data;
  });
  req.on('end', () => {
    var obj = urlLib.parse(req.url, true);

    const url = obj.pathname;
    const GET = obj.query;
    const POST = querystring.parse(str);

    // 区分——接口、文件
    if (url == '/user') {
      switch (GET.type) {
        case 'register':
          if (users[GET.username]) {
            res.write('{"status":false,"msg":"用户已存在"}');
          } else {
            users[GET.username] = GET.password;
            res.write('{"status":true,"msg":"注册成功"}');
          }
          break;
        case 'login':
          if (users[GET.username] == null) {
            res.write('{"status":false,"msg":"用户不存在"}');
          } else if (users[GET.username] != GET.password) {
            res.write('{"status":false,"msg":"用户名或密码有误"}');
          } else {
            res.write('{"status":true,"msg":"登录成功"}');
          }
          break;
        default:
          res.write('{"status":false,"msg":"未知的type"}');
      }
      res.end();
    } else {
      // 读取文件
      var file_url = './www'+url;
      fs.readFile(file_url, (err, data) => {
        err ? res.write('404') : res.write(data)
        res.end();
      });
    }
  });
});

server.listen(8080);

六、模块化

//modle.js - 定义模块
exports.a = 12;

// 批量输出
module.exports = { a: 12, b: 5, c: 8 };
//index.js - 使用模块
const modle = require('./modle.js');

alert(modle.a, modle.b);

七、NPM

NPM 就是 NodeJS 的包管理工具。npm 主要用来下载,安装,管理第三方模块。
1. 发布自己创建的 npm 包

$ npm login
$ npm whoami
$ npm init [-y]   // 会生成 package.json

# 自己建立 index.js 文件
exports.sum = function () {
  var res = 0;
  for (var i = 0; i < arguments.length; i++) {
    res+=arguments[i];
  }
  return res;
}

# 发布
$ npm publish

# 更新
$ npm update <package-name>

# 删除
$ npm unpublish --force

2. 其它 NPM 命令

  • npm info <package-name>:查看包的信息
  • npm info <package-name> versions:查看包的版本信息
  • npm install <package-name>:安装指定的包
  • npm install <package-name> -g:全局安装包
  • npm install <package-name>@<version>:安装指定版本的包
  • npm install <package-name> --save:安装包并记录依赖,会在 package.json 中 dependencies 属性记录依赖
  • npm uninstall <package-name>:卸载包
  • npm root -g:查看全局目录
  • npm config list:查看 npm 配置

八、Express 框架

1、Express 是对 NodeJS 的封装,用于快速创建服务器。

const express = require('express');
const expressStatic = require('express-static');

const server = express();
server.listen(8080);

server.use(expressStatic('./www'));  // 读取 www 文件夹中的静态文件

2、实现登录,接口:/login:username=xxx&password=123

const express = require('express');
const expressStatic = require('express-static');

const server = express();
server.listen(8080);

// 用户数据
var users = { 'ray': '123456', 'zack': '654321' };

server.get('/login', (req, res) => {
  console.log(req.query);  // GET 请求数据
  var username = req.query['username'];
  var password = req.query['password'];
  if (users[username] == null) {
    res.send({status: false, msg: '此用户不存在'});
  } else if (users[username] != password) {
    res.send({status: false, msg: '密码错误'});
  } else {
    res.send({status: true, msg: '登录成功'});
  }
});

server.use(expressStatic('./www'));  // 读取 www 文件夹中的静态文件

3、body-parser处理 post 请求

const express = require('express');
const bodyParser = require('body-parser');

const server = express();
server.listen(8080);

server.use(bodyParser.urlencoded({
  extended: true;     // 开启扩展模式
  limit: 2*1024*1024  // 数据大小限制为 2M
}));

server.use('/', (req, res) => {
  console.log(req.body);  // POST 请求数据
})

4、链式操作

const express = require('express');

const server = express();
server.listen(8080);

server.use((req, res, next) => {
  req.a = 12;
  next();
});

server.use('/', (req, res) => {
  console.log(req.a);  // 12
})

九、cookie 和 session 操作

npm install express express-static cookie-parser cookie-session

1、设置 cookie

const express = require('express');

const server = express();
server.listen(8080);

server.use('/aaa/a.html', (req, res) => {
  res.cookie('user', 'ray', { path: '/aaa', maxAge: 24*3600*1000 });  // 设置 cookie
})

2、读取 cookie

const express = require('express');
const cookieParser = require('cookie-parser');

const server = express();
server.listen(8080);

server.use(cookieParser());
server.use('/aaa/a.html', (req, res) => {
  console.log(req.cookies);
  res.send('ok');
})

3、防止 cookie 被修改

const express = require('express');
const cookieParser = require('cookie-parser');

const server = express();
server.listen(8080);

server.use(cookieParser('asdfieigqn'));  // 解析
server.use('/', (req, res) => {
  req.secret = 'asdfieigqn';
  res.cookie('user', 'ray', { signed: true });

  console.log('签名cookie:', req.signedCookies);  // {user: 'ray'}
  console.log('无签名cookie:', req.cookies);      // {}
  res.send('ok');
})

4、删除 cookie

res.clearCookie('user');

5、使用 cookie-session 记录页面访问次数

const express = require('express');
const cookieParser = require('cookie-parser');
const cookieSession = require('cookie-session');

const server = express();
server.listen(8080);

server.use(cookieParser());
server.use(cookieSession({
  name: 'sess',
  keys: ['key1', 'key2', 'key3'],
  maxAge: 24*3600*1000
}));

server.use('/', (req, res) => {
  // console.log(req.session);
  if (req.session['count'] == null) {
    req.session['count'] = 1;
  } else {
    req.session['count']++;
  }
  console.log(req.session['count']);
  res.send('ok');
})

十、模板引擎

  • jade:破坏式、侵入式、强依赖。npm install jade
  • ejs:温和、非侵入式、弱依赖。npm install ejs

1、使用jade编写 HTML 结构标签

// 1.js
const jade = require('jade');

var str = jade.render('html');

console.log(str);   // 输出<html></html>
// index.js
const jade = require('jade');
// 从文件中读取
var str = jade.renderFile('index.jade', { pretty: true });

console.log(str);
// index.jade  1.缩进表示层级
html
  head
    style
    script(src="a.js")
    link(href="a.css",ref="stylesheet")  // 2.给标签加属性,多个属性用,分隔
  body
    div
      ul
        li 列表一
        li 列表二   // 3.双标签里加内容,空格,直接写,编译生成 <li>列表二</li>
    |abc   // 4. | 防止被解析成 HTML 标签,效果 <body>abc</body>
  // 5. 标签后加 . 使下层代码原样输出,不被解析
    script.
      window.onload = function(){}
  • 使用include引入 JS 代码
    // index.jade
    html
    head
      style
      script
        include a.js
    body
    
// a.js
window.onload = function () {
  var btn = document.getElementById('btn1');
  btn.onclick = function () {
    alert('click me!');
  };
};
  • 使用变量
    // index.js
    const jade = require('jade');
    console.log(jade.renderFile('index.jade', {pretty: true, name: 'ray',
    json: { width: '200px', height: '200px', background: 'red' },
    arr: ['aaa', 'left-wrap', 'active']
    }));
    
// index.jade
html
  head
  body
    -var a=12;   // - 表示这是一句代码
    -var b=5;
    div 结果是:#{a+b}
    div 用户名:#{name}
    span=name    // 这句效果和上句一样
    div(style=json)
    div(class=arr)
    div(class=arr class="box")
  • 输出 HTML 标签
    // index.js
    const jade = require('jade');
    console.log(jade.renderFile('index.jade', { pretty: true,
    content: "<h2>你好</h2>"
    }));
    
// index.jade
html
  head
  body
    div!=content  // 加 ! ,输出 <div><h2>你好</h2></div>

2、jade完整示例

// main.js
const jade = require('jade');
const fs = require('fs');

var str = jade.renderFile('index.jade', { pretty: true });

fs.writeFile('./build/index.html', str, (err) => {
  if (err) console.log('编译失败');
  else console.log('成功');
})
doctype
html
  head
    meta(charset="utf-8")
    title jade测试页面
    style.
      div
      {width:100px;height:100px;background:#ccc}
      div.last {clear:left}
  body
    -var a=0;
    while a<12
      if a%4== && a!=0
        div.last #{a++}
      else
        div=a++

生成的 HTML 页面结构:

lbVLZ9.png

3、ejs的使用

// 1.HTML 中引入变量
<div><%= name %></div>
// 1.js
const ejs = require('ejs');

ejs.renderFile('1.html', { name: 'ray' }, (err, data) => {
  console.log(data);
})
<% var str="<p></p>"; %>
<%- str %>           // ejs 输出 HTML 标签
<% include a.txt %>  // 引入 a.txt 中内容