Node基础要点

Node 基础语法

  • 浏览器Javascript 不能做什么?

    1. 访问数据库
    2. 不能对文件进行操作
    3. 不能对系统进行操作

    原因是为了安全,和浏览器的运行机制有关

  • 在开发人员能力相同的情况下编程语言的能力取决于什么?

    取决于平台,

    • cordova hbuilder 平台 platform
    • java java虚拟机 (运行平台)
    • php php虚拟机
    • +c# .net framework mono
    • +js 解析内核 chrome v8

    1. node 特点

什么是node ?

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

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

    • 事件驱动: 任务执行,发布者,订阅者,事件驱动 ( on emit )

    • 非阻塞: 执行某一个任务的同时也可以执行其他任务

    • 阻塞: 执行某一个任务,这个任务如果没有执行完成,其他任务必须等待

      • 同步
      • 异步
    • I/O: 输入/输出( 数据库,文件系统操作等操作 )

      • 非阻塞I/O模型: 当我们使用Node.js来实现数据库操作、文件系统等操作时,要进行的异步操作,异步操作的核心传统实现方式就是回调函数

2. 服务器:

  • 定义: 可以运行在服务端的一个网站(站点)

  • 种类:

    1. web服务器( 静态服务器 ),可以运行在浏览器中的服务器

    2. api 服务器( 后端接口 ),后端语言暴露一个数据接口,用于前端的请求

1. http 创建web服务器:

  • http 模块

    • createServer( callback(request, response, [next]){ })
    • listen(port, host, callback) //监听服务器 ( 反馈服务器状态)

 // 1. 引入http模块(对象)
 const http = reqiure('http');
 //2. 通过httpP模块上的 createServer 这个api 创建一个函数
 //3. 创建服务器端口和域名
 const port = 8888;
 const host = 'localhost';//127.0.0.1
 const server = http.createServer( (request, response) => {
     response.writeHead(200, {   //设置响应头
         'Content-Type': 'text/html;charset=UTFf8'
 	})
 	response.write('返回给客户端的数据');//向前台发送信息
     response.end(); //发送已经结束
 }).listen( port, host, () => {
     console.log(`The server running at:http://${ host } : ${ port } `)
 });
 
ps:因为浏览器在请求时会默认发送favicon.ico图标请求,如需阻止在http回调函数里环境里判断阻止
代码示例:
    if(request.url.indexOf("favicon.ico") === -1){
        return false;
    };
ps:关于设置响应头解析类型的设置
    "Content-type":"text/html;charset=utf-8"		解析HTML标签以及属性
                    "text/plain"					纯文本
                    "text/css"						解析css
                    "text/javascript"				解析js
                    "text/png"						解析图片
                    "text/json"						json数据
                    "......"


2. express 来创建api服务器

app.get(路由路径,(路由回调函数(路由中间件),next) => { })

const express = require('express');
const app = express() //创建了一个app对象
app.get('/', (req, res, next)  => {})
app.listen(port, hostname, () => {  //创建一个服务器
  console.log(`The server is runinng at:http://${ hostname}:${ port }`)
})

3. Node 中文乱码问题解决:

1. 设置请求头

 response.writeHead(200, {
  	'Content-Type': 'text/html;charset=UTF8'
  })

2. 发送 response.write()

response.write('')

3. toString()

对二进制有效:将二进制 -》 toSrgin()

4. Node 实时监听(自动刷新)

借助第三方工具:

  • nodemon [ 推荐 ]
  • supervisor

5. commonJS规范

  • CommonJs 规范的提出,主要是为了弥补 javascript 没有标准的缺陷,希望javascript 能在任何地方运行,能向java和Python具有开发大型应用的基础能力,可不是停留在脚本程序的阶段。
  • commonJS规范思想 是单独的文件就是一个模块,每一个模块都是一个独立的作用域
  • 每个文件对外接口都是module.exports对象

1. CommonJs的模块规范

CommonJS对模块的定义主要分为:

1. 模块定义

//1. 模块定义
const student = {   //可以是对象(可以传多个)、函数、字符串
    name: 'meijuna',
    jineng () {
        console.log('I cn fly');
    }
}

2. 模块导出

//2. 模块导出
module.exports = student; //安全性不高,只能到处多个
module.exports = {  //更安全,批量导出
	student,
	fn
};

