Node.js学习

本文是在Javascript初学习以后学习的node,node的介绍在Javascript文章的最后

node的介绍

  • 服务器编程语言,运行在服务器上的语言
  • node的语法使用的是ECMAScript的规范
  • node中并没有BOM和DOM,所以不能使用这两部分内容
  • node的组成:模块 和 ECMAScript
  • node中全面支持ES6,唯独不支持ES6的模块化,因为node中有自己的模块化规范
    • ES6的模块化:export,import
    • node的模块化规范:commonJs
      • require
      • exports

环境安装

下载官网:英文 中文 镜像

测试环境: win+r->命令行(运行->cmd)->node -v

版本说明

Vx(主).x(子).x(修正)

主版本: 变化了,1/3的API发生巨变 , 使用方式变化了

子版本: API没有删减,使用方式没变化,内部实现发生了变化

修正版:什么都没变,处理一下bug

V6.8.0 稳定

V6.9.1 非稳定版

beta 测试

rc 、alpha测试稳定

node内置模块的使用

http模块:用来开启服务
fs模块:用来操作磁盘,文件
url模块:用来解析url
querystring模块:用来解析url携带的数据

http的使用

引入服务器模块

let http = require('http')		

创建服务器功能,返回http对象

let app = http.createServer((req,res)=>{
  //request  response
	req //请求体  浏览器->服务器
	req.url  //地址   提取地址栏数据
	
	req.on('data') //提取非地址栏数据 所有的http[s]都会触发end事件
	req.on('end') 
	
	res //响应  服务器->浏览器
	res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});//响应头设置编码格式
	res.write(字符/数据<string><buffer>) //返回数据
	res.end() //结束响应 必须
})

挂载到指定端口

app.listen(端口,[地址][回调])

监听成功,回调一次

端口: 1-65535 1024以下系统占用

虚拟地址localhost 真实域名xx.duapp.com

更新后,需要每次服务器自动重启,所以在开发过程中建议使用插件执行自动刷新

推荐命令行工具:supervisor | nodemon

安装方式: npm install supervisor -g

  • 服务器模块
    • 引入之后
      const obj = http.createServer(function(req, res){
          // 拦截请求req。拦截响应res。处理各种数据。完成各种功能
      })
      obj.listen(3000, function(){
          console.log("服务器开启成功")
      })
      
    • 请求地址:req.url
    • 请求方式:req.method
    • 接收请求时绑定要触发的事件:req.on()
      • data:根据数据大小,有可能多次执行,每次只能拿到部分数据,所以data事件每次执行,都需要先将数据拼接起来
        • 事件处理函数的第一个参数,表示本次接收到的数据
      • end:绝对会在最后一个data之后执行,所以在end中获取拼接之后的数据,绝对是完整数据
    • 向前端写入数据(只能写字符,数据中包含HTML标签,可以把获取到的HTML数据全部响应到前端页面,页面再解析):res.write()
    • 向前端发送结束状态:res.end()

http的路由处理
通过访问提前设定的指定地址,来提供不同的功能

// 启动http服务模块
// 创建http服务功能
require("http").createServer((req, res) => {
    // 根据请求头来响应
    if (req.url === "/login") {
        res.write("Please login");
        console.log("Please login");
    } else if (req.url === "/regist") {
        res.write("Please to regist!");
        console.log("Please to regist!")
    } else {
        res.write("404 Not found!");
        console.log("404 Not found!")
    }
    // 响应结束
    res.end();
    // 监听端口
}).listen(3000, () => {
    console.log("already loding...");
});

fs(文件系统)的使用

除了标准的异步读取模式外,fs也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果,如果同步读取文件发生错误,则需要用try…catch捕获该错误

在fs模块中,提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢?

由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。

服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。

