Nodejs

  |   0 评论   |   0 浏览

前言

 适合有 js 基础的同学学习,概述 nodejs 的各方面应用

Hello World

// 引用http模块
const http = require(" http");
// http 端口
const port = 8080;
// 创建一个Http服务
const server = http.createServer((req, res) => {
  // response(响应)
  res.end(" Hello, world.");
});
// 在端口监听服务
server.listen(port, () => {
  console.log(" Server listening on: http:// localhost:% s", port);
});

基础

...

模块

// 创建模块
const canadianDollar = 0. 91;

function roundTwo( amount) {
  return Math.round( amount * 100) / 100;
}

// 导出模块
exports.canadianToUS = canadian => roundTwo( canadian * canadianDollar);

// 导出模块
exports.USToCanadian = us => roundTwo( us / canadianDollar);

// 引入模块
const currency = require("./ currency");

异步

[
  "Kazakhstan is a huge country... what goes on there?",
  "This weather is making me craaazy",
  "My neighbor sort of howls at night"
]
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <h1>Latest Posts</h1>
    <ul>
      <!-- %会被替换为标题 -->
      <li>%</li>
    </ul>
  </body>
</html>
const http = require("http");
const fs = require("fs");

http
  .createServer((req, res) => {
    if (req.url == "/") {
      // 读取文件,回调异常和数据
      fs.readFile("./title.json", (err, data) => {
        if (err) {
          console.error(err);
        } else {
          // 将文件信息保存在titles中
          const titles = JSON.parse(data.toString());
          // 读取文件,回调异常和数据
          fs.readFile("./template.html", (err, data) => {
            if (err) {
            } else {
              const tmpl = data.toString();
              // 替换
              const html = tmpl.replace("%", titles.join("<li></li>"));
              // response 返回
              res.writeHead(200, { "Content-Type": "text/html" });
              res.end(html);
            }
          });
        }
      });
    }
  })
  .listen(8080, "127.0.0.1");

事件发射

事件发射类似事件监听

const net = require("net");
const server2 = net.createServer(socket => {
  // 当data传入时触发
  socket.on("data", data => {
    socket.write(data);
  });
});

单次事件监听

const net = require("net");
// 当data传入时触发,注意on和once(onece)
socket.once("data", data => {
  socket.write(data);
});

创建事件发射器

const EventEmitter = require("events").EventEmitter;
// 创建一个事件发射器
const channel = new EventEmitter();
// 创建事件监听
channel.on("join", () => {
  console.log("do something this");
});

发射事件

// 发射join激活监听的join事件
channel.emit("join");

demo

const events = require("events");
const net = require("net");
const channel = new events.EventEmitter();

channel.cilents = {};
channel.subscriptions = {};

// join事件,接收id和client
channel.on("join", (id, client) => {
  // 将client放入集合中,id为key
  this.clients[id] = client;
  // ip和信息
  this.subscriptions[id] = (senderId, message) => {
    // 如果id不为自己的id
    if (id != senderId) {
      // client写出信息
      this.clients[id].write(message);
    }
  };
  // 另一个事件监听
  this.on("broadcast", this.subscriptions[id]);
});

const server = net.createServer(client => {
  // 构造id和client
  const id = `${client.remoteAddress}:${client.remotePort}`;
  // 触发join
  channel.emit(" join", id, client);
  // 当data被触发时
  client.on("data", data => {
    data = data.toString();
    // 触发broadcast
    channel.emit(" broadcast", id, data);
  });
});
// 构建离开监听
channel.on('leave,(id)=>{
  channel.removeListener('broadcast',this.subscriptions[id]);
  channel.emit('broadcast',id,`${id} has left the chatroom \n`);
});

// 当client关闭时
client.on('close',()=>{
  // 触发离开监听器
  channel.emit('leave');
})

文件监听器

const fs = require("fs");
const events = require("events");
// 继承EventEmitter创建一个工具
class Watcher extends events.EventEmitter {
  constructor(watchDir, processDir) {
    super();
    this.watchDir = watchDir;
    this.processDir = processDir;
  }

  // 遍历目录发射事件
  watch() {
    fs.readdir(this.watchDir, (err, files) => {
      if (err) throw err;
      for (var index in files) {
        this.emit("process", files[index]);
      }
    });
  }

  // 开始监听
  start() {
    fs.watchFile(this.watchDir, () => {
      this.watch();
    });
  }
}