3. 模块引用

//3. 模块引用
const student = require('./xx.js'); // or
const { student, fn } = requie('./xx.js); //可以按需引用

2. 模块标识符主要分类:

1. 内置模块 (http、fs)

1)url 模块:处理 url

URL模块提供了三种处理path的方法

1) 将path字符类型转成对象
left url = "http://user:[email protected]:8080/p/a/t/h?query=string#hash";
url.parse(url,[可选布尔值],[可选项布尔值]);
    {			
        单词解释			解析属性						属性解释
        协议			protocol:"http:",					协议
        斜线			slashes:true,						是否有//
        认证			auth:"user:pass",					用户名与密码
        主机			host:"host.com:8080",				主机
        接口			port:"8080",						端口
        主机名			hostname:"host.com",				域名
        搞砸			hash:"#hash", 						片段标识符,指向html页面某个dom元素的id
        搜索			search:"?query=string",				? + 查询字符串
        查询			query:"query=string",				查询字符
        路径名			pathname:"/p/a/t/h",				端口号和?之间的 路径那部分
        路径			path:"/p/a/t/h?query=string",		pathname + search 
        水平基准		href:全路径url						 原始路径
    }
    
    2) 将对象转成url字符串
		url.format(obj)
		ps:参考结合parse方法;

	3) 替换或者替换(未验证)
		url.resolve(from,to)
			from 	源地址
			to		需要添加或替换的标签
			ps:form源地址末尾有/就是添加  没有就是替换 具体待验证
2) fs 模块

fs 模块是专门处理操作磁盘文件,特点每个方法都有同步和异步两种 (需注意:同步以sync结尾)

读取文件:
		fs.readFile("文件路径",{opt},function(err,data){
			{opt}:可选项
				encoding:"utf-8"		以utf-8国际编码读取文件内容
				flag:"r+"				read的简称,只读取文件,不存在即报错
				flag:"w+"				write的简称,读写文件,不存在则自动创建

			err:errorObject				报错机制 一般用if判断用throw抛出错误
				列:
					if(err){
						throw err
					}
					ps:console.error()	抛出错误,不影响程序执行
			
			data:成功读取的数据,是以buffer数据二进制存储格式存在
			ps:data.toString()可以转成UTF-8格式,等价于可选项中的encoding:"utf-8";
		});

	写入文件:
		fs.writeFile("写入路径","内容数据",{opt},function(err){});
	
	追加内容:
		fs.appendFile("追加文件路径","内容数据",function(){});
	
	读取文件夹:
		fs.readdir("文件夹路径",function(err,paths){
			ps:readdir是directory的缩写
			paths:成功读取文件夹 用数组存储文件内容
				
		})
	
	创建文件夹:
		fs.mkdir("文件夹路径,同时也是文件夹名称",function(err){})
3)path 模块
拼接路径:
		join(xx,xx,xxx,.)使用平台特定的分隔符,将所有给定的段连接在一起
		ps:常用到的路径
			__dirname	当前执行文件所在的绝对路径
4)querystring 模块

querystring模块一般是对 http 请求的 URL 所带的数据进行解析处理

1) 格式数据格式化为字符串:stringify 
    let objData = {
        name:"skye",
        url:"http://skyelovedj.com"
    }
    let querystring = require("querystring");
    querystring.stringify(objData,"分隔符","分配符")

2) 将参数字符格式为对象:
    let strData = "name:skye&age=18"
    let querystring = require("querystring");
    querystring.parse(strData,"分隔符","分配符")
    ps:分隔符和分配符是可选项

3) 编码:
    querystring.escape("data")			只对符号与中文编码		

4) 解码:
    querystring.Unescape("data")		只对符号与中文解码
5) zlib

制作压缩包的模块

* 可读的流: 可以通过文件系统读取的流 ( 数据流 )

* 可写的流: 可以通过文件系统写入的流

* 管道流 : 连通两个文件的通道 pipe

const fs = require( 'fs' );
const zlib = require( 'zlib'); //制作压缩包的模块
//创建可读的流
const readStream = fs.createReadStream('./a.txt' );
//创建空压缩包
const gzib = zlib.createGzip();
//创建可写的流
const writeStream  = fs.createWriteStream('./b.text.gz');

