目录
目录
02 package.json和package-lock.json
(1)http.request(options,callback)
1.安装 npm install express --save
Node.js
一、Node.js基础
1. 认识Node.js
Node.js是一个javascript运行环境。它让javascript可以开发后端程序,实现几乎其他后端语言实现的所有功能,可以与PHP、Java、Python、.NET、Ruby等后端语言平起平坐。
Nodejs是基于V8引擎,V8是Google发布的开源JavaScript引擎,本身就是用于Chrome浏览器的js解释部分,但是Ryan Dahl 这哥们,鬼才般的,把这个V8搬到了服务器上,用于做服务器的软件。
01 nodejs的特性
-
Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发
-
NodeJs超强的高并发能力,实现高性能服务器
-
开发周期短、开发成本低、学习成本低
02 浏览器环境vs node环境
Node.js 可以解析JS代码(没有浏览器安全级别的限制)提供很多系统级别的API,如:
-
文件的读写 (File System)
const fs = require('fs') fs.readFile('./ajax.png', 'utf-8', (err, content) => { console.log(content) })
-
进程的管理 (Process)
function main(argv) { console.log(argv) } main(process.argv.slice(2))
-
网络通信 (HTTP/HTTPS)
const http = require("http") http.createServer((req,res) => { res.writeHead(200, { "content-type": "text/plain" }) res.write("hello nodejs") res.end() }).listen(3000)
2. 模块、包、commonJS
模块化简介
在node中,一个js文件就是一个模块。
每一个js文件中的js代码都是独立运行在一个函数中,而不是在全局作用域,所以一个模块中的变量和函数在其他模块中是不能访问的。
如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。使用require引入。
exports:对象,该对象用来将变量和函数暴露到外部
require:函数,用来引入外部的模块
module:模块,代表我们当前的模块通过exports只能用.的方式向外暴露自身变量和函数
module.exports既可以通过.的方式,也可以通过直接赋值的方式
module.exports.xxx = xxx
module.exports = {}
注意点:exports就是module的一个属性,即module.exports==exports 为true,但是exports不能通过对象的方式向外暴露属性
// m1.js
const name = 'gp19'
const sayName = () => {
console.log(name)
}
// 接口暴露方法一:使用exports.xxx一个一个向外暴露
exports.name = name
exports.sayName = sayName
// 接口暴露方法二:使用module.exports把要暴露的内容放到一个对象中,批量向外暴露
module.exports = {
name:name
sayName: sayName
}
// 错误方法,不能exports一个对象
exports = {
name:name
sayName: sayName
}
// 其他文件通过require引入即可
// m2.js
const m1 = require('./m1')
m1.say()
如果需要使用es6的模块化规范,需要在package.json中添加"type"="module"
3. Npm&Yarn
01 npm的使用
npm init //构建项目说明,生成 package.json文件
npm install 包名 –g //或者uninstall,update,表示全局安装,卸载或者更新
npm install 包名 --save-dev //uninstall,update 表示全局安装,卸载或者更新
//而参数--save 的作用是在项目下的package.json文件记录安装过的依赖包名称,当复制项目到另外的电脑上,只需运行命令: npm i 就能自动安装项目用到的依赖包
npm list -g //(不加-g,列举当前目录下的安装包)
npm info 包名 //(详细信息) npm info 包名 version(获取最新版本)
npm install md5@1//(安装指定版本)
npm outdated //( 检查包是否已经过时)
02 package.json和package-lock.json
package.json 是在运行 “ npm init ”时生成的,主要记录项目依赖,有以下结构
- name:项目名,也就是在使用npm init 初始化时取的名字,但是如果使用的是npm init -y 快速初始化的话,那这里的名字就是默认存放这个文件的文件名;
- version:版本号;
- private:希不希望授权别人以任何形式使用私有包或未发布的;
- scripts-serve:是vue的项目启动简写配置;
- scripts-build:是vue的打包操作简写配置;
- dependencies:指定了项目运行时所依赖的模块;
- devDependencies:指定项目开发时所需要的模块,也就是在项目开发时才用得上,一旦项目打包上线了,就将移除这里的第三方模块;
package-lock.json是在运行“npm install”时生成的一个文件,用于记录当前状态下项目中实际安装的各个package的版本号、模块下载地址、及这个模块又依赖了哪些依赖。
区别:
为什么有了package.json,还需要package-lock.json文件呢?
当项目中已有 package-lock.json
文件,在安装项目依赖时,将以该文件为主进行解析安装指定版本依赖包,而不是使用 package.json
来解析和安装模块。因为 package 只是指定的版本不够具体,而package-lock
为每个模块及其每个依赖项指定了版本,位置和完整性哈希,所以它每次创建的安装都是相同的。无论你使用什么设备,或者将来安装它都无关紧要,每次都应该给你相同的结果。
dependencies字段的说明:
"dependencies": { "md5": "^2.1.0" } ^ 表示 如果 直接npm install 将会安md5的2.*.* 的最新版本
"dependencies": { "md5": "~2.1.0" } ~ 表示 如果 直接npm install 将会安装md5 2.1.* 的最新版本
"dependencies": { "md5": "*" } * 表示 如果 直接npm install 将会安装 md5的最新版本
02 全局安装 nrm
NRM (npm registry manager)是npm的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速地在 npm 源间切换。
手动切换方法: npm config set registry https://registry.npm.taobao.org
安装 nrm
在命令行执行命令,npm install -g nrm,全局安装nrm。
使用 nrm
执行命令 nrm ls 查看可选的源。 其中,带*的是当前使用的源,上面的输出表明当前源是官方源。
切换 nrm
如果要切换到taobao源,执行命令nrm use taobao。
测试速度
你还可以通过 nrm test 测试相应源的响应时间。
03 yarn使用
npm install -g yarn
对比npm: 速度超快: Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。 超级安全: 在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。 开始新项目 yarn init 添加依赖包 yarn add [package] yarn add [package]@[version] yarn add [package] --dev 升级依赖包 yarn upgrade [package]@[version] 移除依赖包 yarn remove [package] 安装项目的全部依赖 yarn install
5. 内置模块
01 http模块:作为服务器
(1)server对象
// 引入http模块
const http = require('http');
// 创建本地服务器来从其接收数据
const server = http.createServer((req, res) => {
//设置响应头信息
res.writeHead(200, { 'Content-Type': 'application/json' });
//设置响应体信息
res.end(JSON.stringify({
data: 'Hello World!'
}));
});
//启动服务器,8000端口
server.listen(8000);
const http = require('http');
// 创建本地服务器来从其接收数据
const server = http.createServer();
// 监听请求事件,on方法为server绑定了request事件,只要客户端请求服务器的数据就会触发
server.on('request', (req, res) => {
//req:请求对象,包含了与客户端相关的数据和属性
// req.url:客户端请求的 URL 地址
// req.method:客户端的 method 请求类型
//设置 Content-Type 响应头,charset=utf-8防止中文乱码
res.setHeader("Content-Type","text/html; charset=utf-8");
res.writeHead(200, { 'Content-Type': 'application/json' });
//调用 res.end() 向客户端响应一些内容,调用后会关闭流。而res.write()也是响应一些内容,但不会关闭流
res.end(JSON.stringify({
data: 'Hello World!'
}));
});
//启动服务器
server.listen(8000);
第二段代码是通过直接创建一个Server对象,然后为其添加request事件监听。
而server.on()里面配置的写法和在http.createServer()里面直接配置的写法效果是一样的。
其实也就说createServer方法其实本质上也是为http.Server对象添加了一个request事件监听,这似乎更好理解了。
(2)server的事件
http.createServer创建的server对象是一个基于事件的服务器,它是继承自EventEmitter,事实上,nodejs中大部分模块都继承自EventEmitter,包括fs、net等模块,这也是为什么说nodejs基于事件驱动(关于EventEmitter的更多内容可以在官方api下的events模块找到),server提供的事件如下:
-
request:当客户端请求到来时,该事件被触发,提供两个参数req和res,表示请求和响应信息,是最常用的事件
-
connection:当TCP连接建立时,该事件被触发,提供一个参数socket,是net.Socket的实例
-
close:当服务器关闭时,触发事件(注意不是在用户断开连接时)
其中request事件是最常用的,而参数req和res分别是http.IncomingMessage和http.ServerResponse的实例
(3)server的request事件的参数req
req是http.IncomingMessage的实例,http.IncomingMessage是HTTP请求的信息,是后端开发者最关注的内容,一般由server的request事件发送,并作为第一个参数传递,其提供了3个事件,如下:
-
data:当请求体数据到来时,该事件被触发,该事件提供一个参数chunk,表示接受的数据,如果该事件没有被监听,则请求体会被抛弃,该事件可能会被调用多次(这与nodejs是异步的有关系)
-
end:当请求体数据传输完毕时,该事件会被触发,此后不会再有数据
-
close:用户当前请求结束时,该事件被触发,不同于end,如果用户强制终止了传输,也是用close
req实例的属性
(4)server的request事件的参数res
res是http.ServerResponse的实例,http.ServerResponse是返回给客户端的信息,决定了用户最终看到的内容,一般也由server的request事件发送,并作为第二个参数传递,它有三个重要的成员函数,用于返回响应头、响应内容以及结束请求
-
res.writeHead(statusCode,[heasers]):向请求的客户端发送响应头,该函数在一个请求中最多调用一次,如果不调用,则会自动生成一个响应头
-
res.write(data,[encoding]):想请求的客户端发送相应内容,data是一个buffer或者字符串,如果data是字符串,则需要制定编码方式,默认为utf-8,在res.end调用之前可以多次调用
-
res.end([data],[encoding]):结束响应,告知客户端所有发送已经结束,当所有要返回的内容发送完毕时,该函数必需被调用一次,两个可选参数与res.write()相同。如果不调用这个函数,客户端将用于处于等待状态。
02 http模块:作为客户端
http模块提供了两个函数http.request和http.get,功能是作为客户端向http服务器发起请求。
(1)http.request(options,callback)
options是一个类似关联数组的对象,表示请求的参数。常用的参数有host、port(默认为80)、method(默认为GET)、path(请求的相对于根的路径,默认是“/”,其中querystring应该包含在其中,例如/search?query=byvoid)、headers(请求头内容)。
callback作为回调函数,需要传递一个参数,为http.ClientResponse的实例,http.request返回一个http.ClientRequest的实例,一般叫req。
需要记住的是,如果我们使用http.request方法时需要利用返回的http.ClientRequest的实例调用end方法,没有调用end方法,服务器将不会收到信息。
(2)http.get(options,callback)
这个方法是http.request方法的简化版,唯一的区别是http.get自动将请求方法设为了GET请求,同时不需要手动调用req.end()。
因为http.get和http.request方法都是返回一个http.ClientRequest对象,所以我们来看一下这两个对象。
(3)http.ClientRequest
http.ClientRequest是由http.request或者是http.get返回产生的对象,表示一个已经产生而且正在进行中的HTPP请求,提供一个response事件,也就是我们使用http.get和http.request方法中的回调函数所绑定的对象,我们可以显式地绑定这个事件的监听函数
var http=require("http");
var options={
hostname:"cn.bing.com",
port:80
}
var req=http.request(options);
req.on("response",function(res){
res.setEncoding("utf-8");
res.on("data",function(chunk){
console.log(chunk.toString())
});
console.log(res.statusCode);
})
req.on("error",function(err){
console.log(err.message);
});
req.end();
http.ClientRequest也提供了write和end函数,用于向服务器发送请求体,通常用于POST、PUT等操作,所有写操作都必须调用end函数来通知服务器,否则请求无效。此外,这个对象还提供了abort()、setTimeout()等方法,具体可以参考文档
(4)http.ClientReponse
与http.ServerRequest相似,提供了三个事件,data、end、close,分别在数据到达、传输结束和连接结束时触发,其中data事件传递一个参数chunk,表示接受到的数据。其属性如下
此外,这个对象提供了几个特殊的函数
-
response。setEncoding([encoding]):设置默认的编码,当data事件被触发时,数据将会以encoding编码,默认值是null,也就是不编码,以buffer形式存储
-
response.pause():暂停结束数据和发送事件,方便实现下载功能
-
response.resume():从暂停的状态中恢复
03 url模块
1. parse会解析url并转换成一个对象,方便我们对其进行操作。
url模块的以上方法已被弃用,替换为URL类,详见文档。
const url = require('url')
const urlString = 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
const parsedStr = url.parse(urlString)
//会生成一个对象
//Url {
// protocol: null,
// slashes: null,
// auth: null,
// host: null,
// port: null,
// hostname: null,
// hash: null,
// search: null,
// query: null,
// pathname: '/home',
// path: '/home',
// href: '/home'
//}
console.log(parsedStr)
2. format可以将一个对象转换成url
const url = require('url')
const urlObject = {
protocol: 'https:',
slashes: true,
auth: null,
host: 'www.baidu.com:443',
port: '443',
hostname: 'www.baidu.com',
hash: '#tag=110',
search: '?id=8&name=mouse',
query: { id: '8', name: 'mouse' },
pathname: '/ad/index.html',
path: '/ad/index.html?id=8&name=mouse'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)
02.3 resolve用于拼接url
const url = require('url')
var a = url.resolve('/one/two/three', 'four') ( 注意最后加/ ,不加/的区别 )
//如果three后面加/那么就是在路径最后添加 a='/one/two/three/four'
//如果three后面不加/那么就是替换最后一个路径 a='/one/two/four'
var b = url.resolve('http://example.com/', '/one')
//b= 'http://example.com/one'
var c = url.resolve('http://example.com/one', '/two')
//c= 'http://example.com/two'
04 querystring模块,已被弃用,但仍然可用。
官方推荐URLSearchParams替代。或者使用querystringify插件
1. parse 把key=value&key=value形式的字符串转为对象
const querystring = require('querystring')
var qs = 'x=3&y=4'
var parsed = querystring.parse(qs)
console.log(parsed)
//{
// x: '3',
// y: '4'
//}
2. stringify 把对象转为key=value&key=value形式的字符串
const querystring = require('querystring')
var qo = {
x: 3,
y: 4
}
var parsed = querystring.stringify(qo)
console.log(parsed)
//x=3&y=4
3. escape/unescape
const querystring = require('querystring')
var str = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str)
console.log(escaped)
//id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttps%2Fwww.baidu.com
const querystring = require('querystring')
var str = 'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttps%2Fwww.baidu.com'
var unescaped = querystring.unescape(str)
console.log(unescaped)
//id=3&city=北京&url=https://www.baidu.com
05 http模块补充
(1)接口:jsonp
//后端代码
const http = require('http')
const url = require('url')
const app = http.createServer((req, res) => {
let urlObj = url.parse(req.url, true)
switch (urlObj.pathname) {
case '/api/user':
res.end(`${urlObj.query.callback}({"name": "gp145"})`)
break
default:
res.end('404.')
break
}
})
app.listen(8080, () => {
console.log('localhost:8080')
})
前端的html文件中
<script>
var oscript = document.createElement("script")
oscript.src="http://localhost:3000/api/aaa?callback=test"
document.body.appendChild(oscript)
function test(obj){
console.log(obj)
}
</script>
(2) 跨域:CORS
const http = require('http')
const url = require('url')
const querystring = require('querystring')
const app = http.createServer((req, res) => {
let data = ''
let urlObj = url.parse(req.url, true)
res.writeHead(200, {
'content-type': 'application/json;charset=utf-8',
//只要在后端配置下面的语句就可以实现CORS解决跨域
'Access-Control-Allow-Origin': '*'
})
req.on('data', (chunk) => {
data += chunk
})
req.on('end', () => {
responseResult(querystring.parse(data))
})
function responseResult(data) {
switch (urlObj.pathname) {
case '/api/login':
res.end(JSON.stringify({
message: data
}))
break
default:
res.end('404.')
break
}
}
})
app.listen(8080, () => {
console.log('localhost:8080')
})
(3) 模拟get
node也可以当作客户端,向服务器发信息,然后将信息传给前端。相当于做一个中间人。
var http = require('http')
var https = require('https')
// 1、接口 2、跨域
const server = http.createServer((request, response) => {
var url = request.url.substr(1)
var data = ''
response.writeHeader(200, {
'content-type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin': '*'
})
// http.get方法不需要在最后调用req.end()
https.get(`https://m.lagou.com/listmore.json${url}`, (res) => {
//data事件,当服务端接收到数据时触发
res.on('data', (chunk) => {
data += chunk
})
//end事件数据接收完时触发
res.on('end', () => {
response.end(JSON.stringify({