本文是在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中获取拼接之后的数据,绝对是完整数据
- data:根据数据大小,有可能多次执行,每次只能拿到部分数据,所以data事件每次执行,都需要先将数据拼接起来
- 向前端写入数据(只能写字符,数据中包含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流
- err:是否报错
- readFile(要操作的文件的地址名, 字符编码(可选,不写时默认buffer), 报错回调函数(err, data)(可选)
-
报错回调函数(可选)
- 回调函数的第一个参数如果有值,则说明报错,操作失败
- 回调函数的第一个参数如果有值,值为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环境下自带的下载工具
作用
安装模块(包),自动安装依赖,管理包(增,删,更新,项目所有包)
安装到全局环境
- 安装到电脑系统环境下
- 使用时在任何位置都可以使用
- 被全局安装的通常是:命令行工具,脚手架
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只能支持扁平的依赖(嵌套的依赖,由程序员自己解决)
安装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
使用
- 初始化项目
npm init
创建package.json - 通过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
- npm install body-parser
- let bodyParser = require(‘body-parser’)
- 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类型