Node.js笔记

96
kirito_song
0.3 2017.05.17 11:05* 字数 6652
埃罗芒阿老师2.jpg
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正

  • 目录

简书好像不支持锚点、复制搜索(反正也是写给我自己看的)
  • 浏览器
  • javaScript
  • 编程语言的能力
  • Node
  • 安装(Mac)
  • 使用
  • Node---REPL环境
  • 终端命令
  • 异步操作
  • I/O
  • Node多线程
  • NPM包管理工具
  • nrm
  • querystring字符串查询模块
  • URL模块
  • 制作一个简单的表单提交
  • 文件操作
  • 模板字符串
  • HTTP
  • http传输方式
  • NET.socket
  • 手动实现一个简单的静态资源管理服务
  • exports&&require
  • node_modules文件夹
  • package.json
  • Post请求处理(原生)
  • 一些工具框架(库)
  • 浏览器

1、请求http地址(报文---传输的数据文档)
浏览器的最大作用就是将一个url地址封装成一个请求报文、并解析相应报文。
2、服务器返回的相应报文。
报文内容:
html/css/image = >渲染
js=>解释(执行)js

  • javaScript

脚本语言
浏览器中运行
客户端页面交互实现

运行在浏览器内核中的js引擎(engine)下
作用:
 1、操作DOM(对DOM的修改、增删改、注册事件)
 2、AJAX/跨域
 3、BOM对象(页面跳转、历史记录、console.log()、alert()、window对象下的大部分子对象)
 4、ECMAScript

不能进行的操作(由于js运行环境处于客户端。影响安全性):
1、文件操作(文件和文件夹的CRUD增删改查)
2、操作系统信息(版本号等)
  • 编程语言的能力

所有编程语言本身只提供定义变量、函数、对象、流程控制、循环等操作

能力高低取决于运行该语言的平台(环境)----浏览器

对于js来讲。DOM/BOM等能力均由浏览器引擎提供。
java
既是语言、又是平台。
java运行与java虚拟机(vm)平台上(虚拟机本身可以跨操作系统运行)。

 C#
   平台:.net framework(Windows)
   也可以运行于MONO这样的平台(Linux)

 PHP
   既是语言、又是平台
javaScript并不只能运行于浏览器
能运行在哪取决于环境有没有特定的平台
  • Node

  • Node.js并不是一种语言。(使用v8引擎+js语法。V8:高效解析js、大部分为异步。node就是将V8中的一些功能移植到了服务器上)
  • 一个js在服务端的运行平台(runtime)
  • 实际上就是JavaScript的虚拟机
  • 是Node选择了支持JavaScript、而不是JavaScript选择了Node。
  • 与PHP、JSP、Python等直接运行在服务器程序(Apache、Naginx、Tomcat、IIS)上的不同,Node.js跳过了HTTP服务器、不需要建立在任何服务器软件之上。
  • 不同于许多经典架构(LAMP=Linux(操作系统)+Apache(服务器)+MySQL(数据库)+PHP(语言))、有着很大的不同、演变成LMP形式。并不存在web容器(根目录)。�Node部署成功之后、并没有运行服务器、但是Node线程却担当了服务器的作用。
  • 特点

所谓特点、就是node.js如何解决服务器高性能瓶颈问题。
服务器语言(PHP、Java等)会为每一个客户端建立一个新的线程(大约2MB)、理论上一个8GB内存的服务器可以连接4000个用户。
Node只有一个线程、当用户连接时、触发一个内部事件。通过非阻塞I/O、事件驱动机制、让node在宏观上实现并行。使用node.js、一个8GB的服务器可以处理4万用户的链接。

单线程:充分利用CPU.

提高链接上限、但是主线程崩溃会出现大面积崩溃。

非阻塞I/O(non-blocking I/O ):

在执行了I/O操作时、会将I/O操作移交磁盘进行物理并发、并立即执行其后面的代码、I/O结束后以事件的形式返回并执行回调。

事件驱动(event-driven):