module.exports = Watcher;
// 创建一个process监听器,将所有文件名称改成小写
const watcher = new Watcher(watchDir, processedDir);
watcher.on("process", file => {
  const watchFile = `${watchDir}/${file}`;
  const processedFile = `${processedDir}/${file.toLowerCase()}`;
  fs.rename(watchFile, processedFile, err => {
    if (err) throw err;
  });
});

流控

串行流控

setTimeout(() => {
  console.log("1");
  setTimeout(() => {
    console.log("2");
  }, 100);
}, 200);

demo

const fs = require("fs");
const request = require("request");
const htmlparser = require("htmlparser");
const configFilename = "./rss_feeds.txt";

// 串形任务一:检查rss文件是否存在
function checkForRSSFile() {
  fs.exists(configFilename, exists => {
    if (!exists) {
      return next(new Error(`Miss Rss file`));
    }
    next(null, configFilename);
  });
}

// 串形任务二:随机读取rss文件的一个url
function readRSSFile(configFilename) {
  fs.readFile(configFilename, (err, feedList) => {
    if (err) return next(err);
    feedList = feedList
      .toString()
      .replace(/^\s+|\s+$/g, "")
      .split("\n");
    const random = Math.floor(Math.random() * feedList.length);
    next(null, feedList[random]);
  });
}

// 串形任务三:向rss源发请求获取response
function downloadRSSFeed(feedUrl) {
  request({ uri: feedUrl }, (err, res, body) => {
    if (err) return next(err);
    if (res.statusCode !== 200) {
      return next(new Error("err code"));
    }
    next(null, body);
  });
}

// 串形任务四:解析response
function parseRSSFead(rss) {
  const handler = new htmlparser.RssHandler();
  const parser = new htmlparser.Parser(handler);
  parser.parseComplete(rss);
  if (!handler.dom.items.length) {
    return next(new Error("No Rss items Found"));
  }
  const item = handler.dom.items.shift();
  console.log(item.title);
  console.log(item.link);
}

// 任务队列
const tasks = [checkForRSSFile, readRSSFile, downloadRSSFile, parseRSSFead];

// 迭代任务将上一步结果传入下一步任务
function next(err, result) {
  if (err) throw err;
  const currentTask = tasks.shift();
  if (currentTask) {
    currentTask(result);
  }
}

// 开始
next();

并行流控

const fs = require("fs");
const tasks = [];
const wordCounts = {};
const filesDir = "./text";
let completedTasks = 0;

// 输出各项单词数量
function checkIfComplete() {
  completedTasks++;
  if (completedTasks === tasks.length) {
    for (let index in wordCounts) {
      console.log(`${index}: ${wordCounts[index]}`);
    }
  }
}

function addWordCount(word) {
  // 单词列表丽存在这个词吗?
  // 存在+1
  // 不存在初始化1
  wordCounts[word] = wordCounts[word] ? wordCounts[word] + 1 : 1;
}

// 记单词
function countWordsInText(text) {
  const words = text
    .toString()
    .toLowerCase()
    .split(/\W+/)
    .sort();

  words.filter(word => word).forEach(word => addWordCount(word));
}

// 读取文件夹
// 创建任务集合同时调用
fs.readdir(filesDir, (err, files) => {
  if (err) throw err;
  const task = file => {
    files.forEach(file => {
      return () => {
        fs.readFile(file, (err, text) => {
          if (err) throw err;
          countWordsInText(text);
          checkIfComplete();
        });
      };
    })(`${fileDir}/${file}`);
    tasks.push(task);
  };
  tasks.forEach(task => task());
});

NodeWeb

express

创建 node 项目

mkdir web
cd web
#初始化项目
npm init -fy
#安装依赖
npm install --save express

#目录下会多出package.json
#package.json中会自动带入
# dependencies:{express:version}
# package.json描述了项目信息,dependencies描述了项目引入了哪些第三方包

简单的服务器

const express = require("express");
const app = express();

// 端口
const port = process.env.PORT || 3000;

// 拦截action并返回response
app.get("/", (req, res) => {
  res.end("Hello,World");
});

app.listen(port, () => {
  console.log(`Express web app start`);
});

启动 node index.js
将启动命令写入 node 脚本中,打开 package.json

"scripts":{
  "start":"node index.js"
}

运行 npm start 即可调用 scripts 中的 start

RESTFul Web

const express = require("express");
const app = express();
const articles = [{ title: "Example" }];

