Node.js

158 阅读10分钟

之前js主要就是在浏览器中进行dom编程

有了node.js后,js也可以运用在后端

一、node简介

1、什么是node.js

1、Node.js是一个基于chorme v8引擎的javascript运行时

2、特点:单线程、事件驱动、异步I/O、轻量级

相当于餐厅只有一个服务员,但是这个服务员非常厉害,不怕I/O读写,但是比较怕请求里面有业务逻辑计算,这样就无法相应其他请求

3、Node.js是目前最大的开原生态系统之一。NPM(node package management)

2、环境变量

环境变量path,由多个路径组成,每个路径之间用;间隔
执行指令时,会按照path中指定的路径,逐一查找直到最后

二、基本使用

1、案例一

js文件可以直接运行,只需要在终端中进入相应路径,输入node 文件名.js
dom和bom的东西无法使用

let add = (a,b)=>a+b
console.log(add(1,2)) // 3

js模块化规范

  • commonJS规范,node里面使用的规范 ,使用require引入模块
  • AMD规范
  • CMD规范
  • ES6模块化,使用import和export(导入和导出)

2、案例二

fs.readFile(path[,options],callback) 异步操作 fs.readFileSync(path[,options],callback) 同步操作

var fs = require("fs") //引入模块fs

//读取文件
fs.readFile("./res/test.txt",(err,data)=>{
    if(!err){
        console.log(data.toString())
    }
})//后面是回调函数
console.log("end") //先打印出end再打印出文件中的内容,因为这是异步操作,可以先打印出end再调用回调函数

同步操作没有回调函数

var fs = require("fs") //引入模块fs

//读取文件
var data = fs.readFileSync("./res/test.txt")
console.log("data")
console.log("end")//先打印出文件内容,再打印出end,因为这是同步操作,所以这个方法一般不用

3、案例三

创建一个web服务

var http = require("http")
var server = http.createServer((req,res)=>{
    res.end("<h1>hello,Node.js</h1>") //通过end方法将响应的内容写到响应对象中
})
//监听一个端口,回调函数是当端口连接上时要进行的操作
server.listen(3000,()=>{
    console.log("server start.port:3000")
}) 

request headers中的内容

accept:能接受的内容
accept-encoding:能接受的编码

response headers
里面要说清楚返回的内容是什么,编码是什么,不然浏览器会出现乱码

var http = require("http")
var server = http.createServer((req,res)=>{
    res.setHeader("Content-Type","text/html;charset=utf-8")
    res.end("<h1>哈喽,Node.js</h1>") //这样中文就会正确解析出来
})
server.listen(3000,()=>{
    console.log("server start.port:3000")
}) 

三、模块化

1、概述

代码模块化的优点

  • 便于代码的重用
  • 便于代码的维护
  • 提高代码的可读性

问题

  • 如何去定义一个模块
  • 如何使用模块
  • 模块间的关系如何处理

为了解决这些问题就要设置标准,下面这四种标准

ConmmonJS(主要介绍这个标准)

ES6

AMD

CMD

2、模块的定义和使用

node中,一个js文件就是一个模块

每个js文件中的代码都是独立运行在一个函数中的,而不是在一个全局作用域中,所以说一个模块中的变量和函数其他模块无法直接访问

通过exports来向外暴露变量和方法
ModuleA.js

var x = "ModuleA x"
var y = "ModuleA y"
function fun(){
    console.log("ModuleA fun is called")
}
exports.xx = x;
exports.yy = y;
exports.fun = fun;

通过require函数引入外部的模块,自定义的模块需要传入路径,前提是要引入的模块可以被引用 ModuleB.js

var moduleA = require("./ModuleA")
console.log(moduleA.xx)
console.log(moduleA.yy)

模块分为两大类

核心模块

由CommonJS提供 核心模块的标识就是模块的名字

文件模块

由用户自己创建的模块 文件模块的标识就是文件的路径

3、全局对象

