1 项目需求:
- 需要自己创建一个服务器,来渲染我们自己的静态页面;
2 node自带的运行环境repl
- r:read读; e:eval运行; p:print打印; l:loop循环;
- cmd命令:
node
;终止环境:ctrl+c
;
3 path模块
- path.extname():用来拿后缀名的;
index.html
->.html
; - path.join("/foo/aaa","index.html")->
/foo/aaa/index.html
;用来拼接两个地址,通过"/"来拼接; - path.normalize("/foo/aa/..///cc/index.html")->
/foo/cc/index.html
;用来使错误的地址变正常化; - path.parse():可以把路径地址解析成对象;
- 通过obj.ext来获取后缀名;
- 通过obj.name来获取文件名;
path.parse('C:\\path\\dir\\file.txt');
// Returns:
// { root: 'C:\\',
// dir: 'C:\\path\\dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
- path.resolve():相当于一大堆的cd,不停的cd;把路径解析成一个绝对路径;
4 自己写的静态资源服务器
- 思路:
- 将项目文件(包括html,css,js等)放在一个public文件夹中,相当于服务器中的内存磁盘,储存文件;
- 通过地址栏向服务器发送请求;如:一个index.html文件中会引入css文件和js文件,在打开index.html后,会再发送多个请求;
- 一个页面在浏览器中的渲染,与代码
fs.writeHeader(200,{"Content-Type":"xxx"})
中的"Content-Type"有关;所以需要根据不同的文件来设置不同的格式; - 获取请求地址,拿到地址中的pathname,通过path模块中的
var extname=path.extname(pathname)
来获取文件名的后缀,通过后缀来确定不同的类型; - 利用mime模块,通过代码
mime.getType(extname)
来识别不同的扩展名后缀,然后输出对应的类型;赋给Content-Type
;
- 知识点:
- 此处public文件夹,代表的就是一个磁盘空间,指的是服务器的内存,当浏览器发送请求后,服务器会向内存空间中查找数据,然后响应回浏览器,浏览器再渲染获取的数据,显示数据;
- 前后台交互的过程:
- 浏览器中地址栏中输入地址,向后台发送请求;
- 后台服务器拿到地址在自己的内存空间中查找数据,然后将数据响应给浏览器;
- 浏览器拿到数据后通过数据的类型,来在页面上进行渲染;
- 浏览器在渲染页面时,是根据Content-Type的类型来渲染的,所以不同的文件需要设置不同的类型,才能正常在浏览器中显示;
- json文件里面的数据为json格式的对象,通过
fs.readFile
获取的data数据为object对象; - 在node.js中异步问题会很多,通过回调函数callback来获取函数中的返回值;
-
./public
中的./
拿到的是根目录day3; - mime模块,通过require引入,在node_modules中查找使用;
- 代码:
- 地址栏地址:
localhost:8080/index.html
; - 服务器代码:
const http=require("http"); const fs=require("fs");//文件系统模块 const url=require("url"); const path=require("path"); const mime=require("mime"); //创建服务器 const server=http.createServer(function (req,res) { var obj=url.parse(req.url,true);//通过url模块,获取地址对象; var pathname=obj.pathname;//通过对象中的pathname属性,获取地址; var extname=path.extname(pathname);//通过path模块中的excname拿到扩展名后缀; var path1="./public/weijinsuo"+pathname;//给获取的地址拼接相对路径,使其能获取到其文件; fs.readFile(path1,function (err,data) { if(err){ res.end("404"); } res.writeHeader(200,{"Content-Type":mime.getType(extname)}); res.end(data); }) }); //监听 server.listen(8080);
- 地址栏地址:
5 表单上载文件
- 第一步:在自己服务器中打开表单,渲染表单
- 通过
fs.readFile()
读取文件地址,打开表单文件,然后在页面上渲染呈现; - 表单文件中的action地址,在自己的服务器中打开后,提交后,会直接在此环境中打开,所以在action中只设置
/upload
,相当于http://localhost:8080/upload
,如果在webstorm的服务器中直接提交表单,是不能提交成功的,因为在webstorm中打开的是自己的服务器,端口号为63342,提交后,默认在63342端口号下发送请求,是不能发送到自己的服务器的;所以需要注意表单action的设置;
- 通过
- 第二步:表单提交
- 通过file表单,选择文件,通过submit提交按钮,发送请求;
- 通过formidable模块来接收上传的文件
- 引入模块:
const formidable=require("formidable");
- 新建一个对象:
var form=new formidable.IncomingForm();
这一步必须写在请求体内部,不能写在外部,否则会出错; - 设置图片上传后的存储路径:
form.uploadDir="./uploads/";
- 修改文件的名称,否则不能打开;
- 通过
form.parse(req,(err,fields,files)=>{})
来获取上传文件的详细信息; - fields为form表单提交的参数对象,如:
{ user: 'guomushan', password: '111111' }
; - files为一个对象,对象中属性名为表单file的name值,属性值为一个对象;
- 属性值对象中通过path可以拿到图片的地址和名称;如:
var oldpath=files.tupian.path;
- 属性值对象中通过name可以拿到图片在上传之前的文件名;如:
var name1=files.tupian.name;
- 修改文件的名称使用fs文件系统中的
fs.rename(oldpath,newpath,callback)
; - newpath包括:文件的路径+源文件名的名字+时间戳+随机数+源文件的后缀名;
- 涉及到的知识点:1)利用path模块中
var obj=path.parse(name1)
来获取一个对象;obj.ext
获取name1的后缀;obj.name
获取name1的文件名;2)利用silly_datetime模块来获取时间戳;防止名字重复;3)利用Math获取随机数;
- 涉及到的知识点:1)利用path模块中
- 通过
- 引入模块:
- formidable模块
- 安装
npm install formidable --save-dev
; - 引入模块:
const formidable=require("formidable");
- 新建一个form对象:
var form=new formidable.IncomingForm();
- 设置文件上传地址:
form.uploadDir="./uploads/";
- 解析上传的文件:
form.parse(req,(err,fields,files)=>{})
- 安装
- 日期模块silly-datetime
- 安装
npm install silly-datetimt --save-dev
- 引入模块:
const sd=require("silly-datetime");
- 获取时间戳:
var getDate=sd.format(new Date(), 'YYYYMMDDHHmmss');
- 安装
- fs文件系统
-
fs.rename(oldPath,newPath,callback)
:异步地将oldPath重命名为newPath。如果newPath已存在,则覆盖文件;callback 只有一个参数 err; - 其中newPath由相对地址和文件名组成;
-
- 表单提交的两种数据
- 小型数据:设置
enctype="application/x-www-form-urlencoded"
- 大型数据:设置
enctype="multipart/form-data"
,如音频,视频,图片,word文档等;
- 小型数据:设置
- 代码:
- html表单代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>form表单提交</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <!--enctype="multipart/form-data"用于上传大数据;--> <label for="user"> 用户名: <input type="text" name="user" id="user" value="guomushan"> </label> <label for="pass"> 密码: <input type="text" name="password" id="pass" value="111111"> </label><br/><br/> <input type="file" name="tupian" multiple><br/> <input type="submit" value="提交"> </form> </body> </html>
- js服务器代码:
const http=require("http"); const fs=require("fs"); const formidable=require("formidable"); const path=require("path"); const sd=require("silly-datetime"); http.createServer((req,res)=>{ //1.渲染:读取www/form1.html的文件,并且通过浏览器渲染; fs.readFile("./www/form1.html",(err,data)=>{ if(err){ res.end("文件获取失败") } res.end(data); }); //2.提交数据:前端向后台通过post提交数据; if(req.url==="/upload" && req.method.toLowerCase()==="post"){ //验证请求地址和请求方式 //1.创建一个form对象; var form=new formidable.IncomingForm(); //2.图片上传后的存储路径; form.uploadDir="./uploads/"; //3.解析上传的文件; form.parse(req,(err,fields,files)=>{ //fields:{name:value}的文本域,表单数据; files:关于你所上传的这个文件的详细信息; /*console.log(fields);//打印结果:{ user: 'guomushan', password: '111111' } console.log(files);//打印结果为一个对象,属性名为表单file中的name值;*/ //重新命名传上来的文件名字; var oldpath=files.tupian.path; var getDate=sd.format(new Date(), 'YYYYMMDDHHmmss'); var name1=files.tupian.name; var newpath=form.uploadDir+path.parse(name1).name+getDate+Math.floor(Math.random()*1000)+path.parse(name1).ext; fs.rename(oldpath,newpath,(err)=>{ if(err){ res.end("名字更换失败"); return; } }) }) } }).listen(8080);
6 ejs模板
- 定义:嵌入式JavaScript模板,与node和express配合,可以渲染页面;
- ejs文件中:
- 通过
<% xxx%>
来承接上下文; - 通过
<%= xxx%>
来赋值; - 通过
<% include include/xxx.ejs%>
引入views文件夹下的include文件夹中的xxx.ejs文件; - ejs代码:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>我使用的手机是xiaomi<%= n%></h1> <h3>你爱吃什么水果?</h3> <ul> <% for(var i=0; i<fruits.length; i++){%> <li>我最爱吃<%= fruits[i]%></li> <%}%> </ul> </body> </html>
- 通过
- 配合node和express使用:
- 通过express创建服务器;
const express=require("express");//引入模块; const app=express();//创建服务器; app.listen(8080);//监听端口号
- 设置默认模板引擎:
app.set("view engine","ejs")
- 当请求某个地址的时候,开始渲染页面;
- 通过app.get()发送请求;
- 其中
/mei
指的是地址栏中的请求地址localhost:8080/mei
; -
res.render()
指的是响应渲染页面;- "index":指的是views文件夹下的index.ejs文件;在express中默认在views目录下查找文件;可省略ejs后缀;完整写法为:
./views/index.ejs
; - "{n:8}":指的是传给ejs文件中的参数;
app.get("/mei",function(req,res){ res.render("index",{n:8}) })
- "index":指的是views文件夹下的index.ejs文件;在express中默认在views目录下查找文件;可省略ejs后缀;完整写法为:
- 知识点:
- 注意:虽然express中可以不用引入ejs模块,但是我们必须给当前项目下载这个ejs模块;否则,会报错;
- for循环,条件判断语句等直接用
<% xxx%>
;如果具体赋值,必须要等号:<%= xxx%>
; - views是express中默认存储模板的地方;所以,可以省略路径;
- 直接渲染页面:
app.use(express.static("./public/weijinsuo"));//直接在页面中渲染index.html文件;
;
- ejs模板与express服务器配合代码:
- ejs模板代码:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>你喜欢什么运动? 为什么?</h1> <ul> <% for(var i=0; i<animate.length; i++){%> <li>我喜欢<%= animate[i].name%>; 因为<%= animate[i].dec%></li> <%}%> </ul> </body> </html>
- express服务器代码:
const express=require("express"); const app=express();//创建服务器; app.listen(8080); //设置模板引擎 app.set("view engine","ejs"); //get请求:当请求/的时候,默认打开views目录下的pph.ejs文件; app.get("/",function (req,res) { res.render("pph",{ n:8, fruits:["苹果","草莓","西瓜","芒果","榴莲"] }); }); //get请求:当请求/animation的时候,默认打开views目录下的yundong.ejs文件; app.get("/animation",function (req,res) { var animate=[ {name:"篮球",dec:"美好"}, {name:"篮球1",dec:"美好1"}, {name:"篮球2",dec:"美好3"}, {name:"篮球3",dec:"美好2"}, {name:"篮球4",dec:"美好3"}, ]; res.render("yundong",{ animate//es6中属性名和属性值相同,可写一个; }) });
7 express框架
- express的本质
- express是非破坏性的;保留res.write和end;但是多个res.send();
- res.write():只能写string和buffer文件;
- res.send():能写字符串和对象;
- express三步走
- 创建服务器;
- 监听端口号;
- 处理请求;
- express三大能力
- 强大的路由能力;
- 静态资源;
- 直接渲染页面:
app.use(express.static("./public/weijinsuo"));//直接在页面中渲染index.html文件;
; - 当访问
/
的时候,可以不写路由;直接写静态地址;
- 直接渲染页面:
- 模板引擎的配合;
//1:设置渲染的模版引擎为ejs; app.set('view engine','ejs'); //2:views目录,是默认的,所以渲染模版的时候,可以省略相对路径; app.get('/haha',function (req,res) { res.render('haha',{ news:['原生javascript','node','html5+css3'] }) });
- 路由的能力:
- 接受请求的方式:常用有get(),post(),use();
- get和post对路由地址要求很严格;
- use对路由地址要求不严格;可以扩充地址;
//1.get和post请求,路由地址很严格,不能扩充;如下代码中地址栏地址必须是localhost:8080/;localhost:8080/index.html都不能响应页面; app.post("/",function (req, res) { res.send("ok") }); //2.use请求,路由地址不严格,可以扩充地址;"localhost:8080/index.html"和"localhost:8080/index.html/djsd"均可以响应页面; app.use("/index.html",function (req,res) { res.send("dd") });
- express与正则的配合
- 通过正则表达式来设置一类地址;通过
req.params
来获取小分组对象; - 注意:正则表达式中"/"需要转义;
app.get(/^\/student\/(\d{6})(\d{1,2})$/,function (req,res) { console.log(req.params);//拿到的是小分组组成的对象,第一个小分组属性名为0;依次类推;{ '0': '123456', '1': '14' } res.send("学生的学号是:"+req.params[0]+"年龄是:"+req.params[1]); });
- 通过正则表达式来设置一类地址;通过
- express自带的正则功能
- express自带的具有正则功能的路由,未定的用:代表,通过req.params.xxx获取参数;
//地址栏中输入:localhost:8080/teacher/123456; app.get("/teacher/:gonghao",function (req,res) { res.send("老师的工号是:"+req.params.gonghao);//显示的结果:老师的工号是123456; });
- 两个正则同时存在时,会执行严格匹配的代码;
//地址栏:localhost:8080/teacher/123456 //代码会执行第一个代码,不会执行第二个代码;因为第一个代码为严格匹配; app.get("/teacher/:gonghao",function (req,res,next) { res.send("老师的工号是:"+req.params.gonghao); }); app.get("/:name/:hao",function (req, res) { res.send(req.params.name+"的学号是:"+req.params.hao); });
- 两个正则同时存在时,如果想让其执行完严格匹配的代码,继续执行不严格匹配的代码;可以添加next();
app.get("/teacher/:gonghao",function (req,res,next) { console.log(1) next(); }); app.get("/:name/:hao",function (req, res) { console.log(2) res.send(req.params.name+"的学号是:"+req.params.hao); }); //显示的结果:teacher的学号是222222; //在服务器中打印1和2;