app.set("port", process.env.PORT || 3000);
// get获取全部文章
app.get("articles", (req, res, next) => {
  res.end(articles);
});
// post新增文章
app.post("/articles", (req, res, next) => {
  res.end("OK");
});
// get获取id参数文章
app.get("/articles/:id", (req, res, next) => {
  const id = req.params.id;
  res.end(id);
});
// delete删除id文章
app.delete("/articles/:id", (req, res, next) => {
  res.end("deleted");
});
// 启动服务
app.listen(app.get("port"), () => {
  console.log("App started on port", app.get("port"));
});

解析 req.body

npm install --save body-parser

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const article = [{ title: "Example" }];

app.set("port", process.env.PORT || 3000);

// 提供body json支持
app.use(bodyParser.json());

// 提供form支持
app.use(bodyParser.urlencoded({ extended: true }));

app.post("/articles", (req, res, next) => {
  const article = { title: req.body.title };
  article.push(article);
  res.end(article);
});

添加数据库

npm install sqlite3

const sqlite3 = require("sqlite3").verbose();
const dbName = "later.sqlite";
const db = new sqlite3.Database(dbName);

// 序列化一张表 id主键
db.serialize(() => {
  const sql = `
 CREATE TABLE IF NOT EXISTS articles(
   id integer primary key,title,content TEXT
 )`;
  db.run(sql);
});

class Article {
  static all(cb) {
    // 获取所有文章
    db.all("SELECT * FROM articles", cb);
  }

  // 获取指定id的一篇文章
  static find(id, cb) {
    db.get("SELECT * FROM articles WHERE id =?", id, cb);
  }

  // 新增一篇文章
  static create(data, cb) {
    const sql = "INSERT INTO articles(title,content) VALUES (?,?)";
    db.run(sql, data.title, data.content);
  }

  // 删除一篇文章
  static delete(id, cb) {
    if (!id) return cb(new Error("id is null"));
    db.run("DELETE FROM articles WHERE ID = ?", id, cb);
  }
}

重构

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

const app = express();
// 加载数据库模块
const Article = require("./db").Article;

app.set("port", process.env.PORT || 3000);

app.use(bodyParser.join());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/articles',(req,res,next)=>{
  Article.all((err,articles)=>{
    res.end(articles);
  })
})
...

模版

npm install ejs --save
还有更多别的引擎

<% include head %>
<ul>
  <% articles.forEach((article)=>{%>
  <li><a href="/articles/<% article.id %>"> <%= article.title%> </a></li>
  <%)}%>
</ul>
<% include foot %>
...
// 渲染ejs,传入数据articles
res.format({
  html:()=>{res.render('article.ejs',{articles:articles})},
  json:()=>{
    res.end(articles)
  }
})

前端构建(打包)

安装 mocha 测试

npm install --save mocha

写脚本

"scripts":{
  "test":"./node_modules/.bin/mocha test/*.js"
}

创建定制 npm 脚本

#安装 babel编译插件
mkdir es2015-example
cd es2015-example
npm init -y
npm install --save-dev babel-cli babel-preset-es2015
echo '{ "presets": ["es2015"] }' > .babelrc

创建脚本

"scripts":{
  "babel":"./node_modules/.bin/babel browser.js -d build/"
}

npm run babel 运行自定义脚本

Gulp 实现自动化任务

# 全局安装gulp
npm i --global gulp-cli
# 如果需要删除包
# npm rm --global gulp
mkdir gulp-example
cd gulp-example
# 初始化npm项目
npm init -y
# 安装gulp依赖
npm i --save-dev gulp
touch gulpfile.js
# 添加一些插件
# 如果需要删除包
# npm uninstall --save-dev
npm i --save-dev gulp-sourcemaps gulp-babel babel-preset-es2015
npm i --save-dev gulp-concat react react-dom babel-preset-react
npm i --save-dev gulp-watch
const gulp = require("gulp");
const sourcemaps = require("gulp-sourcemaps");
const babel = require("gulp-babel");
const concat = require("gulp-concat");
const watch = require("gulp-watch");

// 定义gulp任务
gulp.task("default", () => {
  return gulp
    .src("app/*.jsx") // 查找所有jsx文件
    .pipe(sourcemaps.init()) // 监视资源文件,为调试构建源码映射
    .pipe(
      babel({
        presets: ["es2015", "react"] // 使用es2015和react配置babel来编译
      })
    )
    .pipe(concat("all.js")) // 所有文件拼接到all.js中
    .pipe(sourcemaps.write(".")) // 单独写入源码映射文件
    .pipe(gulp.dest("dist")); // 将文件输出到dist目录下
});