在node中有一个全局对象 global,作用类似浏览器中的window
在全局中创建的变量都会作为global的属性保存

ModuleC

//这里的a是局部变量,因为之前提到过,模块相当于一个函数
var a  = 100
//b是全局变量
b = 100
console.log(global.b) //100

fun = function(){
    console.log("ModuleC fun is called")
}

ModuleD

var moduleC = require("./ModuleC")
console.log(global.b) //100
global.fun

4、模块中的参数

node中,一个js文件就是一个模块
每个js文件中的代码都是独立运行在一个函数中的
在node执行模块中代码时,它会首先在代码的最顶部,添加如下代码
function(exports,require,module,_filename,_dirname){
内部是我们自己编写的代码
在代码的最底部添加如下代码 }

exports

使用该对象将变量和函数进行暴露

require

使用该对象引入其他模块

module

代表当前模块本身
exports就是module的属性
暴露既可以使用exports,也可以使用module.exports

filename

当前模块的完整路径

dirname

当前模块所在文件夹的完整路径

var x = "ModuleA x"
var y = "ModuleA y"
function fun(){
    console.log("ModuleA fun is called")
}
console.log(arguments) //函数接收的参数列表,能打印出这个说明上面的代码就是运行在一个函数中的
console.log(arguments.callee + "") //这个是arguments的一个属性,但是不推荐使用,es6中已经不推荐使用arguments了,而是推荐使用...

5、exports和module.exports

require方能看到的只有module.exports这个对象,它看不到exports对象,而我们在编写模块的时候用到的exports对象实际上只是对module.exports的引用

www.zhihu.com/question/26…
使用exports的代码

Module1.js

var x = "Module1 x"
var y = "Module1 y"
function fun(){
    console.log("Module1 fun is called")
}
exports.x = x
exports.y = y
exports.fun  = fun

Module2.js

var module1 = require(./Module1)
console.log(module1.x)
console.log(module1.y)
module1.fun()

使用module.exports的代码

var x = "Module1 x"
var y = "Module1 y"
function fun(){
    console.log("Module1 fun is called")
}
module.exports.x = x
module.exports.y = y
module.exports.fun  = fun

//或者下面这个写法
module.exports = {
    x, //属性简写
    y,
    fun
}
exports = {//不能这样写,会报错
    x,y,fun 
}
console.log(module.exports) //{x:'Module1 x',y:'Module1 y',fun:[Function:fun]}
console.log(module.exports=== exports)//false

四、包管理

1、包

将一组组相关的模块组合在一起,形成一个完整独立的功能模块,例如http、fs

包规范由包结构和包描述文件

包中的各种文件

-package.json 包的描述文件

-bin 可执行的二进制文件

-lib js代码

-doc 文档

-test 单元测试

包的描述文件
描述包的相关信息,供外部古曲分析

2、包的描述文件

package.json

name: 模块的名称

version:模块的版本

author: 模块的作者

description:模块的描述

keywords:模块的关键字

contributors:项目的贡献者

homepage:项目主页

repository:项目仓库

dependencies:生产环境下项目的依赖

devdDependencies:开发环境下项目的依赖(开发过程中使用到的工具模块babel、webpack)

scripts: 执行npm脚本命令的简写

3、npm

npm:node package management

node使用npm帮助其完成第三方模块的发布、安装和依赖等

npm init
npm init -y 包初始化

npm install 包名

npm install 包名--save

npm install 包名 --save-dev 下载到开发环境下的依赖中

npm install 包名--global 全局安装,不推荐这种方式

npm remove 包名

正常情况下,npm是从国外的仓库下载,速度可能会比较满,这时候可以用淘宝镜像下载,这时候就要用 cnpm, 也可以使用yarn

五、构建web应用

1、路由

req.url
页面上所有的静态资源都需要在请求中来获取