参考文章:廖雪峰的JavaScript教程

    • writeFile(要操作的文件的地址名, 新增的内容, 报错回调函数(err)(可选))
    • unlink(要操作的文件的地址名, 报错回调函数(err)(可选))
    • unlinkSync(‘文件路径’)
  • 改(重写)

    • 改名

      • rename(要操作的文件的地址名, 改之后的文件的地址名, 报错回调函数(err)(可选))
      • renameSync(‘改前’,‘改后’);
    • 改文件内容

      • writeFile(要操作的文件的地址名,要改的内容(重写这个文件的内容),报错回调函数(err)(可选))
    • readFile(要操作的文件的地址名, 字符编码(可选,不写时默认buffer), 报错回调函数(err, data)(可选)
      • err:是否报错
        • err===null没有报错
        • err!==null报错
      • data:读取出来的数据,buffer流
  • 报错回调函数(可选)

    • 回调函数的第一个参数如果有值,则说明报错,操作失败
    • 回调函数的第一个参数如果有值,值为null,则表示值为空,操作成功
变量 = fs.readFileSync('文件路径') 

处理错误
try{要排错的代码}catch(e){}

querystring的使用

作用:处理查询字符串 如:?key=value&key2=value2

用法

let querystring = require('querystring')

//处理查询字符串,非地址栏的形式传递

let str='a=1&b=2&c=3';

//str -> obj
console.log(querystring.parse(str))

//obj -> str
console.log(querystring.stringify({a:11,b:22,c:33}))

url的使用

作用: 处理 url型的字符串

用法

let url = require('url')

let str = 'http://localhost:3000/html/index.html?a=1&b=2#title';

// url.parse(str,true)  str -> obj  返回 对象obj	true处理query为对象

//把url字符转对象
console.log(url.parse(str,true))

//把url对象,转字符  obj -> str   返回 字符str
console.log(url.format(url.parse(str,true)))

地址栏说明
例子:http://localhost:8002/aaa?username=sdfsdf&content=234234#title

str -> obj 返回 对象 true
protocol: ‘http:’, 协议
slashes: true, 双斜杠
auth: null, 作者
host: ‘localhost:8002’, 主机
port: ‘8002’, 端口
hostname: ‘localhost’, baidu
hash: ‘#title’, 哈希(锚)
search: ‘?username=sdfsdf&content=234234’, 查询字符串
query: ‘username=sdfsdf&content=234234’, 数据
pathname: ‘/aaa’, 文件路径
path: ‘/aaa?username=sdfsdf&content=234234’, 文件路径
href: ‘http://localhost:8002/aaa?username=sdfsdf&content=234234#title’

简易服务器的搭建

// 引入模块
const http = require("http");
const fs = require("fs");
const qs = require("querystring");
const url = require("url");
// 创建服务器功能
const serverObj = http.createServer((req, res) => {
    // 处理请求地址
    const urlObj = url.parse(req.url).pathname;
    // 访问数据
    if (urlObj.pathname.includes("/api")) {
        dataHandle(req, res);
    } else {
        // 访问静态资源
        staticHandle(req, res);
    }
});
// 挂载到端口
serverObj.listen(3000, () => {
    console.log("Service aleady open, location: http://localhost:3000");
});
// 访问数据
function dataHandle(req, res) {
	// get数据:在地址后拼接
    if (req.method === "GET") {
    	// 转换之后成了对象
        const obj = url.parse(req.url, true).query;
        fn(req, res, obj);
    } else if (req.method === "POST") {
    	// post数据:非地址后拼接,只能通过事件获取
        let str = "";
        req.on("data", (d) => {
            str += d;
        });
        req.on("end", () => {
            const obj = qs.parse(str);
            fn(req, res, obj);
        });
    }
}
// 测试功能
function fn(req, res, reqData) {
    console.log(reqData);
    const obj = {
        title: "这是后端接收到的数据,响应给前端",
        data: reqData
    }
    res.write(JSON.stringify(obj));
    res.end();
}
// 访问静态资源
function staticHandle(req, res) {
    const urlObj = url.parse(req.url);
    // "./www"是自定义的静态资源文件夹
    fs.readFile("./www" + urlObj.pathname, (err, data) => {
        if (err) {
            res.write("notFound");
        } else {
            res.write(data);
        }
        res.end();
    });
}

模块化 commonJS

模块化是主要为了制定JS在后端的表现,commonJS 是个规范 nodejs / webpack 是一个实现

ECMA 是个规范 js / as 实现了他

其他模块化规范:seajs.js / require.js CMD/AMD/UMD es6

注意
commonJS 是 nodejs 默认模块管理方式,不支持es6的模块化管理方式,但支持所有es6+语法

作用

使变量具有文件作用域,不污染全局变量

输入require

require('模块名')  | any
  • 引入的是模块输出的对象
  • 引入之后的模块无论怎么修改都不影响原来的定义输出的模块
  • 有指定路径时找指定路径,找不到就报错not found
  • 不指定路径时先找系统模块-> 再从项目环境找node_modules|bower_components (依赖模块)->not found
  • 使用es6输入,默认情况下直接报错Cannot use import statement outside a module(无法在模块外部使用导入语句)
//输入1
let modA = require('./mod/a');
let b = require('./mod/a').b

// 引入的是模块输出的对象
console.log(modA)
console.log(b)

//输入 2
let modB = require('./mod/b');
console.log(modB)

// 引入之后的模块无论怎么修改都不影响原来的定义输出的模块
modB.arr.push('dd')
onsole.log(modB.arr)

//找指定路径 -> not found
let http = require('./http');
//不指定路径:先找系统模块-> 再从项目环境找node_modules|bower_components (依赖模块)->not found
let http = require('http'); // 这里是系统模块
let http = require('bulala'); // 这里是项目环境模块(只要node_modules|bower_components中有bulala.js就能找到

console.log(http)


//使用es6输入,默认情况下直接报错Cannot use import statement outside a module(无法在模块外部使用导入语句)

import modC from './mod/c';
console.log(modC)

输出

批量输出 都是属性
可输出多次,输入的是对象|属性

语法

exports.自定义属性1 =| any
exports.自定义属性2 =| any

例子a.js

let a=12;
let b='bmw';
let fn=()=>console.log('function');

// 批量输出
// exports.自定义属性1 = 值 | any 
//输出多次  外面拿到的是个对象,或者某一个属性
exports.aa = a
exports.b = b

默认输出,只能输出一次,以最后一次输出为准,输出的是什么类型,输入的就是什么类型

语法

module.exports =| any		

例子b.js

let a=12;
let b='bmw';
let fn=()=>console.log('function');
let arr = ['aa','bb','cc'];

//module.exports = 值 | any		只能输出一次  输出什么,外面输入的就是什么
// module.exports = a;
// module.exports = b;//第二次输出会覆盖


module.exports = {
  aa:a,
  b:b
}

module.exports = {
  a,b,fn,arr
}

批量输出将要批量输出的变量存放在数组内
默认输出使用default

语法

//批量输出
export {x,y,...}

//默认输出
export default xxx;

例子c.js

let a=12;
let b='bmw';
let fn=()=>console.log('function');
let arr = ['aa','bb','cc'];

//批量输出
export {a,b,fn,arr}

//默认输出
export default a;

注意
node中没有定义输出时都是空对象,node中没有this,如果强行打印的话就为空对象

bulala.js(与测试输入中的require(‘bulala’)对应)

console.log('我是bulala模块')
// node中没有定义输出时都是空对象,node中没有this,如果强行打印的话就为空对象
console.log(this)

NPM

node环境下自带的下载工具

作用

安装模块(包),自动安装依赖,管理包(增,删,更新,项目所有包)

类似: bower , yarn

安装到全局环境

  • 安装到电脑系统环境下
  • 使用时在任何位置都可以使用
  • 被全局安装的通常是:命令行工具,脚手架
npm i 包名 -g			//		    安装
npm uninstall 包名 -g	 //			卸载

g: global(全局)

安装到项目环境(不加-g)

只能在当前项目环境下使用,需要使用npm代运行(npm install xxx)

初始化项目环境

初始化npm项目命令

npm init

初始化npm管理文件package.json
package-lock.json 文件用来固化依赖

package.json 说明

{
  "name": "npm",	//项目名称
  "version": "0.0.1",	//版本
  "description": "test and play",	//描述
  "main": "index.js", //入口文件
  "dependencies": {  //项目依赖  上线也要用
    "jquery": "^3.2.1"
  },
  "devDependencies": { //开发依赖 上线就不用
    "animate.css": "^3.5.2"
  },
  "scripts": {	//npm 脚本,命令行下使用npm run命令,就可以执行脚本
    "test": "命令行",
    "dev": "nodemon ./app.js", // 执行npm run dev 等同于 nodemon ./app.js
    "start": "nodemon ./app.js", // start最特殊,使用时不用加run,直接npm start
  },
  "repository": {	//仓库信息
    "type": "git",
    "url": "git+https://github.com/alexwa9.github.io/2017-8-28.git"
  },
  "keywords": [  //关键词
    "test",'测试node','oo'
  ],
  "author": "wan9",
  "license": "ISC",	//认证
  "bugs": {
    "url": "https://github.com/alexwa9.github.io/2017-8-28/issues"//问题提交
  },
  "homepage": "https://github.com/alexwa9.github.io/2017-8-28#readme"//首页
}

项目依赖(dependencies)

只能在当前项目下使用,上线了,也需要这个依赖 --save

//安装
npm i 包名 --save
npm install 包名 -S
npm install 包名@x.x.x -S

//卸载
npm uninstall 包名 --save
npm uninstall 包名 -S

开发依赖(devDependencies)

只能在当前项目下使用 ,上线了,依赖不需要了 --save-dev

npm install 包名 --save-dev
npm install 包名 -D

查看包的命令

npm list  列出所有已装包
npm outdated 版本对比(安装过得包)
npm info 包名 查看当前包概要信息 
npm view 包名 versions 查看包历史版本列表

安装所有依赖

npm install 

会安装package.json里面指定的所有包

版本约束

^x.x.x   约束主版本,后续找最新
~x.x.x   保持前两位不变,后续找最新
*		 装最新
x.x.x 	 定死了一个版本

选择源

npm install nrm -g     安装选择源的工具包
nrm ls 查看所有源
nrm test 测试所有源
nrm use 切换源名

安装卡顿时

ctrl + c -> npm uninstall 包名  -> npm cache verify 清除缓存 ->5g网络 -> npm install 包名

发布包

  • 官网 注册 收激活邮件
  • 登录
    • nrm use npm
    • npm login 登录
    • 输入 user/password/email
  • 创建包
    • npm init -y
    • 创建入口index.js
    • 编写,输出
  • 发布
    • npm publish
  • 迭代
    • 修改版本号
    • npm publish
  • 删除
    • npm unpublish

包的发布、迭代、删除,需要在包目录下进行

删除包,有时需要发送邮件

扩展

peerDependencies //发布依赖
optionalDependencies //可选依赖
bundledDependencies //捆绑依赖
contributors //为你的包装做出贡献的人。贡献者是一群人。
files //项目中包含的文件。您可以指定单个文件,整个目录或使用通配符来包含符合特定条件的文件

YARN

包管理工具

安装及使用参考这篇文章
https://blog.csdn.net/yw00yw/article/details/81354533

官网
https://classic.yarnpkg.com/lang/en/
需要的命令行可以在官网查询

与npm的区别
初始化之后都有package.json
区别:

  • npm有package-lock.js,在npm在只是固化依赖
  • yarn有yarn.lock,这个文件已经把依赖模块的版本号全部锁定,当你执行yarn install的时候,yarn会读取这个文件获得依赖的版本号,然后依照这个版本号去安装对应的依赖模块,这样依赖就会被锁定,以后再也不用担心版本号的问题了。其他人或者其他环境下使用的时候,把这个yarn.lock拷贝到相应的环境项目下再安装即可。
    注意:这个文件不要手动修改它,当你使用一些操作如yarn add时,yarn会自动更新yarn.lock。这也是解决npm中的版本问题推出了yarn.lock的机制

yarn.lock的注意事项

  • 不是自己创建出来的
  • 通过添加依赖包创建
  • 通过yarn install --update-checksums更新,yarn.lock文件不存在时候会创建(官方的这个命令的解释:Update checksums in the yarn.lock lockfile if there’s a mismatch between them and their package’s checksum.即如果yarn.lock锁文件中的校验和与其包的校验和不匹配,则更新校验和。)

BOWER

是专门为前端表现设计的包管理器,一切全部为前端考虑

npm 和bower 的最大区别,就是 npm 支持嵌套地依赖管理,而 bower只能支持扁平的依赖(嵌套的依赖,由程序员自己解决)

官网:https://bower.io/

安装bower

npm install -g bower

安装包到全局环境

bower i 包名 -g		// 安装
bower uninstall 包名 -g	// 卸载

安装包到项目环境

初始化项目环境
bower init

bower.json 第三方包管理配置文件

项目依赖

只能在当前项目下使用,上线了,也需要这个依赖 --save

//安装
同npm
bower install 包名#x.x.x -S 指定版本使用#

//卸载
同npm
开发依赖

只能在当前项目下使用 ,上线了,依赖不需要了 --save-dev

同npm

EXPRESS

nodejs库,不用基础做起,工作简单化,点击进入官网,类似的还有 koa

使用

  1. 初始化项目 npm init 创建package.json
  2. 通过npm下载express npm install express

特点

二次封装,非侵入式,增强形

搭建web服务

let express=require('express')
let app=express()
let app.listen(端口,地址,回调)

静态资源托管

app.use(express.static('./www'));

接口响应

支持各种请求方式:get、post、put、delete…

app.请求姿势API(接口名称,处理函数)
app.get(url,(req,res,next)=>{})
app.post(url,(req,res,next)=>{})
...
req 请求体

request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性

req.query //获取地址栏的数据
req.body //获取非地址栏的数据  依赖中间件 
req.headers //获取的是请求头

req.params //获取动态接口名
req.method //获取前端提交方式

req.body依赖中间件

中间件使用:body-parser

  1. npm install body-parser
  2. let bodyParser = require(‘body-parser’)
  3. app.use(bodyParser ())
res 响应体

response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据

res.send(any) //对等 res.write + end
res.end(string|buffer)
res.json(json) //返回json
res.status(404).send({error:1,msg:"Sorry can't find that!"}) //返回一个404

res.jsonp(响应数据) //调用请求时的回调函数并传递响应数据
res.sendFile(path.resolve('public/error.html'))//渲染纯 HTML 文件
jsonp响应
app.set('jsonp callback name',"bulala")//默认callback
app.get('/jsonp接口',(req,res,next)=>res.jsonp(数据))		
处理一部分接口

共有业务逻辑,在一起给处理了

app.all('/admin/*',(req,res,next)=>{}))

