# Node.js

Node.js是一个javascript的运行环境,不是一个框架或者一个库,是一个软件。

# 1. 全局对象

node下的全局对象global

//console.log(global) ==> node引擎下的全局对象

let a = 10;
console.log(global.a)

//解释文件(js文件)的模式下,a不会被挂载到全局对象上。
//交互模式(黑窗口的命令行)下,定义的变量会被挂载到global上。


global.b=20
console.log(b)  //挂载在global上的变量可以直接访问


console.log(global === this); //false  解释文件模式下,this不指向global,而是指向本模块(该js文件);但是在交互模式下,this指向global。

# 2. 模块的使用

# 使用模块的好处(了解)

  • 最大的好处就是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。
  • 使用模块可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己编写模块时,不必考虑名字会与其他模块冲突。

# 模块规范的定义(了解)

  • 一个js文件就是一个模块,模块的作用域时私有的,内部定义的变量或者函数,只在当前的文件(模块)可以使用。
  • 如果别人使用我们模块里面的的东西,那么有两点要做:(以commonJS的Modules规范:Node.js为例)
    • 自己编写的模块,由于模块作用域是私有的,默认情况下,外部是没有办法使用的。可以导出exports或者modules.exports。导出的时候以对象的方式导出。
    • 别人要使用,则需要先引入该模块,使用require引入,并设置一个变量来接收导入的对象。

对书写格式和交互规则的详细描述,就是模块定义规范。

  • AMD规范:Require.js 【模块化规范的鼻祖,旧规范,已经退出历史舞台了】
  • CMD规范:Sea.js 【模块化规范的鼻祖,旧模范,已经退出历史舞台了】
  • CommonJS的Modules规范:Nodejs【现在正在使用的】
  • ES6模块化规范 import ... from ...【现在正在使用的】

# 模块化使用

# commonJS的使用

CommonJS的Modules规范,也是Nodejs自带的规范,就是规范了导出该怎么写,导入该怎么写

//CommonJS模块规范
let a=10;
function fn(){
  console.log('我是模块中的函数fn')
}
class Animal{
  constructor(){
    this.age=0
  }
}


//CommonJS导出的第一种格式
exports.a=a
exports.fn=fn
exports.Animal=Animal
//CommonJS导出的第二种格式
module.exports = {
  a,
  fn,
  Animal
}

//引入模块
const xxx(跟模块文件名一致) = require('./modules/xxx.js');
//使用
console.log(xxx.a)  	//10
xxx.fn()	//我是模块中的函数fn
let ani = new xxx.Animal();
console.log(an1.age)




/*
	注意:
		1.require() 是commonJS引入模块的一个方法
		2.引入模块之后,被一个常量接收,常量名一般跟模块名一致(不是必须,但大家都这么做)
		3.当导入的是js文件时,后缀名可以不写
		4.如果文件名是index.js,则整个文件名都可以不写

*/

# 模块中的this指向

//模块中的this指向本模块
console.log(exports);		//{}
console.log(module.exports)		//{}
console.log(exports===module.exports)		//true

console.log(exports===this)		//true
console.log(this)		//{}
console.log(global===this)		//false

# 3. node常用内置模块

一般项目中模块分为3种:

  • node内置模块
  • 自己书写的模块
  • 第三方模块(使用一个专门的工具npm进行统一管理)

常见的内置模块

  • fs :操作文件
  • http :网络操作
  • path :路径操作
  • querystring :查询参数解析
  • url :url解析
const fs = require('fs');
const http = require('http');
const path = require('path');
const qs = require('querystring');
const url = require('url');

nodejs内置模块的文档网址:http://nodejs.cn/api/

# 4. path内置模块

//引入path模块
const path = require('path');


//跟path无关,但使用path时会用到
console.log(__dirname)	//可以获取到该js文件所在的路径

//通过拼接的方式:获取当前文件的确完整路径
let filepath = path.join(__dirname,'文件名')
	//第一个参数:获取当前文件所在位置
	//当前文件的文件名


//获取同级其他目录下的文件路径
let filepath2 = path.join(__dirname,'文件夹名','文件名')

# 5. fs文件模块

# 同步读取

//同步读取
const fs = require('fs');
const path = require('path');
//获取被读取文件的路径
let filepath = path.join(__dirname, 'demo02.txt');

let ret = fs.readFileSync(filepath,'utf-8');
console.log(ret); //<Buffer e8 8b 8f e4 b8 9c e6 97 ad>  是node引擎存储文件数据的一种格式,以十六进制的方式存储

//通过toString()转成字符串就是我们能看懂的信息了
console.log(ret.toString()); //苏东旭