var http = require("http")
var server = http.createServer((req,res)=>{
    res.setHeader("Content-Type","text/html;charset=utf-8")
    if(req.url == "/favicon.ico"){
        return
    }else if(req.url == "/"){
        res.end("<h1>首页</h1>")
    }else if(req.url == "/music.html"){
        res.end("<h1>音乐频道</h1>")
    }else if(req.url == "/news.html"){
        res.end("<h1>新闻频道</h1>")
    }else {
        res.end("<h1>访问的资源不存在</h1>")
    }
})
server.listen(3000,()=>{
    console.log("server start.port:3000")
}) 

以前使用url伪静态技术
利用相关工具将地址"/emp.jsp?id=001"转化为"/emp/001" 现在使用node就不需要考虑这种问题
下面的正则表达式意思为

以emp开头 ^/emp 后面跟着6位数字 /[\d]{6}$

if(/^\/emp\/[\d]{6}$/.test(req.url)){
    var reg = /^\/emp\/([\d]{6})$/ //将[\d]{6}括了起来,将其变为子正则表达式
    
//reg.exec(req.url)会将整个正则表达式以及子表达式的匹配结果返回,包括编号,位置下标
    var bianhao = reg.exec(req.url)[1] 
    res.end("<h1>员工信息:编号"+bianhao + "</h1>")
}

练习
从json文件中读取数据,根据编号匹配显示到屏幕上

var http = require("http")
var fs = require("fs")
var server = http.createServer((req,res)=>{
    res.setHeader("Content-Type","text/html;charset=utf-8")
    if(req.url == "/favicon.ico"){
        return
    }else if(req.url == "/"){
        res.end("<h1>首页</h1>")
    }else if(req.url == "/music.html"){
        res.end("<h1>音乐频道</h1>")
    }else if(req.url == "/news.html"){
        res.end("<h1>新闻频道</h1>")
    } else if (/^\/emp\/[\d]{6}$/.test(req.url)) {
        var reg = /^\/emp\/([\d]{6})$/
        var bianhao = reg.exec(req.url)[1] 
       fs.readFile("./emps.json",(err,data)=>{
           if(err){
               console.log("文件读取失败")
               return
           }
           var dataObj = JSON.parse(data.toString())
           if(dataObj.hasOwnProperty(bianhao)){
               res.write("<h1>员工信息 编号:" + bianhao+ "</h1>")
               res.write("<h2>姓名:" + dataObj[bianhao]["name"] + "</h2>")
               res.write("<h2>部门:" + dataObj[bianhao]["dept"] + "</h2>")
               res.write("<h2>工资:" + dataObj[bianhao]["sal"] + "</h2>")
               res.end("")
           }else{
               res.end("<h1>编号:"+bianhao+"的员工不存在</h1>")
           }
        })
    }
    else {
        res.end("<h1>访问的资源不存在</h1>")
    }
})
server.listen(3000,()=>{
    console.log("server start.port:3000")
})

jsp、php、aspx 通过代码引擎 动态生成 html

2、静态资源文件的访问

上面使用res.end的写法比较麻烦,与servelt类似。将静态页面的代码嵌入了代码中,这样子看起来非常复杂

所以可以将静态页面单独写到一个html中,再进行读取

下面代码是对静态页面、图片的访问

静态资源文件.js

var http = require("http")
var fs = require("fs")
var server = http.createServer((req,res)=>{
    res.setHeader("Content-Type","text/html;charset=utf-8")
    if(req.url == "/favicon.ico"){
        return
    }else if(req.url == "/"){
        fs.readFile("./public/index.html",(err,data)=>{
            res.end(data)
        })
    }else if(req.url == "/timg.jpg"){
        fs.readFile("./public/timg.jpg",(err,data)=>{
            res.setHeader("Content-Type","image/jpg") /*这里是图片,所以需要重新设置响应头*/ 
            res.end(data) /* data是二进制的字节数据*/
        })
    }else if(req.url == "/style.css"){
        fs.readFile("./public/style.css",(err,data)=>{
            res.setHeader("Content-Type","text/css") /*这里是css,所以需要重新设置响应头*/ 
            res.end(data) 
        })
    }else if(req.url == "/script.js"){
        fs.readFile("./public/script.js",(err,data)=>{
            res.setHeader("Content-Type","application/x-javascript") /*这里是js,所以需要重新设置响应头*/ 
            res.end(data) 
        })
    }else {
        res.end("<h1>访问的资源不存在</h1>")
    }
})
server.listen(3000,()=>{
    console.log("server start.port:3000")
})

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
    <script type="text/javascript" src="script.js"></script>