all匹配全路径 处理所有HTTP

需要next 延续后续

use

安装中间件、路由,接受一个函数,

app.use([地址],中间件|路由|函数体)

//use 做接口响应  响应所有的请求姿势(get,post,....)
//场景: 安装中间件|路由

中间件

middleware, 处理自定义业务,只处理请求到结束响应的中间部分

举例

npm i body-parser -S //安装包
let bodyParser=require('body-parser')//引入中间件
server.use(bodyParser())//安装中间件

body-parser 使用方式,实时查询 npm,可获得最新

中间件的封装

中间件的调用(以body-parser为例)

let express=require('express');//引入

// let bodyParser = require('body-parser') ;//  中间件==函数==类==对象{函数,类,other}
let bdParser = require('./mod/bd-parser') ;

//搭建服务器
let app = express();// http.createServer(()=>{})
// 挂载端口
app.listen(3000)

//安装中间件

// app.use(bodyParser());//函数

// app.use(bdParser())

/* app.use(bdParser({
  // limit: 10
})); */

app.use(bdParser.urlencoded({
  limit: 100
}));

//接口响应
app.get('/api/reg',(req,res)=>{
  console.log(req.body)
})

中间件的封装
如何封装和输出在代码的注释中

/* let bdParser = () => {
  console.log('中间件的配置,运行了')
  中间件的处理实际上写在返回值,到调用处去执行,这和执行方式有关
  return (req,res,next) => {
    console.log('bdparser function')
    req.body='bmw';
    next()
    //req.on('data') 提取非地址栏数据 所有的http[s]都会触发end事件
    // req.on('end') 
  }
}; */