//创建流程
readStream
  .pipe( gzib )
  .pipe( writeStream );

2. 第三方模块

3. 自定义模块

6. 发送数据请求:

  1. http.request
  2. request(封装 http-request 的第三方包)
  3. http.get(options, (res) =>{ })

7. 前端模块化

为什么前端要使用模块化:

模块化:具有特定功能的一个对象(广义理解)

模块定义的流程:

1. 定义模块
2.  导出模块
3.  应用模块

好处:

1. 可以存储多个独立的功能块
2.  复用性高

种类:

1.  ADM ( require.js )
define({
     a:1,
     b:2
 })
 require([./a.js], function( moduleA ) {
 	//moduleA指的是
 })
  1. CMD ( sea.js )
define(function(require, exports, module ){
 })
 require('./b.js', function( moduleB ){
     //moduleB就是b模块中导出的内容
 })
  1. Common.js ( require.js )
 Node 使用了 Common.js 的规范


 /*目录结构: name.js index.js */
 //模块定义 name.js
 const nameObj = {
     name: 'MeiJuna'
 }
 //模块导出
 module.exports = nameObj;
 //模块引用
 const nameObj = require('./nameObj)


 Node中Common.规范使用的三种类型:

  	1. 内置模块 (指的是挂载在Node全局对象身上的 api )
  	2. 自定义模块
  	3. 第三方模块

7. 跨域问题

前端跨域

1. jsonp
2. 反向代理

(前端创建一个虚拟后端服务器,后台服务器帮助我们跨域)

后端跨域

1. 设置响应头
 res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
 // req.headers.origin = http://127.0.0.1:5500/
2. 使用第三方中间件 (cors)
app.use( cors({
 "origin": "*",
 "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
 "preflightContinue": false,
 "optionsSuccessStatus": 200
}))
  • 中间件: 就是具有一定功能的函数

8. 前端的异步流程

异步流程的任务是放在异步队列中的,

  1. 传统的原始异步

    • 异步函数
    • 事件
  2. 使用异步流程工具 ( 别人封装好的东西 )

    es6 Promise 对象

    all :依次执行完再执行

    race: 谁快谁执行

    es6 generator 函数

  在function 关键字后加一个 * 
   * 通过 yield关键字定义任务
   * fn().next() 来执行任务
   结果返回一个对象 {value: '任务结果', done: false }
     value表示 yield关键字有任务执行的结果
     done 表示当前定义的所有的任务十分执行完整的一种状态
   * 理解:
     -多任务的定义,多任务执行
     -让自己定义的多个任务一次执行,上一个任务如果没有完成,下一个任务就不会开始

   function* fn() {
     yield '任务1'
     yield '任务2'
     return '任务'
   }
   console.log( a.next())
   console.log( a.next());

es6(7) async 函数

    es6,7
     * 配合关键字 await表示等待,await 任务1 执行结束才会 执行任务2
     * 使用场景: 数据先请求,然后把结果赋值到变量
     async function fn() {
     	const result = await '任务1';
     	console.log( '任务二' );
    }
    2. 箭头函数的写法
    const fn1 = async () => {
    	const res = await '任务3';
    	console.log( res );
    	cosnole.log( '任务4' );
    }
   node中的 nextTick() 和 setImmediate()


​ nextTIck
​ * setImmediate
​ *
​ * 轮询: 一个事件往复执行,那么每一次执行完成,我们就认为是个轮询
​ * 事件轮询前, 使用nextTick
​ * 事件轮询后, 使用setImmediate
​ *
​ * nextTick > () => > setImmediate

第三方的 async.js 库

文档: async

   cnpm i  yarn -g
   yarn add async -D

- parallel: 并行
- series :串行,return 数组,任务都是完整正确的,如果一个任务失败,后面的任务也会失败

   主线程
   并行 parallel { two: 2, one: 1 }
   串行 series { one: 1, two: 2 }
   结论: 主线程先执行,然后 parallel (谁快谁先走),最后 series 并行 (上个运行完,在运行下一个) 

9. socket 通信

1. 基于后端的通信 ( pc )

  • node 中的 net 模块

服务器

const net = require( 'net');

const hostname = "localhost";
const port = 9000;

const clients = {}; //存储每一个客户端
let count = 0;      //给客户端编号
//1. 创建服务器
const server = new net.createServer()
//2. 连接客户端
server.on( 'connection', client => {
  client.name = ++count; ///每个客户端起个名字
  clients[ client.name ] = client; //将每一个客户端 都存储在 clients 中,key:client.name
  //3. 获取客户端发来的数据
  client.on( 'data', msg => {
    boardcast( client, msg.toString() );
    console.log( `用户 ${ client.name } 说: ${ msg.toString()}` )
 
  });
  //监听客户端的异常
  client.on( 'error', error => {
    console.log(`error is ${ error }`);
  });
  //监听客户端的下线行为
  client.on( 'close', () => {
    //将下线的客户端清除 --清除 clents 对象
    delete clients[ client.name ]
    console.log(`客户端${ client.name } closed~~`)
  })
})
//4. 将客户端发来的信息展示到自己终端上( 广播 )  
function boardcast( client, msg) {
                // 客户端  客户端发来的消息
  for(var key in clients) {
    clients[ key ].write( msg );
  }
}

//5. 监听服务器
server.listen( port, hostname, () =>{
  console.log(`The srerver is ruuing at:http://${hostname}:${port}`)
})

客户端

/* 
  客户端:
    1. 创建socket
    2. socket连接服务器
    3. 给服务器发送信息
*/

// 1. 创建socket

  const net = require( 'net' );

  const socket = new net.Socket();

  const port = 9000;
  const hostname = 'localhost';

  const readline = require( 'readline' );//这是用来做命令行读取

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  
// 2. socket连接服务器

  socket.connect( port,hostname, () => {
    //当我第一次连接好服务器之后,我给服务器发送一个我连接好的信息
    socket.write( '欢迎xxx来到xxx的直播间' )

  })

  //客户端监听自己的异常

  socket.on( 'error', error =>  {
    console.log( `socket error is ${ error }` )
  })

  socket.on( 'close' , () => {
    console.log( `socket connection closed ~~~` )
  })

// 3. 给服务器发送信息

  socket.on( 'data', msg => {
    console.log( msg.toString() )//客户端自己显示写的内容
    say()
  })

  function say () {
    rl.question('请输入: ', ( answer ) => {
      if( answer === 'bye' ){
        //要进行命令行的结束
        socket.destroy() //清除连接
        rl.close()// 关闭命令行读取
      }else{
        socket.write( answer )
      }
    });
  }

2. 基于H5的 webSocket 来完成 ( 应用于移动端 )

服务器

/* 
  主服务器
    1. 通过ws模块来创建服务器
    2. 服务器连接客户端
      - 给客户端编号
    3. 接收客户端发来的信息
    4. 监听客户端下线
*/

// 1. 通过 ws模块 来创建服务器
  const webSocket = require( 'ws' );
  const ws = new webSocket.Server({
    port: 8000,
    host: '10.31.161.48'
  })

// 2. 服务器连接客户端
  const clients = {};

  let count = 0;

  ws.on( 'connection', client => {
    //   - 给客户端编号
    client.name = ++count;
    clients[ client.name ] = client 

    //接收客户端发来的数据

    client.on( 'message', msg => {
      console.log( `客户端 ${ client.name }说:${ msg }`)
      boardcast( client,msg )
    })

    // 4. 监听客户端下线
    client.on( 'close', () => {
      delete clients[ client.name ]
      console.log( `客户端 ${ client.name } closed~~` )
    })

  })

  function boardcast ( client,msg ) {
    for( var key in clients ){
      clients[ key ].send( msg )
    }
  }

  

客户端

/* 
  客户端连接服务器文件
    \(^o^)/~          h5 提供了一个 Socket  的全局对象
*/

  const ws = new WebSocket( 'ws://10.31.161.48:8000' )


  ws.onopen = () => {
    ws.send( '欢迎来到xxx的直播间' )
  }

  ws.onmessage = ( msg ) => {
    const content = document.querySelector( '#content' );
    content.innerHTML += msg.data + '
'
} ws.onerror = ( error ) => { if( error ){ console.log( error ) } } ws.onclose = () => { console.log( `xxx下线了` ) }

3. 低版本浏览器使用的 socket.io

你可能感兴趣的:(Node)