</head>
<body>
    <h1>首页</h1>
    <img src="timg.jpg" alt="">
</body>
</html>

script.js

alert("hello");

style.css

h1{
    color: blue;
}

3、相关模块

url

localhost:3000/emp/1002
这个请求中url为 /emp/1002

console.log(req.url) 

var urljson = url.parse(req.url) //将一个URL字符串转换成对象并返回。
console.log("urljson", urljson)  
var pathname = urljson.pathname
console.log("pathname", pathname)

pathname

/emp/1002

path

path中的属性

路径:localhost:3000/emp.jsp?id=1001&pwd=10

extname

path的后缀名,可以根据它设置响应头

var extname = path.extname(pathname)
console.log("extname", extname)  //jsp

querystring

querystring 模块提供用于解析和格式化 URL 查询字符串的实用工具。

var query = urljson.query
console.log("query", query) //id=1001&pwd-10

var queryjson = querystring.parse(query)  //将字符串解析为一个对象,再通过对象属性进行访问
console.log("queryjson",queryjson.id, queryjson.pwd)  //1001 10

4、模拟apache

如果网址里输入localhost:3000/xx,让它直接跳转到xx里面的index.html页面

解决方案一
在pathname后面添加"/index.html"

if(!extname){ /*如果没有后缀名,则跳转到index.html文件*/
    if(pathname.substr(-1) != "/"){
        pathname += "/index.html"
    }else{
        pathname += "index.html"
    }
}

解决方案二:使用重定向
响应码 302:重定向,当响应码为302时,表示服务器要求浏览器重新再发一个请求,服务器会发送一个响应头Location,它指定了新请求的URL地址

if(!extname){
    if(pathname.substr(-1) != "/"){
        res.writeHead(302,{"Location":pathname + "index.html"})
    }else{
       res.writeHead(302,{"Location":pathname + "index.html"})
    }
    res.end("")
    return
}

六、experss框架

基于 Node.js 平台,快速、开放、极简的 Web 开发框架

1、介绍

node使用http模块构建web应用的缺点

路由不方便,甚至需要用正则表达式来匹配

静态资源服务使用不方便

页面呈现不方便,没有模板引擎的使用

2、基本使用

var express = require("express")
var app = express()

app.get("/",function(req,res){
    res.send("<h1>首页</h1>") /*res.end只能支持两种参数,要么字符串,要么Buffer
    res.send可以支持多种参数,比如可以传json对象,Buffer,String,Array */
})

app.get("/music",function(req,res){
    res.send("<h1>音乐频道</h1>") 
})
app.get("/news",function(req,res){
    res.send("<h1>新闻频道</h1>") 
})

app.get("/emp/:empno",function(req,res){
    res.send("<h1>员工信息 编号:" + req.params.empno+"</h1>") 
})

app.listen(3000,()=>{
    console.log("服务已启动,端口号3000")
})

添加静态文件
这样就能访问静态化的文件

app.use(express.static("public")) /*将要静态化的文件夹写进去*/

__dirname

app.get("/",function(req,res){
    console.log("__dirname",__dirname)  //打印出文件夹完整路径
    console.log(path.join(__dirname,"public")) //在路径最后添加一个“public”

    res.send("<h1>首页</h1>")
})
app.use("/static",express.static(path.join(__dirname,"public")))  //通过/static/index.html访问public文件夹的index.html
//即添加了一个虚拟路径