新用户请求进入、或老用户I/O异步回调返回时、会以事件形式加入事件环、等待调度(回调拥有更高的优先级)。

善于I/O、长连接、不善于计算:

擅长于业务调度(用户表单收集--百度贴吧投诉、考试系统)、如果业务过渡占用CPU进行计算、实际上过多计算会阻塞线程。长连接也是业务调度。

  • 作用:

    1、web网站服务器(直接服务器)---比如淘宝
    2、web网站请求包装、服务器返回数据分发、渲染HTML页面。(间接服务器)---Node的并发数、抗压性高于传统服务器平台。比如天猫
    https://github.com/NetEase/pomelo 网易开源游戏服务端
  • 安装(Mac)

  • 条件:

1、xcode(因为内含GCC、python等)
   xcode-select -p(可以检查一下)
2、python(有了xcode基本python也具备了)
   python -V(区分大小写)
  • 安装Homebrew

ruby -v(安装依赖ruby)
   www.brew.sh(Homebrew官网)
  • 安装Node.js

brew install node
  • 版本控制

n 7.7.2(下载7.7.2版本的node.js)
  • 安装完成之后
  n(上下键选择本地版本。回车确定安装)
  • 使用

nodejs.org(官方教程)

你需要一个js文件控制服务器行为(例:)

 //加载“http”模块。由JavaScript编写、负责创建web服务器、以及处理http相关任务
 const http = require('http');
 //加载其他js文件。无返回值、直接加载即可。
 require('./vue.js')
 //获取调用者对象
 module.parent
 
 //ip、字符串
 const hostname = '127.0.0.1';
 //端口、数字
 const port = 3005;
 
 //创建服务器
 //http.createServer(function(回调函数));添加一个回调函数
 //req含有请求相关的信息(来源url、请求类型)
 //res含有请求响应的内容
 //1.返回文字等信息
 const server = http.createServer(function(req, res){
   res.statusCode = 200;
   //文本内容类型
   res.setHeader('Content-Type', 'text/plain');
   res.end('Hello World\n');
 });
 
 //2.返回一个html页面
 /*不同于Apache。node.js没有web容器的概念、也就是没有文件夹的概念*/
 const server = http.createServer(function(req, res){
     //node作为跨平台文件、不支持同文件夹下直接index_01.html这种windows写法、必须使用./index_01.html的linux写法
     fs.readFile('./index_01.html',(err,data) =>{
         res.statusCode = 200;
         res.setHeader('Content-Type', 'text/html');
         if (!err) {
             res.end(data);
         }else {
             res.end('html not found');
         }
     });
 });
 
 //3.返回路由页面
 const server = http.createServer(function(req, res){
 //http://127.0.0.1:3007/index_01.html    req.url =/index_01.html
 //http://127.0.0.1:3007/index_02.html    req.url =/index_02.html
   var url = req.url;
   //如果你愿意、也可以手写url以隐藏文件路径
   //比如
   if (req.url == '/lalala') url = '/index_01.html';
     fs.readFile('.'+url,(err,data) =>{
         res.statusCode = 200;
         res.setHeader('Content-Type', 'text/html');
         if (!err) {
             res.end(data);
         }else {
             res.end('html not found');
         }
     });
 });
 
 //让服务器在端口监听请求然后调用上面的函数
 server.listen(port, hostname, function()  {
     //成功启动服务器监听时、在控制台打印
     console.log(`Server running at http://${hostname}:${port}/`);
 });

res.end(data);是回调内必须实现的一个函数。

//ES6中匿名函数
function(a){} === (a) => {}

关于路由文件、有个重点