/* let bdParser = () => {
  console.log('中间件的配置,运行了')
  return (req,res,next) => {
    // console.log('bdparser function')
    let str = '';
    req.on('data',(chunk)=>str+=chunk)
    req.on('end',()=>{
      req.body=str;
      next();
    }) 
  }
}; */

let querystring = require('querystring');
/* let bdParser = opts => {
  opts = opts || {};
  opts.limit = opts.limit || 10000;
  return (req,res,next) => {
    // console.log('bdparser function')
    let str = '';
    req.on('data',chunk=>str+=chunk)
    req.on('end',()=>{
      if(str.length > opts.limit){
        throw 'to large...'
      }else{
        req.body=querystring.parse(str)
      }
      next();
    }) 
  }
}; */


let bdParser = {
  urlencoded: opts => {

    opts = opts || {};
    opts.limit = opts.limit || 10000;
  
    return (req,res,next) => {
      // console.log('bdparser function')
      let str = '';
      req.on('data',chunk=>str+=chunk)
      req.on('end',()=>{
        if(str.length > opts.limit){
          throw 'to large...'
        }else{
          req.body=querystring.parse(str)
        }
        next();
      }) 
    }
  
  }
}
// 默认输出
module.exports = bdParser;

后端跳转

res.redirect(url)      指向一个接口