//同步sync:任务按顺序执行,执行完一个任务后再去执行另外一个任务
//异步async:有回调函数的几乎都是异步。如ajax请求、事件、定时器

# 异步读取

//异步读取
const fs = require('fs');
const path = require('path');
//获取被读取文件的路径
let filepath = path.join(__dirname, 'demo02.txt');

fs.readFile(filepath,'utf-8',(err,data)=>{
  //err 错误
  //data 读取到的数据
  if(err){
    console.log(err);
    return;
  }
  console.log(data)
});

# 异步写入

//会覆盖原来的内容,追加appendFile
const fs = require('fs');
const path = require('path');
let filepath = path.join(__dirname, 'demo02.txt');

fs.writeFile(filepath,'hello world!','utf-8',err => {
  if(err){
    console.log(err);
    return;
  }
  console.log('写完了!')
})

# 服务器搭建

使用 http 模块 搭建服务器

//1.引入http模块
const http = require('http');

//2.使用http模块搭建一个http服务器
let server = http.createServer((req,res)=>{
    //req:请求报文,所有和请求有关的都在req中
    //res:响应报文,所有和响应有关的都在res中
    
    //获取请求方式
    let method = req.method;
    //获取请求路径(端口号后面的)
    let reqUrl = req.url;
    console.log('this is server');
    //告诉客户端,服务端的响应结束了
  	//设置响应体
    res.write("<h1>this is server title </h1>")
    res.end());
})

//3.设置监听端口
server.listen(8090);

//简写
let http = require('http');
http.createServer((req,res)=>{
    res.end('Hello World')        
}).listen(3000)

# url模块

解析路径中的参数

//引入url模块
const url = require('url');

//url模块的parse()方法可以解析请求路径中的参数【写在服务器的箭头函数中】
let server = http.createServer((req,res)=>{
    let param = url.parse('请求路径',布尔类型);
    let 变量 = param.query.属性名;                          
})

/*
  参数
    请求路径:req.url
    布尔类型:true    解析后的结果中query就是对象格式
            false   解析后的结果中query就是字符串格式
*/

# 文件系统模块

加载静态资源

//引入fs模块
const fs = require('fs');

if(url == "/路径" && req.method == "请求方式"){
    //fs模块的readFile()方法可以异步读取文件【写在服务器的箭头函数中】
    fs.readFile('要加载的静态资源名',(err,data)=>{
        //err:错误优先机制,回调函数的第一个参数为错误信息
        //data:已经加载的静态资源,如果data获得的是文件内容的二进制数据,若想获得原字符串内容要加toString()方法
        if(err){
            res.write('404')   
        }else{
            res.write(data)
            //res.write(data.toString())
        }
    })
}else{
    let u = '.'+url;
    fs.readFile(u,(err,data)=>{ })
}

使用addListener监听表单中的数据

if(url == "/路径" && req.method == "POST"){
  let 变量 = "";
  //监听表单数据
  req.addListener("data",chunk=>{
    //chunk存储的就是每一次监听到的数据
 
    //我们需要把每一次监听到的内容存储起来
    变量+=chunk;
  })
  //返回监听结果
  req.addListener("end",()=>{
    //处理我们监听到的数据 ,得到的就是查询字符串
    //xxx=xxx&xxx=xxx
  })
}

使用解析查询字符串模块 ==> 就是把查询字符串转换成对象格式

//引入querystring模块
const qs = require('querystring')

let param = qs.parse(要解析的字符串)

//username=admin&pass=123123  =>  {usernme:admin,pass:123123}  

# formidable模块

接收表单数据

//该模块不是nodejs内置模块,需要手动下载
const form = require('formidable');

//判断表单提交
if(url == "/路径" && req.method == "POST"){
    //1.实例化
    let 变量 = new form.IncomingForm();
    //2.设置文件上传路径(将本地文件上传到服务器的哪个文件夹)
    变量.uploadDir='./xxxx';
    //3.设置是否保留文件的扩展名,默认是false
    变量.keepExtensions = true;
    //4.解析
    变量.parse(req,(err,fields,files)={
        // err 错误信息
        // fields 普通字段信息(接收表单中的信息)
        // files 上传文件的详细信息
        console.log(fields);
        console.log(files);
        res.end()
    })
}

//如果是上传文件,需要在html的form标签中添加属性:enctype='multipart/form'

# 数据库

# 数据库的使用

const http = require('http');
//引入mysql模块
const mysql = require('mysql');
//配置数据库
let connection = mysql.createConnection({
    host:'127.0.0.1',
    user:'root',
    password:'rootroot',
    database:'数据库名'
})
//连接数据库
connection.connect()