node.js在每一次读取文件时、都会调取一遍createServer的回调
也就是说、内部的图片也需要手动读取。所以:

 var url = req.url;
 fs.readFile('.'+url,(err,data)

这样的构成就非常重要。
否则就连图片都需要手动指定文件路径了。
当然。对于node.js。根目录就是js文件所在目录。

启动服务器
 1.cd到js文件夹目录下
 2.node servers.js(启动服务器文件 )
 3.control+c(关闭服务器)
带参数运行脚本
 node servers.js 123 asd
 123可以通过process.argv[2]获取
 asd可以通过process.argv[3]获取
 argv[2]||'lalalalal' === argv[2]?argv[2]:'lalalala'
  • Node---REPL环境

//进入REPL环境(类似web控制台、用来测试代码模块)
(控制台)node
node --use_strict//严格模式--直接a = 10;之类会报错
//退出REPS环境
.exit
上次取值替代符号
_ //代表上次所取的值
stdin&&stdout
stdin:采集用户输入
stdout:控制台输出(不带\n的console.log())

一个简单的控制台登录demo

var q = '请输入用户名';
 
 var users = {
     'user1':'111',
     'user2':'222',
     'user3':'333'
 };
 
 process.stdout.write(q+'\n');
 
 var type = 0;
 var user;
 
 process.stdin.on('data',(input) => {
     //获取keys的数组、并且查找是否含有input内容
     input = input.toString().trim();
 
     if (type === 0 ){
         if(Object.keys(users).indexOf(input) === -1) {
             //用户名不存在
             process.stdout.write('用户名不存在、');
             process.stdout.write(q+':\n');
             type = 0;
         }else {
             user = input;
             type = 1;
             process.stdout.write('请输入密码:\n');
         }
     }else {
         var pwd = users[user];
         if (input === pwd){
             process.stdout.write('用户'+user+'登录成功\n');
             type = 0;
         }else  {
             process.stdout.write('密码错误、请重新输入密码:\n')
         }
     }
 });
  • 终端命令

输出控制台输出+创建文件
echo 'aaa' >>1.txt
查看文件内容
cat 1.txt
创建一个目录/文件
mkdir dirname
删除文件/目录
rm -rf dirname

  • 异步操作

1、网络请求
2、文件操作

  • I/O

input/output

  • Node多线程

1、Node平台将一个任务联通该任务的回调函数放到一个时间循环系统中。
2、事件循环高效的管理系统线程池、同事高效执行每一个任务。
3、当任务执行完成过后自动执行回调函数。

Node将所有的阻塞操作、交付给内部线程池。
Node本身主线程主要就是不断的“往返调度”(文件操作、网络请求全部交由其他线程执行)。

Node可以充分利用单核CPU的优势。

  • NPM包管理工具

npm(Node Pack Manager)
npm会随着node的安装、自动安装
Nodejs本身是轻内核、不包含太多的功能。
在开发过程中可能需要引用大量的依赖包(再小的功能都有成熟的模块)
个人开发者制作的功能扩展
https://www.npmjs.com/
require时、有限查找系统模块。
然后才会查找项目中node_modules目录;
查看所有依赖包

 npm ls

查看深度为0的根包

 npm --depth 0

使用

 npm install 包名称
  • nrm

可以自动切换npm服务器指向
//安装

 npm install -g nrm
 //查看所有可用指向
 nrm ls
 //修改npm指向
 nrm use taobao
 //检测节点速度
 nrm test
  • querystring字符串查询模块

  • querystring.parse字符串 =>字典
const querystring = require('querystring');
querystring.parse('foo=bar&abc=xyz&abc=123');
//return
{ foo: 'bar',  abc: ['xyz', '123'] };

默认情况下,查询字符串中的百分号编码的字符会被认为使用了 UTF-8 编码。 如果使用的是另一种字符编码,则 decodeURIComponent 选项需要被指定,如以下例子:

 querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null,{ decodeURIComponent: gbkDecodeURIComponent });
  • URL模块