扩展

req
  • req.app:当callback为外部文件时,用req.app访问express的实例
  • req.baseUrl:获取路由当前安装的URL路径
  • req.cookies:Cookies
  • req.fresh / req.stale:判断请求是否还「新鲜」
  • req.hostname / req.ip:获取主机名和IP地址
  • req.originalUrl:获取原始请求URL
  • req.path:获取请求路径
  • req.protocol:获取协议类型
  • req.route:获取当前匹配的路由
  • req.subdomains:获取子域名
  • req.accepts():检查可接受的请求的文档类型
  • req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码
  • req.get():获取指定的HTTP请求头
  • req.is():判断请求头Content-Type的MIME类型
res
  • res.app:同req.app一样
  • res.append():追加指定HTTP头
  • res.set()在res.append()后将重置之前设置的头
  • res.cookie(name,value [,option]):设置Cookie
  • opition: domain / expires / httpOnly / maxAge / path / secure / signed
  • res.clearCookie():清除Cookie
  • res.download():传送指定路径的文件
  • res.get():返回指定的HTTP头
  • res.location():只设置响应的Location HTTP头,不设置状态码或者close response
  • res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。
  • res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
  • res.set():设置HTTP头,传入object可以一次设置多个头
  • res.status():设置HTTP状态码
  • res.type():设置Content-Type的MIME类型
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值