let server = http.createServer((req,res)=>{
    let url = req.url;
    if(url == '/路径' && req.method == 'GET'){
        let sql = 'sql语句';
        connection.query(sql,(error,results)=>{
            //query(参数1,参数2)
            //参数1:sql语句
            //参数2:回调函数 ==> error错误信息,results返回结果
            if(!error){
                console.log(results);
                res.end()            
            }        
        })    
    }
})

server.listen(8090)

# 数据库预解析

防止sql注入

const http = require('http');
//引入mysql模块
const mysql = require('mysql');
//配置数据库
let connection = mysql.createConnection({
    host:'127.0.0.1',
    user:'root',
    password:'rootroot',
    database:'数据库名'
})
//连接数据库
connection.connect()

let server = http.createServer((req,res)=>{
    let url = req.url;
    if(url == '/路径' && req.method == 'GET'){
        //准备sql
        let sql = 'select * from sql where id = ?';
        //绑定值【必须是数组!!!】
        let val = [10];
        //返回预处理
        let newSql = mysql.format(sql,val);
        
        connection.query(newSql,(error,results)=>{
            if(!error){
                ////affectedRows: 1, 受影响行数
                console.log(results);
                res.end()            
            }        
        })    
    }
})

server.listen(8090)

# Express框架

是Node.js官方推荐的唯一一个Web开发框架。

  • 静态文件服务
  • 路由控制
  • 模版解析支持
  • 动态视图
  • 用户会话
  • 错误控制器
  • 插件支持

官网:http://www.expressjs.com.cn

express是一个基于内置核心http模块的,一个第三方的包,专注于web服务器的构建。

//Express不是内置模块需要下载

// 1. 引入express模块
const express = require('express');

// 2. 创建应用
const app = express();

// 3. 设置静态资源路径
app.use(express.static('node_modules'));

// 4. 设置模版引擎
//ejs需要安装,ejs直接书写业务逻辑,注意注意:不能写注释,写注释直接报错
app.set('view engine','ejs');

// 5. 设置视图路径
app.set('views','./views');

// 6. 设置路由
app.get|post('/',(req,res)=>{
  console.log(req);
  res.send('hello world!')
  //加载视图:将第二个参数的变量data传递给当前的index.ejs
   res.render('index'{变量:数组对象})
   res.end();
})

// 7. 设置监听端口
app.listen(3000,()=>{
  console.log('服务器在3000端口启动')
})

# Cookie的使用

//cookie需要手动安装    'cookie-parser'
//1.引入cookie
const cookie = require('cookie-parser');
//2.应用cookie
app.use(cookie())    //cookie('dssfsdadasda')  可以设置签名密钥(3的第三个参数要设置是否签名)
                     //使用签名的cookie通过  req.signedCookies 获取
//3.设置cookie
res.cookie('cookie名','要存储的数据',{maxAge:xxxxx})
    //第一个参数:cookie名称
    //第二个参数:cookie值
    //第三个参数:cookie的配置选项
        //domain 域名
        //path 路径
        //expires 过期时间
        //maxAge 有效时间
        //httpOnly 只能由web服务器访问
        //secure 是否与https一起使用
        //signed 是否签名
//4.获取cookie
req.cookies.cookie名
//5.删除cookie
res.clearCookie('cookie名')

# Session的使用

特点:

1.session数据保存在服务器端

2.session是以键和值的形式进行存储

3.session依赖于cookie,每个session信息对应的客户端的标识保存在cookie中

//express-session
//1.引入session模块
const session = require('express-session');

//2.注册session
app.use(session({
    secret:'sudongxu',//secret属性的值可以为任意字符串,负责进行加密
    resave:false,    //固定写法,强制保存
    saveUninitialized:true    //固定写法,保存未初始化
}))

//3.设置session
req.session.自定义属性 =//4.获取session
req.session.自定义属性

//5.清空session
req.session.destroy()

# Socket.io

//环境搭建
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server)

app.use(express.static('node_modules'));
app.set('view engine','ejs');
app.set('views','./views');
app.get('/',(req,res)=>{
    res.render('xxx页面')
    res.end()
})
server.listen(端口号)

//服务器端
//连接socket服务器
io.on('connection',function(socket){    //形参是一个对象,该对象有两个函数
    //服务器端发送给客户端的信息
    socket.emit('频道号',{要发送的信息});
    //服务器端接收客户端的信息
    socket.on('频道号',function(res){
        //广播
        socket.broadcast.emit('频道号',res)                        
    })
})

//客户端
引入文件
<script src="/socket.io/socket.io.js">

//使用
let socket = io.connect('/')
//客户端发送给服务器端的信息
socket.emit('频道号',{要发送的信息})
//客户端接收服务器端的信息
socket.on('频道号',function(res){})