const url = require('url');
url.parse()可以将一个完整的url地址分为很多部分、例如:
 var url = req.url;
 res.write('<h1>'+'url  --->   '+url+'</h1>');
 //url ---> /p/index_01.html?id=123
 res.write('<h1>'+'host  --->   '+urls.parse(url).host+'</h1>');
 res.write('<h1>'+'port  --->   '+urls.parse(url).port+'</h1>');
 res.write('<h1>'+'path  --->   '+urls.parse(url).path+'</h1>');
 //path ---> /p/index_01.html?id=123
 res.write('<h1>'+'pathname  --->   '+urls.parse(url).pathname+'</h1>');
 //pathname ---> /p/index_01.html
 res.write('<h1>'+'query  --->   '+urls.parse(url).query+'</h1>');
 query ---> id=123
 //url.parse(urlstr,true).query 可以将参数转化为json对象
 var query = urls.parse(url,true).query;
 res.write('<h1>'+'id  --->   '+ query.id  +'</h1>');
 //id ---> 123
  • 制作一个简单的表单提交

  • HTML

     <form action="http://127.0.0.1:3010/" method="get">
     <input type="text" name="name" />姓名<br>
     <input type="text" name="age" />年龄<br>
     <input type="radio" name="sex" value="男" />男
     <input type="radio" name="sex" value="女" />女
     <br>
     <input type="submit">
     </form>
    
  • Node.js

     const http = require('http');
     var url = require('url');
    
     var hostName = '127.0.0.1';
     var port = 3010;
         
     http.createServer((req,res) => {
       
         var queryJson = url.parse(req.url,true).query;
         var name = queryJson.name;
         var age = queryJson.age;
         var sex = queryJson.sex;
        //支持中文显示
         res.writeHead(200, {
             "Content-Type": "text/html;charset=utf-8"
         });
         
         
         res.write(`<h1>服务器收到了表单请求</h1>`);
         res.write(`姓名:${name}
     年龄:${age}
     性别:${sex}`);
       
       
         res.end();
     }).listen(port,hostName,()=>{
         console.log(`成功监听 http://${hostName}:${port}/`);
     });
    
  • 文件操作

文件操作下、必须使用"绝对路径(物理路径)"

  • fs:

基础的文件操作

  //异步(options为编码格式)
  fs.readFile(path[, options], (err, data) => {
       if (err) throw err;
       console.log(data);
    });
   //同步(options为编码格式)
   try {
       var data = fs.readFileSync(path[, options])
    } catch(error){       }

如不声明编码格式。解析结果会一buffer(二进制缓冲区)格式输出

流的形式读取文件
 const fs = require('fs');
   const rr = fs.createReadStream('foo.txt');
   var dara = ' ';
   rr.on('data', (chunk) => {
         //chunk为每次读取出来的文件块  (buffer---字节数组)
         //chunk.lenth
         data+=chunk.toString();
   });
   rr.on('end', () => {
     console.log('end');
   });
  • buffer:

数据容器。将大文件整个放入缓冲区再分批写入磁盘

   //读取
   buffer.toString('utf8')
   //写入(字符串)
   buffer.write(string[, offset[, length]][, encoding])
  • path:

提供和路径相关的操作

   //获取文件名
   path.basename(temp)
   //获取路径中目录名称
   path.dirname(temp)
   //获取不同系统中路径分隔符、Windows是“;”Linux/MacOS是“:”
   path.delimiter
   //获取环境变量
   process.env.PATH.split(path.delimiter)
   //获取路径中后缀(扩展)名/包含"."
   path.extname(temp)
   //获取文件信息对象.(文件目录、文件名、扩展名等)
   var obj = path.parse(temp);
   //将对象路径转化成字符串
   path.format(obj)
   //判断是否为绝对路径
   path.isAbsolute(temp)
   //拼接路径
   path.join([path1],[path2],..) 
   //根据当前系统常规化一个路径
   var a = path.normalize('C://dev\\abc//cba////1.txt');
   console.log(a);
   //获取to路径相对于from的相对路径
   path.relative(from,to);   
   //通过文件操作获取目录(允许从绝对路径开始)
   path.resolve(__dirname,'..','./','./code');
   //获取当前操作系统中默认用的路径成员分隔符.Windows“\” Linux、MacOS“/”
   path.sep
   //判断操作系统win32:windows&&LinuxMacOS:posix
   //可以通过path.win32//posix改变node操作方式
   console.log(path=== path.win32);
  • readline:

读取大文本/歌词等文件、一行一行读

 const readline = require('readline');
 const fs = require('fs');
 const rl = readline.createInterface({
 input: fs.createReadStream('sample.txt')
   });
     
   rl.on('line', (line) => {
     //确保每次返回一整行数据
     //与createReadStream用流的形式返回buffer不用。
     //readLIne直接返回字符串对象
     console.log(`Line from file: ${line}`);
   });
 const readline = require('readline');
   const rl = readline.createInterface({
       input: process.stdin,
       output: process.stdout,
       prompt: 'OHAI> '
   });
   rl.prompt();  
   //用户输入
   rl.on('line', (line) => {
       switch (line.trim()) {
         case 'hello':
           console.log('world!');
           break;
         default:
           console.log(`Say what? I might have heard${line.trim()}'`);
           break;
       }
       rl.prompt();
   //用户结束了输入。
   }).on('close', () => {
       console.log('Have a great day!');
        process.exit(0);
   });
  • 文件写入
//只要是文件操作。fs以及path就是必须模块
   const fs = require('fs');
   const path = require('path');
   const data = {id:10};
   //文件写入时传入对象data:结果为[object Object]
   //JSON.stringify(data)序列化:结果为{id:10}
   //JSON.parse(data)反序列化
   //写入、覆盖
   fs.writeFile(file, data[, options], callback);
   //追加
   fs.appendFile(file, data[, options], callback);
   //文件流
   var streamWriter = fs.createWriteStream(path.join(__dirname,'temp.txt'));
   streamWriter.write('hello1',() => {
       console.log('+1');
   });
   //同步书写文件
   fs.writeSync(fd, string[, position[, encoding]])
  • pipe()方式copy文件
