1 項(xiàng)目需求:
- 需要自己創(chuàng)建一個(gè)服務(wù)器印机,來(lái)渲染我們自己的靜態(tài)頁(yè)面咏雌;
2 node自帶的運(yùn)行環(huán)境repl
- r:read讀; e:eval運(yùn)行; p:print打印; l:loop循環(huán);
- cmd命令:
node
;終止環(huán)境:ctrl+c
;
3 path模塊
- path.extname():用來(lái)拿后綴名的;
index.html
->.html
; - path.join("/foo/aaa","index.html")->
/foo/aaa/index.html
;用來(lái)拼接兩個(gè)地址另凌,通過(guò)"/"來(lái)拼接惰许; - path.normalize("/foo/aa/..///cc/index.html")->
/foo/cc/index.html
;用來(lái)使錯(cuò)誤的地址變正常化; - path.parse():可以把路徑地址解析成對(duì)象供璧;
- 通過(guò)obj.ext來(lái)獲取后綴名存崖;
- 通過(guò)obj.name來(lái)獲取文件名;
path.parse('C:\\path\\dir\\file.txt');
// Returns:
// { root: 'C:\\',
// dir: 'C:\\path\\dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
- path.resolve():相當(dāng)于一大堆的cd睡毒,不停的cd;把路徑解析成一個(gè)絕對(duì)路徑来惧;
4 自己寫(xiě)的靜態(tài)資源服務(wù)器
- 思路:
- 將項(xiàng)目文件(包括html,css演顾,js等)放在一個(gè)public文件夾中供搀,相當(dāng)于服務(wù)器中的內(nèi)存磁盤(pán),儲(chǔ)存文件钠至;
- 通過(guò)地址欄向服務(wù)器發(fā)送請(qǐng)求葛虐;如:一個(gè)index.html文件中會(huì)引入css文件和js文件,在打開(kāi)index.html后棉钧,會(huì)再發(fā)送多個(gè)請(qǐng)求屿脐;
- 一個(gè)頁(yè)面在瀏覽器中的渲染,與代碼
fs.writeHeader(200,{"Content-Type":"xxx"})
中的"Content-Type"有關(guān);所以需要根據(jù)不同的文件來(lái)設(shè)置不同的格式的诵; - 獲取請(qǐng)求地址万栅,拿到地址中的pathname,通過(guò)path模塊中的
var extname=path.extname(pathname)
來(lái)獲取文件名的后綴西疤,通過(guò)后綴來(lái)確定不同的類(lèi)型烦粒; - 利用mime模塊,通過(guò)代碼
mime.getType(extname)
來(lái)識(shí)別不同的擴(kuò)展名后綴代赁,然后輸出對(duì)應(yīng)的類(lèi)型扰她;賦給Content-Type
;
- 知識(shí)點(diǎn):
- 此處public文件夾管跺,代表的就是一個(gè)磁盤(pán)空間义黎,指的是服務(wù)器的內(nèi)存,當(dāng)瀏覽器發(fā)送請(qǐng)求后豁跑,服務(wù)器會(huì)向內(nèi)存空間中查找數(shù)據(jù)廉涕,然后響應(yīng)回瀏覽器,瀏覽器再渲染獲取的數(shù)據(jù)艇拍,顯示數(shù)據(jù)狐蜕;
- 前后臺(tái)交互的過(guò)程:
- 瀏覽器中地址欄中輸入地址,向后臺(tái)發(fā)送請(qǐng)求卸夕;
- 后臺(tái)服務(wù)器拿到地址在自己的內(nèi)存空間中查找數(shù)據(jù)层释,然后將數(shù)據(jù)響應(yīng)給瀏覽器;
- 瀏覽器拿到數(shù)據(jù)后通過(guò)數(shù)據(jù)的類(lèi)型快集,來(lái)在頁(yè)面上進(jìn)行渲染贡羔;
- 瀏覽器在渲染頁(yè)面時(shí),是根據(jù)Content-Type的類(lèi)型來(lái)渲染的个初,所以不同的文件需要設(shè)置不同的類(lèi)型乖寒,才能正常在瀏覽器中顯示;
- json文件里面的數(shù)據(jù)為json格式的對(duì)象院溺,通過(guò)
fs.readFile
獲取的data數(shù)據(jù)為object對(duì)象楣嘁; - 在node.js中異步問(wèn)題會(huì)很多,通過(guò)回調(diào)函數(shù)callback來(lái)獲取函數(shù)中的返回值珍逸;
-
./public
中的./
拿到的是根目錄day3; - mime模塊逐虚,通過(guò)require引入,在node_modules中查找使用谆膳;
- 代碼:
- 地址欄地址:
localhost:8080/index.html
; - 服務(wù)器代碼:
const http=require("http"); const fs=require("fs");//文件系統(tǒng)模塊 const url=require("url"); const path=require("path"); const mime=require("mime"); //創(chuàng)建服務(wù)器 const server=http.createServer(function (req,res) { var obj=url.parse(req.url,true);//通過(guò)url模塊叭爱,獲取地址對(duì)象; var pathname=obj.pathname;//通過(guò)對(duì)象中的pathname屬性漱病,獲取地址涤伐; var extname=path.extname(pathname);//通過(guò)path模塊中的excname拿到擴(kuò)展名后綴馒胆; var path1="./public/weijinsuo"+pathname;//給獲取的地址拼接相對(duì)路徑,使其能獲取到其文件凝果; fs.readFile(path1,function (err,data) { if(err){ res.end("404"); } res.writeHeader(200,{"Content-Type":mime.getType(extname)}); res.end(data); }) }); //監(jiān)聽(tīng) server.listen(8080);
- 地址欄地址:
5 表單上載文件
- 第一步:在自己服務(wù)器中打開(kāi)表單祝迂,渲染表單
- 通過(guò)
fs.readFile()
讀取文件地址,打開(kāi)表單文件器净,然后在頁(yè)面上渲染呈現(xiàn)型雳; - 表單文件中的action地址,在自己的服務(wù)器中打開(kāi)后山害,提交后纠俭,會(huì)直接在此環(huán)境中打開(kāi),所以在action中只設(shè)置
/upload
浪慌,相當(dāng)于http://localhost:8080/upload
冤荆,如果在webstorm的服務(wù)器中直接提交表單,是不能提交成功的权纤,因?yàn)樵趙ebstorm中打開(kāi)的是自己的服務(wù)器钓简,端口號(hào)為63342,提交后汹想,默認(rèn)在63342端口號(hào)下發(fā)送請(qǐng)求外邓,是不能發(fā)送到自己的服務(wù)器的;所以需要注意表單action的設(shè)置古掏;
- 通過(guò)
- 第二步:表單提交
- 通過(guò)file表單损话,選擇文件,通過(guò)submit提交按鈕槽唾,發(fā)送請(qǐng)求丧枪;
- 通過(guò)formidable模塊來(lái)接收上傳的文件
- 引入模塊:
const formidable=require("formidable");
- 新建一個(gè)對(duì)象:
var form=new formidable.IncomingForm();
這一步必須寫(xiě)在請(qǐng)求體內(nèi)部,不能寫(xiě)在外部庞萍,否則會(huì)出錯(cuò)拧烦; - 設(shè)置圖片上傳后的存儲(chǔ)路徑:
form.uploadDir="./uploads/";
- 修改文件的名稱,否則不能打開(kāi)挂绰;
- 通過(guò)
form.parse(req,(err,fields,files)=>{})
來(lái)獲取上傳文件的詳細(xì)信息屎篱; - fields為form表單提交的參數(shù)對(duì)象服赎,如:
{ user: 'guomushan', password: '111111' }
; - files為一個(gè)對(duì)象葵蒂,對(duì)象中屬性名為表單file的name值,屬性值為一個(gè)對(duì)象重虑;
- 屬性值對(duì)象中通過(guò)path可以拿到圖片的地址和名稱践付;如:
var oldpath=files.tupian.path;
- 屬性值對(duì)象中通過(guò)name可以拿到圖片在上傳之前的文件名;如:
var name1=files.tupian.name;
- 修改文件的名稱使用fs文件系統(tǒng)中的
fs.rename(oldpath,newpath,callback)
; - newpath包括:文件的路徑+源文件名的名字+時(shí)間戳+隨機(jī)數(shù)+源文件的后綴名缺厉;
- 涉及到的知識(shí)點(diǎn):1)利用path模塊中
var obj=path.parse(name1)
來(lái)獲取一個(gè)對(duì)象永高;obj.ext
獲取name1的后綴隧土;obj.name
獲取name1的文件名;2)利用silly_datetime模塊來(lái)獲取時(shí)間戳命爬;防止名字重復(fù)曹傀;3)利用Math獲取隨機(jī)數(shù);
- 涉及到的知識(shí)點(diǎn):1)利用path模塊中
- 通過(guò)
- 引入模塊:
- formidable模塊
- 安裝
npm install formidable --save-dev
饲宛; - 引入模塊:
const formidable=require("formidable");
- 新建一個(gè)form對(duì)象:
var form=new formidable.IncomingForm();
- 設(shè)置文件上傳地址:
form.uploadDir="./uploads/";
- 解析上傳的文件:
form.parse(req,(err,fields,files)=>{})
- 安裝
- 日期模塊silly-datetime
- 安裝
npm install silly-datetimt --save-dev
- 引入模塊:
const sd=require("silly-datetime");
- 獲取時(shí)間戳:
var getDate=sd.format(new Date(), 'YYYYMMDDHHmmss');
- 安裝
- fs文件系統(tǒng)
-
fs.rename(oldPath,newPath,callback)
:異步地將oldPath重命名為newPath皆愉。如果newPath已存在,則覆蓋文件;callback 只有一個(gè)參數(shù) err艇抠; - 其中newPath由相對(duì)地址和文件名組成幕庐;
-
- 表單提交的兩種數(shù)據(jù)
- 小型數(shù)據(jù):設(shè)置
enctype="application/x-www-form-urlencoded"
- 大型數(shù)據(jù):設(shè)置
enctype="multipart/form-data"
,如音頻家淤,視頻异剥,圖片,word文檔等絮重;
- 小型數(shù)據(jù):設(shè)置
- 代碼:
- 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"用于上傳大數(shù)據(jù)冤寿;--> <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服務(wù)器代碼:
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的文件,并且通過(guò)瀏覽器渲染绿鸣; fs.readFile("./www/form1.html",(err,data)=>{ if(err){ res.end("文件獲取失敗") } res.end(data); }); //2.提交數(shù)據(jù):前端向后臺(tái)通過(guò)post提交數(shù)據(jù)疚沐; if(req.url==="/upload" && req.method.toLowerCase()==="post"){ //驗(yàn)證請(qǐng)求地址和請(qǐng)求方式 //1.創(chuàng)建一個(gè)form對(duì)象; var form=new formidable.IncomingForm(); //2.圖片上傳后的存儲(chǔ)路徑潮模; form.uploadDir="./uploads/"; //3.解析上傳的文件亮蛔; form.parse(req,(err,fields,files)=>{ //fields:{name:value}的文本域,表單數(shù)據(jù)擎厢; files:關(guān)于你所上傳的這個(gè)文件的詳細(xì)信息究流; /*console.log(fields);//打印結(jié)果:{ user: 'guomushan', password: '111111' } console.log(files);//打印結(jié)果為一個(gè)對(duì)象,屬性名為表單file中的name值动遭;*/ //重新命名傳上來(lái)的文件名字芬探; 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配合厘惦,可以渲染頁(yè)面偷仿;
- ejs文件中:
- 通過(guò)
<% xxx%>
來(lái)承接上下文; - 通過(guò)
<%= xxx%>
來(lái)賦值宵蕉; - 通過(guò)
<% 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>我使用的手機(jī)是xiaomi<%= n%></h1> <h3>你愛(ài)吃什么水果?</h3> <ul> <% for(var i=0; i<fruits.length; i++){%> <li>我最?lèi)?ài)吃<%= fruits[i]%></li> <%}%> </ul> </body> </html>
- 通過(guò)
- 配合node和express使用:
- 通過(guò)express創(chuàng)建服務(wù)器;
const express=require("express");//引入模塊羡玛; const app=express();//創(chuàng)建服務(wù)器别智; app.listen(8080);//監(jiān)聽(tīng)端口號(hào)
- 設(shè)置默認(rèn)模板引擎:
app.set("view engine","ejs")
- 當(dāng)請(qǐng)求某個(gè)地址的時(shí)候,開(kāi)始渲染頁(yè)面稼稿;
- 通過(guò)app.get()發(fā)送請(qǐng)求薄榛;
- 其中
/mei
指的是地址欄中的請(qǐng)求地址localhost:8080/mei
讳窟; -
res.render()
指的是響應(yīng)渲染頁(yè)面;- "index":指的是views文件夾下的index.ejs文件敞恋;在express中默認(rèn)在views目錄下查找文件丽啡;可省略ejs后綴;完整寫(xiě)法為:
./views/index.ejs
硬猫; - "{n:8}":指的是傳給ejs文件中的參數(shù)碌上;
app.get("/mei",function(req,res){ res.render("index",{n:8}) })
- "index":指的是views文件夾下的index.ejs文件敞恋;在express中默認(rèn)在views目錄下查找文件丽啡;可省略ejs后綴;完整寫(xiě)法為:
- 知識(shí)點(diǎn):
- 注意:雖然express中可以不用引入ejs模塊,但是我們必須給當(dāng)前項(xiàng)目下載這個(gè)ejs模塊浦徊;否則馏予,會(huì)報(bào)錯(cuò);
- for循環(huán)盔性,條件判斷語(yǔ)句等直接用
<% xxx%>
;如果具體賦值霞丧,必須要等號(hào):<%= xxx%>
; - views是express中默認(rèn)存儲(chǔ)模板的地方冕香;所以蛹尝,可以省略路徑;
- 直接渲染頁(yè)面:
app.use(express.static("./public/weijinsuo"));//直接在頁(yè)面中渲染index.html文件悉尾;
;
- ejs模板與express服務(wù)器配合代碼:
- ejs模板代碼:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>你喜歡什么運(yùn)動(dòng)? 為什么?</h1> <ul> <% for(var i=0; i<animate.length; i++){%> <li>我喜歡<%= animate[i].name%>; 因?yàn)?lt;%= animate[i].dec%></li> <%}%> </ul> </body> </html>
- express服務(wù)器代碼:
const express=require("express"); const app=express();//創(chuàng)建服務(wù)器突那; app.listen(8080); //設(shè)置模板引擎 app.set("view engine","ejs"); //get請(qǐng)求:當(dāng)請(qǐng)求/的時(shí)候,默認(rèn)打開(kāi)views目錄下的pph.ejs文件构眯; app.get("/",function (req,res) { res.render("pph",{ n:8, fruits:["蘋(píng)果","草莓","西瓜","芒果","榴蓮"] }); }); //get請(qǐng)求:當(dāng)請(qǐng)求/animation的時(shí)候愕难,默認(rèn)打開(kāi)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中屬性名和屬性值相同惫霸,可寫(xiě)一個(gè)猫缭; }) });
7 express框架
- express的本質(zhì)
- express是非破壞性的;保留res.write和end壹店;但是多個(gè)res.send()猜丹;
- res.write():只能寫(xiě)string和buffer文件;
- res.send():能寫(xiě)字符串和對(duì)象硅卢;
- express三步走
- 創(chuàng)建服務(wù)器射窒;
- 監(jiān)聽(tīng)端口號(hào);
- 處理請(qǐng)求将塑;
- express三大能力
- 強(qiáng)大的路由能力脉顿;
- 靜態(tài)資源;
- 直接渲染頁(yè)面:
app.use(express.static("./public/weijinsuo"));//直接在頁(yè)面中渲染index.html文件抬旺;
; - 當(dāng)訪問(wèn)
/
的時(shí)候弊予,可以不寫(xiě)路由;直接寫(xiě)靜態(tài)地址祥楣;
- 直接渲染頁(yè)面:
- 模板引擎的配合开财;
//1:設(shè)置渲染的模版引擎為ejs; app.set('view engine','ejs'); //2:views目錄汉柒,是默認(rèn)的,所以渲染模版的時(shí)候责鳍,可以省略相對(duì)路徑碾褂; app.get('/haha',function (req,res) { res.render('haha',{ news:['原生javascript','node','html5+css3'] }) });
- 路由的能力:
- 接受請(qǐng)求的方式:常用有g(shù)et(),post(),use();
- get和post對(duì)路由地址要求很?chē)?yán)格;
- use對(duì)路由地址要求不嚴(yán)格历葛;可以擴(kuò)充地址正塌;
//1.get和post請(qǐng)求,路由地址很?chē)?yán)格恤溶,不能擴(kuò)充乓诽;如下代碼中地址欄地址必須是localhost:8080/;localhost:8080/index.html都不能響應(yīng)頁(yè)面; app.post("/",function (req, res) { res.send("ok") }); //2.use請(qǐng)求咒程,路由地址不嚴(yán)格鸠天,可以擴(kuò)充地址;"localhost:8080/index.html"和"localhost:8080/index.html/djsd"均可以響應(yīng)頁(yè)面帐姻; app.use("/index.html",function (req,res) { res.send("dd") });
- express與正則的配合
- 通過(guò)正則表達(dá)式來(lái)設(shè)置一類(lèi)地址稠集;通過(guò)
req.params
來(lái)獲取小分組對(duì)象; - 注意:正則表達(dá)式中"/"需要轉(zhuǎn)義饥瓷;
app.get(/^\/student\/(\d{6})(\d{1,2})$/,function (req,res) { console.log(req.params);//拿到的是小分組組成的對(duì)象剥纷,第一個(gè)小分組屬性名為0;依次類(lèi)推呢铆;{ '0': '123456', '1': '14' } res.send("學(xué)生的學(xué)號(hào)是:"+req.params[0]+"年齡是:"+req.params[1]); });
- 通過(guò)正則表達(dá)式來(lái)設(shè)置一類(lèi)地址稠集;通過(guò)
- express自帶的正則功能
- express自帶的具有正則功能的路由晦鞋,未定的用:代表,通過(guò)req.params.xxx獲取參數(shù)棺克;
//地址欄中輸入:localhost:8080/teacher/123456; app.get("/teacher/:gonghao",function (req,res) { res.send("老師的工號(hào)是:"+req.params.gonghao);//顯示的結(jié)果:老師的工號(hào)是123456鳖宾; });
- 兩個(gè)正則同時(shí)存在時(shí),會(huì)執(zhí)行嚴(yán)格匹配的代碼逆航;
//地址欄:localhost:8080/teacher/123456 //代碼會(huì)執(zhí)行第一個(gè)代碼鼎文,不會(huì)執(zhí)行第二個(gè)代碼;因?yàn)榈谝粋€(gè)代碼為嚴(yán)格匹配因俐; app.get("/teacher/:gonghao",function (req,res,next) { res.send("老師的工號(hào)是:"+req.params.gonghao); }); app.get("/:name/:hao",function (req, res) { res.send(req.params.name+"的學(xué)號(hào)是:"+req.params.hao); });
- 兩個(gè)正則同時(shí)存在時(shí)拇惋,如果想讓其執(zhí)行完嚴(yán)格匹配的代碼,繼續(xù)執(zhí)行不嚴(yán)格匹配的代碼抹剩;可以添加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+"的學(xué)號(hào)是:"+req.params.hao); }); //顯示的結(jié)果:teacher的學(xué)號(hào)是222222撑帖; //在服務(wù)器中打印1和2;