// 监听目录并执行gulp任务
gulp.task("watch", () => {
  watch("app/**.jsx", () => {
    gulp.start("default");
  });
});

webpack

mkdir webpack-example
npm init -y
npm install --save react react-dom
npm install --save-dev webpack babel-loader babel-core
npm install --save-dev babel-preset-es2015 babel-preset-react

创建 webpack.config.js

const path = require("path");
const webpack = require("webpack");
module.exports = {
  // 输入文件
  entry: "./app/index.jsx",
  // 输出文件
  output: { path: __dirname, filename: "dist/bundle.js" },
  module: {
    loaders: [
      {
        // 加载所有jsx文件
        test: /.jsx$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        // 加载es2015,react插件
        query: { presets: ["es2015", "react"] }
      }
    ]
  }
};

服务端框架

1.Koa 轻便极简在中间件中使用 ES2015 生成器语法。适合依赖外部 WebAPI的单页Web程序。
2.hapi的重点是HTTP服务器和路由。适合由很多小服务器组成的轻便后台。
3.Flatiron是一组解耦的模块,既可以当作Web MVC框架来用, 也可以当作更轻便的 Express 库。 Flatiron 跟 Connect 中间 件是兼容的。
4.Kraken是基于Express的,添加了安全特性。可以用于MVC。
5.Sails.js是Rails/Django风格的MVC框架。有ORM和模板系统。
6.DerbyJS 是个同构框架,适合实时程序。
7.LoopBack 帮我们省掉了写套路化代码的工作。它可以快速生成带有数据库支持的RESTAPI,并有个API管理界面。

Connect

安装 npm install connect

// 基础用法
const app = require("connect")();
app.use((req, res, next) => {
  res.end("hello world");
});
app.listen(3000);
// 创建中间件
function hello(req, res) {
  res.setHeader("Content-Type", "text/plain");
  res.end("hello world");
}

// 使用中间件
connect()
  .use(logger)
  .use(hello)
  .listen(3000);

可配置中间件

function setup(options) {
  options...
  return (req, res, next) => {
    ...
  };
}
// 使用中间件
app.use(setup({some:'options'}));

异常处理

function throwError(error, req, res, next) {
  console.error(error);
  res.end("Server error");
}

模版类型

存储数据

连接 Postgres

npm install pg --save

const pg = require("pg");
// 创建db实例
const db = new pg.Client({ databases: "articles" });
db.connect((err, client) => {
  if (err) throw err;
  console.log("connected to databases");
  db.end();
});

定义表

db.query(
  `
  CREATE TABLE IF NOT EXISTS snippets(
    id SERIAL,
    PRIMARY KEY(id),
    body text
  )  
`,
  (err, result) => {
    if (err) throw err;
    console.log('Created table "snippets"');
    db.end();
  }
);

插入数据

const body = "Hello world";
db.query(
  `
INSERT INTO snippets (body) VALUES('${body}')
RETURNING id
`,
  (err, result) => {
    if (err) throw err;
    const id = result.rows[0].id;
    console.log("Inserted row with id %s", id);
  }
);

Knex

支持数据库 PostgreSQL,MSSQL,MYSQL,MARIADB,SQLITE3,ORACLE

npm install knex

db("articles")
  .select("title")
  .where({ title: "Today news" })
  .asCallback((err, articles) => {
    if (err) throw err;
    console.log(articles);
  });

knex sqlite3 demo

const knex = require("knex");
const db = knex({
  client: "sqlite3",
  connection: {
    // 文件模式
    // filename: ':memory:'内存模式
    filename: "tldr.sqlite"
  },
  useNullAsDefault: true
});

module.exports = ()=>{
  return db.schema.createTableIfNotExists('article',table=>{
    // 自增
    table.increments('id').primary();
    table.string('title');
    table.text('content');
  })
}

module.exports.Article = {
  all(){
    return db('articles').orderBy('title');
  }

  find(id){
    return db('articles').where({id}).first();
  }

  create(data){
    return db('articles').insert(data);
  }
  delete(id){
    return db('articles').del().where({id});
  }
};

// 使用上面的sql
db().then(()=>{
  db.Article.create({title:'my article',content:'article content'}).then(()=>{
    db.Article.all().then(articles=>{
      console.log(articles);
      process.exit();
    })
  })
}).catch((error)=>{if(error)throw error});

切换 pg

npm install pg --save

const db = knex({
  client: "pg",
  connection: {
    database: "articles"
  }
});