node优势
- 性能优势:
nodejs PK php- 实际上nodejs性能比PHP高出86倍;
- nodejs 1s才能返回的东西,php需要86s;
- 假设PHP需要200台服务器,node需要3台;
- 实际上nodejs性能比PHP高出86倍;
- 跟前台js配合方便
- 比如gulp,bower,webpack等都需要和nodejs进行配合,实现编译部署等;
- nodejs便于前端学习;因为他提供了一个共js运行的服务器环境;
nodejs-服务器;
注意:
1)写node不能有中文;2)新版nodejs都支持es6;
- http的使用1:写服务器;
服务器本身提供很多服务,具体要哪个服务通过端口号查找;
比如:- 80 web
- 110 邮件
- 3306 数据库
当然,端口可以自己定;一个端口只能有一个服务
- req.url
文件操作fs 及 强行把异步编程同步;
- fs.readFile(文件名,回调函数); 异步操作,所以有回调函数;
- buffer原始的二进制数据;
- 如果想看到文字,用toString();
- fs.writeFile();
- fs.write(文件名,内容,function(err){})
- fs.mkdir('./album/aaa') 创建一个文件夹
- fs.stat()
- fs.readDir
- 异步 VS 同步;
- 异步:多个操作可以同步进行;前一次的操作,如果没完事,后一次也能开始;
- 同步:一次一个;前面没完,后面无法开始;
- fs配合http:server && file
- 把文件放www目录下;
- 注意异步问题;因为readFile是异步的;
如何接收前台的数据请求
- 前台向后台发送数据请求的方式:
- form表单:form必须有submit method action name value
- ajax:判断浏览器
- jsonp:三步
- 对后台来说,他们3都一样,因为后台只接受前端发送的数据;
- 前台<->后台 都是通过http请求;所以,后台只是接收了http请求;
- 对后台,请求的方式不同,过来的内容接收方式不同:
- get:数据在URL当中一起传输;
- post:数据在请求体中传输;(头-header;身体-header);
get请求:
- querystring();
querystring.parse() 方法能把一个 URL 查询字符串(str)解析成一个键值对的集合。
- url.parse()
- 总结:get数据解析
1. 自己切
2. querystring.parse('k=v&k=v');
3. url.parse(req.url,true);
post请求:后台接收post数据
- 浏览器和服务器之间通信,有个脑袋(<32k)和身子(1G);
- 注意:post数据比get大很多;意味着,处理方式有区别;
- post很大,把大块的数据,切成小块分好几次发送;
var str=''; var i=0; //data:每当有一段数据到达的时候,就会发送一次;(很多次) req.on('data',function (data) { console.log(`第${i++}次`) str+=data; }); //end-数据到达的时候(一次) req.on('end',function () { console.log(str); });
小实战:
- 完整的服务器:接收-读取文件-server本身;
- 用户注册登录;
- 做前后台数据交互,先得把"接口"定义出来;
- /user?act=reg&user=aaa&pass=123;
{"ok":false,"msg":"原因"} - /user?act=login&user=aaa&pass=123;
{"ok":true,"msg":"原因"}
后台接口自己随意定,只要跟前台商量了,统一即可;
- /user?act=reg&user=aaa&pass=123;
- 前台对后台的请求一般有两种:
- http://localhost:8080/1.html 对文件的访问
- http://localhost:8080/user?act=login... 对接口的访问;
- 做前后台数据交互,先得把"接口"定义出来;
模块化--
- 系统模块:http,querystring,url,fs;
- 自定义模块:
- 模块管理(包管理器)
自定义模块
- 自定义模块
- require 引入其他模块
- exports "单独"对外输出
- module.exports "批量"对外输出
module.exports===exports =>true;
注意:
nodejs中不存在全局变量;
1)引入自定义模块必须加./
2)如果想对外输出对象,必须exports:
优点:
- 避免全局变量
- 按需导出
- 模块:就是文件和文件之间的关系;
- npm:Nodejs package Manager(Nodejs包管理器)
- 作用:
- 他提供了统一的下载途径;类似于360;
- 自动下载依赖;
- 使用:安装的时候,注意所在目录;
- 下载 npm install xxxx
- 卸载 npm uninstall xxx
- node_modules:存放模块使用;
不仅能放别人的,也可以放自己;但是建议不要混搭; - require的查找顺序
如果有"./"从当前目录找;
如果没有"./",先从系统模块找,再从"node_modules"找,没有就报错; - 我们的依赖包,可能随时更新,我们永远想保持更新,或者保持某一个版本
- 项目越来越大;我们可以用package.json来管理依赖;
- dependencies:项目依赖
- devDependencies:开发时候的依赖;
- 作用:
请求静态资源,注意mime类型;
路径
- require()中的路径,从当前这个js文件出发,找到别人;
- require()哪个文件的时候,就会执行哪个文件;
- fs等其他的模块用到路径的时候,都是相对于cmd命令光标所在的位置;所以,如果层级嵌套的画,建议用绝对路径__dirname;
post请求之文件上传
- formidable模块;- 读文档
- fs.rename改名;
ejs模版
- 后台模板;著名的有两个,ejs,jade;
- 需要安装包:ejs;
- 两个例子:1)直接替换字符串 2)替换页面模板并展示;
- ejs花式模版
<%for(var i=0; i
- 我爱吃<%= news[i]%>
<%}%>
原生node的问题
- 呈递静态页面不方便;需要处理每个http请求;
- 路由处理代码不直观清晰,需要进行很多判断
- 不能集中精力写业务,需考虑很多其他的东西;
express的本质
- 首先,明白express是非破坏式的;比如req和res的功能改进,res.write和end都有,但是多了res.send();
send(字符串和对象都ok)而res.write(string||buffer); - express三步走:
- 创建服务
- 监听
- 处理请求
express整体感知
- express三大能力:
- 强大的路由能力;
- 静态资源
- 模版引擎的配合
路由能力:接收请求方式;常用的有: get()/post()/use()
- 这里的网址不分大小写
- RESTful路由设计
一个路径 "/student",http的method不同,他的功能就不同- get 读取学生信息
- add 添加学生信息
- delete 删除学生信息;
- 路由的基本使用
app.get('/haha',function (req,res) {
res.send('这是hahaa')
});
//正则路由,未知不分用()分组,通过req.params[0]来获取对应的参数;
app.get(/\/student\/(\d{4})/,function (req,res) {
console.log(req.params)
res.send('学生信息,学号:'+req.params[0])
});
//express自带的具有正则功能的路由,未定的用:代表,通过req.params.xxx获取参数;
app.get("/teacher/:gonghao",function (req,res) {
console.log(req.params)
res.send('老师信息,工号:'+req.params.gonghao);
});
//某个人的某个id
app.get("/:username/:id",function (req,res) {
console.log(req.params)
res.send('老师信息,工号:'+req.params.gonghao);
});
静态资源
//当访问/的时候,可以不写路由,直接调用静态文件的index.html;
app.use(express.static('./public'));
模版引擎的配合
//1:设置渲染的模版引擎为ejs;
app.set('view engine','ejs');
//2:views目录,是默认的,所以渲染模版的时候,可以省略相对路径;
app.get('/haha',function (req,res) {
res.render('haha',{
news:['原生javascript','node','html5+css3']
})
});
express的get和post请求
- get请求的参数在URL中,原生node,需要使用URL模块来识别参数字符串;在express中,直接用req.query对象即可;
- post请求在express中不能直接获得,必须使用body-parser模块,才能用req.body得到参数,但如果表单中含有文件上传,那么还需要使用formidable模块;
中间件 middleware
- 路由get,post这些东西;就是中间件,中间件讲究顺序,匹配上第一个之后,就不会往后匹配了;next函数才能够继续往后匹配;所以,get和post对路由卡的很死;
- 解决方式1:写路由的时候,具体的往上写,抽象的往下写;
app.get('/admin/login',function (req,res,next) {
console.log('2');
res.send('管理员登录');
next();
});
app.get('/:username/:id',function (req,res,next) {
console.log('1');
res.send('管理员登录'+req.params.username)
});
- 解决方式2:条件判断
app.get('/:username/:id',function (req,res,next) {
var username=req.params.username;
//检索数据库,如果username不存在,那么next();
if(username){
console.log('1');
res.send('用户信息')
}else{
next();
}
});
app.get('/admin/login',function (req,res,next) {
console.log('2');
res.send('管理员登录');
next();
});
- app.use()也是一个中间件,与get和post不同的是,他的网址不是精确匹配的,而是能够有小文件夹拓展的;如下;
- 当你不写路径的时候,就相当于'/',即相当于所有网址;
- 所以,一般用use当接盘侠;
// GET 'http://www.example.com/admin/new'
app.use('/admin',function (req,res) {
console.log(req.originalUrl); // '/admin/new'
console.log(req.baseUrl); // '/admin'
console.log(req.path);// '/new'
res.send('你好')
});
render和send的区别:
- 渲染内容用res.render()比如,渲染ejs模版内容;默认渲染的都是views文件夹中的;可以省略'./views/'直接写入文件的具体名称:
res.render('index.ejs',{news:[1,2,3]})
- 快速测试页面,使用res.send();
- 如果想使用不同的状态码
res.status(404).send('we cannot find that!')
; - 如果想使用不同的Content-Type,可以:
res.set('Content-Type':'text/html')
;
后台MVC小项目:我的影集
- M:数据models; V:视图views;C:控制器controller(router);
- 指按模块来开发,不要放在同一个文件夹中;减少彼此之间的耦合;
- 基本架构:遵循mvc的思想,创建如下目录:
models 模型:具体的业务都在这里面;所以,真正在干活的都是models
views 视图
controller (router)函数的罗列;
public 静态资源文件夹
uploads 下载内容
app.js 主入口文件
- 路由的设计,都在controller
- 初步框架如下
const express=require('express');
const bodyparser=require('body-parser');
const router=require('./controller');//自定义路由模块
const app=express();
app.listen(8080);
//设置模版引擎
app.set('view engine','ejs');
//设置路由中间件;
//静态页面
app.use(express.static('./public'));
app.use(express.static('./uploads'));//uploads被静态了
//各种请求
//首页
app.get('/',router.showIndex);
app.get('/:album',router.showAlbum);
app.get('/up',router.showUp);
app.post('/up',router.dopost);
//最后的中间件;
app.use((req,res)=>{
res.render('404');
});
- node中全是回调函数,所以我们自己封装的函数中如果有异步,比如I/O,那么就得用回调函数的方法封装;
express中的cookie和session
cookie:
- 产生的原因:
- cookie:保存数据,保存在浏览器端;在浏览器或客户端保存一些数据;并且每次向服务器发送请求的时候,都会带过来;
- 缺点:但凡保存在客户端的东西,都不可信,因为用户可以很方便的修改;
- cookie:大小有限;只有4K;
- cookie两件事:
- 设置cookie
- 读取cookie:读取需要用到cookie-parser
- cookie可用签名signed做到防止篡改
app.use(cookieParser('sdkfjsdfsf'));//是为了读取cookie;
app.get('/',function (req,res) {
//种个cookie
req.secret='sdkfjsdfsf';//签名后的cookie
res.cookie('username','leiei',{
signed:true
})
console.log(req.signedCookies) //签名后的cookie
res.send(req.cookies);//存没有签名的cookie
});
- cookie总结:
cookie的空间非常小。必须省着用;
-
cookie安全性差;
- 校验cookie是否被篡改过;(用signed)
-
添加和读取,删除cookie
- 添加:res.cookie(名字,值,{
path:'/aaa'
maxAge:毫秒
signed:是否需要签名 true/false //能不签就不签;
(signed必须配合secret)
}) - 读取,用中间件cookie-parser
app.use(cookieParser('密钥')) app.get('/',function(){ req.cookies//未签名版 req.signedCookies //签名版 )
- 删除cookie:
res.clearCookie('user');
- 添加:res.cookie(名字,值,{
密钥四步走:secret ; app.use(cookieParser('密钥')) ; signed:true ; req.signedCookies;
session
session:保存数据,保存在服务器;
session:只要服务器有地就存;大小无限了;
-
session不可能独立存在,有cookie才有session;
- session原理:第一次浏览器访问服务器,cookie为空;服务器给该用户种个cookie{sessionid:xx};
- cookie中会有session的id;服务器利用session的id,找到session相关的文件;
- 隐患:session劫持
- 解决措施:1)更换session; 2)cookie加密;
-
session:
- 读取session:cookie-session
- 使用方式;
var ary=[]; for(var i=0; i<1000000; i++){ ary.push('sig_'+Math.random()) } app.use(cookieParser()); app.use(cookieSession({ name:'lei',//更改session的id的名字; keys:ary, maxAge:2*3600*1000//2小时 })); app.get('/',function (req,res) { if(req.session['count']==null){ req.session['count']=1; }else{ req.session['count']++; } console.log(req.session); res.send(req.session['count'].toString()) })
- 获取:req.session;
- 删除:delete req.session;
-
session小总结:
- session依赖cookie;所以cookie-session必须先引入cookie-parser;
app.use(cookieParser()); app.use(cookieSession({ keys: }));