Node.js学习第三天笔记

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模块来接收上传的文件
      1. 引入模块:const formidable=require("formidable");
      2. 新建一个对象:var form=new formidable.IncomingForm(); 这一步必须写在请求体内部,不能写在外部,否则会出错;
      3. 设置图片上传后的存储路径:form.uploadDir="./uploads/";
      4. 修改文件的名称,否则不能打开;
        • 通过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获取随机数;
  • 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})
         })
        
  • 知识点:
    • 注意:虽然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三步走
    1. 创建服务器;
    2. 监听端口号;
    3. 处理请求;
  • express三大能力
    1. 强大的路由能力;
    2. 静态资源;
      • 直接渲染页面:app.use(express.static("./public/weijinsuo"));//直接在页面中渲染index.html文件;;
      • 当访问/的时候,可以不写路由;直接写静态地址;
    3. 模板引擎的配合;
       //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;
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容