readStream.pipe(writeStream);
   writeStream.on('pipe',(src) => {
       src===readStream;
   }

pipe()支持链式编程

  • 判断文件是否存在
fs.stat(path, callback(err, stats))
  • 重命名文件或目录
fs.rename(oldPath, newPath, callback);
   fs.renameSync(oldPath, newPath, callback);
  • 删除文件
fs.unlink(path, callback);
   fs.unlinkSync(path, callback)
  • 创建文件夹(要求路径必须存在)
fs.mkdir(path, callback);
  • 监视文件变化
fs.watchFile(path.join(__dirname,'temp.txt'),(curr, prev) => {
       console.log(`previous:${prev.size}`);
       console.log(`current:${curr.size}`);
   });
  • fs-extra(第三方):

https://www.npmjs.com/package/fs-extra
扩展了文件拷贝、删除等操作

  • 模板字符串

通过不同的引号来包裹
' 普通字符串------` 模板字符串

  console.log(`  鹅,鹅,鹅,
     曲项向天歌。
    白毛浮绿水,
   红掌波清波。`);
 
  console.log(`
    this is ${nameHe}, 
   this is ${nameShe}`
   , nameHe);
  • HTTP

客户端输入url
封装成特定格式(http/https等)的请求报文
通过socket(连接方式)传递给服务端
服务端根据不同协议类型对报文进行解析
。。。。。
客户端接收到服务端返回的报文并加以解析
客户端渲染内容到页面当中

  • http传输方式

PUT:传输文件(<b>增</b>)
DELETE:删除文件(<b>删</b>)
POST:传输实体主题(<b>改</b>)
Get:获取资源(<b>查</b>)
HEAD:获得报文首部
OPTIONS:询问支持的方法
TRACE:追踪路径
CONNECT:要求用隧道协议链接代理
LINK:建立和资源之间联系(1.1已废弃)
UNLINK:断开连接关系(1.1已废弃)

  • NET.socket

一个简单的socketdemo

服务端

 const  net = require('net');
  //创建socket服务器
  var server = net.createServer(socketConnect);

 // 当有客户端与服务器连接时、触发
 function socketConnect (socket) {
     //自己的ip
     var myIp = socket.address().address;
     //其他人的ip
     var userip = socket.remoteAddress;
   
     var clientIp = socket.address().address;
     console.log(`被链接from:${userip}`);
   
     //监听socket数据流
     socket.on('data',(chunk) => {
         process.stdout.write(`客户端输入:${chunk.toString()}`);
         // console.log(`客户端输入:${chunk.toString()}`);
     });
   
    socket.on('end',() => {
   console.log('客户端断开连接');
     });
   
     //通过socket对象向另一端通信
     socket.write('hello kirito!')
 }
   
 // 让socket服务器监听端口
 //post传入0则会由系统分配空闲端口
 var post = 2080;
 server.listen(post,(err) =>{
     //监听成功回调
     if (err){
         //通常是端口被其他对象占用ing
         throw err;
     }
   
     console.log(`服务端开启,监听:${post}端口`);
 });

客户端

const net = require('net');
     
 //与服务端socket作为参数可返回多个不同。客户端只返回一个socket对象.
 const socket = net.createConnection({ port: 2080 }, () => {
     //'connect' listener
     console.log('连接成功!');
   
 });
 //数据接收监听
 socket.on('data', (data) => {
     console.log(`服务端返回${data.toString()}`);
     process.stdout.write('client:');
     // socket.end();
 });
 //链接断开监听
 socket.on('end', () => {
     console.log('disconnected from server');
 });
   
   
 //监听控制台输入
 process.stdin.on('data',input =>{
     process.stdout.write('client:');
     if (input.toString().trim() === 'exit') {
         socket.end();
         return;
  }
     socket.write(input.toString());
 });
  • 手动实现一个简单的静态资源管理服务

 const http = require('http');
 const url = require('url');
 const fs = require('fs');
 const path = require('path');
 
 
 const post = 3000;
 const host = '127.0.0.1';
 var mimeJSON;
 
 
 
 http.createServer((req,res) => {
     //拿到路径
     var pathName = url.parse(req.url).pathname;
     console.log(pathName);
     if (pathName.length == 1){
         pathName = '/index.html';
     }
     var extname = path.extname(pathName);
     //读取文件
     fs.readFile('./static/'+pathName,(err,files) => {
         if (err){
             res.writeHead(404, {
                 "Content-Type": "text/html;charset=utf-8"
             });
             fs.readFile('./static/'+'404.html',(err,files) => {
                 res.end(files);
             });
         }else  {
 
 
             getMine(extname,(mime) => {
                 //MIME类型
                 //网页:text/html
                 //jpg:image/jpg
                 res.writeHead(200, {
                     "Content-Type":mime
                 });
                 res.end(files);
             });
         }
     });
 }).listen(post,host,() => {
     console.log(`成功监听${host}:${post}`);
 });
 
 
 function getMine(extname,callback) {
     var mime;
     if (!mimeJSON) {
         fs.readFile('./static/mime.json',(err,data) => {
             if (err) {
                 mimeJSON = {'aaa': 'dddd'};
             }else {
                 mimeJSON = JSON.parse(data);
             }
             mime = mimeJSON[extname] || 'text/plain';
             console.log('11'+mime);
             callback(mime)
         })
     }else {
         mime = mimeJSON[extname] || 'text/plain';
         console.log('22'+mime);
         callback(mime);
     }
 }

exports&&require

(在前端中、js与js文件通过html文件相互联系。
而在node.js中、二者则通过exports与require相互联系)

  • 暴露函数、变量

当需要从A.js引用B.js内部的变量、函数时。
必须在B中使用exports对象进行暴露。
A中使用require命令引用B.js文件

B

 var msg = '你好';
 exports.msg = msg;

A

 //将foo.exports解固成顶层变量
 const foo = require('./foo.js');
 console.log(foo.msg);
  • 暴露类、构造函数

B

 function People(name,sex,age) {
     this.name = name;
     this.sex = sex;
     this.age = age;
 }
 
 People.prototype.sayHello = function () {
     console.log(this.name+this.sex+this.age);
 }
 
 //此时People就被视为构造函数、可以用new来实例化
 module.exports = People; 

A

 var People = require('./people.js');
 
 var xiaoming = new People('小明','男','12岁');
 
 xiaoming.sayHello();

exports&&module.exports

  • module.exports

  • 将模块的require变成一个函数、或者任意类型的对象。
   module.exports = function(name, age) {
       this.name = name;
       this.age = age;
       this.about = function() {
           console.log(this.name +' is '+ this.age +' years old');
       };
   };

 var Rocker = require('./rocker.js');
 var r = new Rocker('Ozzy', 62);
 r.about(); // Ozzy is 62 years old
  • exports

  • exports.xxx= xxx本质上等于为module.exports添加新的方法。或者属性
  • 如果你已经实现了module.exports、那么之后的 exports.xxx将会失效、不会动态添加.
  • 如果你想你的模块是一个特定的类型就用Module.exports。如果你想的模块是一个典型的“实例化对象”就用exports。
  • node_modules文件夹

假如 /home/ry/projects/foo.js 调用了

require('bar.js') 

那么node查找的位置依次为:

/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js

假如 /home/ry/projects/foo.js 调用了

require('bar') 

1、那么node将会先查找文件:

/home/ry/projects/node_modules/bar/package.json

关于package.json、每一个模块文件夹的根文件中推荐都写一个package.json文件(文件名固定)。node将自动读取内部配置。

 {
   "name": "kiritoVar",
   "version": "0.0.1",
   "main":"ffff.js"
 }

2、然后node将会查找文件:

/home/ry/projects/node_modules/bar.js

3、若没有找到在继续查找文件

/home/ry/projects/node_modules/bar/index.js

package.json

  • 在你的项目完成之后、可以用npm为自己的项目创建一个package.json
 控制台$  npm init
 会出现如下引导(均可不填)

 package name: (beginning) beginning  //项目名称
 version: (1.0.0) //版本
 description: my node demo//项目说明
 entry point: (demo_01.js) url_demo.js//入口
 test command: //测试
 git repository: //git仓库
 keywords: suibian//标签、关键词
 author: kritio_song//作者
 license: (ISC) ISC//版权

然后将会生成一个package.json文件
其中最重要的一点、他将会含有一个dependencies字段

 "dependencies": {
     "date": "^1.0.2"
   其中'^',表示固定这个数字
    "^1.^0.2"就表示固定为1.0.x这样
 },

dependencies下、记录着此项目所有引用的第三方模块、以及node_modules文件夹下你自己创建的模块

  • 作用

在项目目录下 $ npm install
将会检测所有dependencies下的依赖、并且自动安装

Post请求处理

  • 原生
 if (req.url == '/dopost' && req.method.toLowerCase() == 'post'){
        var allData = '';
        //为了防止参数过多影响线程利用、node读取参数的方式是分块读取。参数过多时、可能会多次回调
       req.addListener('data',(chunk) =>{
          allData += chunk;
         });
         //读取完毕
         req.addListener('end',(chunk) =>{
             console.log(allData.toString());
             //输出foo=bar&abc=xyz&abc=123
             //可以使用querystring模块转义
             querystring.parse(str)字符串 =>字典
             res.end(allData);
         });
     }

原生处理post请求、需要两个监听。并且文件上传需要特殊处理

  • formidable
     const http = require('http');
     const path = require('path');
     const fs = require('fs');
     const formidable = require('formidable');
     const post = 8080;
     const host = '127.0.0.1';
     
     
     http.createServer((req,res) => {
         if (req.url == '/dopost' && req.method.toLowerCase() == 'post'){
     
             var form = new formidable.IncomingForm();
             //设置文件上传存放地址
             form.uploadDir = __dirname+"/fileDir";
     
             //执行里面的回调函数时、表单已经接收完毕
             form.parse(req, function(err, fields, files) {
                 //所有的文本域、单选框,都在fields存放
                 //所有的文件域、都在files存放
                 console.log(err);
                 console.log(fields);
                 console.log(files);
     
                 if (files){
                     var extname = path.extname(files.file.name);
                     var random = parseInt(Math.random() * 999999 + 100000);
                     var dir = form.uploadDir;
                     var timestamp=new Date().getTime();
                     var oldName = files.file.path;
                     var newName = dir+'/'+timestamp+random+extname;
                     //修改文件名以及后缀
                     fs.rename(oldName,newName,()=>{
                         console.log(`renameed to ${newName}`);
                     })
                 }
                 res.end('succes');
             });
         }
     }).listen(post,host,() =>{
         console.log(`成功监听${host}:${post}`)
     });
    
  • formidable只能接收表单形式提交上来的post参数。
  • 模板引擎

EJS(通过字符串进行逻辑替换)

  • Embedded JavaScript templates
  • 后台的一个模板引擎
  • 现在很少用、基本都是前端采用模板拼接(比如vue.js)
 const ejs = require('ejs');
 
 var string = '我买了一个iphone<%= a%>s';
 var data = {
     a : 6
 };
 
 var html = ejs.render(string,data);
 
 console.log(html);

也可以直接替换html文件中的模板标记

node文件中:
 const fs = require('fs');
 const ejs = require('ejs');
 const http = require('http');
 
 http.createServer((req,res) => {
 
     fs.readFile('./view/index.ejs',(err ,data ) => {
         var template = data.toString();
         var dictionary = {
                a:6,
                news:['第一个','第二个','第三个']
         };
 
         var html = ejs.render(template,dictionary);
 
         res.writeHead(200, {
             "Content-Type":'text/html'
         });
 
         res.end(html);
     });
 
 }).listen(3000,'127.0.0.1',() =>{
     console.log('监听成功');
 });
html文件中:
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
     <link rel="stylesheet" type="text/css" href="css/css_01.css">
 </head>
 <body>
     <h1>好高兴啊、我今天买了一个iphone<%= a%>s</h1>
     <ul>
     <% 
         for (var i = 0 ; i< news.length ; i ++){
             if(news){
     %>
         <li><%= news[i]%></li>
     <% 
             }
         }
     %>
     </ul>
 </body>
 </html>

<% %>之中支持所有js语法(for if等)

  • include引用

     <% include nav.ejs %>
    
  • 图片处理

http://www.graphicsmagick.org/

  • webSocket

  • 使用SocketIO模块
  • app.js
 /**
  * Created by kiritoSong on 17/9/29.
  */
 var http = require('http');
 var fs = require('fs');
 var server = http.createServer(function (req, res) {
     if (req.url == '/'){
         fs.readFile('./index.html',function (err,data) {
             res.end(data);
         })
 
     }
 });
 
 
 //将server变成socket
 //http://127.0.0.1:3000/socket.io/socket.io.js 会是一个文件地址
 var io = require('socket.io')(server);
 
 //监听链接事件
 io.on('connection',function (socket) {
     socket.on('提问',function (msg) {
 
         console.log('客户端提问:'+msg);
         socket.emit('回答',['吃了','还是没吃']);
     })
 
 
     socket.on('广播',function (msg) {
         //broadcast 广播
         socket.broadcast.emit('有人问我'+msg);
         console.log('客户端提问:'+msg);
         //io.emit可以向所有连接广播
         io.emit('回答',['吃了','还是没吃']);
     })
 });
 
 
 
 server.listen(3000);
  • index.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Socket</title>
 </head>
 <body>
 
     <ht>index页面</ht>
 
     <script src="/socket.io/socket.io.js" type="text/javascript"></script>
 
     <script type="text/javascript">
         //客户端socket、与服务端相互对应
         var socket = io();
 
 
         socket.emit('提问','你吃了么');
 
         socket.on('回答',function (msg) {
             for (var i = 0 ; i < msg.length ; i++ ){
                 console.log('服务器回答'+i+'::'+msg[i]);
             }
         })
     </script>
 </body>
 </html>
WechatIMG8.jpeg
WechatIMG10.jpeg
  • socket.emit('k',msg)负责发送
  • socket.on('k',msg)负责监听
  • 服务器&&客户端均是
  • express搭配socket.io

  • 一些工具框架(库)

  • nodemon

一个可以监听node文件改变、并且重新开启node文件的工具

webSocket(浏览器不支持socket则自动模拟长轮询)

后端
Web note ad 1